Fix OIT depth test (#15991)

# Objective

- The depth test was only using the final depth but it should be testing
each fragments
- Fully transparent fragments should not be added to the list

## Solution

- Test each fragment after sorting

## Testing

before:
TODO

after:
TODO
This commit is contained in:
IceSentry 2024-10-18 21:16:17 -04:00 committed by GitHub
parent 16b39c2b36
commit fcf6067a10
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 21 additions and 9 deletions

View file

@ -5,6 +5,12 @@
#ifdef OIT_ENABLED #ifdef OIT_ENABLED
// Add the fragment to the oit buffer // Add the fragment to the oit buffer
fn oit_draw(position: vec4f, color: vec4f) { 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 ?
if color.a == 0.0 {
return;
}
// get the index of the current fragment relative to the screen size // 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); let screen_index = i32(floor(position.x) + floor(position.y) * view.viewport.z);
// get the size of the buffer. // get the size of the buffer.

View file

@ -34,16 +34,13 @@ fn fragment(in: FullscreenVertexOutput) -> @location(0) vec4<f32> {
} }
return vec4(0.0); return vec4(0.0);
} else { } else {
let result = sort(screen_index, buffer_size); // Load depth for manual depth testing.
reset_indices(screen_index);
// Manually do depth testing.
// This is necessary because early z doesn't seem to trigger in the transparent pass. // 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); let d = textureLoad(depth, vec2<i32>(in.position.xy), 0);
if d > result.depth { let result = sort(screen_index, buffer_size, d);
discard; reset_indices(screen_index);
}
return result.color; return result.color;
} }
@ -61,7 +58,7 @@ struct SortResult {
depth: f32, 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]); var counter = atomicLoad(&layer_ids[screen_index]);
// fill list // fill list
@ -90,10 +87,19 @@ fn sort(screen_index: i32, buffer_size: i32) -> SortResult {
// resolve blend // resolve blend
var final_color = vec4(0.0); var final_color = vec4(0.0);
for (var i = 0; i <= counter; i += 1) { 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 color = fragment_list[i].color;
let alpha = fragment_list[i].alpha; let alpha = fragment_list[i].alpha;
var base_color = vec4(color.rgb * alpha, alpha); var base_color = vec4(color.rgb * alpha, alpha);
final_color = blend(final_color, base_color); final_color = blend(final_color, base_color);
if final_color.a == 1.0 {
break;
}
} }
var result: SortResult; var result: SortResult;
result.color = final_color; result.color = final_color;