Skip to content

⬅️ Back to Table of Contents

📄 AnalyticLightNode.js

📊 Analysis Summary

Metric Count
🔧 Functions 8
🧱 Classes 1
📦 Imports 9
📊 Variables & Constants 3

📚 Table of Contents

🛠️ File Location:

📂 src/nodes/lighting/AnalyticLightNode.js

📦 Imports

Name Source
LightingNode ./LightingNode.js
NodeUpdateType ../core/constants.js
uniform ../core/UniformNode.js
Color ../../math/Color.js
renderGroup ../core/UniformGroupNode.js
shadow ./ShadowNode.js
nodeObject ../tsl/TSLCore.js
lightViewPosition ../accessors/Lights.js
positionView ../accessors/Position.js

Variables & Constants

Name Type Kind Value Exported
shadowColorNode Node let/var this.shadowColorNode
customShadowNode any let/var this.light.shadow.shadowNode
shadowNode any let/var *not shown*

Functions

AnalyticLightNode.getHash(): any

Returns: any

Code
getHash() {

        return this.light.uuid;

    }

AnalyticLightNode.getLightVector(builder: NodeBuilder): any

JSDoc:

/**
     * Returns a node representing a direction vector which points from the current
     * position in view space to the light's position in view space.
     *
     * @param {NodeBuilder} builder - The builder object used for setting up the light.
     * @return {Node<vec3>} The light vector node.
     */

Parameters:

  • builder NodeBuilder

Returns: any

Calls:

  • lightViewPosition( this.light ).sub
Code
getLightVector( builder ) {

        return lightViewPosition( this.light ).sub( builder.context.positionView || positionView );

    }

AnalyticLightNode.setupDirect(): any

JSDoc:

/**
     * Sets up the direct lighting for the analytic light node.
     *
     * @abstract
     * @param {NodeBuilder} builder - The builder object used for setting up the light.
     * @return {Object|undefined} The direct light data (color and direction).
     */

Returns: any

Code
setupDirect( /*builder*/ ) { }

AnalyticLightNode.setupDirectRectArea(): any

JSDoc:

/**
     * Sets up the direct rect area lighting for the analytic light node.
     *
     * @abstract
     * @param {NodeBuilder} builder - The builder object used for setting up the light.
     * @return {Object|undefined} The direct rect area light data.
     */

Returns: any

Code
setupDirectRectArea( /*builder*/ ) { }

AnalyticLightNode.setupShadowNode(): ShadowNode

JSDoc:

/**
     * Setups the shadow node for this light. The method exists so concrete light classes
     * can setup different types of shadow nodes.
     *
     * @return {ShadowNode} The created shadow node.
     */

Returns: ShadowNode

Calls:

  • shadow (from ./ShadowNode.js)
Code
setupShadowNode() {

        return shadow( this.light );

    }

AnalyticLightNode.setupShadow(builder: NodeBuilder): void

JSDoc:

/**
     * Setups the shadow for this light. This method is only executed if the light
     * cast shadows and the current build object receives shadows. It incorporates
     * shadows into the lighting computation.
     *
     * @param {NodeBuilder} builder - The current node builder.
     */

Parameters:

  • builder NodeBuilder

Returns: void

Calls:

  • nodeObject (from ../tsl/TSLCore.js)
  • this.setupShadowNode
  • this.colorNode.mul

Internal Comments:

// (x4)

Code
setupShadow( builder ) {

        const { renderer } = builder;

        if ( renderer.shadowMap.enabled === false ) return;

        let shadowColorNode = this.shadowColorNode;

        if ( shadowColorNode === null ) {

            const customShadowNode = this.light.shadow.shadowNode;

            let shadowNode;

            if ( customShadowNode !== undefined ) {

                shadowNode = nodeObject( customShadowNode );

            } else {

                shadowNode = this.setupShadowNode();

            }

            this.shadowNode = shadowNode;

            this.shadowColorNode = shadowColorNode = this.colorNode.mul( shadowNode );

            this.baseColorNode = this.colorNode;

        }

        //

        this.colorNode = shadowColorNode;

    }

AnalyticLightNode.setup(builder: NodeBuilder): void

JSDoc:

/**
     * Unlike most other nodes, lighting nodes do not return a output node in {@link Node#setup}.
     * The main purpose of lighting nodes is to configure the current {@link LightingModel} and/or
     * invocate the respective interface methods.
     *
     * @param {NodeBuilder} builder - The current node builder.
     */

Parameters:

  • builder NodeBuilder

Returns: void

Calls:

  • this.setupShadow
  • this.shadowNode.dispose
  • this.setupDirect
  • this.setupDirectRectArea
  • builder.lightsNode.setupDirectLight
  • builder.lightsNode.setupDirectRectAreaLight
Code
setup( builder ) {

        this.colorNode = this.baseColorNode || this.colorNode;

        if ( this.light.castShadow ) {

            if ( builder.object.receiveShadow ) {

                this.setupShadow( builder );

            }

        } else if ( this.shadowNode !== null ) {

            this.shadowNode.dispose();
            this.shadowNode = null;
            this.shadowColorNode = null;

        }

        const directLightData = this.setupDirect( builder );
        const directRectAreaLightData = this.setupDirectRectArea( builder );

        if ( directLightData ) {

            builder.lightsNode.setupDirectLight( builder, this, directLightData );

        }

        if ( directRectAreaLightData ) {

            builder.lightsNode.setupDirectRectAreaLight( builder, this, directRectAreaLightData );

        }

    }

AnalyticLightNode.update(): void

JSDoc:

/**
     * The update method is used to update light uniforms per frame.
     * Potentially overwritten in concrete light nodes to update light
     * specific uniforms.
     *
     * @param {NodeFrame} frame - A reference to the current node frame.
     */

Returns: void

Calls:

  • this.color.copy( light.color ).multiplyScalar
Code
update( /*frame*/ ) {

        const { light } = this;

        this.color.copy( light.color ).multiplyScalar( light.intensity );

    }

Classes

AnalyticLightNode

Class Code
class AnalyticLightNode extends LightingNode {

    static get type() {

        return 'AnalyticLightNode';

    }

    /**
     * Constructs a new analytic light node.
     *
     * @param {?Light} [light=null] - The light source.
     */
    constructor( light = null ) {

        super();

        /**
         * The light source.
         *
         * @type {?Light}
         * @default null
         */
        this.light = light;

        /**
         * The light's color value.
         *
         * @type {Color}
         */
        this.color = new Color();

        /**
         * The light's color node. Points to `colorNode` of the light source, if set. Otherwise
         * it creates a uniform node based on {@link AnalyticLightNode#color}.
         *
         * @type {Node}
         */
        this.colorNode = ( light && light.colorNode ) || uniform( this.color ).setGroup( renderGroup );

        /**
         * This property is used to retain a reference to the original value of {@link AnalyticLightNode#colorNode}.
         * The final color node is represented by a different node when using shadows.
         *
         * @type {?Node}
         * @default null
         */
        this.baseColorNode = null;

        /**
         * Represents the light's shadow.
         *
         * @type {?ShadowNode}
         * @default null
         */
        this.shadowNode = null;

        /**
         * Represents the light's shadow color.
         *
         * @type {?Node}
         * @default null
         */
        this.shadowColorNode = null;

        /**
         * This flag can be used for type testing.
         *
         * @type {boolean}
         * @readonly
         * @default true
         */
        this.isAnalyticLightNode = true;

        /**
         * Overwritten since analytic light nodes are updated
         * once per frame.
         *
         * @type {string}
         * @default 'frame'
         */
        this.updateType = NodeUpdateType.FRAME;

    }

    getHash() {

        return this.light.uuid;

    }

    /**
     * Returns a node representing a direction vector which points from the current
     * position in view space to the light's position in view space.
     *
     * @param {NodeBuilder} builder - The builder object used for setting up the light.
     * @return {Node<vec3>} The light vector node.
     */
    getLightVector( builder ) {

        return lightViewPosition( this.light ).sub( builder.context.positionView || positionView );

    }

    /**
     * Sets up the direct lighting for the analytic light node.
     *
     * @abstract
     * @param {NodeBuilder} builder - The builder object used for setting up the light.
     * @return {Object|undefined} The direct light data (color and direction).
     */
    setupDirect( /*builder*/ ) { }

    /**
     * Sets up the direct rect area lighting for the analytic light node.
     *
     * @abstract
     * @param {NodeBuilder} builder - The builder object used for setting up the light.
     * @return {Object|undefined} The direct rect area light data.
     */
    setupDirectRectArea( /*builder*/ ) { }

    /**
     * Setups the shadow node for this light. The method exists so concrete light classes
     * can setup different types of shadow nodes.
     *
     * @return {ShadowNode} The created shadow node.
     */
    setupShadowNode() {

        return shadow( this.light );

    }

    /**
     * Setups the shadow for this light. This method is only executed if the light
     * cast shadows and the current build object receives shadows. It incorporates
     * shadows into the lighting computation.
     *
     * @param {NodeBuilder} builder - The current node builder.
     */
    setupShadow( builder ) {

        const { renderer } = builder;

        if ( renderer.shadowMap.enabled === false ) return;

        let shadowColorNode = this.shadowColorNode;

        if ( shadowColorNode === null ) {

            const customShadowNode = this.light.shadow.shadowNode;

            let shadowNode;

            if ( customShadowNode !== undefined ) {

                shadowNode = nodeObject( customShadowNode );

            } else {

                shadowNode = this.setupShadowNode();

            }

            this.shadowNode = shadowNode;

            this.shadowColorNode = shadowColorNode = this.colorNode.mul( shadowNode );

            this.baseColorNode = this.colorNode;

        }

        //

        this.colorNode = shadowColorNode;

    }

    /**
     * Unlike most other nodes, lighting nodes do not return a output node in {@link Node#setup}.
     * The main purpose of lighting nodes is to configure the current {@link LightingModel} and/or
     * invocate the respective interface methods.
     *
     * @param {NodeBuilder} builder - The current node builder.
     */
    setup( builder ) {

        this.colorNode = this.baseColorNode || this.colorNode;

        if ( this.light.castShadow ) {

            if ( builder.object.receiveShadow ) {

                this.setupShadow( builder );

            }

        } else if ( this.shadowNode !== null ) {

            this.shadowNode.dispose();
            this.shadowNode = null;
            this.shadowColorNode = null;

        }

        const directLightData = this.setupDirect( builder );
        const directRectAreaLightData = this.setupDirectRectArea( builder );

        if ( directLightData ) {

            builder.lightsNode.setupDirectLight( builder, this, directLightData );

        }

        if ( directRectAreaLightData ) {

            builder.lightsNode.setupDirectRectAreaLight( builder, this, directRectAreaLightData );

        }

    }

    /**
     * The update method is used to update light uniforms per frame.
     * Potentially overwritten in concrete light nodes to update light
     * specific uniforms.
     *
     * @param {NodeFrame} frame - A reference to the current node frame.
     */
    update( /*frame*/ ) {

        const { light } = this;

        this.color.copy( light.color ).multiplyScalar( light.intensity );

    }

}

Methods

getHash(): any
Code
getHash() {

        return this.light.uuid;

    }
getLightVector(builder: NodeBuilder): any
Code
getLightVector( builder ) {

        return lightViewPosition( this.light ).sub( builder.context.positionView || positionView );

    }
setupDirect(): any
Code
setupDirect( /*builder*/ ) { }
setupDirectRectArea(): any
Code
setupDirectRectArea( /*builder*/ ) { }
setupShadowNode(): ShadowNode
Code
setupShadowNode() {

        return shadow( this.light );

    }
setupShadow(builder: NodeBuilder): void
Code
setupShadow( builder ) {

        const { renderer } = builder;

        if ( renderer.shadowMap.enabled === false ) return;

        let shadowColorNode = this.shadowColorNode;

        if ( shadowColorNode === null ) {

            const customShadowNode = this.light.shadow.shadowNode;

            let shadowNode;

            if ( customShadowNode !== undefined ) {

                shadowNode = nodeObject( customShadowNode );

            } else {

                shadowNode = this.setupShadowNode();

            }

            this.shadowNode = shadowNode;

            this.shadowColorNode = shadowColorNode = this.colorNode.mul( shadowNode );

            this.baseColorNode = this.colorNode;

        }

        //

        this.colorNode = shadowColorNode;

    }
setup(builder: NodeBuilder): void
Code
setup( builder ) {

        this.colorNode = this.baseColorNode || this.colorNode;

        if ( this.light.castShadow ) {

            if ( builder.object.receiveShadow ) {

                this.setupShadow( builder );

            }

        } else if ( this.shadowNode !== null ) {

            this.shadowNode.dispose();
            this.shadowNode = null;
            this.shadowColorNode = null;

        }

        const directLightData = this.setupDirect( builder );
        const directRectAreaLightData = this.setupDirectRectArea( builder );

        if ( directLightData ) {

            builder.lightsNode.setupDirectLight( builder, this, directLightData );

        }

        if ( directRectAreaLightData ) {

            builder.lightsNode.setupDirectRectAreaLight( builder, this, directRectAreaLightData );

        }

    }
update(): void
Code
update( /*frame*/ ) {

        const { light } = this;

        this.color.copy( light.color ).multiplyScalar( light.intensity );

    }