diff --git a/examples/jsm/tsl/lighting/DynamicLightsNode.js b/examples/jsm/tsl/lighting/DynamicLightsNode.js index ec8649fdcbf6c1..2688ac44b88e56 100644 --- a/examples/jsm/tsl/lighting/DynamicLightsNode.js +++ b/examples/jsm/tsl/lighting/DynamicLightsNode.js @@ -1,5 +1,5 @@ import { LightsNode, NodeUtils, warn } from 'three/webgpu'; -import { nodeObject } from 'three/tsl'; +import { clearcoatNormalView, nodeObject, normalView, positionViewDirection } from 'three/tsl'; import AmbientLightDataNode from './data/AmbientLightDataNode.js'; import DirectionalLightDataNode from './data/DirectionalLightDataNode.js'; @@ -25,20 +25,42 @@ const _lightTypeToMaxProp = { HemisphereLight: 'maxHemisphereLights' }; +const _persistentBatchLightTypes = [ + 'PointLight', + 'SpotLight' +]; + +const _batchLightTypes = [ + 'AmbientLight', + 'DirectionalLight', + 'PointLight', + 'SpotLight', + 'HemisphereLight' +]; + const sortLights = ( lights ) => lights.sort( ( a, b ) => a.id - b.id ); const isSpecialSpotLight = ( light ) => { - return light.isSpotLight === true && ( light.map !== null || light.colorNode !== undefined ); + return light.isSpotLight === true && ( + light.map !== null || + light.colorNode !== undefined || + ( light.iesMap && light.iesMap.isTexture === true ) + ); }; -const canBatchLight = ( light ) => { +const getBatchLightType = ( light ) => { + + if ( light.isNode === true || light.castShadow === true || isSpecialSpotLight( light ) === true ) return null; - return light.isNode !== true && - light.castShadow !== true && - isSpecialSpotLight( light ) === false && - _lightTypeToDataNode[ light.constructor.name ] !== undefined; + if ( light.isAmbientLight === true ) return 'AmbientLight'; + if ( light.isDirectionalLight === true ) return 'DirectionalLight'; + if ( light.isPointLight === true && light.isSpotLight !== true ) return 'PointLight'; + if ( light.isSpotLight === true ) return 'SpotLight'; + if ( light.isHemisphereLight === true ) return 'HemisphereLight'; + + return null; }; @@ -100,6 +122,7 @@ class DynamicLightsNode extends LightsNode { this.maxHemisphereLights = options.maxHemisphereLights !== undefined ? options.maxHemisphereLights : 4; this._dataNodes = new Map(); + this._initPersistentDataNodes(); } @@ -110,10 +133,11 @@ class DynamicLightsNode extends LightsNode { for ( let i = 0; i < this._lights.length; i ++ ) { const light = this._lights[ i ]; + const typeName = getBatchLightType( light ); - if ( canBatchLight( light ) ) { + if ( typeName !== null ) { - typeSet.add( light.constructor.name ); + typeSet.add( typeName ); } else { @@ -124,8 +148,9 @@ class DynamicLightsNode extends LightsNode { const hashMap = light.map !== null ? light.map.id : - 1; const hashColorNode = light.colorNode ? light.colorNode.getCacheKey() : - 1; + const hashIESMap = light.iesMap && light.iesMap.isTexture === true ? light.iesMap.id : - 1; - _hashData.push( hashMap, hashColorNode ); + _hashData.push( hashMap, hashColorNode, hashIESMap ); } @@ -169,9 +194,10 @@ class DynamicLightsNode extends LightsNode { } - if ( canBatchLight( light ) ) { + const typeName = getBatchLightType( light ); + + if ( typeName !== null ) { - const typeName = light.constructor.name; const typeLights = lightsByType.get( typeName ); if ( typeLights === undefined ) { @@ -198,39 +224,29 @@ class DynamicLightsNode extends LightsNode { } - for ( const [ typeName, typeLights ] of lightsByType ) { - - let dataNode = this._dataNodes.get( typeName ); + for ( const typeName of _batchLightTypes ) { - if ( dataNode === undefined ) { - - const DataNodeClass = _lightTypeToDataNode[ typeName ]; - const maxProp = _lightTypeToMaxProp[ typeName ]; - const maxCount = maxProp !== undefined ? this[ maxProp ] : undefined; - - dataNode = maxCount !== undefined ? new DataNodeClass( maxCount ) : new DataNodeClass(); + const typeLights = lightsByType.get( typeName ); + const hasType = typeLights !== undefined || this._dataNodes.has( typeName ) === true; - this._dataNodes.set( typeName, dataNode ); + if ( hasType === false ) continue; - } + const dataNode = this._getDataNode( typeName ); - dataNode.setLights( typeLights ); + dataNode.setLights( typeLights || [] ); lightNodes.push( dataNode ); } - for ( const [ typeName, dataNode ] of this._dataNodes ) { - - if ( lightsByType.has( typeName ) === false ) { + this._lightNodes = lightNodes; - dataNode.setLights( [] ); - lightNodes.push( dataNode ); + } - } + setupLights( builder, lightNodes ) { - } + this._setupSharedLightingInputs( builder ); - this._lightNodes = lightNodes; + super.setupLights( builder, lightNodes ); } @@ -248,15 +264,63 @@ class DynamicLightsNode extends LightsNode { } + _setupSharedLightingInputs( builder ) { + + normalView.toStack(); + positionViewDirection.toStack(); + + if ( builder.context.lightingModel?.clearcoat === true ) { + + clearcoatNormalView.toStack(); + + } + + } + + _initPersistentDataNodes() { + + for ( const typeName of _persistentBatchLightTypes ) { + + const maxProp = _lightTypeToMaxProp[ typeName ]; + const maxCount = maxProp !== undefined ? this[ maxProp ] : undefined; + + if ( maxCount !== undefined && maxCount <= 0 ) continue; + + this._getDataNode( typeName ); + + } + + } + + _getDataNode( typeName ) { + + let dataNode = this._dataNodes.get( typeName ); + + if ( dataNode === undefined ) { + + const DataNodeClass = _lightTypeToDataNode[ typeName ]; + const maxProp = _lightTypeToMaxProp[ typeName ]; + const maxCount = maxProp !== undefined ? this[ maxProp ] : undefined; + + dataNode = maxCount !== undefined ? new DataNodeClass( maxCount ) : new DataNodeClass(); + + this._dataNodes.set( typeName, dataNode ); + + } + + return dataNode; + + } + _updateDataNodeLights( lights ) { const lightsByType = new Map(); for ( const light of lights ) { - if ( canBatchLight( light ) === false ) continue; + const typeName = getBatchLightType( light ); - const typeName = light.constructor.name; + if ( typeName === null ) continue; const typeLights = lightsByType.get( typeName ); if ( typeLights === undefined ) { diff --git a/examples/jsm/tsl/lighting/data/DirectionalLightDataNode.js b/examples/jsm/tsl/lighting/data/DirectionalLightDataNode.js index 893972d1d13847..bff7c56f7d913a 100644 --- a/examples/jsm/tsl/lighting/data/DirectionalLightDataNode.js +++ b/examples/jsm/tsl/lighting/data/DirectionalLightDataNode.js @@ -1,5 +1,5 @@ import { Color, Node, Vector3 } from 'three/webgpu'; -import { Loop, NodeUpdateType, renderGroup, uniform, uniformArray, vec3 } from 'three/tsl'; +import { Loop, NodeUpdateType, normalView, positionViewDirection, renderGroup, uniform, uniformArray, vec3 } from 'three/tsl'; const _lightPosition = /*@__PURE__*/ new Vector3(); const _targetPosition = /*@__PURE__*/ new Vector3(); @@ -87,6 +87,9 @@ class DirectionalLightDataNode extends Node { const dynDiffuse = vec3( 0 ).toVar( 'dynDirectionalDiffuse' ); const dynSpecular = vec3( 0 ).toVar( 'dynDirectionalSpecular' ); + normalView.toStack(); + positionViewDirection.toStack(); + Loop( this.countNode, ( { i } ) => { const lightColor = this.colorsNode.element( i ).toVar(); diff --git a/examples/jsm/tsl/lighting/data/PointLightDataNode.js b/examples/jsm/tsl/lighting/data/PointLightDataNode.js index 33a07e8cb3273a..9ce34f3f1b30e8 100644 --- a/examples/jsm/tsl/lighting/data/PointLightDataNode.js +++ b/examples/jsm/tsl/lighting/data/PointLightDataNode.js @@ -1,5 +1,5 @@ import { Color, Node, Vector3, Vector4 } from 'three/webgpu'; -import { Loop, NodeUpdateType, getDistanceAttenuation, positionView, renderGroup, uniform, uniformArray, vec3 } from 'three/tsl'; +import { Loop, NodeUpdateType, getDistanceAttenuation, normalView, positionView, positionViewDirection, renderGroup, uniform, uniformArray, vec3 } from 'three/tsl'; const _position = /*@__PURE__*/ new Vector3(); @@ -96,6 +96,9 @@ class PointLightDataNode extends Node { const dynDiffuse = vec3( 0 ).toVar( 'dynPointDiffuse' ); const dynSpecular = vec3( 0 ).toVar( 'dynPointSpecular' ); + normalView.toStack(); + positionViewDirection.toStack(); + Loop( this.countNode, ( { i } ) => { const positionAndCutoff = this.positionsAndCutoffNode.element( i ); diff --git a/examples/jsm/tsl/lighting/data/SpotLightDataNode.js b/examples/jsm/tsl/lighting/data/SpotLightDataNode.js index 63025a48857673..e50a29b8622f21 100644 --- a/examples/jsm/tsl/lighting/data/SpotLightDataNode.js +++ b/examples/jsm/tsl/lighting/data/SpotLightDataNode.js @@ -1,5 +1,5 @@ import { Color, Node, Vector3, Vector4 } from 'three/webgpu'; -import { Loop, NodeUpdateType, getDistanceAttenuation, positionView, renderGroup, smoothstep, uniform, uniformArray, vec3 } from 'three/tsl'; +import { Loop, NodeUpdateType, getDistanceAttenuation, normalView, positionView, positionViewDirection, renderGroup, smoothstep, uniform, uniformArray, vec3 } from 'three/tsl'; const _lightPosition = /*@__PURE__*/ new Vector3(); const _targetPosition = /*@__PURE__*/ new Vector3(); @@ -114,6 +114,9 @@ class SpotLightDataNode extends Node { const dynDiffuse = vec3( 0 ).toVar( 'dynSpotDiffuse' ); const dynSpecular = vec3( 0 ).toVar( 'dynSpotSpecular' ); + normalView.toStack(); + positionViewDirection.toStack(); + Loop( this.countNode, ( { i } ) => { const positionAndCutoff = this.positionsAndCutoffNode.element( i );