1use bevy::{
6 core_pipeline::prepass::{DepthPrepass, MotionVectorPrepass, NormalPrepass},
7 light::NotShadowCaster,
8 pbr::PbrPlugin,
9 prelude::*,
10 reflect::TypePath,
11 render::render_resource::{AsBindGroup, ShaderType},
12 shader::ShaderRef,
13};
14
15const PREPASS_SHADER_ASSET_PATH: &str = "shaders/show_prepass.wgsl";
17const MATERIAL_SHADER_ASSET_PATH: &str = "shaders/custom_material.wgsl";
18
19fn main() {
20 App::new()
21 .add_plugins((
22 DefaultPlugins.set(PbrPlugin {
23 ..default()
28 }),
29 MaterialPlugin::<CustomMaterial>::default(),
30 MaterialPlugin::<PrepassOutputMaterial> {
31 prepass_enabled: false,
34 ..default()
35 },
36 ))
37 .add_systems(Startup, setup)
38 .add_systems(Update, (rotate, toggle_prepass_view))
39 .run();
40}
41
42fn setup(
44 mut commands: Commands,
45 mut meshes: ResMut<Assets<Mesh>>,
46 mut materials: ResMut<Assets<CustomMaterial>>,
47 mut std_materials: ResMut<Assets<StandardMaterial>>,
48 mut depth_materials: ResMut<Assets<PrepassOutputMaterial>>,
49 asset_server: Res<AssetServer>,
50) {
51 commands.spawn((
53 Camera3d::default(),
54 Transform::from_xyz(-2.0, 3., 5.0).looking_at(Vec3::ZERO, Vec3::Y),
55 Msaa::Off,
57 DepthPrepass,
60 NormalPrepass,
62 MotionVectorPrepass,
64 ));
65
66 commands.spawn((
68 Mesh3d(meshes.add(Plane3d::default().mesh().size(5.0, 5.0))),
69 MeshMaterial3d(std_materials.add(Color::srgb(0.3, 0.5, 0.3))),
70 ));
71
72 commands.spawn((
76 Mesh3d(meshes.add(Rectangle::new(20.0, 20.0))),
77 MeshMaterial3d(depth_materials.add(PrepassOutputMaterial {
78 settings: ShowPrepassSettings::default(),
79 })),
80 Transform::from_xyz(-0.75, 1.25, 3.0).looking_at(Vec3::new(2.0, -2.5, -5.0), Vec3::Y),
81 NotShadowCaster,
82 ));
83
84 commands.spawn((
86 Mesh3d(meshes.add(Cuboid::default())),
87 MeshMaterial3d(materials.add(CustomMaterial {
88 color: LinearRgba::WHITE,
89 color_texture: Some(asset_server.load("branding/icon.png")),
90 alpha_mode: AlphaMode::Opaque,
91 })),
92 Transform::from_xyz(-1.0, 0.5, 0.0),
93 Rotates,
94 ));
95
96 commands.spawn((
98 Mesh3d(meshes.add(Cuboid::default())),
99 MeshMaterial3d(std_materials.add(StandardMaterial {
100 alpha_mode: AlphaMode::Mask(1.0),
101 base_color_texture: Some(asset_server.load("branding/icon.png")),
102 ..default()
103 })),
104 Transform::from_xyz(0.0, 0.5, 0.0),
105 ));
106
107 commands.spawn((
110 Mesh3d(meshes.add(Cuboid::default())),
111 MeshMaterial3d(materials.add(CustomMaterial {
112 color: LinearRgba::WHITE,
113 color_texture: Some(asset_server.load("branding/icon.png")),
114 alpha_mode: AlphaMode::Blend,
115 })),
116 Transform::from_xyz(1.0, 0.5, 0.0),
117 ));
118
119 commands.spawn((
121 PointLight {
122 shadows_enabled: true,
123 ..default()
124 },
125 Transform::from_xyz(4.0, 8.0, 4.0),
126 ));
127
128 commands.spawn((
129 Text::default(),
130 Node {
131 position_type: PositionType::Absolute,
132 top: px(12),
133 left: px(12),
134 ..default()
135 },
136 children![
137 TextSpan::new("Prepass Output: transparent\n"),
138 TextSpan::new("\n\n"),
139 TextSpan::new("Controls\n"),
140 TextSpan::new("---------------\n"),
141 TextSpan::new("Space - Change output\n"),
142 ],
143 ));
144}
145
146#[derive(Asset, TypePath, AsBindGroup, Debug, Clone)]
148struct CustomMaterial {
149 #[uniform(0)]
150 color: LinearRgba,
151 #[texture(1)]
152 #[sampler(2)]
153 color_texture: Option<Handle<Image>>,
154 alpha_mode: AlphaMode,
155}
156
157impl Material for CustomMaterial {
160 fn fragment_shader() -> ShaderRef {
161 MATERIAL_SHADER_ASSET_PATH.into()
162 }
163
164 fn alpha_mode(&self) -> AlphaMode {
165 self.alpha_mode
166 }
167
168 }
174
175#[derive(Component)]
176struct Rotates;
177
178fn rotate(mut q: Query<&mut Transform, With<Rotates>>, time: Res<Time>) {
179 for mut t in q.iter_mut() {
180 let rot = (ops::sin(time.elapsed_secs()) * 0.5 + 0.5) * std::f32::consts::PI * 2.0;
181 t.rotation = Quat::from_rotation_z(rot);
182 }
183}
184
185#[derive(Debug, Clone, Default, ShaderType)]
186struct ShowPrepassSettings {
187 show_depth: u32,
188 show_normals: u32,
189 show_motion_vectors: u32,
190 padding_1: u32,
191 padding_2: u32,
192}
193
194#[derive(Asset, TypePath, AsBindGroup, Debug, Clone)]
196struct PrepassOutputMaterial {
197 #[uniform(0)]
198 settings: ShowPrepassSettings,
199}
200
201impl Material for PrepassOutputMaterial {
202 fn fragment_shader() -> ShaderRef {
203 PREPASS_SHADER_ASSET_PATH.into()
204 }
205
206 fn alpha_mode(&self) -> AlphaMode {
208 AlphaMode::Blend
209 }
210}
211
212fn toggle_prepass_view(
214 mut prepass_view: Local<u32>,
215 keycode: Res<ButtonInput<KeyCode>>,
216 material_handle: Single<&MeshMaterial3d<PrepassOutputMaterial>>,
217 mut materials: ResMut<Assets<PrepassOutputMaterial>>,
218 text: Single<Entity, With<Text>>,
219 mut writer: TextUiWriter,
220) {
221 if keycode.just_pressed(KeyCode::Space) {
222 *prepass_view = (*prepass_view + 1) % 4;
223
224 let label = match *prepass_view {
225 0 => "transparent",
226 1 => "depth",
227 2 => "normals",
228 3 => "motion vectors",
229 _ => unreachable!(),
230 };
231 let text = *text;
232 *writer.text(text, 1) = format!("Prepass Output: {label}\n");
233 writer.for_each_color(text, |mut color| {
234 color.0 = Color::WHITE;
235 });
236
237 let mat = materials.get_mut(*material_handle).unwrap();
238 mat.settings.show_depth = (*prepass_view == 1) as u32;
239 mat.settings.show_normals = (*prepass_view == 2) as u32;
240 mat.settings.show_motion_vectors = (*prepass_view == 3) as u32;
241 }
242}