PPSSRTrace.bsl 5.0 KB

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