hdrFilteringFunctions.fx 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. #ifdef NUM_SAMPLES
  2. #if NUM_SAMPLES > 0
  3. const float NUM_SAMPLES_FLOAT = float(NUM_SAMPLES);
  4. const float NUM_SAMPLES_FLOAT_INVERSED = 1. / NUM_SAMPLES_FLOAT;
  5. const float K = 4.;
  6. //
  7. //
  8. // Importance sampling GGX - Trowbridge-Reitz
  9. // ------------------------------------------
  10. //
  11. // Important samples are chosen to integrate Dggx() * cos(theta) over the hemisphere.
  12. //
  13. // All calculations are made in tangent space, with n = [0 0 1]
  14. //
  15. // l h (important sample)
  16. // .\ /.
  17. // . \ / .
  18. // . \ / .
  19. // . \/ .
  20. // ----+---o----+-------> n [0 0 1]
  21. // cos(2*theta) cos(theta)
  22. // = n•l = n•h
  23. //
  24. // v = n
  25. // f0 = f90 = 1
  26. // V = 1
  27. //
  28. // h is micro facet's normal
  29. //
  30. // l is the reflection of v (i.e.: n) around h ==> n•h = l•h = v•h
  31. //
  32. // h = important_sample_ggx()
  33. //
  34. // n•h = [0 0 1]•h = h.z
  35. //
  36. // l = reflect(-n, h)
  37. // = 2 * (n•h) * h - n;
  38. //
  39. // n•l = cos(2 * theta)
  40. // = cos(theta)^2 - sin(theta)^2
  41. // = (n•h)^2 - (1 - (n•h)^2)
  42. // = 2(n•h)^2 - 1
  43. //
  44. //
  45. // pdf() = D(h) <n•h> |J(h)|
  46. //
  47. // 1
  48. // |J(h)| = ----------
  49. // 4 <v•h>
  50. //
  51. // v = n -> <v•h>/<n•h> = 1
  52. //
  53. // pdf() = D(h) / 4
  54. //
  55. //
  56. // Pre-filtered importance sampling
  57. // --------------------------------
  58. //
  59. // see: "Real-time Shading with Filtered Importance Sampling", Jaroslav Krivanek
  60. // see: "GPU-Based Importance Sampling, GPU Gems 3", Mark Colbert
  61. //
  62. //
  63. // Ωs
  64. // lod = log4(K ----)
  65. // Ωp
  66. //
  67. // log4(K) = 1, works well for box filters
  68. // K = 4
  69. //
  70. // 1
  71. // Ωs = ---------, solid-angle of an important sample
  72. // N * pdf
  73. //
  74. // 4 PI
  75. // Ωp ~ --------------, solid-angle of a sample in the base cubemap
  76. // texel_count
  77. //
  78. //
  79. // Evaluating the integral
  80. // -----------------------
  81. //
  82. // K fr(h)
  83. // Er() = --- ∑ ------- L(h) <n•l>
  84. // N h pdf
  85. //
  86. // with:
  87. //
  88. // fr() = D(h)
  89. //
  90. // N
  91. // K = -----------------
  92. // fr(h)
  93. // ∑ ------- <n•l>
  94. // h pdf
  95. //
  96. //
  97. // It results that:
  98. //
  99. // K 4 <v•h>
  100. // Er() = --- ∑ D(h) ------------ L(h) <n•l>
  101. // N h D(h) <n•h>
  102. //
  103. // v = n -> <v•h>/<n•h> = 1
  104. //
  105. // K
  106. // Er() = 4 --- ∑ L(h) <n•l>
  107. // N h
  108. //
  109. // N 4
  110. // Er() = ------------- --- ∑ V(v) <n•l>
  111. // 4 ∑ <n•l> N
  112. //
  113. //
  114. // +------------------------------+
  115. // | ∑ <n•l> L(h) |
  116. // | Er() = -------------- |
  117. // | ∑ <n•l> |
  118. // +------------------------------+
  119. //
  120. //
  121. vec3 irradiance(samplerCube inputTexture, vec3 N, vec2 filteringInfo) {
  122. vec3 n = normalize(N);
  123. vec3 result = vec3(0.0);
  124. vec3 tangent = abs(n.z) < 0.999 ? vec3(0., 0., 1.) : vec3(1., 0., 0.);
  125. tangent = normalize(cross(tangent, n));
  126. vec3 bitangent = cross(n, tangent);
  127. mat3 tbn = mat3(tangent, bitangent, n);
  128. float maxLevel = filteringInfo.y;
  129. float dim0 = filteringInfo.x;
  130. float omegaP = (4. * PI) / (6. * dim0 * dim0);
  131. #ifdef WEBGL2
  132. for(uint i = 0u; i < NUM_SAMPLES; ++i)
  133. #else
  134. for(int i = 0; i < NUM_SAMPLES; ++i)
  135. #endif
  136. {
  137. vec2 Xi = hammersley(i, NUM_SAMPLES);
  138. vec3 Ls = hemisphereCosSample(Xi);
  139. Ls = normalize(Ls);
  140. vec3 Ns = vec3(0., 0., 1.);
  141. float NoL = dot(Ns, Ls);
  142. if (NoL > 0.) {
  143. float pdf_inversed = PI / NoL;
  144. float omegaS = NUM_SAMPLES_FLOAT_INVERSED * pdf_inversed;
  145. float l = log4(omegaS) - log4(omegaP) + log4(K);
  146. float mipLevel = clamp(l, 0.0, maxLevel);
  147. vec3 c = textureCubeLodEXT(inputTexture, tbn * Ls, mipLevel).rgb;
  148. #ifdef GAMMA_INPUT
  149. c = toLinearSpace(c);
  150. #endif
  151. result += c;
  152. }
  153. }
  154. result = result * NUM_SAMPLES_FLOAT_INVERSED;
  155. return result;
  156. }
  157. vec3 radiance(float alphaG, samplerCube inputTexture, vec3 N, vec2 filteringInfo) {
  158. vec3 n = normalize(N);
  159. if (alphaG == 0.) {
  160. vec3 c = textureCube(inputTexture, n).rgb;
  161. #ifdef GAMMA_INPUT
  162. c = toLinearSpace(c);
  163. #endif
  164. return c;
  165. }
  166. vec3 result = vec3(0.);
  167. vec3 tangent = abs(n.z) < 0.999 ? vec3(0., 0., 1.) : vec3(1., 0., 0.);
  168. tangent = normalize(cross(tangent, n));
  169. vec3 bitangent = cross(n, tangent);
  170. mat3 tbn = mat3(tangent, bitangent, n);
  171. float maxLevel = filteringInfo.y;
  172. float dim0 = filteringInfo.x;
  173. float omegaP = (4. * PI) / (6. * dim0 * dim0);
  174. float weight = 0.;
  175. #ifdef WEBGL2
  176. for(uint i = 0u; i < NUM_SAMPLES; ++i)
  177. #else
  178. for(int i = 0; i < NUM_SAMPLES; ++i)
  179. #endif
  180. {
  181. vec2 Xi = hammersley(i, NUM_SAMPLES);
  182. vec3 H = hemisphereImportanceSampleDggx(Xi, alphaG);
  183. float NoV = 1.;
  184. float NoH = H.z;
  185. float NoH2 = H.z * H.z;
  186. float NoL = 2. * NoH2 - 1.;
  187. vec3 L = vec3(2. * NoH * H.x, 2. * NoH * H.y, NoL);
  188. L = normalize(L);
  189. if (NoL > 0.) {
  190. float pdf_inversed = 4. / normalDistributionFunction_TrowbridgeReitzGGX(NoH, alphaG);
  191. float omegaS = NUM_SAMPLES_FLOAT_INVERSED * pdf_inversed;
  192. float l = log4(omegaS) - log4(omegaP) + log4(K);
  193. float mipLevel = clamp(float(l), 0.0, maxLevel);
  194. weight += NoL;
  195. vec3 c = textureCubeLodEXT(inputTexture, tbn * L, mipLevel).rgb;
  196. #ifdef GAMMA_INPUT
  197. c = toLinearSpace(c);
  198. #endif
  199. result += c * NoL;
  200. }
  201. }
  202. result = result / weight;
  203. return result;
  204. }
  205. #endif
  206. #endif