Initial project import
This commit is contained in:
300
node_modules/three/examples/jsm/tsl/lighting/DynamicLightsNode.js
generated
vendored
Normal file
300
node_modules/three/examples/jsm/tsl/lighting/DynamicLightsNode.js
generated
vendored
Normal file
@@ -0,0 +1,300 @@
|
||||
import { LightsNode, NodeUtils, warn } from 'three/webgpu';
|
||||
import { nodeObject } from 'three/tsl';
|
||||
|
||||
import AmbientLightDataNode from './data/AmbientLightDataNode.js';
|
||||
import DirectionalLightDataNode from './data/DirectionalLightDataNode.js';
|
||||
import PointLightDataNode from './data/PointLightDataNode.js';
|
||||
import SpotLightDataNode from './data/SpotLightDataNode.js';
|
||||
import HemisphereLightDataNode from './data/HemisphereLightDataNode.js';
|
||||
|
||||
const _lightNodeRef = /*@__PURE__*/ new WeakMap();
|
||||
const _hashData = [];
|
||||
|
||||
const _lightTypeToDataNode = {
|
||||
AmbientLight: AmbientLightDataNode,
|
||||
DirectionalLight: DirectionalLightDataNode,
|
||||
PointLight: PointLightDataNode,
|
||||
SpotLight: SpotLightDataNode,
|
||||
HemisphereLight: HemisphereLightDataNode
|
||||
};
|
||||
|
||||
const _lightTypeToMaxProp = {
|
||||
DirectionalLight: 'maxDirectionalLights',
|
||||
PointLight: 'maxPointLights',
|
||||
SpotLight: 'maxSpotLights',
|
||||
HemisphereLight: 'maxHemisphereLights'
|
||||
};
|
||||
|
||||
const sortLights = ( lights ) => lights.sort( ( a, b ) => a.id - b.id );
|
||||
|
||||
const isSpecialSpotLight = ( light ) => {
|
||||
|
||||
return light.isSpotLight === true && ( light.map !== null || light.colorNode !== undefined );
|
||||
|
||||
};
|
||||
|
||||
const canBatchLight = ( light ) => {
|
||||
|
||||
return light.isNode !== true &&
|
||||
light.castShadow !== true &&
|
||||
isSpecialSpotLight( light ) === false &&
|
||||
_lightTypeToDataNode[ light.constructor.name ] !== undefined;
|
||||
|
||||
};
|
||||
|
||||
const getOrCreateLightNode = ( light, nodeLibrary ) => {
|
||||
|
||||
const lightNodeClass = nodeLibrary.getLightNodeClass( light.constructor );
|
||||
|
||||
if ( lightNodeClass === null ) {
|
||||
|
||||
warn( `DynamicLightsNode: Light node not found for ${ light.constructor.name }.` );
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
if ( _lightNodeRef.has( light ) === false ) {
|
||||
|
||||
_lightNodeRef.set( light, new lightNodeClass( light ) );
|
||||
|
||||
}
|
||||
|
||||
return _lightNodeRef.get( light );
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* A custom version of `LightsNode` that batches supported analytic lights into
|
||||
* uniform arrays and loops.
|
||||
*
|
||||
* Unsupported lights, node lights, shadow-casting lights, and projected spot
|
||||
* lights keep the default per-light path.
|
||||
*
|
||||
* @augments LightsNode
|
||||
* @three_import import { DynamicLightsNode } from 'three/addons/tsl/lighting/DynamicLightsNode.js';
|
||||
*/
|
||||
class DynamicLightsNode extends LightsNode {
|
||||
|
||||
static get type() {
|
||||
|
||||
return 'DynamicLightsNode';
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new dynamic lights node.
|
||||
*
|
||||
* @param {Object} [options={}] - Dynamic lighting configuration.
|
||||
* @param {number} [options.maxDirectionalLights=8] - Maximum number of batched directional lights.
|
||||
* @param {number} [options.maxPointLights=16] - Maximum number of batched point lights.
|
||||
* @param {number} [options.maxSpotLights=16] - Maximum number of batched spot lights.
|
||||
* @param {number} [options.maxHemisphereLights=4] - Maximum number of batched hemisphere lights.
|
||||
*/
|
||||
constructor( options = {} ) {
|
||||
|
||||
super();
|
||||
|
||||
this.maxDirectionalLights = options.maxDirectionalLights !== undefined ? options.maxDirectionalLights : 8;
|
||||
this.maxPointLights = options.maxPointLights !== undefined ? options.maxPointLights : 16;
|
||||
this.maxSpotLights = options.maxSpotLights !== undefined ? options.maxSpotLights : 16;
|
||||
this.maxHemisphereLights = options.maxHemisphereLights !== undefined ? options.maxHemisphereLights : 4;
|
||||
|
||||
this._dataNodes = new Map();
|
||||
|
||||
}
|
||||
|
||||
customCacheKey() {
|
||||
|
||||
const typeSet = new Set();
|
||||
|
||||
for ( let i = 0; i < this._lights.length; i ++ ) {
|
||||
|
||||
const light = this._lights[ i ];
|
||||
|
||||
if ( canBatchLight( light ) ) {
|
||||
|
||||
typeSet.add( light.constructor.name );
|
||||
|
||||
} else {
|
||||
|
||||
_hashData.push( light.id );
|
||||
_hashData.push( light.castShadow ? 1 : 0 );
|
||||
|
||||
if ( light.isSpotLight === true ) {
|
||||
|
||||
const hashMap = light.map !== null ? light.map.id : - 1;
|
||||
const hashColorNode = light.colorNode ? light.colorNode.getCacheKey() : - 1;
|
||||
|
||||
_hashData.push( hashMap, hashColorNode );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for ( const typeName of this._dataNodes.keys() ) {
|
||||
|
||||
typeSet.add( typeName );
|
||||
|
||||
}
|
||||
|
||||
for ( const typeName of [ ...typeSet ].sort() ) {
|
||||
|
||||
_hashData.push( NodeUtils.hashString( typeName ) );
|
||||
|
||||
}
|
||||
|
||||
const cacheKey = NodeUtils.hashArray( _hashData );
|
||||
|
||||
_hashData.length = 0;
|
||||
|
||||
return cacheKey;
|
||||
|
||||
}
|
||||
|
||||
setupLightsNode( builder ) {
|
||||
|
||||
const lightNodes = [];
|
||||
const lightsByType = new Map();
|
||||
const lights = sortLights( this._lights );
|
||||
const nodeLibrary = builder.renderer.library;
|
||||
|
||||
for ( const light of lights ) {
|
||||
|
||||
if ( light.isNode === true ) {
|
||||
|
||||
lightNodes.push( nodeObject( light ) );
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
if ( canBatchLight( light ) ) {
|
||||
|
||||
const typeName = light.constructor.name;
|
||||
const typeLights = lightsByType.get( typeName );
|
||||
|
||||
if ( typeLights === undefined ) {
|
||||
|
||||
lightsByType.set( typeName, [ light ] );
|
||||
|
||||
} else {
|
||||
|
||||
typeLights.push( light );
|
||||
|
||||
}
|
||||
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
const lightNode = getOrCreateLightNode( light, nodeLibrary );
|
||||
|
||||
if ( lightNode !== null ) {
|
||||
|
||||
lightNodes.push( lightNode );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for ( const [ typeName, typeLights ] of lightsByType ) {
|
||||
|
||||
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 );
|
||||
|
||||
}
|
||||
|
||||
dataNode.setLights( typeLights );
|
||||
lightNodes.push( dataNode );
|
||||
|
||||
}
|
||||
|
||||
for ( const [ typeName, dataNode ] of this._dataNodes ) {
|
||||
|
||||
if ( lightsByType.has( typeName ) === false ) {
|
||||
|
||||
dataNode.setLights( [] );
|
||||
lightNodes.push( dataNode );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this._lightNodes = lightNodes;
|
||||
|
||||
}
|
||||
|
||||
setLights( lights ) {
|
||||
|
||||
super.setLights( lights );
|
||||
|
||||
if ( this._dataNodes.size > 0 ) {
|
||||
|
||||
this._updateDataNodeLights( lights );
|
||||
|
||||
}
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
_updateDataNodeLights( lights ) {
|
||||
|
||||
const lightsByType = new Map();
|
||||
|
||||
for ( const light of lights ) {
|
||||
|
||||
if ( canBatchLight( light ) === false ) continue;
|
||||
|
||||
const typeName = light.constructor.name;
|
||||
const typeLights = lightsByType.get( typeName );
|
||||
|
||||
if ( typeLights === undefined ) {
|
||||
|
||||
lightsByType.set( typeName, [ light ] );
|
||||
|
||||
} else {
|
||||
|
||||
typeLights.push( light );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for ( const [ typeName, dataNode ] of this._dataNodes ) {
|
||||
|
||||
dataNode.setLights( lightsByType.get( typeName ) || [] );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
get hasLights() {
|
||||
|
||||
return super.hasLights || this._dataNodes.size > 0;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default DynamicLightsNode;
|
||||
|
||||
/**
|
||||
* TSL function that creates a dynamic lights node.
|
||||
*
|
||||
* @tsl
|
||||
* @function
|
||||
* @param {Object} [options={}] - Dynamic lighting configuration.
|
||||
* @return {DynamicLightsNode} The created dynamic lights node.
|
||||
*/
|
||||
export const dynamicLights = ( options = {} ) => new DynamicLightsNode( options );
|
||||
442
node_modules/three/examples/jsm/tsl/lighting/TiledLightsNode.js
generated
vendored
Normal file
442
node_modules/three/examples/jsm/tsl/lighting/TiledLightsNode.js
generated
vendored
Normal file
@@ -0,0 +1,442 @@
|
||||
import { DataTexture, FloatType, RGBAFormat, Vector2, Vector3, LightsNode, NodeUpdateType } from 'three/webgpu';
|
||||
|
||||
import {
|
||||
attributeArray, nodeProxy, int, float, vec2, ivec2, ivec4, uniform, Break, Loop, positionView,
|
||||
Fn, If, Return, textureLoad, instanceIndex, screenCoordinate, directPointLight
|
||||
} from 'three/tsl';
|
||||
|
||||
/**
|
||||
* TSL function that checks if a circle intersects with an axis-aligned bounding box (AABB).
|
||||
*
|
||||
* @tsl
|
||||
* @function
|
||||
* @param {Node<vec2>} circleCenter - The center of the circle.
|
||||
* @param {Node<float>} radius - The radius of the circle.
|
||||
* @param {Node<vec2>} minBounds - The minimum bounds of the AABB.
|
||||
* @param {Node<vec2>} maxBounds - The maximum bounds of the AABB.
|
||||
* @return {Node<bool>} True if the circle intersects the AABB.
|
||||
*/
|
||||
export const circleIntersectsAABB = /*@__PURE__*/ Fn( ( [ circleCenter, radius, minBounds, maxBounds ] ) => {
|
||||
|
||||
// Find the closest point on the AABB to the circle's center using method chaining
|
||||
const closestX = minBounds.x.max( circleCenter.x.min( maxBounds.x ) );
|
||||
const closestY = minBounds.y.max( circleCenter.y.min( maxBounds.y ) );
|
||||
|
||||
// Compute the distance between the circle's center and the closest point
|
||||
const distX = circleCenter.x.sub( closestX );
|
||||
const distY = circleCenter.y.sub( closestY );
|
||||
|
||||
// Calculate the squared distance
|
||||
const distSquared = distX.mul( distX ).add( distY.mul( distY ) );
|
||||
|
||||
return distSquared.lessThanEqual( radius.mul( radius ) );
|
||||
|
||||
} ).setLayout( {
|
||||
name: 'circleIntersectsAABB',
|
||||
type: 'bool',
|
||||
inputs: [
|
||||
{ name: 'circleCenter', type: 'vec2' },
|
||||
{ name: 'radius', type: 'float' },
|
||||
{ name: 'minBounds', type: 'vec2' },
|
||||
{ name: 'maxBounds', type: 'vec2' }
|
||||
]
|
||||
} );
|
||||
|
||||
const _vector3 = /*@__PURE__*/ new Vector3();
|
||||
const _size = /*@__PURE__*/ new Vector2();
|
||||
|
||||
/**
|
||||
* A custom version of `LightsNode` implementing tiled lighting. This node is used in
|
||||
* {@link TiledLighting} to overwrite the renderer's default lighting with
|
||||
* a custom implementation.
|
||||
*
|
||||
* @augments LightsNode
|
||||
* @three_import import { tiledLights } from 'three/addons/tsl/lighting/TiledLightsNode.js';
|
||||
*/
|
||||
class TiledLightsNode extends LightsNode {
|
||||
|
||||
static get type() {
|
||||
|
||||
return 'TiledLightsNode';
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new tiled lights node.
|
||||
*
|
||||
* @param {number} [maxLights=1024] - The maximum number of lights.
|
||||
* @param {number} [tileSize=32] - The tile size.
|
||||
*/
|
||||
constructor( maxLights = 1024, tileSize = 32 ) {
|
||||
|
||||
super();
|
||||
|
||||
this.materialLights = [];
|
||||
this.tiledLights = [];
|
||||
|
||||
/**
|
||||
* The maximum number of lights.
|
||||
*
|
||||
* @type {number}
|
||||
* @default 1024
|
||||
*/
|
||||
this.maxLights = maxLights;
|
||||
|
||||
/**
|
||||
* The tile size.
|
||||
*
|
||||
* @type {number}
|
||||
* @default 32
|
||||
*/
|
||||
this.tileSize = tileSize;
|
||||
|
||||
this._bufferSize = null;
|
||||
this._lightIndexes = null;
|
||||
this._screenTileIndex = null;
|
||||
this._compute = null;
|
||||
this._lightsTexture = null;
|
||||
|
||||
this._lightsCount = uniform( 0, 'int' );
|
||||
this._tileLightCount = 8;
|
||||
this._screenSize = uniform( new Vector2() );
|
||||
this._cameraProjectionMatrix = uniform( 'mat4' );
|
||||
this._cameraViewMatrix = uniform( 'mat4' );
|
||||
|
||||
this.updateBeforeType = NodeUpdateType.RENDER;
|
||||
|
||||
}
|
||||
|
||||
customCacheKey() {
|
||||
|
||||
return this._compute.getCacheKey() + super.customCacheKey();
|
||||
|
||||
}
|
||||
|
||||
updateLightsTexture() {
|
||||
|
||||
const { _lightsTexture: lightsTexture, tiledLights } = this;
|
||||
|
||||
const data = lightsTexture.image.data;
|
||||
const lineSize = lightsTexture.image.width * 4;
|
||||
|
||||
this._lightsCount.value = tiledLights.length;
|
||||
|
||||
for ( let i = 0; i < tiledLights.length; i ++ ) {
|
||||
|
||||
const light = tiledLights[ i ];
|
||||
|
||||
// world position
|
||||
|
||||
_vector3.setFromMatrixPosition( light.matrixWorld );
|
||||
|
||||
// store data
|
||||
|
||||
const offset = i * 4;
|
||||
|
||||
data[ offset + 0 ] = _vector3.x;
|
||||
data[ offset + 1 ] = _vector3.y;
|
||||
data[ offset + 2 ] = _vector3.z;
|
||||
data[ offset + 3 ] = light.distance;
|
||||
|
||||
data[ lineSize + offset + 0 ] = light.color.r * light.intensity;
|
||||
data[ lineSize + offset + 1 ] = light.color.g * light.intensity;
|
||||
data[ lineSize + offset + 2 ] = light.color.b * light.intensity;
|
||||
data[ lineSize + offset + 3 ] = light.decay;
|
||||
|
||||
}
|
||||
|
||||
lightsTexture.needsUpdate = true;
|
||||
|
||||
}
|
||||
|
||||
updateBefore( frame ) {
|
||||
|
||||
const { renderer, camera } = frame;
|
||||
|
||||
this.updateProgram( renderer );
|
||||
|
||||
this.updateLightsTexture( camera );
|
||||
|
||||
this._cameraProjectionMatrix.value = camera.projectionMatrix;
|
||||
this._cameraViewMatrix.value = camera.matrixWorldInverse;
|
||||
|
||||
renderer.getDrawingBufferSize( _size );
|
||||
this._screenSize.value.copy( _size );
|
||||
|
||||
renderer.compute( this._compute );
|
||||
|
||||
}
|
||||
|
||||
setLights( lights ) {
|
||||
|
||||
const { tiledLights, materialLights } = this;
|
||||
|
||||
let materialindex = 0;
|
||||
let tiledIndex = 0;
|
||||
|
||||
for ( const light of lights ) {
|
||||
|
||||
if ( light.isPointLight === true ) {
|
||||
|
||||
tiledLights[ tiledIndex ++ ] = light;
|
||||
|
||||
} else {
|
||||
|
||||
materialLights[ materialindex ++ ] = light;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
materialLights.length = materialindex;
|
||||
tiledLights.length = tiledIndex;
|
||||
|
||||
return super.setLights( materialLights );
|
||||
|
||||
}
|
||||
|
||||
getBlock( block = 0 ) {
|
||||
|
||||
return this._lightIndexes.element( this._screenTileIndex.mul( int( 2 ).add( int( block ) ) ) );
|
||||
|
||||
}
|
||||
|
||||
getTile( element ) {
|
||||
|
||||
element = int( element );
|
||||
|
||||
const stride = int( 4 );
|
||||
const tileOffset = element.div( stride );
|
||||
const tileIndex = this._screenTileIndex.mul( int( 2 ) ).add( tileOffset );
|
||||
|
||||
return this._lightIndexes.element( tileIndex ).element( element.mod( stride ) );
|
||||
|
||||
}
|
||||
|
||||
getLightData( index ) {
|
||||
|
||||
index = int( index );
|
||||
|
||||
const dataA = textureLoad( this._lightsTexture, ivec2( index, 0 ) );
|
||||
const dataB = textureLoad( this._lightsTexture, ivec2( index, 1 ) );
|
||||
|
||||
const position = dataA.xyz;
|
||||
const viewPosition = this._cameraViewMatrix.mul( position );
|
||||
const distance = dataA.w;
|
||||
const color = dataB.rgb;
|
||||
const decay = dataB.w;
|
||||
|
||||
return {
|
||||
position,
|
||||
viewPosition,
|
||||
distance,
|
||||
color,
|
||||
decay
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
setupLights( builder, lightNodes ) {
|
||||
|
||||
this.updateProgram( builder.renderer );
|
||||
|
||||
//
|
||||
|
||||
const lightingModel = builder.context.reflectedLight;
|
||||
|
||||
// force declaration order, before of the loop
|
||||
lightingModel.directDiffuse.toStack();
|
||||
lightingModel.directSpecular.toStack();
|
||||
|
||||
super.setupLights( builder, lightNodes );
|
||||
|
||||
Fn( () => {
|
||||
|
||||
Loop( this._tileLightCount, ( { i } ) => {
|
||||
|
||||
const lightIndex = this.getTile( i );
|
||||
|
||||
If( lightIndex.equal( int( 0 ) ), () => {
|
||||
|
||||
Break();
|
||||
|
||||
} );
|
||||
|
||||
const { color, decay, viewPosition, distance } = this.getLightData( lightIndex.sub( 1 ) );
|
||||
|
||||
builder.lightsNode.setupDirectLight( builder, this, directPointLight( {
|
||||
color,
|
||||
lightVector: viewPosition.sub( positionView ),
|
||||
cutoffDistance: distance,
|
||||
decayExponent: decay
|
||||
} ) );
|
||||
|
||||
} );
|
||||
|
||||
}, 'void' )();
|
||||
|
||||
}
|
||||
|
||||
getBufferFitSize( value ) {
|
||||
|
||||
const multiple = this.tileSize;
|
||||
|
||||
return Math.ceil( value / multiple ) * multiple;
|
||||
|
||||
}
|
||||
|
||||
setSize( width, height ) {
|
||||
|
||||
width = this.getBufferFitSize( width );
|
||||
height = this.getBufferFitSize( height );
|
||||
|
||||
if ( ! this._bufferSize || this._bufferSize.width !== width || this._bufferSize.height !== height ) {
|
||||
|
||||
this.create( width, height );
|
||||
|
||||
}
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
updateProgram( renderer ) {
|
||||
|
||||
renderer.getDrawingBufferSize( _size );
|
||||
|
||||
const width = this.getBufferFitSize( _size.width );
|
||||
const height = this.getBufferFitSize( _size.height );
|
||||
|
||||
if ( this._bufferSize === null ) {
|
||||
|
||||
this.create( width, height );
|
||||
|
||||
} else if ( this._bufferSize.width !== width || this._bufferSize.height !== height ) {
|
||||
|
||||
this.create( width, height );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
create( width, height ) {
|
||||
|
||||
const { tileSize, maxLights } = this;
|
||||
|
||||
const bufferSize = new Vector2( width, height );
|
||||
const lineSize = Math.floor( bufferSize.width / tileSize );
|
||||
const count = Math.floor( ( bufferSize.width * bufferSize.height ) / tileSize );
|
||||
|
||||
// buffers
|
||||
|
||||
const lightsData = new Float32Array( maxLights * 4 * 2 ); // 2048 lights, 4 elements(rgba), 2 components, 1 component per line (position, distance, color, decay)
|
||||
const lightsTexture = new DataTexture( lightsData, lightsData.length / 8, 2, RGBAFormat, FloatType );
|
||||
|
||||
const lightIndexesArray = new Int32Array( count * 4 * 2 );
|
||||
const lightIndexes = attributeArray( lightIndexesArray, 'ivec4' ).setName( 'lightIndexes' );
|
||||
|
||||
// compute
|
||||
|
||||
const getBlock = ( index ) => {
|
||||
|
||||
const tileIndex = instanceIndex.mul( int( 2 ) ).add( int( index ) );
|
||||
|
||||
return lightIndexes.element( tileIndex );
|
||||
|
||||
};
|
||||
|
||||
const getTile = ( elementIndex ) => {
|
||||
|
||||
elementIndex = int( elementIndex );
|
||||
|
||||
const stride = int( 4 );
|
||||
const tileOffset = elementIndex.div( stride );
|
||||
const tileIndex = instanceIndex.mul( int( 2 ) ).add( tileOffset );
|
||||
|
||||
return lightIndexes.element( tileIndex ).element( elementIndex.mod( stride ) );
|
||||
|
||||
};
|
||||
|
||||
const compute = Fn( () => {
|
||||
|
||||
const { _cameraProjectionMatrix: cameraProjectionMatrix, _bufferSize: bufferSize, _screenSize: screenSize } = this;
|
||||
|
||||
const tiledBufferSize = bufferSize.clone().divideScalar( tileSize ).floor();
|
||||
|
||||
const tileScreen = vec2(
|
||||
instanceIndex.mod( tiledBufferSize.width ),
|
||||
instanceIndex.div( tiledBufferSize.width )
|
||||
).mul( tileSize ).div( screenSize );
|
||||
|
||||
const blockSize = float( tileSize ).div( screenSize );
|
||||
const minBounds = tileScreen;
|
||||
const maxBounds = minBounds.add( blockSize );
|
||||
|
||||
const index = int( 0 ).toVar();
|
||||
|
||||
getBlock( 0 ).assign( ivec4( 0 ) );
|
||||
getBlock( 1 ).assign( ivec4( 0 ) );
|
||||
|
||||
Loop( this.maxLights, ( { i } ) => {
|
||||
|
||||
If( index.greaterThanEqual( this._tileLightCount ).or( int( i ).greaterThanEqual( int( this._lightsCount ) ) ), () => {
|
||||
|
||||
Return();
|
||||
|
||||
} );
|
||||
|
||||
const { viewPosition, distance } = this.getLightData( i );
|
||||
|
||||
const projectedPosition = cameraProjectionMatrix.mul( viewPosition );
|
||||
const ndc = projectedPosition.div( projectedPosition.w );
|
||||
const screenPosition = ndc.xy.mul( 0.5 ).add( 0.5 ).flipY();
|
||||
|
||||
const distanceFromCamera = viewPosition.z;
|
||||
const pointRadius = distance.div( distanceFromCamera );
|
||||
|
||||
If( circleIntersectsAABB( screenPosition, pointRadius, minBounds, maxBounds ), () => {
|
||||
|
||||
getTile( index ).assign( i.add( int( 1 ) ) );
|
||||
index.addAssign( int( 1 ) );
|
||||
|
||||
} );
|
||||
|
||||
} );
|
||||
|
||||
} )().compute( count ).setName( 'Update Tiled Lights' );
|
||||
|
||||
// screen coordinate lighting indexes
|
||||
|
||||
const screenTile = screenCoordinate.div( tileSize ).floor().toVar();
|
||||
const screenTileIndex = screenTile.x.add( screenTile.y.mul( lineSize ) );
|
||||
|
||||
// assigns
|
||||
|
||||
this._bufferSize = bufferSize;
|
||||
this._lightIndexes = lightIndexes;
|
||||
this._screenTileIndex = screenTileIndex;
|
||||
this._compute = compute;
|
||||
this._lightsTexture = lightsTexture;
|
||||
|
||||
}
|
||||
|
||||
get hasLights() {
|
||||
|
||||
return super.hasLights || this.tiledLights.length > 0;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default TiledLightsNode;
|
||||
|
||||
/**
|
||||
* TSL function that creates a tiled lights node.
|
||||
*
|
||||
* @tsl
|
||||
* @function
|
||||
* @param {number} [maxLights=1024] - The maximum number of lights.
|
||||
* @param {number} [tileSize=32] - The tile size.
|
||||
* @return {TiledLightsNode} The tiled lights node.
|
||||
*/
|
||||
export const tiledLights = /*@__PURE__*/ nodeProxy( TiledLightsNode );
|
||||
61
node_modules/three/examples/jsm/tsl/lighting/data/AmbientLightDataNode.js
generated
vendored
Normal file
61
node_modules/three/examples/jsm/tsl/lighting/data/AmbientLightDataNode.js
generated
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
import { Color, Node } from 'three/webgpu';
|
||||
import { NodeUpdateType, renderGroup, uniform } from 'three/tsl';
|
||||
|
||||
/**
|
||||
* Batched data node for ambient lights in dynamic lighting mode.
|
||||
*
|
||||
* @augments Node
|
||||
*/
|
||||
class AmbientLightDataNode extends Node {
|
||||
|
||||
static get type() {
|
||||
|
||||
return 'AmbientLightDataNode';
|
||||
|
||||
}
|
||||
|
||||
constructor() {
|
||||
|
||||
super();
|
||||
|
||||
this._color = new Color();
|
||||
this._lights = [];
|
||||
|
||||
this.colorNode = uniform( this._color ).setGroup( renderGroup );
|
||||
this.updateType = NodeUpdateType.RENDER;
|
||||
|
||||
}
|
||||
|
||||
setLights( lights ) {
|
||||
|
||||
this._lights = lights;
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
update() {
|
||||
|
||||
this._color.setScalar( 0 );
|
||||
|
||||
for ( let i = 0; i < this._lights.length; i ++ ) {
|
||||
|
||||
const light = this._lights[ i ];
|
||||
|
||||
this._color.r += light.color.r * light.intensity;
|
||||
this._color.g += light.color.g * light.intensity;
|
||||
this._color.b += light.color.b * light.intensity;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
setup( builder ) {
|
||||
|
||||
builder.context.irradiance.addAssign( this.colorNode );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default AmbientLightDataNode;
|
||||
111
node_modules/three/examples/jsm/tsl/lighting/data/DirectionalLightDataNode.js
generated
vendored
Normal file
111
node_modules/three/examples/jsm/tsl/lighting/data/DirectionalLightDataNode.js
generated
vendored
Normal file
@@ -0,0 +1,111 @@
|
||||
import { Color, Node, Vector3 } from 'three/webgpu';
|
||||
import { Loop, NodeUpdateType, renderGroup, uniform, uniformArray, vec3 } from 'three/tsl';
|
||||
|
||||
const _lightPosition = /*@__PURE__*/ new Vector3();
|
||||
const _targetPosition = /*@__PURE__*/ new Vector3();
|
||||
|
||||
const warn = ( message ) => {
|
||||
|
||||
console.warn( `THREE.DirectionalLightDataNode: ${ message }` );
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Batched data node for directional lights in dynamic lighting mode.
|
||||
*
|
||||
* @augments Node
|
||||
*/
|
||||
class DirectionalLightDataNode extends Node {
|
||||
|
||||
static get type() {
|
||||
|
||||
return 'DirectionalLightDataNode';
|
||||
|
||||
}
|
||||
|
||||
constructor( maxCount = 8 ) {
|
||||
|
||||
super();
|
||||
|
||||
this.maxCount = maxCount;
|
||||
this._lights = [];
|
||||
this._colors = [];
|
||||
this._directions = [];
|
||||
|
||||
for ( let i = 0; i < maxCount; i ++ ) {
|
||||
|
||||
this._colors.push( new Color() );
|
||||
this._directions.push( new Vector3() );
|
||||
|
||||
}
|
||||
|
||||
this.colorsNode = uniformArray( this._colors, 'color' ).setGroup( renderGroup );
|
||||
this.directionsNode = uniformArray( this._directions, 'vec3' ).setGroup( renderGroup );
|
||||
this.countNode = uniform( 0, 'int' ).setGroup( renderGroup );
|
||||
this.updateType = NodeUpdateType.RENDER;
|
||||
|
||||
}
|
||||
|
||||
setLights( lights ) {
|
||||
|
||||
if ( lights.length > this.maxCount ) {
|
||||
|
||||
warn( `${ lights.length } lights exceed the configured max of ${ this.maxCount }. Excess lights are ignored.` );
|
||||
|
||||
}
|
||||
|
||||
this._lights = lights;
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
update( { camera } ) {
|
||||
|
||||
const count = Math.min( this._lights.length, this.maxCount );
|
||||
|
||||
this.countNode.value = count;
|
||||
|
||||
for ( let i = 0; i < count; i ++ ) {
|
||||
|
||||
const light = this._lights[ i ];
|
||||
|
||||
this._colors[ i ].copy( light.color ).multiplyScalar( light.intensity );
|
||||
|
||||
_lightPosition.setFromMatrixPosition( light.matrixWorld );
|
||||
_targetPosition.setFromMatrixPosition( light.target.matrixWorld );
|
||||
|
||||
this._directions[ i ].subVectors( _lightPosition, _targetPosition ).transformDirection( camera.matrixWorldInverse );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
setup( builder ) {
|
||||
|
||||
const { lightingModel, reflectedLight } = builder.context;
|
||||
const dynDiffuse = vec3( 0 ).toVar( 'dynDirectionalDiffuse' );
|
||||
const dynSpecular = vec3( 0 ).toVar( 'dynDirectionalSpecular' );
|
||||
|
||||
Loop( this.countNode, ( { i } ) => {
|
||||
|
||||
const lightColor = this.colorsNode.element( i ).toVar();
|
||||
const lightDirection = this.directionsNode.element( i ).normalize().toVar();
|
||||
|
||||
lightingModel.direct( {
|
||||
lightDirection,
|
||||
lightColor,
|
||||
lightNode: { light: {}, shadowNode: null },
|
||||
reflectedLight: { directDiffuse: dynDiffuse, directSpecular: dynSpecular }
|
||||
}, builder );
|
||||
|
||||
} );
|
||||
|
||||
reflectedLight.directDiffuse.addAssign( dynDiffuse );
|
||||
reflectedLight.directSpecular.addAssign( dynSpecular );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default DirectionalLightDataNode;
|
||||
99
node_modules/three/examples/jsm/tsl/lighting/data/HemisphereLightDataNode.js
generated
vendored
Normal file
99
node_modules/three/examples/jsm/tsl/lighting/data/HemisphereLightDataNode.js
generated
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
import { Color, Node, Vector3 } from 'three/webgpu';
|
||||
import { Loop, NodeUpdateType, mix, normalWorld, renderGroup, uniform, uniformArray } from 'three/tsl';
|
||||
|
||||
const warn = ( message ) => {
|
||||
|
||||
console.warn( `THREE.HemisphereLightDataNode: ${ message }` );
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Batched data node for hemisphere lights in dynamic lighting mode.
|
||||
*
|
||||
* @augments Node
|
||||
*/
|
||||
class HemisphereLightDataNode extends Node {
|
||||
|
||||
static get type() {
|
||||
|
||||
return 'HemisphereLightDataNode';
|
||||
|
||||
}
|
||||
|
||||
constructor( maxCount = 4 ) {
|
||||
|
||||
super();
|
||||
|
||||
this.maxCount = maxCount;
|
||||
this._lights = [];
|
||||
this._skyColors = [];
|
||||
this._groundColors = [];
|
||||
this._directions = [];
|
||||
|
||||
for ( let i = 0; i < maxCount; i ++ ) {
|
||||
|
||||
this._skyColors.push( new Color() );
|
||||
this._groundColors.push( new Color() );
|
||||
this._directions.push( new Vector3() );
|
||||
|
||||
}
|
||||
|
||||
this.skyColorsNode = uniformArray( this._skyColors, 'color' ).setGroup( renderGroup );
|
||||
this.groundColorsNode = uniformArray( this._groundColors, 'color' ).setGroup( renderGroup );
|
||||
this.directionsNode = uniformArray( this._directions, 'vec3' ).setGroup( renderGroup );
|
||||
this.countNode = uniform( 0, 'int' ).setGroup( renderGroup );
|
||||
this.updateType = NodeUpdateType.RENDER;
|
||||
|
||||
}
|
||||
|
||||
setLights( lights ) {
|
||||
|
||||
if ( lights.length > this.maxCount ) {
|
||||
|
||||
warn( `${ lights.length } lights exceed the configured max of ${ this.maxCount }. Excess lights are ignored.` );
|
||||
|
||||
}
|
||||
|
||||
this._lights = lights;
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
update() {
|
||||
|
||||
const count = Math.min( this._lights.length, this.maxCount );
|
||||
|
||||
this.countNode.value = count;
|
||||
|
||||
for ( let i = 0; i < count; i ++ ) {
|
||||
|
||||
const light = this._lights[ i ];
|
||||
|
||||
this._skyColors[ i ].copy( light.color ).multiplyScalar( light.intensity );
|
||||
this._groundColors[ i ].copy( light.groundColor ).multiplyScalar( light.intensity );
|
||||
this._directions[ i ].setFromMatrixPosition( light.matrixWorld ).normalize();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
setup( builder ) {
|
||||
|
||||
Loop( this.countNode, ( { i } ) => {
|
||||
|
||||
const skyColor = this.skyColorsNode.element( i );
|
||||
const groundColor = this.groundColorsNode.element( i );
|
||||
const lightDirection = this.directionsNode.element( i );
|
||||
const hemiDiffuseWeight = normalWorld.dot( lightDirection ).mul( 0.5 ).add( 0.5 );
|
||||
const irradiance = mix( groundColor, skyColor, hemiDiffuseWeight );
|
||||
|
||||
builder.context.irradiance.addAssign( irradiance );
|
||||
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default HemisphereLightDataNode;
|
||||
134
node_modules/three/examples/jsm/tsl/lighting/data/PointLightDataNode.js
generated
vendored
Normal file
134
node_modules/three/examples/jsm/tsl/lighting/data/PointLightDataNode.js
generated
vendored
Normal file
@@ -0,0 +1,134 @@
|
||||
import { Color, Node, Vector3, Vector4 } from 'three/webgpu';
|
||||
import { Loop, NodeUpdateType, getDistanceAttenuation, positionView, renderGroup, uniform, uniformArray, vec3 } from 'three/tsl';
|
||||
|
||||
const _position = /*@__PURE__*/ new Vector3();
|
||||
|
||||
const warn = ( message ) => {
|
||||
|
||||
console.warn( `THREE.PointLightDataNode: ${ message }` );
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Batched data node for point lights in dynamic lighting mode.
|
||||
*
|
||||
* @augments Node
|
||||
*/
|
||||
class PointLightDataNode extends Node {
|
||||
|
||||
static get type() {
|
||||
|
||||
return 'PointLightDataNode';
|
||||
|
||||
}
|
||||
|
||||
constructor( maxCount = 16 ) {
|
||||
|
||||
super();
|
||||
|
||||
this.maxCount = maxCount;
|
||||
this._lights = [];
|
||||
this._colors = [];
|
||||
this._positionsAndCutoff = [];
|
||||
this._decays = [];
|
||||
|
||||
for ( let i = 0; i < maxCount; i ++ ) {
|
||||
|
||||
this._colors.push( new Color() );
|
||||
this._positionsAndCutoff.push( new Vector4() );
|
||||
this._decays.push( new Vector4() );
|
||||
|
||||
}
|
||||
|
||||
this.colorsNode = uniformArray( this._colors, 'color' ).setGroup( renderGroup );
|
||||
this.positionsAndCutoffNode = uniformArray( this._positionsAndCutoff, 'vec4' ).setGroup( renderGroup );
|
||||
this.decaysNode = uniformArray( this._decays, 'vec4' ).setGroup( renderGroup );
|
||||
this.countNode = uniform( 0, 'int' ).setGroup( renderGroup );
|
||||
this.updateType = NodeUpdateType.RENDER;
|
||||
|
||||
}
|
||||
|
||||
setLights( lights ) {
|
||||
|
||||
if ( lights.length > this.maxCount ) {
|
||||
|
||||
warn( `${ lights.length } lights exceed the configured max of ${ this.maxCount }. Excess lights are ignored.` );
|
||||
|
||||
}
|
||||
|
||||
this._lights = lights;
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
update( { camera } ) {
|
||||
|
||||
const count = Math.min( this._lights.length, this.maxCount );
|
||||
|
||||
this.countNode.value = count;
|
||||
|
||||
for ( let i = 0; i < count; i ++ ) {
|
||||
|
||||
const light = this._lights[ i ];
|
||||
|
||||
this._colors[ i ].copy( light.color ).multiplyScalar( light.intensity );
|
||||
|
||||
_position.setFromMatrixPosition( light.matrixWorld );
|
||||
_position.applyMatrix4( camera.matrixWorldInverse );
|
||||
|
||||
const positionAndCutoff = this._positionsAndCutoff[ i ];
|
||||
positionAndCutoff.x = _position.x;
|
||||
positionAndCutoff.y = _position.y;
|
||||
positionAndCutoff.z = _position.z;
|
||||
positionAndCutoff.w = light.distance;
|
||||
|
||||
this._decays[ i ].x = light.decay;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
setup( builder ) {
|
||||
|
||||
const surfacePosition = builder.context.positionView || positionView;
|
||||
const { lightingModel, reflectedLight } = builder.context;
|
||||
const dynDiffuse = vec3( 0 ).toVar( 'dynPointDiffuse' );
|
||||
const dynSpecular = vec3( 0 ).toVar( 'dynPointSpecular' );
|
||||
|
||||
Loop( this.countNode, ( { i } ) => {
|
||||
|
||||
const positionAndCutoff = this.positionsAndCutoffNode.element( i );
|
||||
const lightViewPosition = positionAndCutoff.xyz;
|
||||
const cutoffDistance = positionAndCutoff.w;
|
||||
const decayExponent = this.decaysNode.element( i ).x;
|
||||
|
||||
const lightVector = lightViewPosition.sub( surfacePosition ).toVar();
|
||||
const lightDirection = lightVector.normalize().toVar();
|
||||
const lightDistance = lightVector.length();
|
||||
|
||||
const attenuation = getDistanceAttenuation( {
|
||||
lightDistance,
|
||||
cutoffDistance,
|
||||
decayExponent
|
||||
} );
|
||||
|
||||
const lightColor = this.colorsNode.element( i ).mul( attenuation ).toVar();
|
||||
|
||||
lightingModel.direct( {
|
||||
lightDirection,
|
||||
lightColor,
|
||||
lightNode: { light: {}, shadowNode: null },
|
||||
reflectedLight: { directDiffuse: dynDiffuse, directSpecular: dynSpecular }
|
||||
}, builder );
|
||||
|
||||
} );
|
||||
|
||||
reflectedLight.directDiffuse.addAssign( dynDiffuse );
|
||||
reflectedLight.directSpecular.addAssign( dynSpecular );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default PointLightDataNode;
|
||||
161
node_modules/three/examples/jsm/tsl/lighting/data/SpotLightDataNode.js
generated
vendored
Normal file
161
node_modules/three/examples/jsm/tsl/lighting/data/SpotLightDataNode.js
generated
vendored
Normal file
@@ -0,0 +1,161 @@
|
||||
import { Color, Node, Vector3, Vector4 } from 'three/webgpu';
|
||||
import { Loop, NodeUpdateType, getDistanceAttenuation, positionView, renderGroup, smoothstep, uniform, uniformArray, vec3 } from 'three/tsl';
|
||||
|
||||
const _lightPosition = /*@__PURE__*/ new Vector3();
|
||||
const _targetPosition = /*@__PURE__*/ new Vector3();
|
||||
|
||||
const warn = ( message ) => {
|
||||
|
||||
console.warn( `THREE.SpotLightDataNode: ${ message }` );
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Batched data node for simple spot lights in dynamic lighting mode.
|
||||
*
|
||||
* Projected spot lights keep the default per-light path.
|
||||
*
|
||||
* @augments Node
|
||||
*/
|
||||
class SpotLightDataNode extends Node {
|
||||
|
||||
static get type() {
|
||||
|
||||
return 'SpotLightDataNode';
|
||||
|
||||
}
|
||||
|
||||
constructor( maxCount = 16 ) {
|
||||
|
||||
super();
|
||||
|
||||
this.maxCount = maxCount;
|
||||
this._lights = [];
|
||||
this._colors = [];
|
||||
this._positionsAndCutoff = [];
|
||||
this._directionsAndDecay = [];
|
||||
this._cones = [];
|
||||
|
||||
for ( let i = 0; i < maxCount; i ++ ) {
|
||||
|
||||
this._colors.push( new Color() );
|
||||
this._positionsAndCutoff.push( new Vector4() );
|
||||
this._directionsAndDecay.push( new Vector4() );
|
||||
this._cones.push( new Vector4() );
|
||||
|
||||
}
|
||||
|
||||
this.colorsNode = uniformArray( this._colors, 'color' ).setGroup( renderGroup );
|
||||
this.positionsAndCutoffNode = uniformArray( this._positionsAndCutoff, 'vec4' ).setGroup( renderGroup );
|
||||
this.directionsAndDecayNode = uniformArray( this._directionsAndDecay, 'vec4' ).setGroup( renderGroup );
|
||||
this.conesNode = uniformArray( this._cones, 'vec4' ).setGroup( renderGroup );
|
||||
this.countNode = uniform( 0, 'int' ).setGroup( renderGroup );
|
||||
this.updateType = NodeUpdateType.RENDER;
|
||||
|
||||
}
|
||||
|
||||
setLights( lights ) {
|
||||
|
||||
if ( lights.length > this.maxCount ) {
|
||||
|
||||
warn( `${ lights.length } lights exceed the configured max of ${ this.maxCount }. Excess lights are ignored.` );
|
||||
|
||||
}
|
||||
|
||||
this._lights = lights;
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
update( { camera } ) {
|
||||
|
||||
const count = Math.min( this._lights.length, this.maxCount );
|
||||
|
||||
this.countNode.value = count;
|
||||
|
||||
for ( let i = 0; i < count; i ++ ) {
|
||||
|
||||
const light = this._lights[ i ];
|
||||
|
||||
this._colors[ i ].copy( light.color ).multiplyScalar( light.intensity );
|
||||
|
||||
_lightPosition.setFromMatrixPosition( light.matrixWorld );
|
||||
_lightPosition.applyMatrix4( camera.matrixWorldInverse );
|
||||
|
||||
const positionAndCutoff = this._positionsAndCutoff[ i ];
|
||||
positionAndCutoff.x = _lightPosition.x;
|
||||
positionAndCutoff.y = _lightPosition.y;
|
||||
positionAndCutoff.z = _lightPosition.z;
|
||||
positionAndCutoff.w = light.distance;
|
||||
|
||||
_lightPosition.setFromMatrixPosition( light.matrixWorld );
|
||||
_targetPosition.setFromMatrixPosition( light.target.matrixWorld );
|
||||
_lightPosition.sub( _targetPosition ).transformDirection( camera.matrixWorldInverse );
|
||||
|
||||
const directionAndDecay = this._directionsAndDecay[ i ];
|
||||
directionAndDecay.x = _lightPosition.x;
|
||||
directionAndDecay.y = _lightPosition.y;
|
||||
directionAndDecay.z = _lightPosition.z;
|
||||
directionAndDecay.w = light.decay;
|
||||
|
||||
const cone = this._cones[ i ];
|
||||
cone.x = Math.cos( light.angle );
|
||||
cone.y = Math.cos( light.angle * ( 1 - light.penumbra ) );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
setup( builder ) {
|
||||
|
||||
const surfacePosition = builder.context.positionView || positionView;
|
||||
const { lightingModel, reflectedLight } = builder.context;
|
||||
const dynDiffuse = vec3( 0 ).toVar( 'dynSpotDiffuse' );
|
||||
const dynSpecular = vec3( 0 ).toVar( 'dynSpotSpecular' );
|
||||
|
||||
Loop( this.countNode, ( { i } ) => {
|
||||
|
||||
const positionAndCutoff = this.positionsAndCutoffNode.element( i );
|
||||
const lightViewPosition = positionAndCutoff.xyz;
|
||||
const cutoffDistance = positionAndCutoff.w;
|
||||
|
||||
const directionAndDecay = this.directionsAndDecayNode.element( i );
|
||||
const spotDirection = directionAndDecay.xyz;
|
||||
const decayExponent = directionAndDecay.w;
|
||||
|
||||
const cone = this.conesNode.element( i );
|
||||
const coneCos = cone.x;
|
||||
const penumbraCos = cone.y;
|
||||
|
||||
const lightVector = lightViewPosition.sub( surfacePosition ).toVar();
|
||||
const lightDirection = lightVector.normalize().toVar();
|
||||
const lightDistance = lightVector.length();
|
||||
|
||||
const angleCos = lightDirection.dot( spotDirection );
|
||||
const spotAttenuation = smoothstep( coneCos, penumbraCos, angleCos );
|
||||
const distanceAttenuation = getDistanceAttenuation( {
|
||||
lightDistance,
|
||||
cutoffDistance,
|
||||
decayExponent
|
||||
} );
|
||||
|
||||
const lightColor = this.colorsNode.element( i ).mul( spotAttenuation ).mul( distanceAttenuation ).toVar();
|
||||
|
||||
lightingModel.direct( {
|
||||
lightDirection,
|
||||
lightColor,
|
||||
lightNode: { light: {}, shadowNode: null },
|
||||
reflectedLight: { directDiffuse: dynDiffuse, directSpecular: dynSpecular }
|
||||
}, builder );
|
||||
|
||||
} );
|
||||
|
||||
reflectedLight.directDiffuse.addAssign( dynDiffuse );
|
||||
reflectedLight.directSpecular.addAssign( dynSpecular );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default SpotLightDataNode;
|
||||
Reference in New Issue
Block a user