PhysicalLightingModel.js 3.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. import BRDF_Lambert from './BSDF/BRDF_Lambert.js';
  2. import BRDF_GGX from './BSDF/BRDF_GGX.js';
  3. import DFGApprox from './BSDF/DFGApprox.js';
  4. import {
  5. ShaderNode,
  6. vec3, mul, saturate, add, sub, dot, div, transformedNormalView,
  7. pow, exp2, dotNV,
  8. diffuseColor, specularColor, roughness, temp
  9. } from '../shadernode/ShaderNodeElements.js';
  10. // Fdez-Agüera's "Multiple-Scattering Microfacet Model for Real-Time Image Based Lighting"
  11. // Approximates multiscattering in order to preserve energy.
  12. // http://www.jcgt.org/published/0008/01/03/
  13. const computeMultiscattering = ( singleScatter, multiScatter, specularF90 = 1 ) => {
  14. const fab = DFGApprox.call( { roughness } );
  15. const FssEss = add( mul( specularColor, fab.x ), mul( specularF90, fab.y ) );
  16. const Ess = add( fab.x, fab.y );
  17. const Ems = sub( 1.0, Ess );
  18. const Favg = add( specularColor, mul( sub( 1.0, specularColor ), 0.047619 ) ); // 1/21
  19. const Fms = div( mul( FssEss, Favg ), sub( 1.0, mul( Ems, Favg ) ) );
  20. singleScatter.add( FssEss );
  21. multiScatter.add( mul( Fms, Ems ) );
  22. };
  23. const RE_IndirectSpecular_Physical = new ShaderNode( ( inputs ) => {
  24. const { radiance, iblIrradiance, reflectedLight } = inputs;
  25. // Both indirect specular and indirect diffuse light accumulate here
  26. const singleScattering = temp( vec3() );
  27. const multiScattering = temp( vec3() );
  28. const cosineWeightedIrradiance = mul( iblIrradiance, 1 / Math.PI );
  29. computeMultiscattering( singleScattering, multiScattering );
  30. const diffuse = mul( diffuseColor, sub( 1.0, add( singleScattering, multiScattering ) ) );
  31. reflectedLight.indirectSpecular.add( mul( radiance, singleScattering ) );
  32. reflectedLight.indirectSpecular.add( mul( multiScattering, cosineWeightedIrradiance ) );
  33. reflectedLight.indirectDiffuse.add( mul( diffuse, cosineWeightedIrradiance ) );
  34. } );
  35. const RE_IndirectDiffuse_Physical = new ShaderNode( ( inputs ) => {
  36. const { irradiance, reflectedLight } = inputs;
  37. reflectedLight.indirectDiffuse.add( mul( irradiance, BRDF_Lambert.call( { diffuseColor } ) ) );
  38. } );
  39. const RE_Direct_Physical = new ShaderNode( ( inputs ) => {
  40. const { lightDirection, lightColor, reflectedLight } = inputs;
  41. const dotNL = saturate( dot( transformedNormalView, lightDirection ) );
  42. const irradiance = mul( dotNL, lightColor );
  43. reflectedLight.directDiffuse.add( mul( irradiance, BRDF_Lambert.call( { diffuseColor: diffuseColor.rgb } ) ) );
  44. reflectedLight.directSpecular.add( mul( irradiance, BRDF_GGX.call( { lightDirection, f0: specularColor, f90: 1, roughness } ) ) );
  45. } );
  46. const RE_AmbientOcclusion_Physical = new ShaderNode( ( { ambientOcclusion, reflectedLight } ) => {
  47. const aoNV = add( dotNV, ambientOcclusion );
  48. const aoExp = exp2( sub( mul( - 16.0, roughness ), 1.0 ) );
  49. const aoNode = saturate( add( sub( pow( aoNV, aoExp ), 1.0 ), ambientOcclusion ) );
  50. reflectedLight.indirectDiffuse.mul( ambientOcclusion );
  51. reflectedLight.indirectSpecular.mul( aoNode );
  52. } );
  53. const PhysicalLightingModel = {
  54. direct: RE_Direct_Physical,
  55. indirectDiffuse: RE_IndirectDiffuse_Physical,
  56. indirectSpecular: RE_IndirectSpecular_Physical,
  57. ambientOcclusion: RE_AmbientOcclusion_Physical
  58. };
  59. export default PhysicalLightingModel;