decal/
decal.rs

1//! Decal rendering.
2//! Note: On Wasm, this example only runs on WebGPU
3
4#[path = "../helpers/camera_controller.rs"]
5mod camera_controller;
6
7use bevy::{
8    anti_alias::fxaa::Fxaa,
9    core_pipeline::prepass::DepthPrepass,
10    pbr::decal::{ForwardDecal, ForwardDecalMaterial, ForwardDecalMaterialExt},
11    prelude::*,
12};
13use camera_controller::{CameraController, CameraControllerPlugin};
14use rand::{Rng, SeedableRng};
15use rand_chacha::ChaCha8Rng;
16
17fn main() {
18    App::new()
19        .add_plugins((DefaultPlugins, CameraControllerPlugin))
20        .add_systems(Startup, setup)
21        .run();
22}
23
24fn setup(
25    mut commands: Commands,
26    mut meshes: ResMut<Assets<Mesh>>,
27    mut standard_materials: ResMut<Assets<StandardMaterial>>,
28    mut decal_standard_materials: ResMut<Assets<ForwardDecalMaterial<StandardMaterial>>>,
29    asset_server: Res<AssetServer>,
30) {
31    // Spawn the forward decal
32    commands.spawn((
33        Name::new("Decal"),
34        ForwardDecal,
35        MeshMaterial3d(decal_standard_materials.add(ForwardDecalMaterial {
36            base: StandardMaterial {
37                base_color_texture: Some(asset_server.load("textures/uv_checker_bw.png")),
38                ..default()
39            },
40            extension: ForwardDecalMaterialExt {
41                depth_fade_factor: 1.0,
42            },
43        })),
44        Transform::from_scale(Vec3::splat(4.0)),
45    ));
46
47    commands.spawn((
48        Name::new("Camera"),
49        Camera3d::default(),
50        CameraController::default(),
51        // Must enable the depth prepass to render forward decals
52        DepthPrepass,
53        // Must disable MSAA to use decals on WebGPU
54        Msaa::Off,
55        // FXAA is a fine alternative to MSAA for anti-aliasing
56        Fxaa::default(),
57        Transform::from_xyz(2.0, 9.5, 2.5).looking_at(Vec3::ZERO, Vec3::Y),
58    ));
59
60    let white_material = standard_materials.add(Color::WHITE);
61
62    commands.spawn((
63        Name::new("Floor"),
64        Mesh3d(meshes.add(Rectangle::from_length(10.0))),
65        MeshMaterial3d(white_material.clone()),
66        Transform::from_rotation(Quat::from_rotation_x(-std::f32::consts::FRAC_PI_2)),
67    ));
68
69    // Spawn a few cube with random rotations to showcase how the decals behave with non-flat geometry
70    let num_obs = 10;
71    let mut rng = ChaCha8Rng::seed_from_u64(19878367467713);
72    for i in 0..num_obs {
73        for j in 0..num_obs {
74            let rotation_axis: [f32; 3] = rng.random();
75            let rotation_vec: Vec3 = rotation_axis.into();
76            let rotation: u32 = rng.random_range(0..360);
77            let transform = Transform::from_xyz(
78                (-num_obs + 1) as f32 / 2.0 + i as f32,
79                -0.2,
80                (-num_obs + 1) as f32 / 2.0 + j as f32,
81            )
82            .with_rotation(Quat::from_axis_angle(
83                rotation_vec.normalize_or_zero(),
84                (rotation as f32).to_radians(),
85            ));
86
87            commands.spawn((
88                Mesh3d(meshes.add(Cuboid::from_length(0.6))),
89                MeshMaterial3d(white_material.clone()),
90                transform,
91            ));
92        }
93    }
94
95    commands.spawn((
96        Name::new("Light"),
97        PointLight {
98            shadows_enabled: true,
99            ..default()
100        },
101        Transform::from_xyz(4.0, 8.0, 4.0),
102    ));
103}