PPSSRTrace.bsl 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. #include "$ENGINE$\PPBase.bslinc"
  2. #include "$ENGINE$\GBufferInput.bslinc"
  3. #include "$ENGINE$\PerCameraData.bslinc"
  4. #include "$ENGINE$\ImportanceSampling.bslinc"
  5. #include "$ENGINE$\ColorSpace.bslinc"
  6. #define HI_Z 1
  7. #include "$ENGINE$\RayMarch.bslinc"
  8. technique PPSSRTrace
  9. {
  10. mixin PPBase;
  11. mixin PerCameraData;
  12. mixin GBufferInput;
  13. mixin RayMarch;
  14. mixin ImportanceSampling;
  15. mixin ColorSpace;
  16. stencil
  17. {
  18. enabled = true;
  19. reference = 0;
  20. front = { keep, keep, keep, eq };
  21. readmask = 0x7F;
  22. };
  23. code
  24. {
  25. [internal]
  26. cbuffer Input
  27. {
  28. float4 gNDCToHiZUV;
  29. float2 gHiZUVToScreenUV;
  30. int2 gHiZSize;
  31. int gHiZNumMips;
  32. float gIntensity;
  33. float2 gRoughnessScaleBias;
  34. int gTemporalJitter;
  35. }
  36. #ifndef MSAA_RESOLVE_0TH
  37. #define MSAA_RESOLVE_0TH 0
  38. #endif
  39. #if QUALITY == 0
  40. #define NUM_RAYS 1
  41. #elif QUALITY == 1
  42. #define NUM_RAYS 4
  43. #elif QUALITY == 2
  44. #define NUM_RAYS 8
  45. #elif QUALITY == 3
  46. #define NUM_RAYS 12
  47. #else
  48. #define NUM_RAYS 16
  49. #endif
  50. Texture2D gSceneColor;
  51. SamplerState gSceneColorSamp;
  52. Texture2D gHiZ;
  53. float random (float2 st)
  54. {
  55. // From https://thebookofshaders.com/10/
  56. return frac(sin(dot(st.xy, float2(12.9898, 78.233))) * 43758.5453123);
  57. }
  58. // Specialized morton code for 4x4 tiles
  59. uint mortonCode4x4(uint x, uint y)
  60. {
  61. return (x & 0x1) | ((x << 1) & 0x4)
  62. | (y << 1) & 0x2 | ((y << 2) & 0x8);
  63. }
  64. float4 fsmain(VStoFS input, float4 pixelPos : SV_Position
  65. #if MSAA_COUNT > 1 && !MSAA_RESOLVE_0TH
  66. , uint sampleIdx : SV_SampleIndex
  67. #endif
  68. ) : SV_Target0
  69. {
  70. #if MSAA_RESOLVE_0TH
  71. uint sampleIdx = 0;
  72. #endif
  73. #if MSAA_COUNT > 1
  74. SurfaceData surfData = getGBufferData(trunc(pixelPos.xy), sampleIdx);
  75. #else
  76. SurfaceData surfData = getGBufferData(input.uv0);
  77. #endif
  78. float3 P = NDCToWorld(input.screenPos, surfData.depth);
  79. float3 V = normalize(gViewOrigin - P);
  80. float3 N = surfData.worldNormal.xyz;
  81. float roughness = surfData.roughness;
  82. float roughness2 = roughness * roughness;
  83. float roughness4 = roughness2 * roughness2;
  84. // Jitter ray offset in 4x4 tile, in order to avoid stairstep artifacts
  85. uint pixelIdx = mortonCode4x4((uint)pixelPos.x, (uint)pixelPos.y);
  86. RayMarchParams rayMarchParams;
  87. rayMarchParams.bufferSize = gHiZSize;
  88. rayMarchParams.numMips = gHiZNumMips;
  89. rayMarchParams.NDCToHiZUV = gNDCToHiZUV;
  90. rayMarchParams.HiZUVToScreenUV = gHiZUVToScreenUV;
  91. rayMarchParams.rayOrigin = P;
  92. // Make sure each pixel chooses different ray directions (noise looks better than repeating patterns)
  93. //// Magic integer is arbitrary, in order to convert from [0, 1] float
  94. uint2 pixRandom = random(pixelPos.xy + gTemporalJitter * uint2(61, 85)) * uint2(0x36174842, 0x15249835);
  95. float4 sum = 0;
  96. [loop]
  97. for(int i = 0; i < NUM_RAYS; ++i)
  98. {
  99. uint rayRandom = (pixelIdx + (gTemporalJitter + i * 207) & 15);
  100. rayMarchParams.jitterOffset = rayRandom / 15.0f - 0.5f;
  101. float2 e = hammersleySequence(i, NUM_RAYS, pixRandom);
  102. float2 sphericalH = importanceSampleGGX(e, roughness4);
  103. float cosTheta = sphericalH.x;
  104. float phi = sphericalH.y;
  105. float sinTheta = sqrt(1.0f - cosTheta * cosTheta);
  106. float3 H = sphericalToCartesian(cosTheta, sinTheta, phi);
  107. // Transform H to world space
  108. float3 up = abs(H.z) < 0.999 ? float3(0, 0, 1) : float3(1, 0, 0);
  109. float3 tangentX = normalize(cross(up, N));
  110. float3 tangentY = cross(N, tangentX);
  111. H = tangentX * H.x + tangentY * H.y + N * H.z;
  112. float3 R = 2 * dot( V, H ) * H - V;
  113. // Eliminate rays pointing towards the viewer. They won't hit anything, plus they can screw up precision
  114. // and cause ray step offset to be too small, causing self-intersections.
  115. R = normalize(R); // Note: Normalization required?
  116. float dirFade = saturate(dot(R, gViewDir) * 10);
  117. if(dirFade < 0.00001f)
  118. continue;
  119. // Eliminate rays pointing below the surface
  120. if(dot(R, N) < 0.0005f)
  121. continue;
  122. rayMarchParams.rayDir = R;
  123. float4 rayHit = rayMarch(gHiZ, gDepthBufferSamp, rayMarchParams);
  124. if(rayHit.w < 1.0f) // Hit
  125. {
  126. float4 color = gSceneColor.Sample(gSceneColorSamp, rayHit.xy);
  127. color.a = 1.0f; // Marks the pixel as SSR
  128. // Fade out near screen edges
  129. float2 rayHitNDC = UVToNDC(rayHit.xy);
  130. float2 vignette = saturate(abs(rayHitNDC) * 5.0f - 4.0f);
  131. color = color * saturate(1.0f - dot(vignette, vignette));
  132. // Tonemap the color to get a nicer visual average
  133. color.rgb /= (1 + LuminanceRGB(color.rgb));
  134. sum += color * dirFade;
  135. }
  136. }
  137. // Divide by total number of rays, instead of actual number of rays that passed the test. This scales down the
  138. // contribution for pixels for which many rays failed the test and might not be accurate.
  139. float4 output = sum / NUM_RAYS;
  140. // Move back to high range (reverse tonemap)
  141. output.rgb /= (1 - LuminanceRGB(output.rgb));
  142. // Fade based on roughness
  143. float fadeValue = 1.0f - saturate(surfData.roughness * gRoughnessScaleBias.x + gRoughnessScaleBias.y);
  144. output *= fadeValue;
  145. output *= gIntensity;
  146. return output;
  147. }
  148. };
  149. };