//! Shows how to render to a texture. Useful for mirrors, UI, or exporting images. use bevy::{ core_pipeline::clear_color::ClearColorConfig, prelude::*, render::{ camera::RenderTarget, render_resource::{ Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages, }, view::RenderLayers, }, }; fn main() { App::new() .add_plugins(DefaultPlugins) .add_startup_system(setup) .add_system(cube_rotator_system) .add_system(rotator_system) .run(); } // Marks the first pass cube (rendered to a texture.) #[derive(Component)] struct FirstPassCube; // Marks the main pass cube, to which the texture is applied. #[derive(Component)] struct MainPassCube; fn setup( mut commands: Commands, mut meshes: ResMut>, mut materials: ResMut>, mut images: ResMut>, ) { let size = Extent3d { width: 512, height: 512, ..default() }; // This is the texture that will be rendered to. let mut image = Image { texture_descriptor: TextureDescriptor { label: None, size, dimension: TextureDimension::D2, format: TextureFormat::Bgra8UnormSrgb, mip_level_count: 1, sample_count: 1, usage: TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST | TextureUsages::RENDER_ATTACHMENT, }, ..default() }; // fill image.data with zeroes image.resize(size); let image_handle = images.add(image); let cube_handle = meshes.add(Mesh::from(shape::Cube { size: 4.0 })); let cube_material_handle = materials.add(StandardMaterial { base_color: Color::rgb(0.8, 0.7, 0.6), reflectance: 0.02, unlit: false, ..default() }); // This specifies the layer used for the first pass, which will be attached to the first pass camera and cube. let first_pass_layer = RenderLayers::layer(1); // The cube that will be rendered to the texture. commands .spawn_bundle(PbrBundle { mesh: cube_handle, material: cube_material_handle, transform: Transform::from_translation(Vec3::new(0.0, 0.0, 1.0)), ..default() }) .insert(FirstPassCube) .insert(first_pass_layer); // Light // NOTE: Currently lights are shared between passes - see https://github.com/bevyengine/bevy/issues/3462 commands.spawn_bundle(PointLightBundle { transform: Transform::from_translation(Vec3::new(0.0, 0.0, 10.0)), ..default() }); commands .spawn_bundle(Camera3dBundle { camera_3d: Camera3d { clear_color: ClearColorConfig::Custom(Color::WHITE), ..default() }, camera: Camera { // render before the "main pass" camera priority: -1, target: RenderTarget::Image(image_handle.clone()), ..default() }, transform: Transform::from_translation(Vec3::new(0.0, 0.0, 15.0)) .looking_at(Vec3::default(), Vec3::Y), ..default() }) .insert(first_pass_layer); let cube_size = 4.0; let cube_handle = meshes.add(Mesh::from(shape::Box::new(cube_size, cube_size, cube_size))); // This material has the texture that has been rendered. let material_handle = materials.add(StandardMaterial { base_color_texture: Some(image_handle), reflectance: 0.02, unlit: false, ..default() }); // Main pass cube, with material containing the rendered first pass texture. commands .spawn_bundle(PbrBundle { mesh: cube_handle, material: material_handle, transform: Transform { translation: Vec3::new(0.0, 0.0, 1.5), rotation: Quat::from_rotation_x(-std::f32::consts::PI / 5.0), ..default() }, ..default() }) .insert(MainPassCube); // The main pass camera. commands.spawn_bundle(Camera3dBundle { transform: Transform::from_translation(Vec3::new(0.0, 0.0, 15.0)) .looking_at(Vec3::default(), Vec3::Y), ..default() }); } /// Rotates the inner cube (first pass) fn rotator_system(time: Res