BSDFs.js 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. import { ShaderNode,
  2. add, addTo, sub, mul, div, saturate, dot, pow, pow2, exp2, normalize, max, sqrt, negate,
  3. cond, greaterThan, and,
  4. transformedNormalView, positionViewDirection,
  5. diffuseColor, specularColor, roughness,
  6. PI, RECIPROCAL_PI, EPSILON
  7. } from '../ShaderNode.js';
  8. export const F_Schlick = new ShaderNode( ( inputs ) => {
  9. const { f0, f90, dotVH } = inputs;
  10. // Original approximation by Christophe Schlick '94
  11. // float fresnel = pow( 1.0 - dotVH, 5.0 );
  12. // Optimized variant (presented by Epic at SIGGRAPH '13)
  13. // https://cdn2.unrealengine.com/Resources/files/2013SiggraphPresentationsNotes-26915738.pdf
  14. const fresnel = exp2( mul( sub( mul( - 5.55473, dotVH ), 6.98316 ), dotVH ) );
  15. return add( mul( f0, sub( 1.0, fresnel ) ), mul( f90, fresnel ) );
  16. } ); // validated
  17. export const BRDF_Lambert = new ShaderNode( ( inputs ) => {
  18. return mul( RECIPROCAL_PI, inputs.diffuseColor ); // punctual light
  19. } ); // validated
  20. export const getDistanceAttenuation = new ShaderNode( ( inputs ) => {
  21. const { lightDistance, cutoffDistance, decayExponent } = inputs;
  22. return cond(
  23. and( greaterThan( cutoffDistance, 0 ), greaterThan( decayExponent, 0 ) ),
  24. pow( saturate( add( div( negate( lightDistance ), cutoffDistance ), 1.0 ) ), decayExponent ),
  25. 1.0
  26. );
  27. } ); // validated
  28. //
  29. // STANDARD
  30. //
  31. // Moving Frostbite to Physically Based Rendering 3.0 - page 12, listing 2
  32. // https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf
  33. export const V_GGX_SmithCorrelated = new ShaderNode( ( inputs ) => {
  34. const { alpha, dotNL, dotNV } = inputs;
  35. const a2 = pow2( alpha );
  36. const gv = mul( dotNL, sqrt( add( a2, mul( sub( 1.0, a2 ), pow2( dotNV ) ) ) ) );
  37. const gl = mul( dotNV, sqrt( add( a2, mul( sub( 1.0, a2 ), pow2( dotNL ) ) ) ) );
  38. return div( 0.5, max( add( gv, gl ), EPSILON ) );
  39. } ); // validated
  40. // Microfacet Models for Refraction through Rough Surfaces - equation (33)
  41. // http://graphicrants.blogspot.com/2013/08/specular-brdf-reference.html
  42. // alpha is "roughness squared" in Disney’s reparameterization
  43. export const D_GGX = new ShaderNode( ( inputs ) => {
  44. const { alpha, dotNH } = inputs;
  45. const a2 = pow2( alpha );
  46. const denom = add( mul( pow2( dotNH ), sub( a2, 1.0 ) ), 1.0 ); // avoid alpha = 0 with dotNH = 1
  47. return mul( RECIPROCAL_PI, div( a2, pow2( denom ) ) );
  48. } ); // validated
  49. // GGX Distribution, Schlick Fresnel, GGX_SmithCorrelated Visibility
  50. export const BRDF_Specular_GGX = new ShaderNode( ( inputs ) => {
  51. const { lightDirection, f0, f90, roughness } = inputs;
  52. const alpha = pow2( roughness ); // UE4's roughness
  53. const halfDir = normalize( add( lightDirection, positionViewDirection ) );
  54. const dotNL = saturate( dot( transformedNormalView, lightDirection ) );
  55. const dotNV = saturate( dot( transformedNormalView, positionViewDirection ) );
  56. const dotNH = saturate( dot( transformedNormalView, halfDir ) );
  57. const dotVH = saturate( dot( positionViewDirection, halfDir ) );
  58. const F = F_Schlick( { f0, f90, dotVH } );
  59. const V = V_GGX_SmithCorrelated( { alpha, dotNL, dotNV } );
  60. const D = D_GGX( { alpha, dotNH } );
  61. return mul( F, mul( V, D ) );
  62. } ); // validated
  63. export const RE_Direct_Physical = new ShaderNode( ( inputs ) => {
  64. const { lightDirection, lightColor, directDiffuse, directSpecular } = inputs;
  65. const dotNL = saturate( dot( transformedNormalView, lightDirection ) );
  66. let irradiance = mul( dotNL, lightColor );
  67. irradiance = mul( irradiance, PI ); // punctual light
  68. addTo( directDiffuse, mul( irradiance, BRDF_Lambert( { diffuseColor } ) ) );
  69. addTo( directSpecular, mul( irradiance, BRDF_Specular_GGX( { lightDirection, f0: specularColor, f90: 1, roughness } ) ) );
  70. } );
  71. export const PhysicalLightingModel = new ShaderNode( ( inputs/*, builder*/ ) => {
  72. // PHYSICALLY_CORRECT_LIGHTS <-> builder.renderer.physicallyCorrectLights === true
  73. RE_Direct_Physical( inputs );
  74. } );