| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188 |
- #include "$ENGINE$\PPBase.bslinc"
- #include "$ENGINE$\GBufferInput.bslinc"
- #include "$ENGINE$\PerCameraData.bslinc"
- #include "$ENGINE$\ImportanceSampling.bslinc"
- #include "$ENGINE$\ColorSpace.bslinc"
- #define HI_Z 1
- #include "$ENGINE$\RayMarch.bslinc"
- technique PPSSRTrace
- {
- mixin PPBase;
- mixin PerCameraData;
- mixin GBufferInput;
- mixin RayMarch;
- mixin ImportanceSampling;
- mixin ColorSpace;
- stencil
- {
- enabled = true;
- reference = 0;
- front = { keep, keep, keep, eq };
- readmask = 0x7F;
- };
-
- code
- {
- [internal]
- cbuffer Input
- {
- float4 gNDCToHiZUV;
- float2 gHiZUVToScreenUV;
- int2 gHiZSize;
- int gHiZNumMips;
- float gIntensity;
- float2 gRoughnessScaleBias;
- int gTemporalJitter;
- }
-
- #ifndef MSAA_RESOLVE_0TH
- #define MSAA_RESOLVE_0TH 0
- #endif
-
- #if QUALITY == 0
- #define NUM_RAYS 1
- #elif QUALITY == 1
- #define NUM_RAYS 4
- #elif QUALITY == 2
- #define NUM_RAYS 8
- #elif QUALITY == 3
- #define NUM_RAYS 12
- #else
- #define NUM_RAYS 16
- #endif
-
- Texture2D gSceneColor;
- SamplerState gSceneColorSamp;
-
- Texture2D gHiZ;
- SamplerState gHiZSamp;
-
- float random (float2 st)
- {
- // From https://thebookofshaders.com/10/
- return frac(sin(dot(st.xy, float2(12.9898, 78.233))) * 43758.5453123);
- }
-
- // Specialized morton code for 4x4 tiles
- uint mortonCode4x4(uint x, uint y)
- {
- return (x & 0x1) | ((x << 1) & 0x4)
- | (y << 1) & 0x2 | ((y << 2) & 0x8);
- }
- float4 fsmain(VStoFS input, float4 pixelPos : SV_Position
- #if MSAA_COUNT > 1 && !MSAA_RESOLVE_0TH
- , uint sampleIdx : SV_SampleIndex
- #endif
- ) : SV_Target0
- {
- #if MSAA_RESOLVE_0TH
- uint sampleIdx = 0;
- #endif
-
- #if MSAA_COUNT > 1
- SurfaceData surfData = getGBufferData(trunc(input.uv0.xy), sampleIdx);
- #else
- SurfaceData surfData = getGBufferData(input.uv0);
- #endif
-
- float3 P = NDCToWorld(input.screenPos, surfData.depth);
- float3 V = normalize(gViewOrigin - P);
- float3 N = surfData.worldNormal.xyz;
-
- float roughness = surfData.roughness;
-
- float roughness2 = roughness * roughness;
- float roughness4 = roughness2 * roughness2;
-
- // Jitter ray offset in 4x4 tile, in order to avoid stairstep artifacts
- uint pixelIdx = mortonCode4x4((uint)pixelPos.x, (uint)pixelPos.y);
-
- RayMarchParams rayMarchParams;
- rayMarchParams.bufferSize = gHiZSize;
- rayMarchParams.numMips = gHiZNumMips;
- rayMarchParams.NDCToHiZUV = gNDCToHiZUV;
- rayMarchParams.HiZUVToScreenUV = gHiZUVToScreenUV;
- rayMarchParams.rayOrigin = P;
- // Make sure each pixel chooses different ray directions (noise looks better than repeating patterns)
- //// Magic integer is arbitrary, in order to convert from [0, 1] float
- uint2 pixRandom = random(pixelPos.xy + gTemporalJitter * uint2(61, 85)) * uint2(0x36174842, 0x15249835);
- float4 sum = 0;
- [loop]
- for(int i = 0; i < NUM_RAYS; ++i)
- {
- uint rayRandom = (pixelIdx + (gTemporalJitter + i * 207) & 15);
- rayMarchParams.jitterOffset = rayRandom / 15.0f - 0.5f;
-
- float2 e = hammersleySequence(i, NUM_RAYS, pixRandom);
- float2 sphericalH = importanceSampleGGX(e, roughness4);
-
- float cosTheta = sphericalH.x;
- float phi = sphericalH.y;
- float sinTheta = sqrt(1.0f - cosTheta * cosTheta);
-
- float3 H = sphericalToCartesian(cosTheta, sinTheta, phi);
- // Transform H to world space
- float3 up = abs(H.z) < 0.999 ? float3(0, 0, 1) : float3(1, 0, 0);
- float3 tangentX = normalize(cross(up, N));
- float3 tangentY = cross(N, tangentX);
-
- H = tangentX * H.x + tangentY * H.y + N * H.z;
- float3 R = 2 * dot( V, H ) * H - V;
-
- // Eliminate rays pointing towards the viewer. They won't hit anything, plus they can screw up precision
- // and cause ray step offset to be too small, causing self-intersections.
- R = normalize(R); // Note: Normalization required?
- float dirFade = saturate(dot(R, gViewDir) * 10);
-
- if(dirFade < 0.00001f)
- continue;
-
- // Eliminate rays pointing below the surface
- if(dot(R, N) < 0.0005f)
- continue;
-
- rayMarchParams.rayDir = R;
- float4 rayHit = rayMarch(gHiZ, gHiZSamp, rayMarchParams);
- if(rayHit.w < 1.0f) // Hit
- {
- float4 color = gSceneColor.Sample(gSceneColorSamp, rayHit.xy);
- color.a = 1.0f; // Marks the pixel as SSR
-
- // Fade out near screen edges
- float2 rayHitNDC = UVToNDC(rayHit.xy);
- float2 vignette = saturate(abs(rayHitNDC) * 5.0f - 4.0f);
-
- color = color * saturate(1.0f - dot(vignette, vignette));
-
- // Tonemap the color to get a nicer visual average
- color.rgb /= (1 + LuminanceRGB(color.rgb));
-
- sum += color * dirFade;
- }
- }
-
- // Divide by total number of rays, instead of actual number of rays that passed the test. This scales down the
- // contribution for pixels for which many rays failed the test and might not be accurate.
- float4 output = sum / NUM_RAYS;
-
- // Move back to high range (reverse tonemap)
- output.rgb /= (1 - LuminanceRGB(output.rgb));
-
- // Fade based on roughness
- float fadeValue = 1.0f - saturate(surfData.roughness * gRoughnessScaleBias.x + gRoughnessScaleBias.y);
-
- output *= fadeValue;
- output *= gIntensity;
-
- return output;
- }
- };
- };
|