EnvironmentNode.js 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. import LightingNode from './LightingNode.js';
  2. import { cache } from '../core/CacheNode.js';
  3. import { context } from '../core/ContextNode.js';
  4. import { roughness, clearcoatRoughness } from '../core/PropertyNode.js';
  5. import { cameraViewMatrix } from '../accessors/CameraNode.js';
  6. import { transformedClearcoatNormalView, transformedNormalView, transformedNormalWorld } from '../accessors/NormalNode.js';
  7. import { positionViewDirection } from '../accessors/PositionNode.js';
  8. import { addNodeClass } from '../core/Node.js';
  9. import { float } from '../shadernode/ShaderNode.js';
  10. import { reference } from '../accessors/ReferenceNode.js';
  11. import { transformedBentNormalView } from '../accessors/AccessorsUtils.js';
  12. import { pmremTexture } from '../pmrem/PMREMNode.js';
  13. const envNodeCache = new WeakMap();
  14. class EnvironmentNode extends LightingNode {
  15. constructor( envNode = null ) {
  16. super();
  17. this.envNode = envNode;
  18. }
  19. setup( builder ) {
  20. let envNode = this.envNode;
  21. if ( envNode.isTextureNode ) {
  22. let cacheEnvNode = envNodeCache.get( envNode.value );
  23. if ( cacheEnvNode === undefined ) {
  24. cacheEnvNode = pmremTexture( envNode.value );
  25. envNodeCache.set( envNode.value, cacheEnvNode );
  26. }
  27. envNode = cacheEnvNode;
  28. }
  29. //
  30. const { material } = builder;
  31. const envMap = material.envMap;
  32. const intensity = envMap ? reference( 'envMapIntensity', 'float', builder.material ) : reference( 'environmentIntensity', 'float', builder.scene ); // @TODO: Add materialEnvIntensity in MaterialNode
  33. const useAnisotropy = material.useAnisotropy === true || material.anisotropy > 0;
  34. const radianceNormalView = useAnisotropy ? transformedBentNormalView : transformedNormalView;
  35. const radiance = context( envNode, createRadianceContext( roughness, radianceNormalView ) ).mul( intensity );
  36. const irradiance = context( envNode, createIrradianceContext( transformedNormalWorld ) ).mul( Math.PI ).mul( intensity );
  37. const isolateRadiance = cache( radiance );
  38. const isolateIrradiance = cache( irradiance );
  39. //
  40. builder.context.radiance.addAssign( isolateRadiance );
  41. builder.context.iblIrradiance.addAssign( isolateIrradiance );
  42. //
  43. const clearcoatRadiance = builder.context.lightingModel.clearcoatRadiance;
  44. if ( clearcoatRadiance ) {
  45. const clearcoatRadianceContext = context( envNode, createRadianceContext( clearcoatRoughness, transformedClearcoatNormalView ) ).mul( intensity );
  46. const isolateClearcoatRadiance = cache( clearcoatRadianceContext );
  47. clearcoatRadiance.addAssign( isolateClearcoatRadiance );
  48. }
  49. }
  50. }
  51. const createRadianceContext = ( roughnessNode, normalViewNode ) => {
  52. let reflectVec = null;
  53. return {
  54. getUV: () => {
  55. if ( reflectVec === null ) {
  56. reflectVec = positionViewDirection.negate().reflect( normalViewNode );
  57. // Mixing the reflection with the normal is more accurate and keeps rough objects from gathering light from behind their tangent plane.
  58. reflectVec = roughnessNode.mul( roughnessNode ).mix( reflectVec, normalViewNode ).normalize();
  59. reflectVec = reflectVec.transformDirection( cameraViewMatrix );
  60. }
  61. return reflectVec;
  62. },
  63. getTextureLevel: () => {
  64. return roughnessNode;
  65. }
  66. };
  67. };
  68. const createIrradianceContext = ( normalWorldNode ) => {
  69. return {
  70. getUV: () => {
  71. return normalWorldNode;
  72. },
  73. getTextureLevel: () => {
  74. return float( 1.0 );
  75. }
  76. };
  77. };
  78. export default EnvironmentNode;
  79. addNodeClass( 'EnvironmentNode', EnvironmentNode );