Skip to content

⬅️ Back to Table of Contents

📄 TiledLightsNode.js

📊 Analysis Summary

Metric Count
🔧 Functions 14
🧱 Classes 1
📦 Imports 25
📊 Variables & Constants 20

📚 Table of Contents

🛠️ File Location:

📂 examples/jsm/tsl/lighting/TiledLightsNode.js

📦 Imports

Name Source
DataTexture three/webgpu
FloatType three/webgpu
RGBAFormat three/webgpu
Vector2 three/webgpu
Vector3 three/webgpu
LightsNode three/webgpu
NodeUpdateType three/webgpu
attributeArray three/tsl
nodeProxy three/tsl
int three/tsl
float three/tsl
vec2 three/tsl
ivec2 three/tsl
ivec4 three/tsl
uniform three/tsl
Break three/tsl
Loop three/tsl
positionView three/tsl
Fn three/tsl
If three/tsl
Return three/tsl
textureLoad three/tsl
instanceIndex three/tsl
screenCoordinate three/tsl
directPointLight three/tsl

Variables & Constants

Name Type Kind Value Exported
_vector3 any let/var new Vector3()
_size any let/var new Vector2()
data any let/var lightsTexture.image.data
lineSize number let/var lightsTexture.image.width * 4
light any let/var tiledLights[ i ]
offset number let/var i * 4
materialindex number let/var 0
tiledIndex number let/var 0
position any let/var dataA.xyz
distance any let/var dataA.w
color any let/var dataB.rgb
decay any let/var dataB.w
lightingModel any let/var builder.context.reflectedLight
multiple number let/var this.tileSize
bufferSize any let/var new Vector2( width, height )
lightsData Float32Array<ArrayBuffer> let/var new Float32Array( maxLights * 4 * 2 )
lightsTexture any let/var new DataTexture( lightsData, lightsData.length / 8, 2, RGBAFormat, FloatType )
lightIndexesArray Int32Array<ArrayBuffer> let/var new Int32Array( count * 4 * 2 )
minBounds any let/var tileScreen
distanceFromCamera any let/var viewPosition.z

Functions

TiledLightsNode.customCacheKey(): any

Returns: any

Calls:

  • this._compute.getCacheKey
  • super.customCacheKey
Code
customCacheKey() {

        return this._compute.getCacheKey() + super.customCacheKey();

    }

TiledLightsNode.updateLightsTexture(): void

Returns: void

Calls:

  • _vector3.setFromMatrixPosition

Internal Comments:

// world position (x4)
// store data (x2)

Code
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;

    }

TiledLightsNode.updateBefore(frame: any): void

Parameters:

  • frame any

Returns: void

Calls:

  • this.updateProgram
  • this.updateLightsTexture
  • renderer.getDrawingBufferSize
  • this._screenSize.value.copy
  • renderer.compute
Code
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 );

    }

TiledLightsNode.setLights(lights: any): any

Parameters:

  • lights any

Returns: any

Calls:

  • super.setLights
Code
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 );

    }

TiledLightsNode.getBlock(block: number): any

Parameters:

  • block number

Returns: any

Calls:

  • this._lightIndexes.element
  • this._screenTileIndex.mul
  • int( 2 ).add
  • int (from three/tsl)
Code
getBlock( block = 0 ) {

        return this._lightIndexes.element( this._screenTileIndex.mul( int( 2 ).add( int( block ) ) ) );

    }

TiledLightsNode.getTile(element: any): any

Parameters:

  • element any

Returns: any

Calls:

  • int (from three/tsl)
  • element.div
  • this._screenTileIndex.mul( int( 2 ) ).add
  • this._lightIndexes.element( tileIndex ).element
  • element.mod
Code
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 ) );

    }

TiledLightsNode.getLightData(index: any): { position: any; viewPosition: any; distance: any; color: any; decay: any; }

Parameters:

  • index any

Returns: { position: any; viewPosition: any; distance: any; color: any; decay: any; }

Calls:

  • int (from three/tsl)
  • textureLoad (from three/tsl)
  • ivec2 (from three/tsl)
  • this._cameraViewMatrix.mul
Code
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
        };

    }

TiledLightsNode.setupLights(builder: any, lightNodes: any): void

Parameters:

  • builder any
  • lightNodes any

Returns: void

Calls:

  • this.updateProgram
  • lightingModel.directDiffuse.toStack
  • lightingModel.directSpecular.toStack
  • super.setupLights
  • complex_call_5670

Internal Comments:

// (x2)
// force declaration order, before of the loop (x5)

Code
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' )();

    }

TiledLightsNode.getBufferFitSize(value: any): number

Parameters:

  • value any

Returns: number

Calls:

  • Math.ceil
Code
getBufferFitSize( value ) {

        const multiple = this.tileSize;

        return Math.ceil( value / multiple ) * multiple;

    }

TiledLightsNode.setSize(width: any, height: any): this

Parameters:

  • width any
  • height any

Returns: this

Calls:

  • this.getBufferFitSize
  • this.create
Code
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;

    }

TiledLightsNode.updateProgram(renderer: any): void

Parameters:

  • renderer any

Returns: void

Calls:

  • renderer.getDrawingBufferSize
  • this.getBufferFitSize
  • this.create
Code
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 );

        }

    }

TiledLightsNode.create(width: any, height: any): void

Parameters:

  • width any
  • height any

Returns: void

Calls:

  • Math.floor
  • attributeArray( lightIndexesArray, 'ivec4' ).setName
  • instanceIndex.mul( int( 2 ) ).add
  • int (from three/tsl)
  • lightIndexes.element
  • elementIndex.div
  • lightIndexes.element( tileIndex ).element
  • elementIndex.mod
  • `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`
    
    • screenCoordinate.div( tileSize ).floor().toVar
    • screenTile.x.add
    • screenTile.y.mul

Internal Comments:

// buffers (x2)
// compute (x2)
// screen coordinate lighting indexes (x2)
// assigns (x4)

Code
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 );

        // 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;

    }

getBlock(index: any): any

Parameters:

  • index any

Returns: any

Calls:

  • instanceIndex.mul( int( 2 ) ).add
  • int (from three/tsl)
  • lightIndexes.element
Code
( index ) => {

            const tileIndex = instanceIndex.mul( int( 2 ) ).add( int( index ) );

            return lightIndexes.element( tileIndex );

        }

getTile(elementIndex: any): any

Parameters:

  • elementIndex any

Returns: any

Calls:

  • int (from three/tsl)
  • elementIndex.div
  • instanceIndex.mul( int( 2 ) ).add
  • lightIndexes.element( tileIndex ).element
  • elementIndex.mod
Code
( 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 ) );

        }

Classes

TiledLightsNode

Class Code
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 );

        // 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;

    }

}

Methods

customCacheKey(): any
Code
customCacheKey() {

        return this._compute.getCacheKey() + super.customCacheKey();

    }
updateLightsTexture(): void
Code
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: any): void
Code
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: any): any
Code
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: number): any
Code
getBlock( block = 0 ) {

        return this._lightIndexes.element( this._screenTileIndex.mul( int( 2 ).add( int( block ) ) ) );

    }
getTile(element: any): any
Code
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: any): { position: any; viewPosition: any; distance: any; color: any; decay: any; }
Code
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: any, lightNodes: any): void
Code
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: any): number
Code
getBufferFitSize( value ) {

        const multiple = this.tileSize;

        return Math.ceil( value / multiple ) * multiple;

    }
setSize(width: any, height: any): this
Code
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: any): void
Code
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: any, height: any): void
Code
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 );

        // 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;

    }