pbrBRDFFunctions.fx 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. // Constants
  2. #define FRESNEL_MAXIMUM_ON_ROUGH 0.25
  3. // ______________________________________________________________________
  4. //
  5. // BRDF LOOKUP
  6. // ______________________________________________________________________
  7. #ifdef MS_BRDF_ENERGY_CONSERVATION
  8. // http://www.jcgt.org/published/0008/01/03/
  9. // http://advances.realtimerendering.com/s2018/Siggraph%202018%20HDRP%20talk_with%20notes.pdf
  10. vec3 getEnergyConservationFactor(const vec3 specularEnvironmentR0, const vec3 environmentBrdf) {
  11. return 1.0 + specularEnvironmentR0 * (1.0 / environmentBrdf.y - 1.0);
  12. }
  13. #endif
  14. #ifdef ENVIRONMENTBRDF
  15. vec3 getBRDFLookup(float NdotV, float perceptualRoughness) {
  16. // Indexed on cos(theta) and roughness
  17. vec2 UV = vec2(NdotV, perceptualRoughness);
  18. // We can find the scale and offset to apply to the specular value.
  19. vec4 brdfLookup = texture2D(environmentBrdfSampler, UV);
  20. #ifdef ENVIRONMENTBRDF_RGBD
  21. brdfLookup.rgb = fromRGBD(brdfLookup.rgba);
  22. #endif
  23. return brdfLookup.rgb;
  24. }
  25. vec3 getReflectanceFromBRDFLookup(const vec3 specularEnvironmentR0, const vec3 specularEnvironmentR90, const vec3 environmentBrdf) {
  26. #ifdef BRDF_V_HEIGHT_CORRELATED
  27. vec3 reflectance = (specularEnvironmentR90 - specularEnvironmentR0) * environmentBrdf.x + specularEnvironmentR0 * environmentBrdf.y;
  28. // Simplification if F90 = 1 vec3 reflectance = (specularEnvironmentR90 - specularEnvironmentR0) * environmentBrdf.xxx + specularEnvironmentR0 * environmentBrdf.yyy;
  29. #else
  30. vec3 reflectance = specularEnvironmentR0 * environmentBrdf.x + specularEnvironmentR90 * environmentBrdf.y;
  31. #endif
  32. return reflectance;
  33. }
  34. vec3 getReflectanceFromBRDFLookup(const vec3 specularEnvironmentR0, const vec3 environmentBrdf) {
  35. #ifdef BRDF_V_HEIGHT_CORRELATED
  36. vec3 reflectance = mix(environmentBrdf.xxx, specularEnvironmentR0, environmentBrdf.yyy);
  37. #else
  38. vec3 reflectance = specularEnvironmentR0 * environmentBrdf.x + environmentBrdf.y;
  39. #endif
  40. return reflectance;
  41. }
  42. #endif
  43. /* NOT USED
  44. #if defined(SHEEN) && defined(SHEEN_SOFTER)
  45. // Approximation of (integral on hemisphere)[f_sheen*cos(theta)*dtheta*dphi]
  46. float getBRDFLookupCharlieSheen(float NdotV, float perceptualRoughness)
  47. {
  48. float c = 1.0 - NdotV;
  49. float c3 = c*c*c;
  50. return 0.65584461 * c3 + 1.0 / (4.16526551 + exp(-7.97291361*perceptualRoughness+6.33516894));
  51. }
  52. #endif
  53. */
  54. #if !defined(ENVIRONMENTBRDF) || defined(REFLECTIONMAP_SKYBOX) || defined(ALPHAFRESNEL)
  55. vec3 getReflectanceFromAnalyticalBRDFLookup_Jones(float VdotN, vec3 reflectance0, vec3 reflectance90, float smoothness)
  56. {
  57. // Schlick fresnel approximation, extended with basic smoothness term so that rough surfaces do not approach reflectance90 at grazing angle
  58. float weight = mix(FRESNEL_MAXIMUM_ON_ROUGH, 1.0, smoothness);
  59. return reflectance0 + weight * (reflectance90 - reflectance0) * pow5(saturate(1.0 - VdotN));
  60. }
  61. #endif
  62. #if defined(SHEEN) && defined(ENVIRONMENTBRDF)
  63. /**
  64. * The sheen BRDF not containing F can be easily stored in the blue channel of the BRDF texture.
  65. * The blue channel contains DCharlie * VAshikhmin * NdotL as a lokkup table
  66. */
  67. vec3 getSheenReflectanceFromBRDFLookup(const vec3 reflectance0, const vec3 environmentBrdf) {
  68. vec3 sheenEnvironmentReflectance = reflectance0 * environmentBrdf.b;
  69. return sheenEnvironmentReflectance;
  70. }
  71. #endif
  72. // ______________________________________________________________________
  73. //
  74. // Schlick/Fresnel
  75. // ______________________________________________________________________
  76. // iorI incident iorT transmitted
  77. // Schlick's approximation for R0 (Fresnel Reflectance Values)
  78. // Keep for references
  79. // vec3 getR0fromIORs(vec3 iorT, vec3 iorI) {
  80. // vec3 t = (iorT - iorI) / (iorT + iorI);
  81. // return t * t;
  82. // }
  83. // vec3 getR0fromAirToSurfaceIORT(vec3 iorT) {
  84. // return getR0fromIOR(iorT, vec3(1.0));
  85. // }
  86. // vec3 getIORTfromAirToSurfaceR0(vec3 f0) {
  87. // vec3 s = sqrt(f0);
  88. // return (1.0 + s) / (1.0 - s);
  89. // }
  90. // f0 Remapping due to layers
  91. // vec3 getR0RemappedForClearCoat(vec3 f0, vec3 clearCoatF0) {
  92. // vec3 iorBase = getIORfromAirToSurfaceR0(f0);
  93. // vec3 clearCoatIor = getIORfromAirToSurfaceR0(clearCoatF0);
  94. // return getR0fromIOR(iorBase, clearCoatIor);
  95. // }
  96. vec3 fresnelSchlickGGX(float VdotH, vec3 reflectance0, vec3 reflectance90)
  97. {
  98. return reflectance0 + (reflectance90 - reflectance0) * pow5(1.0 - VdotH);
  99. }
  100. float fresnelSchlickGGX(float VdotH, float reflectance0, float reflectance90)
  101. {
  102. return reflectance0 + (reflectance90 - reflectance0) * pow5(1.0 - VdotH);
  103. }
  104. #ifdef CLEARCOAT
  105. // Knowing ior clear coat is fix for the material
  106. // Solving iorbase = 1 + sqrt(fo) / (1 - sqrt(fo)) and f0base = square((iorbase - iorclearcoat) / (iorbase + iorclearcoat))
  107. // provide f0base = square(A + B * sqrt(fo)) / (B + A * sqrt(fo))
  108. // where A = 1 - iorclearcoat
  109. // and B = 1 + iorclearcoat
  110. vec3 getR0RemappedForClearCoat(vec3 f0) {
  111. #ifdef CLEARCOAT_DEFAULTIOR
  112. #ifdef MOBILE
  113. return saturate(f0 * (f0 * 0.526868 + 0.529324) - 0.0482256);
  114. #else
  115. return saturate(f0 * (f0 * (0.941892 - 0.263008 * f0) + 0.346479) - 0.0285998);
  116. #endif
  117. #else
  118. vec3 s = sqrt(f0);
  119. vec3 t = (vClearCoatRefractionParams.z + vClearCoatRefractionParams.w * s) / (vClearCoatRefractionParams.w + vClearCoatRefractionParams.z * s);
  120. return t * t;
  121. #endif
  122. }
  123. #endif
  124. // ______________________________________________________________________
  125. //
  126. // Distribution
  127. // ______________________________________________________________________
  128. // Trowbridge-Reitz (GGX)
  129. // Generalised Trowbridge-Reitz with gamma power=2.0
  130. float normalDistributionFunction_TrowbridgeReitzGGX(float NdotH, float alphaG)
  131. {
  132. // Note: alphaG is average slope (gradient) of the normals in slope-space.
  133. // It is also the (trigonometric) tangent of the median distribution value, i.e. 50% of normals have
  134. // a tangent (gradient) closer to the macrosurface than this slope.
  135. float a2 = square(alphaG);
  136. float d = NdotH * NdotH * (a2 - 1.0) + 1.0;
  137. return a2 / (PI * d * d);
  138. }
  139. #ifdef SHEEN
  140. // http://www.aconty.com/pdf/s2017_pbs_imageworks_sheen.pdf
  141. // https://knarkowicz.wordpress.com/2018/01/04/cloth-shading/
  142. float normalDistributionFunction_CharlieSheen(float NdotH, float alphaG)
  143. {
  144. float invR = 1. / alphaG;
  145. float cos2h = NdotH * NdotH;
  146. float sin2h = 1. - cos2h;
  147. return (2. + invR) * pow(sin2h, invR * .5) / (2. * PI);
  148. }
  149. #endif
  150. #ifdef ANISOTROPIC
  151. // GGX Distribution Anisotropic
  152. // https://blog.selfshadow.com/publications/s2012-shading-course/burley/s2012_pbs_disney_brdf_notes_v3.pdf Addenda
  153. float normalDistributionFunction_BurleyGGX_Anisotropic(float NdotH, float TdotH, float BdotH, const vec2 alphaTB) {
  154. float a2 = alphaTB.x * alphaTB.y;
  155. vec3 v = vec3(alphaTB.y * TdotH, alphaTB.x * BdotH, a2 * NdotH);
  156. float v2 = dot(v, v);
  157. float w2 = a2 / v2;
  158. return a2 * w2 * w2 * RECIPROCAL_PI;
  159. }
  160. #endif
  161. // ______________________________________________________________________
  162. //
  163. // Visibility/Geometry
  164. // ______________________________________________________________________
  165. #ifdef BRDF_V_HEIGHT_CORRELATED
  166. // GGX Mask/Shadowing Isotropic
  167. // Heitz http://jcgt.org/published/0003/02/03/paper.pdf
  168. // https://twvideo01.ubm-us.net/o1/vault/gdc2017/Presentations/Hammon_Earl_PBR_Diffuse_Lighting.pdf
  169. float smithVisibility_GGXCorrelated(float NdotL, float NdotV, float alphaG) {
  170. #ifdef MOBILE
  171. // Appply simplification as all squared root terms are below 1 and squared
  172. float GGXV = NdotL * (NdotV * (1.0 - alphaG) + alphaG);
  173. float GGXL = NdotV * (NdotL * (1.0 - alphaG) + alphaG);
  174. return 0.5 / (GGXV + GGXL);
  175. #else
  176. float a2 = alphaG * alphaG;
  177. float GGXV = NdotL * sqrt(NdotV * (NdotV - a2 * NdotV) + a2);
  178. float GGXL = NdotV * sqrt(NdotL * (NdotL - a2 * NdotL) + a2);
  179. return 0.5 / (GGXV + GGXL);
  180. #endif
  181. }
  182. #else
  183. // From Microfacet Models for Refraction through Rough Surfaces, Walter et al. 2007
  184. // Keep for references
  185. // float smithVisibilityG1_TrowbridgeReitzGGX(float dot, float alphaG)
  186. // {
  187. // float tanSquared = (1.0 - dot * dot) / (dot * dot);
  188. // return 2.0 / (1.0 + sqrt(1.0 + alphaG * alphaG * tanSquared));
  189. // }
  190. // float smithVisibility_TrowbridgeReitzGGX_Walter(float NdotL, float NdotV, float alphaG)
  191. // {
  192. // float visibility = smithVisibilityG1_TrowbridgeReitzGGX(NdotL, alphaG) * smithVisibilityG1_TrowbridgeReitzGGX(NdotV, alphaG);
  193. // visibility /= (4.0 * NdotL * NdotV); // Cook Torance Denominator integrated in visibility to avoid issues when visibility function changes.
  194. // return visibility;
  195. // }
  196. // From smithVisibilityG1_TrowbridgeReitzGGX * dot / dot to cancel the cook
  197. // torrance denominator :-)
  198. float smithVisibilityG1_TrowbridgeReitzGGXFast(float dot, float alphaG)
  199. {
  200. #ifdef MOBILE
  201. // Appply simplification as all squared root terms are below 1 and squared
  202. return 1.0 / (dot + alphaG + (1.0 - alphaG) * dot ));
  203. #else
  204. float alphaSquared = alphaG * alphaG;
  205. return 1.0 / (dot + sqrt(alphaSquared + (1.0 - alphaSquared) * dot * dot));
  206. #endif
  207. }
  208. float smithVisibility_TrowbridgeReitzGGXFast(float NdotL, float NdotV, float alphaG)
  209. {
  210. float visibility = smithVisibilityG1_TrowbridgeReitzGGXFast(NdotL, alphaG) * smithVisibilityG1_TrowbridgeReitzGGXFast(NdotV, alphaG);
  211. // No Cook Torance Denominator as it is canceled out in the previous form
  212. return visibility;
  213. }
  214. #endif
  215. #ifdef ANISOTROPIC
  216. // GGX Mask/Shadowing Anisotropic
  217. // Heitz http://jcgt.org/published/0003/02/03/paper.pdf
  218. float smithVisibility_GGXCorrelated_Anisotropic(float NdotL, float NdotV, float TdotV, float BdotV, float TdotL, float BdotL, const vec2 alphaTB) {
  219. float lambdaV = NdotL * length(vec3(alphaTB.x * TdotV, alphaTB.y * BdotV, NdotV));
  220. float lambdaL = NdotV * length(vec3(alphaTB.x * TdotL, alphaTB.y * BdotL, NdotL));
  221. float v = 0.5 / (lambdaV + lambdaL);
  222. return v;
  223. }
  224. #endif
  225. #ifdef CLEARCOAT
  226. float visibility_Kelemen(float VdotH) {
  227. // Simplified form integration the cook torrance denminator.
  228. // Expanded is nl * nv / vh2 which factor with 1 / (4 * nl * nv)
  229. // giving 1 / (4 * vh2))
  230. return 0.25 / (VdotH * VdotH);
  231. }
  232. #endif
  233. #ifdef SHEEN
  234. // https://knarkowicz.wordpress.com/2018/01/04/cloth-shading/
  235. // https://blog.selfshadow.com/publications/s2017-shading-course/imageworks/s2017_pbs_imageworks_sheen.pdf
  236. // http://www.cs.utah.edu/~premoze/dbrdf/dBRDF.pdf
  237. float visibility_Ashikhmin(float NdotL, float NdotV)
  238. {
  239. return 1. / (4. * (NdotL + NdotV - NdotL * NdotV));
  240. }
  241. /* NOT USED
  242. #ifdef SHEEN_SOFTER
  243. // http://www.aconty.com/pdf/s2017_pbs_imageworks_sheen.pdf
  244. float l(float x, float alphaG)
  245. {
  246. float oneMinusAlphaSq = (1.0 - alphaG) * (1.0 - alphaG);
  247. float a = mix(21.5473, 25.3245, oneMinusAlphaSq);
  248. float b = mix(3.82987, 3.32435, oneMinusAlphaSq);
  249. float c = mix(0.19823, 0.16801, oneMinusAlphaSq);
  250. float d = mix(-1.97760, -1.27393, oneMinusAlphaSq);
  251. float e = mix(-4.32054, -4.85967, oneMinusAlphaSq);
  252. return a / (1.0 + b * pow(x, c)) + d * x + e;
  253. }
  254. float lambdaSheen(float cosTheta, float alphaG)
  255. {
  256. return abs(cosTheta) < 0.5 ? exp(l(cosTheta, alphaG)) : exp(2.0 * l(0.5, alphaG) - l(1.0 - cosTheta, alphaG));
  257. }
  258. float visibility_CharlieSheen(float NdotL, float NdotV, float alphaG)
  259. {
  260. float G = 1.0 / (1.0 + lambdaSheen(NdotV, alphaG) + lambdaSheen(NdotL, alphaG));
  261. return G / (4.0 * NdotV * NdotL);
  262. }
  263. #endif
  264. */
  265. #endif
  266. // ______________________________________________________________________
  267. //
  268. // DiffuseBRDF
  269. // ______________________________________________________________________
  270. // Disney diffuse term
  271. // https://blog.selfshadow.com/publications/s2012-shading-course/burley/s2012_pbs_disney_brdf_notes_v3.pdf
  272. // Page 14
  273. float diffuseBRDF_Burley(float NdotL, float NdotV, float VdotH, float roughness) {
  274. // Diffuse fresnel falloff as per Disney principled BRDF, and in the spirit of
  275. // of general coupled diffuse/specular models e.g. Ashikhmin Shirley.
  276. float diffuseFresnelNV = pow5(saturateEps(1.0 - NdotL));
  277. float diffuseFresnelNL = pow5(saturateEps(1.0 - NdotV));
  278. float diffuseFresnel90 = 0.5 + 2.0 * VdotH * VdotH * roughness;
  279. float fresnel =
  280. (1.0 + (diffuseFresnel90 - 1.0) * diffuseFresnelNL) *
  281. (1.0 + (diffuseFresnel90 - 1.0) * diffuseFresnelNV);
  282. return fresnel / PI;
  283. }
  284. #ifdef SS_TRANSLUCENCY
  285. // Pixar diffusion profile
  286. // http://graphics.pixar.com/library/ApproxBSSRDF/paper.pdf
  287. vec3 transmittanceBRDF_Burley(const vec3 tintColor, const vec3 diffusionDistance, float thickness) {
  288. vec3 S = 1. / maxEps(diffusionDistance);
  289. vec3 temp = exp((-0.333333333 * thickness) * S);
  290. return tintColor.rgb * 0.25 * (temp * temp * temp + 3.0 * temp);
  291. }
  292. // Extends the dark area to prevent seams
  293. // Keep it energy conserving by using McCauley solution: https://blog.selfshadow.com/2011/12/31/righting-wrap-part-1/
  294. float computeWrappedDiffuseNdotL(float NdotL, float w) {
  295. float t = 1.0 + w;
  296. float invt2 = 1.0 / square(t);
  297. return saturate((NdotL + w) * invt2);
  298. }
  299. #endif