Skip to main content

decal/
decal.rs

1//! Decal rendering.
2//! Note: On Wasm, this example only runs on WebGPU
3
4use bevy::{
5    camera_controller::free_camera::{FreeCamera, FreeCameraPlugin},
6    core_pipeline::prepass::DepthPrepass,
7    pbr::decal::{ForwardDecal, ForwardDecalMaterial, ForwardDecalMaterialExt},
8    prelude::*,
9};
10use chacha20::ChaCha8Rng;
11use rand::{RngExt, SeedableRng};
12
13fn main() {
14    App::new()
15        .add_plugins((DefaultPlugins, FreeCameraPlugin))
16        .add_systems(Startup, setup)
17        .run();
18}
19
20fn setup(
21    mut commands: Commands,
22    mut meshes: ResMut<Assets<Mesh>>,
23    mut standard_materials: ResMut<Assets<StandardMaterial>>,
24    mut decal_standard_materials: ResMut<Assets<ForwardDecalMaterial<StandardMaterial>>>,
25    asset_server: Res<AssetServer>,
26) {
27    // Spawn the forward decal
28    commands.spawn((
29        Name::new("Decal Blue"),
30        ForwardDecal,
31        MeshMaterial3d(decal_standard_materials.add(ForwardDecalMaterial {
32            base: StandardMaterial {
33                base_color_texture: Some(asset_server.load("textures/uv_checker_bw.png")),
34                base_color: Color::srgb(0.3, 0.5, 0.8),
35                alpha_mode: AlphaMode::Opaque,
36                depth_bias: 0.0,
37                ..default()
38            },
39            extension: ForwardDecalMaterialExt {
40                depth_fade_factor: 1.0,
41            },
42        })),
43        Transform::from_scale(Vec3::splat(4.0)).with_translation(Vec3::new(-1.0, 0.0, -1.0)),
44    ));
45
46    commands.spawn((
47        Name::new("Decal Green"),
48        ForwardDecal,
49        MeshMaterial3d(decal_standard_materials.add(ForwardDecalMaterial {
50            base: StandardMaterial {
51                base_color_texture: Some(asset_server.load("textures/uv_checker_bw.png")),
52                base_color: Color::srgba(0.0, 1.0, 0.0, 0.5),
53                alpha_mode: AlphaMode::Blend,
54                depth_bias: 2.0,
55                ..default()
56            },
57            extension: ForwardDecalMaterialExt {
58                depth_fade_factor: 1.0,
59            },
60        })),
61        Transform::from_scale(Vec3::splat(4.0)).with_translation(Vec3::new(1.0, 0.0, -1.0)),
62    ));
63
64    commands.spawn((
65        Name::new("Decal Red"),
66        ForwardDecal,
67        MeshMaterial3d(decal_standard_materials.add(ForwardDecalMaterial {
68            base: StandardMaterial {
69                base_color_texture: Some(asset_server.load("textures/uv_checker_bw.png")),
70                base_color: Color::srgba(1.0, 0.0, 0.0, 0.5),
71                alpha_mode: AlphaMode::Add,
72                depth_bias: 4.0,
73                ..default()
74            },
75            extension: ForwardDecalMaterialExt {
76                depth_fade_factor: 1.0,
77            },
78        })),
79        Transform::from_scale(Vec3::splat(4.0)).with_translation(Vec3::new(0.0, 0.0, 1.0)),
80    ));
81
82    commands.spawn((
83        Name::new("Camera"),
84        Camera3d::default(),
85        FreeCamera::default(),
86        // Must enable the depth prepass to render forward decals
87        DepthPrepass,
88        Transform::from_xyz(2.0, 9.5, 2.5).looking_at(Vec3::ZERO, Vec3::Y),
89    ));
90
91    let white_material = standard_materials.add(Color::WHITE);
92
93    commands.spawn((
94        Name::new("Floor"),
95        Mesh3d(meshes.add(Rectangle::from_length(10.0))),
96        MeshMaterial3d(white_material.clone()),
97        Transform::from_rotation(Quat::from_rotation_x(-std::f32::consts::FRAC_PI_2)),
98    ));
99
100    // Spawn a few cube with random rotations to showcase how the decals behave with non-flat geometry
101    let num_obs = 10;
102    let mut rng = ChaCha8Rng::seed_from_u64(19878367467713);
103    for i in 0..num_obs {
104        for j in 0..num_obs {
105            let rotation_axis: [f32; 3] = rng.random();
106            let rotation_vec: Vec3 = rotation_axis.into();
107            let rotation: u32 = rng.random_range(0..360);
108            let transform = Transform::from_xyz(
109                (-num_obs + 1) as f32 / 2.0 + i as f32,
110                -0.2,
111                (-num_obs + 1) as f32 / 2.0 + j as f32,
112            )
113            .with_rotation(Quat::from_axis_angle(
114                rotation_vec.normalize_or_zero(),
115                (rotation as f32).to_radians(),
116            ));
117
118            commands.spawn((
119                Mesh3d(meshes.add(Cuboid::from_length(0.6))),
120                MeshMaterial3d(white_material.clone()),
121                transform,
122            ));
123        }
124    }
125
126    commands.spawn((
127        Name::new("Light"),
128        PointLight {
129            shadow_maps_enabled: true,
130            ..default()
131        },
132        Transform::from_xyz(4.0, 8.0, 4.0),
133    ));
134}