ReflectionCubeImportanceSample.bsl 5.6 KB

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