-
Notifications
You must be signed in to change notification settings - Fork 373
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix web depth/projection regression, causing incorrect rendering on a…
…ll 3D scenes (#2170) * Revert "Fix depth precision issues on WebGL due to different NDC space (#2123)" This reverts commit 4f60fd7. * fudge depth offsets on Webgl * hardware_tier global constant, better depth handling for gles * Improve the depth offset for all platforms --------- Co-authored-by: Emil Ernerfeldt <[email protected]>
- Loading branch information
Showing
5 changed files
with
86 additions
and
77 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,35 +1,59 @@ | ||
#import <../global_bindings.wgsl> | ||
#import <../types.wgsl> | ||
|
||
/* | ||
We use reverse infinite depth, as promoted by https://developer.nvidia.com/content/depth-precision-visualized | ||
|
||
The projection matrix (from `glam::Mat4::perspective_infinite_reverse_rh`) looks like this: | ||
|
||
f / aspect_ratio 0 0 0 | ||
0 f 0 0 | ||
0 0 0 z_near | ||
0 0 -1 0 | ||
|
||
This means after multiplication with xyzw (with w=1) we end up with: | ||
|
||
x_proj: x * f / aspect_ratio, | ||
y_proj: y * f, | ||
z_proj: w * z_near, | ||
w_proj: -z | ||
|
||
This is then projected by dividing with w, giving: | ||
|
||
x_ndc: x_proj / w_proj | ||
y_ndc: y_proj / w_proj | ||
z_ndc: z_proj / w_proj | ||
|
||
Without z offset, we get this: | ||
|
||
x_ndc: x * f / aspect_ratio / -z | ||
y_ndc: y * f / -z | ||
z_ndc: w * z_near / -z | ||
|
||
The negative -z axis is away from the camera, so with w=1 we get | ||
z_near mapping to z_ndc=1, and infinity mapping to z_ndc=0. | ||
|
||
The code below act on the *_proj values by adding a scale multiplier on `w_proj` resulting in: | ||
x_ndc: x_proj / (-z * w_scale) | ||
y_ndc: y_proj / (-z * w_scale) | ||
z_ndc: z_proj / (-z * w_scale) | ||
*/ | ||
|
||
fn apply_depth_offset(position: Vec4, offset: f32) -> Vec4 { | ||
// We're using inverse z, i.e. 0.0 is far, 1.0 is near. | ||
// We want a positive offset to move towards the viewer, so offset needs to be added. | ||
// | ||
// With this in place we still may cross over to 0.0 (the far plane) too early, | ||
// making objects disappear into the far when they'd be otherwise still rendered. | ||
// Since we're actually supposed to have an *infinite* far plane this should never happen! | ||
// Therefore we simply dictacte a minimum z value. | ||
// This ofc wrecks the depth offset and may cause z fighting with all very far away objects, but it's better than having things disappear! | ||
|
||
if true { | ||
// This path assumes a `f32` depth buffer! | ||
|
||
// 1.0 * eps _should_ be enough, but in practice it causes Z-fighting for unknown reasons. | ||
// Maybe because of GPU interpolation of vertex coordinates? | ||
let eps = 5.0 * f32eps; | ||
|
||
return Vec4( | ||
position.xy, | ||
max(position.z * (1.0 + eps * offset), f32eps), | ||
position.w | ||
); | ||
} else { | ||
// Causes Z-collision at far distances | ||
let eps = f32eps; | ||
return Vec4( | ||
position.xy, | ||
max(position.z + eps * offset * position.w, f32eps), | ||
position.w | ||
); | ||
} | ||
// On GLES/WebGL, the NDC clipspace range for depth is from -1 to 1 and y is flipped. | ||
// wgpu/Naga counteracts this by patching all vertex shaders with: | ||
// "gl_Position.yz = vec2(-gl_Position.y, gl_Position.z * 2.0 - gl_Position.w);", | ||
// This doesn't matter for us though. | ||
|
||
// This path assumes a `f32` depth buffer. | ||
|
||
// We set up the depth comparison to Greater, so that large z means closer (overdraw). | ||
// We want a greater offset to win over a smaller offset, | ||
// so a great depth offset should result in a large z_ndc. | ||
// How do we get there? We let large depth offset lead to a smaller divisor (w_proj): | ||
|
||
return Vec4( | ||
position.xyz, | ||
position.w * (1.0 - f32eps * offset), | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters