hdrFilteringFunctions.fx 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  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. #define inline
  122. vec3 irradiance(samplerCube inputTexture, vec3 inputN, vec2 filteringInfo)
  123. {
  124. vec3 n = normalize(inputN);
  125. vec3 result = vec3(0.0);
  126. vec3 tangent = abs(n.z) < 0.999 ? vec3(0., 0., 1.) : vec3(1., 0., 0.);
  127. tangent = normalize(cross(tangent, n));
  128. vec3 bitangent = cross(n, tangent);
  129. mat3 tbn = mat3(tangent, bitangent, n);
  130. float maxLevel = filteringInfo.y;
  131. float dim0 = filteringInfo.x;
  132. float omegaP = (4. * PI) / (6. * dim0 * dim0);
  133. #ifdef WEBGL2
  134. for(uint i = 0u; i < NUM_SAMPLES; ++i)
  135. #else
  136. for(int i = 0; i < NUM_SAMPLES; ++i)
  137. #endif
  138. {
  139. vec2 Xi = hammersley(i, NUM_SAMPLES);
  140. vec3 Ls = hemisphereCosSample(Xi);
  141. Ls = normalize(Ls);
  142. vec3 Ns = vec3(0., 0., 1.);
  143. float NoL = dot(Ns, Ls);
  144. if (NoL > 0.) {
  145. float pdf_inversed = PI / NoL;
  146. float omegaS = NUM_SAMPLES_FLOAT_INVERSED * pdf_inversed;
  147. float l = log4(omegaS) - log4(omegaP) + log4(K);
  148. float mipLevel = clamp(l, 0.0, maxLevel);
  149. vec3 c = textureCubeLodEXT(inputTexture, tbn * Ls, mipLevel).rgb;
  150. #ifdef GAMMA_INPUT
  151. c = toLinearSpace(c);
  152. #endif
  153. result += c;
  154. }
  155. }
  156. result = result * NUM_SAMPLES_FLOAT_INVERSED;
  157. return result;
  158. }
  159. #define inline
  160. vec3 radiance(float alphaG, samplerCube inputTexture, vec3 inputN, vec2 filteringInfo)
  161. {
  162. vec3 n = normalize(inputN);
  163. if (alphaG == 0.) {
  164. vec3 c = textureCube(inputTexture, n).rgb;
  165. #ifdef GAMMA_INPUT
  166. c = toLinearSpace(c);
  167. #endif
  168. return c;
  169. }
  170. vec3 result = vec3(0.);
  171. vec3 tangent = abs(n.z) < 0.999 ? vec3(0., 0., 1.) : vec3(1., 0., 0.);
  172. tangent = normalize(cross(tangent, n));
  173. vec3 bitangent = cross(n, tangent);
  174. mat3 tbn = mat3(tangent, bitangent, n);
  175. float maxLevel = filteringInfo.y;
  176. float dim0 = filteringInfo.x;
  177. float omegaP = (4. * PI) / (6. * dim0 * dim0);
  178. float weight = 0.;
  179. #ifdef WEBGL2
  180. for(uint i = 0u; i < NUM_SAMPLES; ++i)
  181. #else
  182. for(int i = 0; i < NUM_SAMPLES; ++i)
  183. #endif
  184. {
  185. vec2 Xi = hammersley(i, NUM_SAMPLES);
  186. vec3 H = hemisphereImportanceSampleDggx(Xi, alphaG);
  187. float NoV = 1.;
  188. float NoH = H.z;
  189. float NoH2 = H.z * H.z;
  190. float NoL = 2. * NoH2 - 1.;
  191. vec3 L = vec3(2. * NoH * H.x, 2. * NoH * H.y, NoL);
  192. L = normalize(L);
  193. if (NoL > 0.) {
  194. float pdf_inversed = 4. / normalDistributionFunction_TrowbridgeReitzGGX(NoH, alphaG);
  195. float omegaS = NUM_SAMPLES_FLOAT_INVERSED * pdf_inversed;
  196. float l = log4(omegaS) - log4(omegaP) + log4(K);
  197. float mipLevel = clamp(float(l), 0.0, maxLevel);
  198. weight += NoL;
  199. vec3 c = textureCubeLodEXT(inputTexture, tbn * L, mipLevel).rgb;
  200. #ifdef GAMMA_INPUT
  201. c = toLinearSpace(c);
  202. #endif
  203. result += c * NoL;
  204. }
  205. }
  206. result = result / weight;
  207. return result;
  208. }
  209. #endif
  210. #endif