docs/bevy/examples/3d/render_to_texture.rs 4 K · 123 lines · raw · history

1 //! Shows how to render to a texture. Useful for mirrors, UI, or exporting images.
2
3 use std::f32::consts::PI;
4
5 use bevy::camera::RenderTarget;
6 use bevy::{camera::visibility::RenderLayers, prelude::*, render::render_resource::TextureFormat};
7
8 fn main() {
9 App::new()
10 .add_plugins(DefaultPlugins)
11 .add_systems(Startup, setup)
12 .add_systems(Update, (cube_rotator_system, rotator_system))
13 .run();
14 }
15
16 // Marks the first pass cube (rendered to a texture.)
17 #[derive(Component)]
18 struct FirstPassCube;
19
20 // Marks the main pass cube, to which the texture is applied.
21 #[derive(Component)]
22 struct MainPassCube;
23
24 fn setup(
25 mut commands: Commands,
26 mut meshes: ResMut<Assets<Mesh>>,
27 mut materials: ResMut<Assets<StandardMaterial>>,
28 mut images: ResMut<Assets<Image>>,
29 ) {
30 // This is the texture that will be rendered to.
31 let image = Image::new_target_texture(
32 512,
33 512,
34 TextureFormat::Rgba8Unorm,
35 Some(TextureFormat::Rgba8UnormSrgb),
36 );
37
38 let image_handle = images.add(image);
39
40 let cube_handle = meshes.add(Cuboid::new(4.0, 4.0, 4.0));
41 let cube_material_handle = materials.add(StandardMaterial {
42 base_color: Color::srgb(0.8, 0.7, 0.6),
43 reflectance: 0.02,
44 unlit: false,
45 ..default()
46 });
47
48 // This specifies the layer used for the first pass, which will be attached to the first pass camera and cube.
49 let first_pass_layer = RenderLayers::layer(1);
50
51 // The cube that will be rendered to the texture.
52 commands.spawn((
53 Mesh3d(cube_handle),
54 MeshMaterial3d(cube_material_handle),
55 Transform::from_translation(Vec3::new(0.0, 0.0, 1.0)),
56 FirstPassCube,
57 first_pass_layer.clone(),
58 ));
59
60 // Light
61 // NOTE: we add the light to both layers so it affects both the rendered-to-texture cube, and the cube on which we display the texture
62 // Setting the layer to RenderLayers::layer(0) would cause the main view to be lit, but the rendered-to-texture cube to be unlit.
63 // Setting the layer to RenderLayers::layer(1) would cause the rendered-to-texture cube to be lit, but the main view to be unlit.
64 commands.spawn((
65 PointLight::default(),
66 Transform::from_translation(Vec3::new(0.0, 0.0, 10.0)),
67 RenderLayers::layer(0).with(1),
68 ));
69
70 commands.spawn((
71 Camera3d::default(),
72 Camera {
73 // render before the "main pass" camera
74 order: -1,
75 clear_color: Color::WHITE.into(),
76 ..default()
77 },
78 RenderTarget::Image(image_handle.clone().into()),
79 Transform::from_translation(Vec3::new(0.0, 0.0, 15.0)).looking_at(Vec3::ZERO, Vec3::Y),
80 first_pass_layer,
81 ));
82
83 let cube_size = 4.0;
84 let cube_handle = meshes.add(Cuboid::new(cube_size, cube_size, cube_size));
85
86 // This material has the texture that has been rendered.
87 let material_handle = materials.add(StandardMaterial {
88 base_color_texture: Some(image_handle),
89 reflectance: 0.02,
90 unlit: false,
91 ..default()
92 });
93
94 // Main pass cube, with material containing the rendered first pass texture.
95 commands.spawn((
96 Mesh3d(cube_handle),
97 MeshMaterial3d(material_handle),
98 Transform::from_xyz(0.0, 0.0, 1.5).with_rotation(Quat::from_rotation_x(-PI / 5.0)),
99 MainPassCube,
100 ));
101
102 // The main pass camera.
103 commands.spawn((
104 Camera3d::default(),
105 Transform::from_xyz(0.0, 0.0, 15.0).looking_at(Vec3::ZERO, Vec3::Y),
106 ));
107 }
108
109 /// Rotates the inner cube (first pass)
110 fn rotator_system(time: Res<Time>, mut query: Query<&mut Transform, With<FirstPassCube>>) {
111 for mut transform in &mut query {
112 transform.rotate_x(1.5 * time.delta_secs());
113 transform.rotate_z(1.3 * time.delta_secs());
114 }
115 }
116
117 /// Rotates the outer cube (main pass)
118 fn cube_rotator_system(time: Res<Time>, mut query: Query<&mut Transform, With<MainPassCube>>) {
119 for mut transform in &mut query {
120 transform.rotate_x(1.0 * time.delta_secs());
121 transform.rotate_y(0.7 * time.delta_secs());
122 }
123 }