render_to_texture/
render_to_texture.rs

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