Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/examples/3d/order_independent_transparency.rs
6592 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},
9
core_pipeline::oit::OrderIndependentTransparencySettings,
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
));
37
38
// light
39
commands.spawn((
40
PointLight {
41
shadows_enabled: false,
42
..default()
43
},
44
Transform::from_xyz(4.0, 8.0, 4.0),
45
RenderLayers::layer(1),
46
));
47
48
// spawn help text
49
commands.spawn((
50
Text::default(),
51
Node {
52
position_type: PositionType::Absolute,
53
top: px(12),
54
left: px(12),
55
..default()
56
},
57
RenderLayers::layer(1),
58
children![
59
TextSpan::new("Press T to toggle OIT\n"),
60
TextSpan::new("OIT Enabled"),
61
TextSpan::new("\nPress C to cycle test scenes"),
62
],
63
));
64
65
// spawn default scene
66
spawn_spheres(&mut commands, &mut meshes, &mut materials);
67
}
68
69
fn toggle_oit(
70
mut commands: Commands,
71
text: Single<Entity, With<Text>>,
72
keyboard_input: Res<ButtonInput<KeyCode>>,
73
q: Single<(Entity, Has<OrderIndependentTransparencySettings>), With<Camera3d>>,
74
mut text_writer: TextUiWriter,
75
) {
76
if keyboard_input.just_pressed(KeyCode::KeyT) {
77
let (e, has_oit) = *q;
78
*text_writer.text(*text, 2) = if has_oit {
79
// Removing the component will completely disable OIT for this camera
80
commands
81
.entity(e)
82
.remove::<OrderIndependentTransparencySettings>();
83
"OIT disabled".to_string()
84
} else {
85
// Adding the component to the camera will render any transparent meshes
86
// with OIT instead of alpha blending
87
commands
88
.entity(e)
89
.insert(OrderIndependentTransparencySettings::default());
90
"OIT enabled".to_string()
91
};
92
}
93
}
94
95
fn cycle_scenes(
96
mut commands: Commands,
97
keyboard_input: Res<ButtonInput<KeyCode>>,
98
mut meshes: ResMut<Assets<Mesh>>,
99
mut materials: ResMut<Assets<StandardMaterial>>,
100
q: Query<Entity, With<Mesh3d>>,
101
mut scene_id: Local<usize>,
102
) {
103
if keyboard_input.just_pressed(KeyCode::KeyC) {
104
// despawn current scene
105
for e in &q {
106
commands.entity(e).despawn();
107
}
108
// increment scene_id
109
*scene_id = (*scene_id + 1) % 2;
110
// spawn next scene
111
match *scene_id {
112
0 => spawn_spheres(&mut commands, &mut meshes, &mut materials),
113
1 => spawn_occlusion_test(&mut commands, &mut meshes, &mut materials),
114
_ => unreachable!(),
115
}
116
}
117
}
118
119
/// Spawns 3 overlapping spheres
120
/// Technically, when using `alpha_to_coverage` with MSAA this particular example wouldn't break,
121
/// but it breaks when disabling MSAA and is enough to show the difference between OIT enabled vs disabled.
122
fn spawn_spheres(
123
commands: &mut Commands,
124
meshes: &mut Assets<Mesh>,
125
materials: &mut Assets<StandardMaterial>,
126
) {
127
let pos_a = Vec3::new(-1.0, 0.75, 0.0);
128
let pos_b = Vec3::new(0.0, -0.75, 0.0);
129
let pos_c = Vec3::new(1.0, 0.75, 0.0);
130
131
let offset = Vec3::new(0.0, 0.0, 0.0);
132
133
let sphere_handle = meshes.add(Sphere::new(2.0).mesh());
134
135
let alpha = 0.25;
136
137
let render_layers = RenderLayers::layer(1);
138
139
commands.spawn((
140
Mesh3d(sphere_handle.clone()),
141
MeshMaterial3d(materials.add(StandardMaterial {
142
base_color: RED.with_alpha(alpha).into(),
143
alpha_mode: AlphaMode::Blend,
144
..default()
145
})),
146
Transform::from_translation(pos_a + offset),
147
render_layers.clone(),
148
));
149
commands.spawn((
150
Mesh3d(sphere_handle.clone()),
151
MeshMaterial3d(materials.add(StandardMaterial {
152
base_color: GREEN.with_alpha(alpha).into(),
153
alpha_mode: AlphaMode::Blend,
154
..default()
155
})),
156
Transform::from_translation(pos_b + offset),
157
render_layers.clone(),
158
));
159
commands.spawn((
160
Mesh3d(sphere_handle.clone()),
161
MeshMaterial3d(materials.add(StandardMaterial {
162
base_color: BLUE.with_alpha(alpha).into(),
163
alpha_mode: AlphaMode::Blend,
164
..default()
165
})),
166
Transform::from_translation(pos_c + offset),
167
render_layers.clone(),
168
));
169
}
170
171
/// Spawn a combination of opaque cubes and transparent spheres.
172
/// This is useful to make sure transparent meshes drawn with OIT
173
/// are properly occluded by opaque meshes.
174
fn spawn_occlusion_test(
175
commands: &mut Commands,
176
meshes: &mut Assets<Mesh>,
177
materials: &mut Assets<StandardMaterial>,
178
) {
179
let sphere_handle = meshes.add(Sphere::new(1.0).mesh());
180
let cube_handle = meshes.add(Cuboid::from_size(Vec3::ONE).mesh());
181
let cube_material = materials.add(Color::srgb(0.8, 0.7, 0.6));
182
183
let render_layers = RenderLayers::layer(1);
184
185
// front
186
let x = -2.5;
187
commands.spawn((
188
Mesh3d(cube_handle.clone()),
189
MeshMaterial3d(cube_material.clone()),
190
Transform::from_xyz(x, 0.0, 2.0),
191
render_layers.clone(),
192
));
193
commands.spawn((
194
Mesh3d(sphere_handle.clone()),
195
MeshMaterial3d(materials.add(StandardMaterial {
196
base_color: RED.with_alpha(0.5).into(),
197
alpha_mode: AlphaMode::Blend,
198
..default()
199
})),
200
Transform::from_xyz(x, 0., 0.),
201
render_layers.clone(),
202
));
203
204
// intersection
205
commands.spawn((
206
Mesh3d(cube_handle.clone()),
207
MeshMaterial3d(cube_material.clone()),
208
Transform::from_xyz(x, 0.0, 1.0),
209
render_layers.clone(),
210
));
211
commands.spawn((
212
Mesh3d(sphere_handle.clone()),
213
MeshMaterial3d(materials.add(StandardMaterial {
214
base_color: RED.with_alpha(0.5).into(),
215
alpha_mode: AlphaMode::Blend,
216
..default()
217
})),
218
Transform::from_xyz(0., 0., 0.),
219
render_layers.clone(),
220
));
221
222
// back
223
let x = 2.5;
224
commands.spawn((
225
Mesh3d(cube_handle.clone()),
226
MeshMaterial3d(cube_material.clone()),
227
Transform::from_xyz(x, 0.0, -2.0),
228
render_layers.clone(),
229
));
230
commands.spawn((
231
Mesh3d(sphere_handle.clone()),
232
MeshMaterial3d(materials.add(StandardMaterial {
233
base_color: RED.with_alpha(0.5).into(),
234
alpha_mode: AlphaMode::Blend,
235
..default()
236
})),
237
Transform::from_xyz(x, 0., 0.),
238
render_layers.clone(),
239
));
240
}
241
242