Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/examples/3d/order_independent_transparency.rs
9367 views
1
//! A simple 3D scene showing how alpha blending can break and how order independent transparency (OIT) can fix it.
2
//!
3
//! See [`OrderIndependentTransparencyPlugin`] for the trade-offs of using OIT.
4
//!
5
//! [`OrderIndependentTransparencyPlugin`]: bevy::core_pipeline::oit::OrderIndependentTransparencyPlugin
6
use bevy::{
7
camera::visibility::RenderLayers,
8
color::palettes::css::{BLUE, GREEN, RED, YELLOW},
9
core_pipeline::{oit::OrderIndependentTransparencySettings, prepass::DepthPrepass},
10
prelude::*,
11
};
12
13
fn main() {
14
App::new()
15
.add_plugins(DefaultPlugins)
16
.add_systems(Startup, setup)
17
.add_systems(Update, (toggle_oit, cycle_scenes))
18
.run();
19
}
20
21
/// set up a simple 3D scene
22
fn setup(
23
mut commands: Commands,
24
mut meshes: ResMut<Assets<Mesh>>,
25
mut materials: ResMut<Assets<StandardMaterial>>,
26
) {
27
// camera
28
commands.spawn((
29
Camera3d::default(),
30
Transform::from_xyz(0.0, 0.0, 10.0).looking_at(Vec3::ZERO, Vec3::Y),
31
// Add this component to this camera to render transparent meshes using OIT
32
OrderIndependentTransparencySettings::default(),
33
RenderLayers::layer(1),
34
// Msaa currently doesn't work with OIT
35
Msaa::Off,
36
// Optional: depth prepass can help OIT filter out fragments occluded by opaque objects
37
DepthPrepass,
38
));
39
40
// light
41
commands.spawn((
42
PointLight {
43
shadow_maps_enabled: false,
44
..default()
45
},
46
Transform::from_xyz(4.0, 8.0, 4.0),
47
RenderLayers::layer(1),
48
));
49
50
// spawn help text
51
commands.spawn((
52
Text::default(),
53
Node {
54
position_type: PositionType::Absolute,
55
top: px(12),
56
left: px(12),
57
..default()
58
},
59
RenderLayers::layer(1),
60
children![
61
TextSpan::new("Press T to toggle OIT\n"),
62
TextSpan::new("OIT Enabled"),
63
TextSpan::new("\nPress C to cycle test scenes"),
64
],
65
));
66
67
// spawn default scene
68
spawn_spheres(&mut commands, &mut meshes, &mut materials);
69
}
70
71
fn toggle_oit(
72
mut commands: Commands,
73
text: Single<Entity, With<Text>>,
74
keyboard_input: Res<ButtonInput<KeyCode>>,
75
q: Single<(Entity, Has<OrderIndependentTransparencySettings>), With<Camera3d>>,
76
mut text_writer: TextUiWriter,
77
) {
78
if keyboard_input.just_pressed(KeyCode::KeyT) {
79
let (e, has_oit) = *q;
80
*text_writer.text(*text, 2) = if has_oit {
81
// Removing the component will completely disable OIT for this camera
82
commands
83
.entity(e)
84
.remove::<OrderIndependentTransparencySettings>();
85
"OIT disabled".to_string()
86
} else {
87
// Adding the component to the camera will render any transparent meshes
88
// with OIT instead of alpha blending
89
commands
90
.entity(e)
91
.insert(OrderIndependentTransparencySettings::default());
92
"OIT enabled".to_string()
93
};
94
}
95
}
96
97
fn cycle_scenes(
98
mut commands: Commands,
99
keyboard_input: Res<ButtonInput<KeyCode>>,
100
mut meshes: ResMut<Assets<Mesh>>,
101
mut materials: ResMut<Assets<StandardMaterial>>,
102
q: Query<Entity, With<Mesh3d>>,
103
mut scene_id: Local<usize>,
104
asset_server: Res<AssetServer>,
105
) {
106
if keyboard_input.just_pressed(KeyCode::KeyC) {
107
// despawn current scene
108
for e in &q {
109
commands.entity(e).despawn();
110
}
111
// increment scene_id
112
*scene_id = (*scene_id + 1) % 4;
113
// spawn next scene
114
match *scene_id {
115
0 => spawn_spheres(&mut commands, &mut meshes, &mut materials),
116
1 => spawn_quads(&mut commands, &mut meshes, &mut materials),
117
2 => spawn_occlusion_test(&mut commands, &mut meshes, &mut materials),
118
3 => {
119
spawn_auto_instancing_test(
120
&mut commands,
121
&mut meshes,
122
&mut materials,
123
asset_server,
124
);
125
}
126
_ => unreachable!(),
127
}
128
}
129
}
130
131
/// Spawns 3 overlapping spheres
132
/// Technically, when using `alpha_to_coverage` with MSAA this particular example wouldn't break,
133
/// but it breaks when disabling MSAA and is enough to show the difference between OIT enabled vs disabled.
134
fn spawn_spheres(
135
commands: &mut Commands,
136
meshes: &mut Assets<Mesh>,
137
materials: &mut Assets<StandardMaterial>,
138
) {
139
let pos_a = Vec3::new(-1.0, 0.75, 0.0);
140
let pos_b = Vec3::new(0.0, -0.75, 0.0);
141
let pos_c = Vec3::new(1.0, 0.75, 0.0);
142
143
let offset = Vec3::new(0.0, 0.0, 0.0);
144
145
let sphere_handle = meshes.add(Sphere::new(2.0).mesh());
146
147
let alpha = 0.25;
148
149
let render_layers = RenderLayers::layer(1);
150
151
commands.spawn((
152
Mesh3d(sphere_handle.clone()),
153
MeshMaterial3d(materials.add(StandardMaterial {
154
base_color: RED.with_alpha(alpha).into(),
155
alpha_mode: AlphaMode::Blend,
156
..default()
157
})),
158
Transform::from_translation(pos_a + offset),
159
render_layers.clone(),
160
));
161
commands.spawn((
162
Mesh3d(sphere_handle.clone()),
163
MeshMaterial3d(materials.add(StandardMaterial {
164
base_color: GREEN.with_alpha(alpha).into(),
165
alpha_mode: AlphaMode::Blend,
166
..default()
167
})),
168
Transform::from_translation(pos_b + offset),
169
render_layers.clone(),
170
));
171
commands.spawn((
172
Mesh3d(sphere_handle.clone()),
173
MeshMaterial3d(materials.add(StandardMaterial {
174
base_color: BLUE.with_alpha(alpha).into(),
175
alpha_mode: AlphaMode::Blend,
176
..default()
177
})),
178
Transform::from_translation(pos_c + offset),
179
render_layers.clone(),
180
));
181
}
182
183
fn spawn_quads(
184
commands: &mut Commands,
185
meshes: &mut Assets<Mesh>,
186
materials: &mut Assets<StandardMaterial>,
187
) {
188
let quad_handle = meshes.add(Rectangle::new(3.0, 3.0).mesh());
189
let render_layers = RenderLayers::layer(1);
190
let xform = |x, y, z| {
191
Transform::from_rotation(Quat::from_rotation_y(0.5))
192
.mul_transform(Transform::from_xyz(x, y, z))
193
};
194
commands.spawn((
195
Mesh3d(quad_handle.clone()),
196
MeshMaterial3d(materials.add(StandardMaterial {
197
base_color: RED.with_alpha(0.5).into(),
198
alpha_mode: AlphaMode::Blend,
199
..default()
200
})),
201
xform(1.0, -0.1, 0.),
202
render_layers.clone(),
203
));
204
commands.spawn((
205
Mesh3d(quad_handle.clone()),
206
MeshMaterial3d(materials.add(StandardMaterial {
207
base_color: BLUE.with_alpha(0.8).into(),
208
alpha_mode: AlphaMode::Blend,
209
..default()
210
})),
211
xform(0.5, 0.2, -0.5),
212
render_layers.clone(),
213
));
214
commands.spawn((
215
Mesh3d(quad_handle.clone()),
216
MeshMaterial3d(materials.add(StandardMaterial {
217
base_color: GREEN.with_green(1.0).with_alpha(0.5).into(),
218
alpha_mode: AlphaMode::Blend,
219
..default()
220
})),
221
xform(0.0, 0.4, -1.),
222
render_layers.clone(),
223
));
224
commands.spawn((
225
Mesh3d(quad_handle.clone()),
226
MeshMaterial3d(materials.add(StandardMaterial {
227
base_color: YELLOW.with_alpha(0.3).into(),
228
alpha_mode: AlphaMode::Blend,
229
..default()
230
})),
231
xform(-0.5, 0.6, -1.1),
232
render_layers.clone(),
233
));
234
commands.spawn((
235
Mesh3d(quad_handle.clone()),
236
MeshMaterial3d(materials.add(StandardMaterial {
237
base_color: BLUE.with_alpha(0.2).into(),
238
alpha_mode: AlphaMode::Blend,
239
..default()
240
})),
241
xform(-0.8, 0.8, -1.2),
242
render_layers.clone(),
243
));
244
}
245
246
/// Spawn a combination of opaque cubes and transparent spheres.
247
/// This is useful to make sure transparent meshes drawn with OIT
248
/// are properly occluded by opaque meshes.
249
fn spawn_occlusion_test(
250
commands: &mut Commands,
251
meshes: &mut Assets<Mesh>,
252
materials: &mut Assets<StandardMaterial>,
253
) {
254
let sphere_handle = meshes.add(Sphere::new(1.0).mesh());
255
let cube_handle = meshes.add(Cuboid::from_size(Vec3::ONE).mesh());
256
let cube_material = materials.add(Color::srgb(0.8, 0.7, 0.6));
257
258
let render_layers = RenderLayers::layer(1);
259
260
// front
261
let x = -2.5;
262
commands.spawn((
263
Mesh3d(cube_handle.clone()),
264
MeshMaterial3d(cube_material.clone()),
265
Transform::from_xyz(x, 0.0, 2.0),
266
render_layers.clone(),
267
));
268
commands.spawn((
269
Mesh3d(sphere_handle.clone()),
270
MeshMaterial3d(materials.add(StandardMaterial {
271
base_color: RED.with_alpha(0.5).into(),
272
alpha_mode: AlphaMode::Blend,
273
..default()
274
})),
275
Transform::from_xyz(x, 0., 0.),
276
render_layers.clone(),
277
));
278
279
// intersection
280
commands.spawn((
281
Mesh3d(cube_handle.clone()),
282
MeshMaterial3d(cube_material.clone()),
283
Transform::from_xyz(x, 0.0, 1.0),
284
render_layers.clone(),
285
));
286
commands.spawn((
287
Mesh3d(sphere_handle.clone()),
288
MeshMaterial3d(materials.add(StandardMaterial {
289
base_color: RED.with_alpha(0.5).into(),
290
alpha_mode: AlphaMode::Blend,
291
..default()
292
})),
293
Transform::from_xyz(0., 0., 0.),
294
render_layers.clone(),
295
));
296
297
// back
298
let x = 2.5;
299
commands.spawn((
300
Mesh3d(cube_handle.clone()),
301
MeshMaterial3d(cube_material.clone()),
302
Transform::from_xyz(x, 0.0, -2.0),
303
render_layers.clone(),
304
));
305
commands.spawn((
306
Mesh3d(sphere_handle.clone()),
307
MeshMaterial3d(materials.add(StandardMaterial {
308
base_color: RED.with_alpha(0.5).into(),
309
alpha_mode: AlphaMode::Blend,
310
..default()
311
})),
312
Transform::from_xyz(x, 0., 0.),
313
render_layers.clone(),
314
));
315
}
316
317
fn spawn_auto_instancing_test(
318
commands: &mut Commands,
319
meshes: &mut Assets<Mesh>,
320
materials: &mut Assets<StandardMaterial>,
321
asset_server: Res<AssetServer>,
322
) {
323
let render_layers = RenderLayers::layer(1);
324
325
let cube = meshes.add(Cuboid::new(1.0, 1.0, 1.0));
326
let material_handle = materials.add(StandardMaterial {
327
alpha_mode: AlphaMode::Blend,
328
base_color_texture: Some(asset_server.load("textures/slice_square.png")),
329
..Default::default()
330
});
331
let mut bundles = Vec::with_capacity(3 * 3 * 3);
332
333
for z in -1..=1 {
334
for y in -1..=1 {
335
for x in -1..=1 {
336
bundles.push((
337
Mesh3d(cube.clone()),
338
MeshMaterial3d(material_handle.clone()),
339
Transform::from_xyz(x as f32 * 2.0, y as f32 * 2.0, z as f32 * 2.0),
340
render_layers.clone(),
341
));
342
}
343
}
344
}
345
commands.spawn_batch(bundles);
346
}
347
348