PPSSRTrace.bsl 5.1 KB

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