pbrFunctions.fx 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. // Constants
  2. #define RECIPROCAL_PI2 0.15915494
  3. #define FRESNEL_MAXIMUM_ON_ROUGH 0.25
  4. // AlphaG epsilon to avoid numerical issues
  5. #define MINIMUMVARIANCE 0.0005
  6. float convertRoughnessToAverageSlope(float roughness)
  7. {
  8. // Calculate AlphaG as square of roughness; add epsilon to avoid numerical issues
  9. return square(roughness) + MINIMUMVARIANCE;
  10. }
  11. vec2 getAARoughnessFactors(vec3 normalVector) {
  12. #ifdef SPECULARAA
  13. vec3 nDfdx = dFdx(normalVector.xyz);
  14. vec3 nDfdy = dFdy(normalVector.xyz);
  15. float slopeSquare = max(dot(nDfdx, nDfdx), dot(nDfdy, nDfdy));
  16. // Vive analytical lights roughness factor.
  17. float geometricRoughnessFactor = pow(clamp(slopeSquare , 0., 1.), 0.333);
  18. // Adapt linear roughness (alphaG) to geometric curvature of the current pixel.
  19. float geometricAlphaGFactor = sqrt(slopeSquare);
  20. // BJS factor.
  21. geometricAlphaGFactor *= 0.75;
  22. return vec2(geometricRoughnessFactor, geometricAlphaGFactor);
  23. #else
  24. return vec2(0.);
  25. #endif
  26. }
  27. // From Microfacet Models for Refraction through Rough Surfaces, Walter et al. 2007
  28. // Keep for references
  29. // float smithVisibilityG1_TrowbridgeReitzGGX(float dot, float alphaG)
  30. // {
  31. // float tanSquared = (1.0 - dot * dot) / (dot * dot);
  32. // return 2.0 / (1.0 + sqrt(1.0 + alphaG * alphaG * tanSquared));
  33. // }
  34. // float smithVisibility_TrowbridgeReitzGGX_Walter(float NdotL, float NdotV, float alphaG)
  35. // {
  36. // float visibility = smithVisibilityG1_TrowbridgeReitzGGX(NdotL, alphaG) * smithVisibilityG1_TrowbridgeReitzGGX(NdotV, alphaG);
  37. // visibility /= (4.0 * NdotL * NdotV); // Cook Torance Denominator integrated in visibility to avoid issues when visibility function changes.
  38. // return visibility;
  39. // }
  40. // From smithVisibilityG1_TrowbridgeReitzGGX * dot / dot to cancel the cook
  41. // torrance denominator :-)
  42. float smithVisibilityG1_TrowbridgeReitzGGXFast(float dot, float alphaG)
  43. {
  44. float alphaSquared = alphaG * alphaG;
  45. return 1.0 / (dot + sqrt(alphaSquared + (1.0 - alphaSquared) * dot * dot));
  46. }
  47. // From smithVisibilityG1_TrowbridgeReitzGGXFast
  48. // Appply simplification as all squared root terms are below 1 and squared
  49. // Ready to be used
  50. // float smithVisibilityG1_TrowbridgeReitzGGXMobile(float dot, float alphaG)
  51. // {
  52. // return 1.0 / (dot + alpha + (1.0 - alpha) * dot ));
  53. // }
  54. float smithVisibility_TrowbridgeReitzGGXFast(float NdotL, float NdotV, float alphaG)
  55. {
  56. float visibility = smithVisibilityG1_TrowbridgeReitzGGXFast(NdotL, alphaG) * smithVisibilityG1_TrowbridgeReitzGGXFast(NdotV, alphaG);
  57. // No Cook Torance Denominator as it is canceled out in the previous form
  58. return visibility;
  59. }
  60. float kelemenVisibility(float VdotH) {
  61. // Simplified form integration the cook torrance denminator.
  62. // Expanded is nl * nv / vh2 which factor with 1 / (4 * nl * nv)
  63. // giving 1 / (4 * vh2))
  64. return 0.25 / (VdotH * VdotH);
  65. }
  66. // Trowbridge-Reitz (GGX)
  67. // Generalised Trowbridge-Reitz with gamma power=2.0
  68. float normalDistributionFunction_TrowbridgeReitzGGX(float NdotH, float alphaG)
  69. {
  70. // Note: alphaG is average slope (gradient) of the normals in slope-space.
  71. // It is also the (trigonometric) tangent of the median distribution value, i.e. 50% of normals have
  72. // a tangent (gradient) closer to the macrosurface than this slope.
  73. float a2 = square(alphaG);
  74. float d = NdotH * NdotH * (a2 - 1.0) + 1.0;
  75. return a2 / (PI * d * d);
  76. }
  77. // Aniso parameter remapping
  78. // https://blog.selfshadow.com/publications/s2017-shading-course/imageworks/s2017_pbs_imageworks_slides_v2.pdf page 24
  79. vec2 getAnisotropicRoughness(float alphaG, float anisotropy) {
  80. float alphaT = max(alphaG * (1.0 + anisotropy), MINIMUMVARIANCE);
  81. float alphaB = max(alphaG * (1.0 - anisotropy), MINIMUMVARIANCE);
  82. return vec2(alphaT, alphaB);
  83. }
  84. // GGX Distribution Anisotropic
  85. // https://blog.selfshadow.com/publications/s2012-shading-course/burley/s2012_pbs_disney_brdf_notes_v3.pdf Addenda
  86. float normalDistributionFunction_BurleyGGX_Anisotropic(float NdotH, float TdotH, float BdotH, const vec2 alphaTB) {
  87. float a2 = alphaTB.x * alphaTB.y;
  88. vec3 v = vec3(alphaTB.y * TdotH, alphaTB.x * BdotH, a2 * NdotH);
  89. float v2 = dot(v, v);
  90. float w2 = a2 / v2;
  91. return a2 * w2 * w2 * (1.0 / PI);
  92. }
  93. // GGX Mask/Shadowing Anisotropic
  94. // Heitz http://jcgt.org/published/0003/02/03/paper.pdf
  95. float smithVisibility_GGXCorrelated_Anisotropic(float NdotV, float NdotL, float TdotV, float BdotV, float TdotL, float BdotL, const vec2 alphaTB) {
  96. float lambdaV = NdotL * length(vec3(alphaTB.x * TdotV, alphaTB.y * BdotV, NdotV));
  97. float lambdaL = NdotV * length(vec3(alphaTB.x * TdotL, alphaTB.y * BdotL, NdotL));
  98. float v = 0.5 / (lambdaV + lambdaL);
  99. return v;
  100. }
  101. vec3 fresnelSchlickGGXVec3(float VdotH, vec3 reflectance0, vec3 reflectance90)
  102. {
  103. return reflectance0 + (reflectance90 - reflectance0) * pow(1.0 - VdotH, 5.0);
  104. }
  105. float fresnelSchlickGGXFloat(float VdotH, float reflectance0, float reflectance90)
  106. {
  107. return reflectance0 + (reflectance90 - reflectance0) * pow(1.0 - VdotH, 5.0);
  108. }
  109. vec3 fresnelSchlickEnvironmentGGX(float VdotN, vec3 reflectance0, vec3 reflectance90, float smoothness)
  110. {
  111. // Schlick fresnel approximation, extended with basic smoothness term so that rough surfaces do not approach reflectance90 at grazing angle
  112. float weight = mix(FRESNEL_MAXIMUM_ON_ROUGH, 1.0, smoothness);
  113. return reflectance0 + weight * (reflectance90 - reflectance0) * pow(clamp(1.0 - VdotN, 0., 1.), 5.0);
  114. }
  115. float computeDiffuseTerm(float NdotL, float NdotV, float VdotH, float roughness)
  116. {
  117. // Diffuse fresnel falloff as per Disney principled BRDF, and in the spirit of
  118. // of general coupled diffuse/specular models e.g. Ashikhmin Shirley.
  119. float diffuseFresnelNV = pow(clamp(1.0 - NdotL, 0.000001, 1.), 5.0);
  120. float diffuseFresnelNL = pow(clamp(1.0 - NdotV, 0.000001, 1.), 5.0);
  121. float diffuseFresnel90 = 0.5 + 2.0 * VdotH * VdotH * roughness;
  122. float fresnel =
  123. (1.0 + (diffuseFresnel90 - 1.0) * diffuseFresnelNL) *
  124. (1.0 + (diffuseFresnel90 - 1.0) * diffuseFresnelNV);
  125. return fresnel / PI;
  126. }
  127. // Cook Torance Specular computation.
  128. vec3 computeSpecularTerm(float NdotH, float NdotL, float NdotV, float VdotH, float roughness, vec3 reflectance0, vec3 reflectance90, float geometricRoughnessFactor) {
  129. roughness = max(roughness, geometricRoughnessFactor);
  130. float alphaG = convertRoughnessToAverageSlope(roughness);
  131. float distribution = normalDistributionFunction_TrowbridgeReitzGGX(NdotH, alphaG);
  132. float visibility = smithVisibility_TrowbridgeReitzGGXFast(NdotL, NdotV, alphaG);
  133. float specTerm = max(0., visibility * distribution);
  134. vec3 fresnel = fresnelSchlickGGXVec3(VdotH, reflectance0, reflectance90);
  135. return fresnel * specTerm;
  136. }
  137. 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) {
  138. float alphaG = convertRoughnessToAverageSlope(roughness);
  139. vec2 alphaTB = getAnisotropicRoughness(alphaG, anisotropy);
  140. alphaTB = max(alphaTB, geometricRoughnessFactor * geometricRoughnessFactor);
  141. float distribution = normalDistributionFunction_BurleyGGX_Anisotropic(NdotH, TdotH, BdotH, alphaTB);
  142. float visibility = smithVisibility_GGXCorrelated_Anisotropic(NdotV, NdotL, TdotV, BdotV, TdotL, BdotL, alphaTB);
  143. float specTerm = max(0., visibility * distribution);
  144. vec3 fresnel = fresnelSchlickGGXVec3(VdotH, reflectance0, reflectance90);
  145. return fresnel * specTerm;
  146. }
  147. vec2 computeClearCoatTerm(float NdotH, float VdotH, float clearCoatRoughness, float geometricRoughnessFactor, float clearCoatIntensity) {
  148. clearCoatRoughness = max(clearCoatRoughness, geometricRoughnessFactor);
  149. float alphaG = convertRoughnessToAverageSlope(clearCoatRoughness);
  150. float distribution = normalDistributionFunction_TrowbridgeReitzGGX(NdotH, alphaG);
  151. float visibility = kelemenVisibility(VdotH);
  152. float clearCoatTerm = max(0., visibility * distribution);
  153. // fo = 4% based on the IOR of a air-polyurethane interface.
  154. // the max reflectance is relying on our special trick to prevent weird values on highly diffuse materials.
  155. // To let as a configuration if required
  156. const float reflectance0 = 0.04;
  157. const float reflectance90 = 1.;
  158. float fresnel = fresnelSchlickGGXFloat(VdotH, reflectance0, reflectance90);
  159. fresnel *= clearCoatIntensity;
  160. return vec2(fresnel * clearCoatTerm, 1.0 - fresnel);
  161. }
  162. float adjustRoughnessFromLightProperties(float roughness, float lightRadius, float lightDistance)
  163. {
  164. #if defined(USEPHYSICALLIGHTFALLOFF) || defined(USEGLTFLIGHTFALLOFF)
  165. // At small angle this approximation works.
  166. float lightRoughness = lightRadius / lightDistance;
  167. // Distribution can sum.
  168. float totalRoughness = clamp(lightRoughness + roughness, 0., 1.);
  169. return totalRoughness;
  170. #else
  171. return roughness;
  172. #endif
  173. }
  174. float computeDefaultMicroSurface(float microSurface, vec3 reflectivityColor)
  175. {
  176. const float kReflectivityNoAlphaWorkflow_SmoothnessMax = 0.95;
  177. float reflectivityLuminance = getLuminance(reflectivityColor);
  178. float reflectivityLuma = sqrt(reflectivityLuminance);
  179. microSurface = reflectivityLuma * kReflectivityNoAlphaWorkflow_SmoothnessMax;
  180. return microSurface;
  181. }
  182. // For typical incident reflectance range (between 4% to 100%) set the grazing reflectance to 100% for typical fresnel effect.
  183. // For very low reflectance range on highly diffuse objects (below 4%), incrementally reduce grazing reflecance to 0%.
  184. float fresnelGrazingReflectance(float reflectance0) {
  185. float reflectance90 = clamp(reflectance0 * 25.0, 0.0, 1.0);
  186. return reflectance90;
  187. }
  188. // To enable 8 bit textures to be used we need to pack and unpack the LOD
  189. //inverse alpha is used to work around low-alpha bugs in Edge and Firefox
  190. #define UNPACK_LOD(x) (1.0 - x) * 255.0
  191. float getLodFromAlphaG(float cubeMapDimensionPixels, float alphaG, float NdotV) {
  192. float microsurfaceAverageSlope = alphaG;
  193. // Compensate for solid angle change between half-vector measure (Blinn-Phong) and reflected-vector measure (Phong):
  194. // dWr = 4*cos(theta)*dWh,
  195. // where dWr = solid angle (delta omega) in environment incident radiance (reflection-vector) measure;
  196. // where dWh = solid angle (delta omega) in microfacet normal (half-vector) measure;
  197. // so the relationship is proportional to cosine theta = NdotV.
  198. // The constant factor of four is handled elsewhere as part of the scale/offset filter parameters.
  199. microsurfaceAverageSlope *= sqrt(abs(NdotV));
  200. float microsurfaceAverageSlopeTexels = microsurfaceAverageSlope * cubeMapDimensionPixels;
  201. float lod = log2(microsurfaceAverageSlopeTexels);
  202. return lod;
  203. }
  204. float environmentRadianceOcclusion(float ambientOcclusion, float NdotVUnclamped) {
  205. // Best balanced (implementation time vs result vs perf) analytical environment specular occlusion found.
  206. // http://research.tri-ace.com/Data/cedec2011_RealtimePBR_Implementation_e.pptx
  207. float temp = NdotVUnclamped + ambientOcclusion;
  208. return clamp(square(temp) - 1.0 + ambientOcclusion, 0.0, 1.0);
  209. }
  210. float environmentHorizonOcclusion(vec3 view, vec3 normal) {
  211. // http://marmosetco.tumblr.com/post/81245981087
  212. vec3 reflection = reflect(view, normal);
  213. float temp = clamp( 1.0 + 1.1 * dot(reflection, normal), 0.0, 1.0);
  214. return square(temp);
  215. }