Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix OIT depth test #15991

Merged
merged 1 commit into from
Oct 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions crates/bevy_core_pipeline/src/oit/oit_draw.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@
#ifdef OIT_ENABLED
// Add the fragment to the oit buffer
fn oit_draw(position: vec4f, color: vec4f) {
// Don't add fully transparent fragments to the list
// because we don't want to have to sort them in the resolve pass
// TODO should this be comparing with < espilon ?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose this could be something like:

const EPSILON: f32 = 4.88e-04;
if color.a < EPSILON { ... }

But tbh I'm not seeing that this is done when comparing color values anywhere in the codebase.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea behind that comment is more about having a cutoff for almost transparent pixels. Instead of fully transparent, the reason is that every fragment we add to the list costs us more memory. Right now it doesn't really matter because we have a fixed buffer size, but eventually I want to work on making it smaller.

if color.a == 0.0 {
return;
}
// get the index of the current fragment relative to the screen size
let screen_index = i32(floor(position.x) + floor(position.y) * view.viewport.z);
// get the size of the buffer.
Expand Down
24 changes: 15 additions & 9 deletions crates/bevy_core_pipeline/src/oit/resolve/oit_resolve.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,13 @@ fn fragment(in: FullscreenVertexOutput) -> @location(0) vec4<f32> {
}
return vec4(0.0);
} else {
let result = sort(screen_index, buffer_size);
reset_indices(screen_index);

// Manually do depth testing.
// Load depth for manual depth testing.
// This is necessary because early z doesn't seem to trigger in the transparent pass.
// Once we have a per pixel linked list it should be done much earlier
// This should be done during the draw pass so those fragments simply don't exist in the list,
// but this requires a bigger refactor
let d = textureLoad(depth, vec2<i32>(in.position.xy), 0);
if d > result.depth {
discard;
}
let result = sort(screen_index, buffer_size, d);
reset_indices(screen_index);

return result.color;
}
Expand All @@ -61,7 +58,7 @@ struct SortResult {
depth: f32,
}

fn sort(screen_index: i32, buffer_size: i32) -> SortResult {
fn sort(screen_index: i32, buffer_size: i32, opaque_depth: f32) -> SortResult {
var counter = atomicLoad(&layer_ids[screen_index]);

// fill list
Expand Down Expand Up @@ -90,10 +87,19 @@ fn sort(screen_index: i32, buffer_size: i32) -> SortResult {
// resolve blend
var final_color = vec4(0.0);
for (var i = 0; i <= counter; i += 1) {
// depth testing
// This needs to happen here because we can only stop iterating if the fragment is
// occluded by something opaque and the fragments need to be sorted first
if fragment_list[i].depth < opaque_depth {
break;
}
let color = fragment_list[i].color;
let alpha = fragment_list[i].alpha;
var base_color = vec4(color.rgb * alpha, alpha);
final_color = blend(final_color, base_color);
if final_color.a == 1.0 {
break;
}
}
var result: SortResult;
result.color = final_color;
Expand Down