pbrFunctions.fx 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  1. // Constants
  2. #define RECIPROCAL_PI2 0.15915494
  3. #define RECIPROCAL_PI 0.31830988618
  4. #define FRESNEL_MAXIMUM_ON_ROUGH 0.25
  5. // AlphaG epsilon to avoid numerical issues
  6. #define MINIMUMVARIANCE 0.0005
  7. #define CLEARCOATREFLECTANCE90 1.0
  8. float convertRoughnessToAverageSlope(float roughness)
  9. {
  10. // Calculate AlphaG as square of roughness; add epsilon to avoid numerical issues
  11. return square(roughness) + MINIMUMVARIANCE;
  12. }
  13. vec2 getAARoughnessFactors(vec3 normalVector) {
  14. #ifdef SPECULARAA
  15. vec3 nDfdx = dFdx(normalVector.xyz);
  16. vec3 nDfdy = dFdy(normalVector.xyz);
  17. float slopeSquare = max(dot(nDfdx, nDfdx), dot(nDfdy, nDfdy));
  18. // Vive analytical lights roughness factor.
  19. float geometricRoughnessFactor = pow(clamp(slopeSquare , 0., 1.), 0.333);
  20. // Adapt linear roughness (alphaG) to geometric curvature of the current pixel.
  21. float geometricAlphaGFactor = sqrt(slopeSquare);
  22. // BJS factor.
  23. geometricAlphaGFactor *= 0.75;
  24. return vec2(geometricRoughnessFactor, geometricAlphaGFactor);
  25. #else
  26. return vec2(0.);
  27. #endif
  28. }
  29. #ifdef MS_BRDF_ENERGY_CONSERVATION
  30. // http://www.jcgt.org/published/0008/01/03/
  31. // http://advances.realtimerendering.com/s2018/Siggraph%202018%20HDRP%20talk_with%20notes.pdf
  32. vec3 getEnergyConservationFactor(const vec3 specularEnvironmentR0, vec2 environmentBrdf) {
  33. return 1.0 + specularEnvironmentR0 * (1.0 / environmentBrdf.y - 1.0);
  34. }
  35. #endif
  36. vec2 getBRDFLookup(float NdotV, float perceptualRoughness, sampler2D brdfSampler) {
  37. // Indexed on cos(theta) and roughness
  38. vec2 UV = vec2(NdotV, perceptualRoughness);
  39. // We can find the scale and offset to apply to the specular value.
  40. vec2 brdfLookup = texture2D(brdfSampler, UV).xy;
  41. return brdfLookup;
  42. }
  43. /**
  44. * Special thanks to @romainguy for all the support :-)
  45. * Analytical approximation of the pre-filtered DFG terms for the cloth shading
  46. * model. This approximation is based on the Estevez & Kulla distribution term
  47. * ("Charlie" sheen) and the Neubelt visibility term. See brdf.fs for more
  48. * details.
  49. */
  50. vec2 getCharlieSheenAnalyticalBRDFLookup_RomainGuy(float NoV, float roughness) {
  51. const vec3 c0 = vec3(0.95, 1250.0, 0.0095);
  52. const vec4 c1 = vec4(0.04, 0.2, 0.3, 0.2);
  53. float a = 1.0 - NoV;
  54. float b = 1.0 - roughness;
  55. float n = pow(c1.x + a, 64.0);
  56. float e = b - c0.x;
  57. float g = exp2(-(e * e) * c0.y);
  58. float f = b + c1.y;
  59. float a2 = a * a;
  60. float a3 = a2 * a;
  61. float c = n * g + c1.z * (a + c1.w) * roughness + f * f * a3 * a3 * a2;
  62. float r = min(c, 18.0);
  63. return vec2(r, r * c0.z);
  64. }
  65. vec3 getReflectanceFromBRDFLookup(const vec3 specularEnvironmentR0, vec2 environmentBrdf) {
  66. #ifdef BRDF_V_HEIGHT_CORRELATED
  67. vec3 reflectance = mix(environmentBrdf.xxx, environmentBrdf.yyy, specularEnvironmentR0);
  68. #else
  69. vec3 reflectance = specularEnvironmentR0 * environmentBrdf.x + environmentBrdf.y;
  70. #endif
  71. return reflectance;
  72. }
  73. vec3 getReflectanceFromAnalyticalBRDFLookup_Jones(float VdotN, vec3 reflectance0, vec3 reflectance90, float smoothness)
  74. {
  75. // Schlick fresnel approximation, extended with basic smoothness term so that rough surfaces do not approach reflectance90 at grazing angle
  76. float weight = mix(FRESNEL_MAXIMUM_ON_ROUGH, 1.0, smoothness);
  77. return reflectance0 + weight * (reflectance90 - reflectance0) * pow(clamp(1.0 - VdotN, 0., 1.), 5.0);
  78. }
  79. vec3 getSheenReflectanceFromBRDFLookup(const vec3 reflectance0, float NdotV, float sheenAlphaG) {
  80. vec2 environmentSheenBrdf = getCharlieSheenAnalyticalBRDFLookup_RomainGuy(NdotV, sheenAlphaG);
  81. vec3 reflectance = reflectance0 * environmentSheenBrdf.x + environmentSheenBrdf.y;
  82. return reflectance;
  83. }
  84. // Schlick's approximation for R0 (Fresnel Reflectance Values)
  85. // Keep for references
  86. // vec3 getR0fromAirToSurfaceIOR(vec3 ior1) {
  87. // return getR0fromIOR(ior1, vec3(1.0));
  88. // }
  89. // vec3 getR0fromIOR(vec3 ior1, vec3 ior2) {
  90. // vec3 t = (ior1 - ior2) / (ior1 + ior2);
  91. // return t * t;
  92. // }
  93. // vec3 getIORfromAirToSurfaceR0(vec3 f0) {
  94. // vec3 s = sqrt(f0);
  95. // return (1.0 + s) / (1.0 - s);
  96. // }
  97. // f0 Remapping due to layers
  98. // vec3 getR0RemappedForClearCoat(vec3 f0, vec3 clearCoatF0) {
  99. // vec3 iorBase = getIORfromAirToSurfaceR0(f0);
  100. // vec3 clearCoatIor = getIORfromAirToSurfaceR0(clearCoatF0);
  101. // return getR0fromIOR(iorBase, clearCoatIor);
  102. // }
  103. #ifdef CLEARCOAT
  104. // Knowing ior clear coat is fix for the material
  105. // Solving iorbase = 1 + sqrt(fo) / (1 - sqrt(fo)) and f0base = square((iorbase - iorclearcoat) / (iorbase - iorclearcoat))
  106. // provide f0base = square(A + B * sqrt(fo)) / (B + A * sqrt(fo))
  107. // where A = 1 - iorclearcoat
  108. // and B = 1 + iorclearcoat
  109. vec3 getR0RemappedForClearCoat(vec3 f0) {
  110. #ifdef CLEARCOAT_DEFAULTIOR
  111. #ifdef MOBILE
  112. return clamp(f0 * (f0 * 0.526868 + 0.529324) - 0.0482256, 0., 1.);
  113. #else
  114. return clamp(f0 * (f0 * (0.941892 - 0.263008 * f0) + 0.346479) - 0.0285998, 0., 1.);
  115. #endif
  116. #else
  117. vec3 s = sqrt(f0);
  118. vec3 t = (vClearCoatRefractionParams.z + vClearCoatRefractionParams.w * s) / (vClearCoatRefractionParams.w + vClearCoatRefractionParams.z * s);
  119. return t * t;
  120. #endif
  121. }
  122. #endif
  123. // From Microfacet Models for Refraction through Rough Surfaces, Walter et al. 2007
  124. // Keep for references
  125. // float smithVisibilityG1_TrowbridgeReitzGGX(float dot, float alphaG)
  126. // {
  127. // float tanSquared = (1.0 - dot * dot) / (dot * dot);
  128. // return 2.0 / (1.0 + sqrt(1.0 + alphaG * alphaG * tanSquared));
  129. // }
  130. // float smithVisibility_TrowbridgeReitzGGX_Walter(float NdotL, float NdotV, float alphaG)
  131. // {
  132. // float visibility = smithVisibilityG1_TrowbridgeReitzGGX(NdotL, alphaG) * smithVisibilityG1_TrowbridgeReitzGGX(NdotV, alphaG);
  133. // visibility /= (4.0 * NdotL * NdotV); // Cook Torance Denominator integrated in visibility to avoid issues when visibility function changes.
  134. // return visibility;
  135. // }
  136. // From smithVisibilityG1_TrowbridgeReitzGGX * dot / dot to cancel the cook
  137. // torrance denominator :-)
  138. float smithVisibilityG1_TrowbridgeReitzGGXFast(float dot, float alphaG)
  139. {
  140. #ifdef MOBILE
  141. // Appply simplification as all squared root terms are below 1 and squared
  142. return 1.0 / (dot + alphaG + (1.0 - alphaG) * dot ));
  143. #else
  144. float alphaSquared = alphaG * alphaG;
  145. return 1.0 / (dot + sqrt(alphaSquared + (1.0 - alphaSquared) * dot * dot));
  146. #endif
  147. }
  148. float smithVisibility_TrowbridgeReitzGGXFast(float NdotL, float NdotV, float alphaG)
  149. {
  150. float visibility = smithVisibilityG1_TrowbridgeReitzGGXFast(NdotL, alphaG) * smithVisibilityG1_TrowbridgeReitzGGXFast(NdotV, alphaG);
  151. // No Cook Torance Denominator as it is canceled out in the previous form
  152. return visibility;
  153. }
  154. float visibility_Kelemen(float VdotH) {
  155. // Simplified form integration the cook torrance denminator.
  156. // Expanded is nl * nv / vh2 which factor with 1 / (4 * nl * nv)
  157. // giving 1 / (4 * vh2))
  158. return 0.25 / (VdotH * VdotH);
  159. }
  160. // https://knarkowicz.wordpress.com/2018/01/04/cloth-shading/
  161. // https://blog.selfshadow.com/publications/s2017-shading-course/imageworks/s2017_pbs_imageworks_sheen.pdf
  162. // http://www.cs.utah.edu/~premoze/dbrdf/dBRDF.pdf
  163. float visibility_Ashikhmin(float NdotL, float NdotV)
  164. {
  165. return 1. / (4. * (NdotL + NdotV - NdotL * NdotV));
  166. }
  167. float normalDistributionFunction_CharlieSheen(float NdotH, float alphaG)
  168. {
  169. float invR = 1. / alphaG;
  170. float cos2h = NdotH * NdotH;
  171. float sin2h = 1. - cos2h;
  172. return (2. + invR) * pow(sin2h, invR * .5) / (2. * PI);
  173. }
  174. // Trowbridge-Reitz (GGX)
  175. // Generalised Trowbridge-Reitz with gamma power=2.0
  176. float normalDistributionFunction_TrowbridgeReitzGGX(float NdotH, float alphaG)
  177. {
  178. // Note: alphaG is average slope (gradient) of the normals in slope-space.
  179. // It is also the (trigonometric) tangent of the median distribution value, i.e. 50% of normals have
  180. // a tangent (gradient) closer to the macrosurface than this slope.
  181. float a2 = square(alphaG);
  182. float d = NdotH * NdotH * (a2 - 1.0) + 1.0;
  183. return a2 / (PI * d * d);
  184. }
  185. // Aniso parameter remapping
  186. // https://blog.selfshadow.com/publications/s2017-shading-course/imageworks/s2017_pbs_imageworks_slides_v2.pdf page 24
  187. vec2 getAnisotropicRoughness(float alphaG, float anisotropy) {
  188. float alphaT = max(alphaG * (1.0 + anisotropy), MINIMUMVARIANCE);
  189. float alphaB = max(alphaG * (1.0 - anisotropy), MINIMUMVARIANCE);
  190. return vec2(alphaT, alphaB);
  191. }
  192. // Aniso Bent Normals
  193. // Mc Alley https://www.gdcvault.com/play/1022235/Rendering-the-World-of-Far
  194. vec3 getAnisotropicBentNormals(const vec3 T, const vec3 B, const vec3 N, const vec3 V, float anisotropy) {
  195. vec3 anisotropicFrameDirection = anisotropy >= 0.0 ? B : T;
  196. vec3 anisotropicFrameTangent = cross(normalize(anisotropicFrameDirection), V);
  197. vec3 anisotropicFrameNormal = cross(anisotropicFrameTangent, anisotropicFrameDirection);
  198. vec3 anisotropicNormal = normalize(mix(N, anisotropicFrameNormal, abs(anisotropy)));
  199. return anisotropicNormal;
  200. // should we also do http://advances.realtimerendering.com/s2018/Siggraph%202018%20HDRP%20talk_with%20notes.pdf page 80 ?
  201. }
  202. // GGX Distribution Anisotropic
  203. // https://blog.selfshadow.com/publications/s2012-shading-course/burley/s2012_pbs_disney_brdf_notes_v3.pdf Addenda
  204. float normalDistributionFunction_BurleyGGX_Anisotropic(float NdotH, float TdotH, float BdotH, const vec2 alphaTB) {
  205. float a2 = alphaTB.x * alphaTB.y;
  206. vec3 v = vec3(alphaTB.y * TdotH, alphaTB.x * BdotH, a2 * NdotH);
  207. float v2 = dot(v, v);
  208. float w2 = a2 / v2;
  209. return a2 * w2 * w2 * RECIPROCAL_PI;
  210. }
  211. // GGX Mask/Shadowing Isotropic
  212. // Heitz http://jcgt.org/published/0003/02/03/paper.pdf
  213. // https://twvideo01.ubm-us.net/o1/vault/gdc2017/Presentations/Hammon_Earl_PBR_Diffuse_Lighting.pdf
  214. float smithVisibility_GGXCorrelated(float NdotV, float NdotL, float alphaG) {
  215. #ifdef MOBILE
  216. // Appply simplification as all squared root terms are below 1 and squared
  217. float GGXV = NdotL * (NdotV * (1.0 - alphaG) + alphaG);
  218. float GGXL = NdotV * (NdotL * (1.0 - alphaG) + alphaG);
  219. return 0.5 / (GGXV + GGXL);
  220. #else
  221. float a2 = alphaG * alphaG;
  222. float GGXV = NdotL * sqrt(NdotV * NdotV * (1.0 - a2) + a2);
  223. float GGXL = NdotV * sqrt(NdotL * NdotL * (1.0 - a2) + a2);
  224. return 0.5 / (GGXV + GGXL);
  225. #endif
  226. }
  227. // GGX Mask/Shadowing Anisotropic
  228. // Heitz http://jcgt.org/published/0003/02/03/paper.pdf
  229. float smithVisibility_GGXCorrelated_Anisotropic(float NdotV, float NdotL, float TdotV, float BdotV, float TdotL, float BdotL, const vec2 alphaTB) {
  230. float lambdaV = NdotL * length(vec3(alphaTB.x * TdotV, alphaTB.y * BdotV, NdotV));
  231. float lambdaL = NdotV * length(vec3(alphaTB.x * TdotL, alphaTB.y * BdotL, NdotL));
  232. float v = 0.5 / (lambdaV + lambdaL);
  233. return v;
  234. }
  235. vec3 fresnelSchlickGGX(float VdotH, vec3 reflectance0, vec3 reflectance90)
  236. {
  237. return reflectance0 + (reflectance90 - reflectance0) * pow(1.0 - VdotH, 5.0);
  238. }
  239. float fresnelSchlickGGX(float VdotH, float reflectance0, float reflectance90)
  240. {
  241. return reflectance0 + (reflectance90 - reflectance0) * pow(1.0 - VdotH, 5.0);
  242. }
  243. // From beer lambert law I1/I0 = e −α′lc
  244. // c is considered included in alpha
  245. // https://blog.selfshadow.com/publications/s2017-shading-course/drobot/s2017_pbs_multilayered.pdf page 47
  246. // where L on a thin constant size layer can be (d * ((NdotLRefract + NdotVRefract) / (NdotLRefract * NdotVRefract))
  247. vec3 cocaLambert(float NdotVRefract, float NdotLRefract, vec3 alpha, float thickness) {
  248. return exp(alpha * -(thickness * ((NdotLRefract + NdotVRefract) / (NdotLRefract * NdotVRefract))));
  249. }
  250. // From beerLambert Solves what alpha should be for a given resutlt at a known distance.
  251. vec3 computeColorAtDistanceInMedia(vec3 color, float distance) {
  252. return -log(color) / distance;
  253. }
  254. // Disney diffuse term
  255. // https://blog.selfshadow.com/publications/s2012-shading-course/burley/s2012_pbs_disney_brdf_notes_v3.pdf
  256. // Page 14
  257. float computeDiffuseTerm(float NdotL, float NdotV, float VdotH, float roughness) {
  258. // Diffuse fresnel falloff as per Disney principled BRDF, and in the spirit of
  259. // of general coupled diffuse/specular models e.g. Ashikhmin Shirley.
  260. float diffuseFresnelNV = pow(clamp(1.0 - NdotL, 0.000001, 1.), 5.0);
  261. float diffuseFresnelNL = pow(clamp(1.0 - NdotV, 0.000001, 1.), 5.0);
  262. float diffuseFresnel90 = 0.5 + 2.0 * VdotH * VdotH * roughness;
  263. float fresnel =
  264. (1.0 + (diffuseFresnel90 - 1.0) * diffuseFresnelNL) *
  265. (1.0 + (diffuseFresnel90 - 1.0) * diffuseFresnelNV);
  266. return fresnel / PI;
  267. }
  268. // Cook Torance Specular computation.
  269. vec3 computeSpecularTerm(float NdotH, float NdotL, float NdotV, float VdotH, float roughness, vec3 reflectance0, vec3 reflectance90, float geometricRoughnessFactor) {
  270. roughness = max(roughness, geometricRoughnessFactor);
  271. float alphaG = convertRoughnessToAverageSlope(roughness);
  272. float distribution = normalDistributionFunction_TrowbridgeReitzGGX(NdotH, alphaG);
  273. #ifdef BRDF_V_HEIGHT_CORRELATED
  274. float visibility = smithVisibility_GGXCorrelated(NdotL, NdotV, alphaG);
  275. #else
  276. float visibility = smithVisibility_TrowbridgeReitzGGXFast(NdotL, NdotV, alphaG);
  277. #endif
  278. float specTerm = max(0., visibility * distribution);
  279. vec3 fresnel = fresnelSchlickGGX(VdotH, reflectance0, reflectance90);
  280. return fresnel * specTerm;
  281. }
  282. #ifdef SHEEN
  283. vec3 computeSheenTerm(float NdotH, float NdotL, float NdotV, float VdotH, float roughness, vec3 reflectance0, vec3 reflectance90, float geometricRoughnessFactor) {
  284. roughness = max(roughness, geometricRoughnessFactor);
  285. float alphaG = convertRoughnessToAverageSlope(roughness);
  286. float distribution = normalDistributionFunction_CharlieSheen(NdotH, alphaG);
  287. float visibility = visibility_Ashikhmin(NdotL, NdotV);
  288. float specTerm = max(0., visibility * distribution);
  289. vec3 fresnel = fresnelSchlickGGX(VdotH, reflectance0, reflectance90);
  290. return vec3(specTerm);
  291. }
  292. #endif
  293. #ifdef ANISOTROPIC
  294. vec3 computeAnisotropicSpecularTerm(float NdotH, float NdotL, float NdotV, float VdotH, float TdotH, float BdotH, float TdotV, float BdotV, float TdotL, float BdotL, float roughness, float anisotropy, vec3 reflectance0, vec3 reflectance90, float geometricRoughnessFactor) {
  295. float alphaG = convertRoughnessToAverageSlope(roughness);
  296. vec2 alphaTB = getAnisotropicRoughness(alphaG, anisotropy);
  297. alphaTB = max(alphaTB, geometricRoughnessFactor * geometricRoughnessFactor);
  298. float distribution = normalDistributionFunction_BurleyGGX_Anisotropic(NdotH, TdotH, BdotH, alphaTB);
  299. float visibility = smithVisibility_GGXCorrelated_Anisotropic(NdotV, NdotL, TdotV, BdotV, TdotL, BdotL, alphaTB);
  300. float specTerm = max(0., visibility * distribution);
  301. vec3 fresnel = fresnelSchlickGGX(VdotH, reflectance0, reflectance90);
  302. return fresnel * specTerm;
  303. }
  304. #endif
  305. #ifdef CLEARCOAT
  306. vec2 computeClearCoatTerm(float NdotH, float VdotH, float clearCoatRoughness, float geometricRoughnessFactor, float clearCoatIntensity) {
  307. clearCoatRoughness = max(clearCoatRoughness, geometricRoughnessFactor);
  308. float alphaG = convertRoughnessToAverageSlope(clearCoatRoughness);
  309. float distribution = normalDistributionFunction_TrowbridgeReitzGGX(NdotH, alphaG);
  310. float visibility = visibility_Kelemen(VdotH);
  311. float clearCoatTerm = max(0., visibility * distribution);
  312. float fresnel = fresnelSchlickGGX(VdotH, vClearCoatRefractionParams.x, CLEARCOATREFLECTANCE90);
  313. fresnel *= clearCoatIntensity;
  314. return vec2(fresnel * clearCoatTerm, 1.0 - fresnel);
  315. }
  316. vec3 computeClearCoatAbsorption(float NdotVRefract, float NdotLRefract, vec3 clearCoatColor, float clearCoatThickness, float clearCoatIntensity) {
  317. vec3 clearCoatAbsorption = mix(vec3(1.0),
  318. cocaLambert(NdotVRefract, NdotLRefract, clearCoatColor, clearCoatThickness),
  319. clearCoatIntensity);
  320. return clearCoatAbsorption;
  321. }
  322. #endif
  323. float adjustRoughnessFromLightProperties(float roughness, float lightRadius, float lightDistance)
  324. {
  325. #if defined(USEPHYSICALLIGHTFALLOFF) || defined(USEGLTFLIGHTFALLOFF)
  326. // At small angle this approximation works.
  327. float lightRoughness = lightRadius / lightDistance;
  328. // Distribution can sum.
  329. float totalRoughness = clamp(lightRoughness + roughness, 0., 1.);
  330. return totalRoughness;
  331. #else
  332. return roughness;
  333. #endif
  334. }
  335. float computeDefaultMicroSurface(float microSurface, vec3 reflectivityColor)
  336. {
  337. const float kReflectivityNoAlphaWorkflow_SmoothnessMax = 0.95;
  338. float reflectivityLuminance = getLuminance(reflectivityColor);
  339. float reflectivityLuma = sqrt(reflectivityLuminance);
  340. microSurface = reflectivityLuma * kReflectivityNoAlphaWorkflow_SmoothnessMax;
  341. return microSurface;
  342. }
  343. // For typical incident reflectance range (between 4% to 100%) set the grazing reflectance to 100% for typical fresnel effect.
  344. // For very low reflectance range on highly diffuse objects (below 4%), incrementally reduce grazing reflecance to 0%.
  345. float fresnelGrazingReflectance(float reflectance0) {
  346. float reflectance90 = clamp(reflectance0 * 25.0, 0.0, 1.0);
  347. return reflectance90;
  348. }
  349. // To enable 8 bit textures to be used we need to pack and unpack the LOD
  350. //inverse alpha is used to work around low-alpha bugs in Edge and Firefox
  351. #define UNPACK_LOD(x) (1.0 - x) * 255.0
  352. float getLodFromAlphaG(float cubeMapDimensionPixels, float alphaG, float NdotV) {
  353. float microsurfaceAverageSlope = alphaG;
  354. // Compensate for solid angle change between half-vector measure (Blinn-Phong) and reflected-vector measure (Phong):
  355. // dWr = 4*cos(theta)*dWh,
  356. // where dWr = solid angle (delta omega) in environment incident radiance (reflection-vector) measure;
  357. // where dWh = solid angle (delta omega) in microfacet normal (half-vector) measure;
  358. // so the relationship is proportional to cosine theta = NdotV.
  359. // The constant factor of four is handled elsewhere as part of the scale/offset filter parameters.
  360. microsurfaceAverageSlope *= sqrt(abs(NdotV));
  361. float microsurfaceAverageSlopeTexels = microsurfaceAverageSlope * cubeMapDimensionPixels;
  362. float lod = log2(microsurfaceAverageSlopeTexels);
  363. return lod;
  364. }
  365. float environmentRadianceOcclusion(float ambientOcclusion, float NdotVUnclamped) {
  366. // Best balanced (implementation time vs result vs perf) analytical environment specular occlusion found.
  367. // http://research.tri-ace.com/Data/cedec2011_RealtimePBR_Implementation_e.pptx
  368. float temp = NdotVUnclamped + ambientOcclusion;
  369. return clamp(square(temp) - 1.0 + ambientOcclusion, 0.0, 1.0);
  370. }
  371. float environmentHorizonOcclusion(vec3 view, vec3 normal) {
  372. // http://marmosetco.tumblr.com/post/81245981087
  373. vec3 reflection = reflect(view, normal);
  374. float temp = clamp( 1.0 + 1.1 * dot(reflection, normal), 0.0, 1.0);
  375. return square(temp);
  376. }