ReflectionCubeImportanceSample.bsl 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. #include "$ENGINE$\PPBase.bslinc"
  2. #include "$ENGINE$\ReflectionCubemapCommon.bslinc"
  3. Parameters =
  4. {
  5. int gCubeFace;
  6. SamplerCUBE gInputSamp : alias("gInputTex");
  7. TextureCUBE gInputTex;
  8. };
  9. Blocks =
  10. {
  11. Block Input;
  12. };
  13. Technique
  14. : inherits("PPBase")
  15. : inherits("ReflectionCubemapCommon") =
  16. {
  17. Language = "HLSL11";
  18. Pass =
  19. {
  20. Fragment =
  21. {
  22. #define PI 3.1415926
  23. // From Hacker's Delight
  24. float reverseBits(uint bits)
  25. {
  26. bits = (bits << 16u) | (bits >> 16u);
  27. bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
  28. bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
  29. bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
  30. bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
  31. return float(bits) * 2.3283064365386963e-10; // 0x100000000
  32. }
  33. float2 hammersleySequence(uint i, uint count)
  34. {
  35. float2 output;
  36. output.x = i / (float)count;
  37. output.y = reverseBits(i);
  38. return output;
  39. }
  40. // Returns cos(theta) in x and phi in y
  41. float2 importanceSampleGGX(float2 e, float roughness4)
  42. {
  43. // See GGXImportanceSample.nb for derivation (essentially, take base GGX, normalize it,
  44. // generate PDF, split PDF into marginal probability for theta and conditional probability
  45. // for phi. Plug those into the CDF, invert it.)
  46. float cosTheta = sqrt((1.0f - e.x) / (1.0f + (roughness4 - 1.0f) * e.y));
  47. float phi = 2.0f * PI * e.y;
  48. return float2(cosTheta, phi);
  49. }
  50. float3 sphericalToCartesian(float cosTheta, float sinTheta, float phi)
  51. {
  52. float3 output;
  53. output.x = sinTheta * cos(phi);
  54. output.y = sinTheta * sin(phi);
  55. output.z = cosTheta;
  56. return output;
  57. }
  58. float pdfGGX(float cosTheta, float sinTheta, float roughness4)
  59. {
  60. float d = (cosTheta*roughness4 - cosTheta) * cosTheta + 1;
  61. return roughness4 * cosTheta * sinTheta / (d*d*PI);
  62. }
  63. cbuffer Input
  64. {
  65. int gCubeFace;
  66. int gMipLevel;
  67. float gPrecomputedMipFactor;
  68. }
  69. SamplerState gInputSamp;
  70. TextureCube gInputTex;
  71. float4 main(VStoFS input) : SV_Target0
  72. {
  73. float2 scaledUV = input.uv0 * 2.0f - 1.0f;
  74. float3 N = getDirFromCubeFace(gCubeFace, scaledUV);
  75. N = normalize(N);
  76. // Determine which mip level to sample from depending on PDF and cube -> sphere mapping distortion
  77. float distortion = rcp(pow(N.x * N.x + N.y * N.y + N.z * N.z, 3.0f/2.0f));
  78. float roughness = mapMipLevelToRoughness(gMipLevel);
  79. float roughness2 = roughness * roughness;
  80. float roughness4 = roughness2 * roughness2;
  81. float4 sum = 0;
  82. for(uint i = 0; i < NUM_SAMPLES; i++)
  83. {
  84. float2 random = hammersleySequence(i, NUM_SAMPLES);
  85. float2 sphericalH = importanceSampleGGX(random, roughness4);
  86. float cosTheta = sphericalH.x;
  87. float phi = sphericalH.y;
  88. float sinTheta = sqrt(1.0f - cosTheta);
  89. float3 H = sphericalToCartesian(cosTheta, sinTheta, phi);
  90. float PDF = pdfGGX(cosTheta, sinTheta, roughness4);
  91. // Transform H to world space
  92. float3 up = abs(H.z) < 0.999 ? float3(0, 0, 1) : float3(1, 0, 0);
  93. float3 tangentX = normalize(cross(up, N));
  94. float3 tangentY = cross(N, tangentX);
  95. H = tangentX * H.x + tangentY * H.y + N * H.z;
  96. // Calculating mip level from distortion and pdf and described by http://http.developer.nvidia.com/GPUGems3/gpugems3_ch20.html
  97. int mipLevel = max(gPrecomputedMipFactor - 0.5f * log2(PDF * distortion), 0);
  98. // Note: Adding +1 bias as it looks better
  99. mipLevel++;
  100. // sum += H * GGX / PDF. In GGX/PDF most factors cancel out and we're left with 1/sin*cos
  101. sum += gInputTex.SampleLevel(gInputSamp, H, mipLevel) / (cosTheta * sinTheta);
  102. }
  103. return sum / NUM_SAMPLES;
  104. }
  105. };
  106. };
  107. };
  108. Technique
  109. : inherits("PPBase")
  110. : inherits("ReflectionCubemapCommon") =
  111. {
  112. Language = "GLSL";
  113. Pass =
  114. {
  115. Fragment =
  116. {
  117. in VStoFS
  118. {
  119. layout(location = 0) vec2 uv0;
  120. } FSInput;
  121. layout(location = 0) out vec4 fragColor;
  122. layout(binding = 0) uniform Input
  123. {
  124. int gCubeFace;
  125. };
  126. layout(binding = 1) uniform samplerCube gInputTex;
  127. void main()
  128. {
  129. vec2 scaledUV = FSInput.uv0 * 2.0f - 1.0f;
  130. vec3 N = getDirFromCubeFace(gCubeFace, scaledUV);
  131. fragColor = texture(gInputTex, dir);
  132. }
  133. };
  134. };
  135. };