Kelsidavis-WoWee/assets/shaders/fsr2_motion.comp.glsl
Kelsi a8500a80b5
Some checks are pending
Build / Build (arm64) (push) Waiting to run
Build / Build (x86-64) (push) Waiting to run
Build / Build (macOS arm64) (push) Waiting to run
Build / Build (windows-arm64) (push) Waiting to run
Build / Build (windows-x86-64) (push) Waiting to run
Security / CodeQL (C/C++) (push) Waiting to run
Security / Semgrep (push) Waiting to run
Security / Sanitizer Build (ASan/UBSan) (push) Waiting to run
FSR2: selective clamp, tonemapped accumulation, terrain load radius 3
- Selective neighborhood clamp: only modify history when there's actual
  motion or disocclusion — static pixels pass history through untouched,
  preventing jitter-chasing from the shifting variance box
- Tonemapped accumulation: Reinhard tonemap before blend compresses bright
  edges so they don't disproportionately cause jitter
- Jitter-aware sample weighting: blend 3-20% based on sample proximity
- Soft MV dead zone: smoothstep instead of step avoids spatial discontinuity
- Aggressive velocity response: 30%/px motion, 50% cap, 80% disocclusion
- Terrain loading: radius 3 (49 tiles) to prevent spawn hitches,
  processOneReadyTile for smooth progress bar updates
2026-03-08 15:15:44 -07:00

50 lines
1.9 KiB
GLSL

#version 450
layout(local_size_x = 8, local_size_y = 8) in;
layout(set = 0, binding = 0) uniform sampler2D depthBuffer;
layout(set = 0, binding = 1, rg16f) uniform writeonly image2D motionVectors;
layout(push_constant) uniform PushConstants {
mat4 reprojMatrix; // prevUnjitteredVP * inverse(currentUnjitteredVP)
vec4 resolution; // xy = internal size, zw = 1/internal size
vec4 jitterOffset; // xy = current jitter (NDC), zw = unused
} pc;
void main() {
ivec2 pixelCoord = ivec2(gl_GlobalInvocationID.xy);
ivec2 imgSize = ivec2(pc.resolution.xy);
if (pixelCoord.x >= imgSize.x || pixelCoord.y >= imgSize.y) return;
float depth = texelFetch(depthBuffer, pixelCoord, 0).r;
// Pixel center UV and NDC
vec2 uv = (vec2(pixelCoord) + 0.5) * pc.resolution.zw;
vec2 ndc = uv * 2.0 - 1.0;
// Unjitter the NDC: the scene was rendered with jitter applied to
// projection[2][0/1]. For RH perspective (P[2][3]=-1, clip.w=-vz):
// jittered_ndc = unjittered_ndc - jitter
// unjittered_ndc = ndc + jitter
vec2 unjitteredNDC = ndc + pc.jitterOffset.xy;
// Reproject to previous frame via unjittered VP matrices
vec4 clipPos = vec4(unjitteredNDC, depth, 1.0);
vec4 prevClip = pc.reprojMatrix * clipPos;
vec2 prevNdc = prevClip.xy / prevClip.w;
vec2 prevUV = prevNdc * 0.5 + 0.5;
// Current unjittered UV for this pixel's world content
vec2 currentUnjitteredUV = unjitteredNDC * 0.5 + 0.5;
// Motion between unjittered positions — jitter-free.
// For a static scene (identity reprojMatrix), this is exactly zero.
vec2 motion = prevUV - currentUnjitteredUV;
// Soft dead zone: smoothly fade out sub-pixel noise from float precision
// in reprojMatrix (avoids hard spatial discontinuity from step())
float motionPx = length(motion * pc.resolution.xy);
motion *= smoothstep(0.0, 0.05, motionPx);
imageStore(motionVectors, pixelCoord, vec4(motion, 0.0, 0.0));
}