Skip to content

Commit 8be9715

Browse files
mvaligurskyMartin Valigursky
andauthored
fix: correct TAA temporal reprojection on WebGPU (#8517)
Fix ghosting artifacts in TAA on WebGPU caused by incorrect UV-to-NDC reconstruction in the reproject function. The getImageEffectUV Y-flip applied in the vertex shader was not accounted for, producing wrong world-space positions and broken history reprojection during camera movement. Remove the old upside-down workaround hack from the TAA resolve shader and the compensating UV flip from the compose shader. Clean up dead WEBGPU ifdef blocks in the GLSL TAA resolve shader. Fixed #8452 Made-with: Cursor Co-authored-by: Martin Valigursky <mvaligursky@snapchat.com>
1 parent e92069b commit 8be9715

File tree

4 files changed

+15
-39
lines changed

4 files changed

+15
-39
lines changed

src/scene/shader-lib/glsl/chunks/render-pass/frag/compose/compose.js

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,6 @@ export default /* glsl */`
2424
2525
vec2 uv = uv0;
2626
27-
// TAA pass renders upside-down on WebGPU, flip it here
28-
#ifdef TAA
29-
#ifdef WEBGPU
30-
uv.y = 1.0 - uv.y;
31-
#endif
32-
#endif
33-
3427
vec4 scene = texture2DLod(sceneTexture, uv, 0.0);
3528
vec3 result = scene.rgb;
3629

src/scene/shader-lib/glsl/chunks/render-pass/frag/taaResolve.js

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,7 @@ export default /* glsl */`
1414
vec2 reproject(vec2 uv, float depth) {
1515
1616
// fragment NDC
17-
#ifndef WEBGPU
18-
depth = depth * 2.0 - 1.0;
19-
#endif
17+
depth = depth * 2.0 - 1.0;
2018
vec4 ndc = vec4(uv * 2.0 - 1.0, depth, 1.0);
2119
2220
// remove jitter from the current frame
@@ -54,18 +52,8 @@ export default /* glsl */`
5452
5553
void main()
5654
{
57-
vec2 uv = uv0;
58-
59-
#ifdef WEBGPU
60-
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
61-
// This hack is needed on webgpu, which makes TAA work but the resulting image is upside-down.
62-
// We could flip the image in the following pass, but ideally a better solution should be found.
63-
uv.y = 1.0 - uv.y;
64-
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
65-
#endif
66-
6755
// current frame
68-
vec4 srcColor = texture2D(sourceTexture, uv);
56+
vec4 srcColor = texture2D(sourceTexture, uv0);
6957
7058
// current depth is in linear space, convert it to non-linear space
7159
float linearDepth = getLinearScreenDepth(uv0);
@@ -87,7 +75,7 @@ export default /* glsl */`
8775
#endif
8876
8977
// handle disocclusion by clamping the history color
90-
vec4 historyColorClamped = colorClamp(uv, historyColor);
78+
vec4 historyColorClamped = colorClamp(uv0, historyColor);
9179
9280
// handle history buffer outside of the frame
9381
float mixFactor = (historyUv.x < 0.0 || historyUv.x > 1.0 || historyUv.y < 0.0 || historyUv.y > 1.0) ?

src/scene/shader-lib/wgsl/chunks/render-pass/frag/compose/compose.js

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,6 @@ export default /* wgsl */`
2727
var output: FragmentOutput;
2828
var uv = uv0;
2929
30-
// TAA pass renders upside-down on WebGPU, flip it here
31-
#ifdef TAA
32-
uv.y = 1.0 - uv.y;
33-
#endif
34-
3530
let scene = textureSampleLevel(sceneTexture, sceneTextureSampler, uv, 0.0);
3631
var result = scene.rgb;
3732

src/scene/shader-lib/wgsl/chunks/render-pass/frag/taaResolve.js

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,11 @@ export default /* wgsl */`
1313
1414
varying uv0: vec2f;
1515
16-
fn reproject(uv: vec2f, depth: f32) -> vec2f {
16+
fn reproject(uv_in: vec2f, depth: f32) -> vec2f {
17+
18+
// uv was Y-flipped by getImageEffectUV for texture sampling,
19+
// un-flip to reconstruct correct NDC (viewProj matrices use standard Y convention)
20+
var uv = vec2f(uv_in.x, 1.0 - uv_in.y);
1721
1822
var ndc = vec4f(uv * 2.0 - 1.0, depth, 1.0);
1923
@@ -27,7 +31,11 @@ export default /* wgsl */`
2731
// world position to screen space of the previous frame
2832
let screenPrevious = uniform.matrix_viewProjectionPrevious * worldPosition;
2933
30-
return (screenPrevious.xy / screenPrevious.w) * 0.5 + 0.5;
34+
// flip result back to texture sampling convention
35+
var result = (screenPrevious.xy / screenPrevious.w) * 0.5 + 0.5;
36+
result.y = 1.0 - result.y;
37+
38+
return result;
3139
}
3240
3341
fn colorClamp(uv: vec2f, historyColor: vec4f) -> vec4f {
@@ -54,16 +62,8 @@ export default /* wgsl */`
5462
fn fragmentMain(input: FragmentInput) -> FragmentOutput {
5563
var output: FragmentOutput;
5664
57-
var uv = input.uv0;
58-
59-
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
60-
// This hack is needed on webgpu, which makes TAA work but the resulting image is upside-down.
61-
// We could flip the image in the following pass, but ideally a better solution should be found.
62-
uv.y = 1.0 - uv.y;
63-
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
64-
6565
// current frame
66-
let srcColor = textureSample(sourceTexture, sourceTextureSampler, uv);
66+
let srcColor = textureSample(sourceTexture, sourceTextureSampler, uv0);
6767
6868
// current depth is in linear space, convert it to non-linear space
6969
let linearDepth = getLinearScreenDepth(uv0);
@@ -85,7 +85,7 @@ export default /* wgsl */`
8585
#endif
8686
8787
// handle disocclusion by clamping the history color
88-
let historyColorClamped = colorClamp(uv, historyColor);
88+
let historyColorClamped = colorClamp(uv0, historyColor);
8989
9090
// handle history buffer outside of the frame
9191
let mixFactor_condition = historyUv.x < 0.0 || historyUv.x > 1.0 || historyUv.y < 0.0 || historyUv.y > 1.0;

0 commit comments

Comments
 (0)