Skip to content

Commit a2583f6

Browse files
committed
Sort in desc order and fix early termination in blending
1 parent 395acf0 commit a2583f6

File tree

2 files changed

+50
-11
lines changed

2 files changed

+50
-11
lines changed

crates/bevy_core_pipeline/src/oit/resolve/oit_resolve.wgsl

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,9 @@ fn fragment(in: FullscreenVertexOutput) -> @location(0) vec4<f32> {
5454

5555
fn resolve(header: u32, opaque_depth: f32) -> vec4<f32> {
5656
// Contains all the colors and depth for this specific fragment
57-
// Fragments are sorted from back to front.
57+
// Fragments are sorted from front to back (depth values are in descending order)
58+
// This should make insertion sort slightly faster
59+
// because transparent pass sorts objects so the linked list iteration is usually in descending order.
5860
var fragment_list: array<OitFragment, SORTED_FRAGMENT_MAX_COUNT>;
5961
var final_color = vec4<f32>(0.0);
6062

@@ -81,7 +83,7 @@ fn resolve(header: u32, opaque_depth: f32) -> vec4<f32> {
8183
var i = sorted_frag_count;
8284
for(; i > 0; i -= 1) {
8385
// short-circuit can't be used in for(;;;), https://github.com/gfx-rs/wgpu/issues/4394
84-
if depth_alpha.x < fragment_list[i - 1].depth {
86+
if depth_alpha.x > fragment_list[i - 1].depth {
8587
fragment_list[i] = fragment_list[i - 1];
8688
} else {
8789
break;
@@ -91,16 +93,18 @@ fn resolve(header: u32, opaque_depth: f32) -> vec4<f32> {
9193
fragment_list[i].alpha = depth_alpha.y;
9294
fragment_list[i].depth = depth_alpha.x;
9395
sorted_frag_count += 1;
94-
} else if fragment_list[0].depth < depth_alpha.x {
95-
// The fragment is closer than the farthest sorted one.
96-
// First, make room by blending the farthest fragment from the sorted list.
96+
} else if fragment_list[0].depth > depth_alpha.x {
97+
// The fragment is farther than the nearest sorted one.
98+
// First, make room by blending the nearest fragment from the sorted list.
9799
// Then, insert the fragment in the sorted list.
98100
// This is an approximation.
99-
final_color = blend(vec4f(fragment_list[0].color * fragment_list[0].alpha, fragment_list[0].alpha), final_color);
101+
let nearest_color = fragment_list[0].color;
102+
let nearest_alpha = fragment_list[0].alpha;
103+
final_color = blend(final_color, vec4f(nearest_color * nearest_alpha, nearest_alpha));
100104
var i = 0u;
101105
for(; i < SORTED_FRAGMENT_MAX_COUNT - 1; i += 1) {
102106
// short-circuit can't be used in for(;;;), https://github.com/gfx-rs/wgpu/issues/4394
103-
if fragment_list[i + 1].depth < depth_alpha.x {
107+
if depth_alpha.x < fragment_list[i + 1].depth {
104108
fragment_list[i] = fragment_list[i + 1];
105109
} else {
106110
break;
@@ -110,10 +114,10 @@ fn resolve(header: u32, opaque_depth: f32) -> vec4<f32> {
110114
fragment_list[i].alpha = depth_alpha.y;
111115
fragment_list[i].depth = depth_alpha.x;
112116
} else {
113-
// The next fragment is farther than any of the sorted ones.
117+
// The next fragment is nearer than any of the sorted ones.
114118
// Blend it early.
115119
// This is an approximation.
116-
final_color = blend(vec4f(color * depth_alpha.y, depth_alpha.y), final_color);
120+
final_color = blend(final_color, vec4f(color * depth_alpha.y, depth_alpha.y));
117121
}
118122
}
119123

@@ -122,7 +126,7 @@ fn resolve(header: u32, opaque_depth: f32) -> vec4<f32> {
122126
let color = fragment_list[i].color;
123127
let alpha = fragment_list[i].alpha;
124128
var base_color = vec4(color.rgb * alpha, alpha);
125-
final_color = blend(base_color, final_color);
129+
final_color = blend(final_color, base_color);
126130
if final_color.a == 1.0 {
127131
break;
128132
}

examples/3d/order_independent_transparency.rs

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,19 +101,23 @@ fn cycle_scenes(
101101
mut materials: ResMut<Assets<StandardMaterial>>,
102102
q: Query<Entity, With<Mesh3d>>,
103103
mut scene_id: Local<usize>,
104+
asset_server: Res<AssetServer>,
104105
) {
105106
if keyboard_input.just_pressed(KeyCode::KeyC) {
106107
// despawn current scene
107108
for e in &q {
108109
commands.entity(e).despawn();
109110
}
110111
// increment scene_id
111-
*scene_id = (*scene_id + 1) % 3;
112+
*scene_id = (*scene_id + 1) % 4;
112113
// spawn next scene
113114
match *scene_id {
114115
0 => spawn_spheres(&mut commands, &mut meshes, &mut materials),
115116
1 => spawn_quads(&mut commands, &mut meshes, &mut materials),
116117
2 => spawn_occlusion_test(&mut commands, &mut meshes, &mut materials),
118+
3 => {
119+
spawn_auto_instancing_test(&mut commands, &mut meshes, &mut materials, asset_server)
120+
}
117121
_ => unreachable!(),
118122
}
119123
}
@@ -304,3 +308,34 @@ fn spawn_occlusion_test(
304308
render_layers.clone(),
305309
));
306310
}
311+
312+
fn spawn_auto_instancing_test(
313+
commands: &mut Commands,
314+
meshes: &mut Assets<Mesh>,
315+
materials: &mut Assets<StandardMaterial>,
316+
asset_server: Res<AssetServer>,
317+
) {
318+
let render_layers = RenderLayers::layer(1);
319+
320+
let cube = meshes.add(Cuboid::new(1.0, 1.0, 1.0));
321+
let material_handle = materials.add(StandardMaterial {
322+
alpha_mode: AlphaMode::Blend,
323+
base_color_texture: Some(asset_server.load("textures/slice_square.png")),
324+
..Default::default()
325+
});
326+
let mut bundles = Vec::with_capacity(3 * 3 * 3);
327+
328+
for z in -1..=1 {
329+
for y in -1..=1 {
330+
for x in -1..=1 {
331+
bundles.push((
332+
Mesh3d(cube.clone()),
333+
MeshMaterial3d(material_handle.clone()),
334+
Transform::from_xyz(x as f32 * 2.0, y as f32 * 2.0, z as f32 * 2.0),
335+
render_layers.clone(),
336+
));
337+
}
338+
}
339+
}
340+
commands.spawn_batch(bundles);
341+
}

0 commit comments

Comments
 (0)