EnvironmentNode.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  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 { equirectUV } from '../utils/EquirectUVNode.js';
  6. import { specularMIPLevel } from '../utils/SpecularMIPLevelNode.js';
  7. import { cameraViewMatrix } from '../accessors/CameraNode.js';
  8. import { transformedClearcoatNormalView, transformedNormalView, transformedNormalWorld } from '../accessors/NormalNode.js';
  9. import { positionViewDirection } from '../accessors/PositionNode.js';
  10. import { addNodeClass } from '../core/Node.js';
  11. import { float, vec2 } from '../shadernode/ShaderNode.js';
  12. import { cubeTexture } from '../accessors/CubeTextureNode.js';
  13. import { reference } from '../accessors/ReferenceNode.js';
  14. const envNodeCache = new WeakMap();
  15. class EnvironmentNode extends LightingNode {
  16. constructor( envNode = null ) {
  17. super();
  18. this.envNode = envNode;
  19. }
  20. setup( builder ) {
  21. let envNode = this.envNode;
  22. if ( envNode.isTextureNode && envNode.value.isCubeTexture !== true ) {
  23. let cacheEnvNode = envNodeCache.get( envNode.value );
  24. if ( cacheEnvNode === undefined ) {
  25. const texture = envNode.value;
  26. const renderer = builder.renderer;
  27. // @TODO: Add dispose logic here
  28. const cubeRTT = builder.getCubeRenderTarget( 512 ).fromEquirectangularTexture( renderer, texture );
  29. cacheEnvNode = cubeTexture( cubeRTT.texture );
  30. envNodeCache.set( envNode.value, cacheEnvNode );
  31. }
  32. envNode = cacheEnvNode;
  33. }
  34. //
  35. const intensity = reference( 'envMapIntensity', 'float', builder.material ); // @TODO: Add materialEnvIntensity in MaterialNode
  36. const radiance = context( envNode, createRadianceContext( roughness, transformedNormalView ) ).mul( intensity );
  37. const irradiance = context( envNode, createIrradianceContext( transformedNormalWorld ) ).mul( Math.PI ).mul( intensity );
  38. const isolateRadiance = cache( radiance );
  39. //
  40. const { stack } = builder;
  41. stack.addAssign( builder.context.radiance, isolateRadiance );
  42. stack.addAssign( builder.context.iblIrradiance, irradiance );
  43. //
  44. const clearcoatRadiance = builder.context.lightingModel.clearcoatRadiance;
  45. if ( clearcoatRadiance ) {
  46. const clearcoatRadianceContext = context( envNode, createRadianceContext( clearcoatRoughness, transformedClearcoatNormalView ) ).mul( intensity );
  47. const isolateClearcoatRadiance = cache( clearcoatRadianceContext );
  48. stack.addAssign( clearcoatRadiance, isolateClearcoatRadiance );
  49. }
  50. }
  51. }
  52. const createRadianceContext = ( roughnessNode, normalViewNode ) => {
  53. let reflectVec = null;
  54. let textureUVNode = null;
  55. return {
  56. getUVNode: ( textureNode ) => {
  57. let node = null;
  58. if ( reflectVec === null ) {
  59. reflectVec = positionViewDirection.negate().reflect( normalViewNode );
  60. reflectVec = roughnessNode.mul( roughnessNode ).mix( reflectVec, normalViewNode ).normalize();
  61. reflectVec = reflectVec.transformDirection( cameraViewMatrix );
  62. }
  63. if ( textureNode.isCubeTextureNode ) {
  64. node = reflectVec;
  65. } else if ( textureNode.isTextureNode ) {
  66. if ( textureUVNode === null ) {
  67. // @TODO: Needed PMREM
  68. textureUVNode = equirectUV( reflectVec );
  69. }
  70. node = textureUVNode;
  71. }
  72. return node;
  73. },
  74. getSamplerLevelNode: () => {
  75. return roughnessNode;
  76. },
  77. getMIPLevelAlgorithmNode: ( textureNode, levelNode ) => {
  78. return specularMIPLevel( textureNode, levelNode );
  79. }
  80. };
  81. };
  82. const createIrradianceContext = ( normalWorldNode ) => {
  83. let textureUVNode = null;
  84. return {
  85. getUVNode: ( textureNode ) => {
  86. let node = null;
  87. if ( textureNode.isCubeTextureNode ) {
  88. node = normalWorldNode;
  89. } else if ( textureNode.isTextureNode ) {
  90. if ( textureUVNode === null ) {
  91. // @TODO: Needed PMREM
  92. textureUVNode = equirectUV( normalWorldNode );
  93. textureUVNode = vec2( textureUVNode.x, textureUVNode.y.oneMinus() );
  94. }
  95. node = textureUVNode;
  96. }
  97. return node;
  98. },
  99. getSamplerLevelNode: () => {
  100. return float( 1 );
  101. },
  102. getMIPLevelAlgorithmNode: ( textureNode, levelNode ) => {
  103. return specularMIPLevel( textureNode, levelNode );
  104. }
  105. };
  106. };
  107. export default EnvironmentNode;
  108. addNodeClass( 'EnvironmentNode', EnvironmentNode );