PPSSRTrace.bsl 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  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. };
  22. code
  23. {
  24. [internal]
  25. cbuffer Input
  26. {
  27. float4 gNDCToHiZUV;
  28. float2 gHiZUVToScreenUV;
  29. int2 gHiZSize;
  30. int gHiZNumMips;
  31. float gIntensity;
  32. float2 gRoughnessScaleBias;
  33. }
  34. Texture2D gSceneColor;
  35. SamplerState gSceneColorSamp;
  36. Texture2D gHiZ;
  37. float random (float2 st)
  38. {
  39. // From https://thebookofshaders.com/10/
  40. return frac(sin(dot(st.xy, float2(12.9898, 78.233))) * 43758.5453123);
  41. }
  42. // Specialized morton code for 4x4 tiles
  43. uint mortonCode4x4(uint x, uint y)
  44. {
  45. return (x & 0x1) | ((x << 1) & 0x4)
  46. | (y << 1) & 0x2 | ((y << 2) & 0x8);
  47. }
  48. float4 fsmain(VStoFS input, float4 pixelPos : SV_Position) : SV_Target0
  49. {
  50. // TODO - Support MSAA?
  51. SurfaceData surfData = getGBufferData(input.uv0);
  52. float3 P = NDCToWorld(input.screenPos, surfData.depth);
  53. float3 V = normalize(gViewOrigin - P);
  54. float3 N = surfData.worldNormal.xyz;
  55. float roughness = surfData.roughness;
  56. roughness = 0.3f;//DEBUG ONLY
  57. float roughness2 = roughness * roughness;
  58. float roughness4 = roughness2 * roughness2;
  59. // TODO - DEBUG ONLY - Only handle reflections on up facing surfaces
  60. if(dot(N, float3(0,1,0)) < 0.8)
  61. return gSceneColor.Sample(gSceneColorSamp, input.uv0);
  62. else
  63. N = float3(0,1,0);
  64. // Jitter ray offset in 4x4 tile, in order to avoid stairstep artifacts
  65. uint pixelIdx = mortonCode4x4((uint)pixelPos.x, (uint)pixelPos.y);
  66. float jitterOffset = (pixelIdx & 15) / 15.0f - 0.5f; // TODO - Also add per-frame jitter
  67. RayMarchParams rayMarchParams;
  68. rayMarchParams.bufferSize = gHiZSize;
  69. rayMarchParams.numMips = gHiZNumMips;
  70. rayMarchParams.NDCToHiZUV = gNDCToHiZUV;
  71. rayMarchParams.HiZUVToScreenUV = gHiZUVToScreenUV;
  72. rayMarchParams.rayOrigin = P;
  73. rayMarchParams.jitterOffset = jitterOffset;
  74. int NUM_RAYS = 64; // DEBUG ONLY
  75. float4 sum = 0;
  76. [loop]
  77. for(int i = 0; i < NUM_RAYS; ++i)
  78. {
  79. // TODO - Add per-frame random? (for temporal filtering)
  80. float2 random = hammersleySequence(i, NUM_RAYS);
  81. float2 sphericalH = importanceSampleGGX(random, roughness4);
  82. float cosTheta = sphericalH.x;
  83. float phi = sphericalH.y;
  84. float sinTheta = sqrt(1.0f - cosTheta * cosTheta);
  85. float3 H = sphericalToCartesian(cosTheta, sinTheta, phi);
  86. // Transform H to world space
  87. float3 up = abs(H.z) < 0.999 ? float3(0, 0, 1) : float3(1, 0, 0);
  88. float3 tangentX = normalize(cross(up, N));
  89. float3 tangentY = cross(N, tangentX);
  90. H = tangentX * H.x + tangentY * H.y + N * H.z;
  91. float3 R = 2 * dot( V, H ) * H - V;
  92. // Eliminate rays pointing towards the viewer. They won't hit anything, plus they can screw up precision
  93. // and cause ray step offset to be too small, causing self-intersections.
  94. R = normalize(R); // Note: Normalization required?
  95. if(dot(R, gViewDir) < 0.0f)
  96. continue;
  97. // Eliminate rays pointing below the surface
  98. if(dot(R, N) < 0.0005f)
  99. continue;
  100. rayMarchParams.rayDir = R;
  101. float4 rayHit = rayMarch(gHiZ, gDepthBufferSamp, rayMarchParams);
  102. if(rayHit.w < 1.0f) // Hit
  103. {
  104. float4 color = gSceneColor.Sample(gSceneColorSamp, rayHit.xy);
  105. color.a = 1.0f; // Marks the pixel as SSR
  106. // Fade out near screen edges
  107. float2 rayHitNDC = UVToNDC(rayHit.xy);
  108. float2 vignette = saturate(abs(rayHitNDC) * 5.0f - 4.0f);
  109. color = color * saturate(1.0f - dot(vignette, vignette));
  110. // Note: Not accounting for PDF here since we don't evaluate BRDF until later. Looks good though.
  111. // Tonemap the color to get a nicer visual average
  112. color.rgb /= (1 + LuminanceRGB(color.rgb));
  113. sum += color;
  114. }
  115. }
  116. // Divide by total number of rays, instead of actual number of rays that passed the test. This scales down the
  117. // contribution for pixels for which many rays failed the test and might not be accurate.
  118. float4 output = sum / NUM_RAYS;
  119. // Move back to high range (reverse tonemap)
  120. output.rgb /= (1 - LuminanceRGB(output.rgb));
  121. // Fade based on roughness
  122. float fadeValue = 1.0f - saturate(surfData.roughness * gRoughnessScaleBias.x + gRoughnessScaleBias.y);
  123. output *= fadeValue;
  124. output *= gIntensity;
  125. return output;
  126. }
  127. };
  128. };