Skip to content

⬅️ Back to Table of Contents

📄 Lensflare.js

📊 Analysis Summary

Metric Count
🧱 Classes 2
📦 Imports 14
📊 Variables & Constants 31

📚 Table of Contents

🛠️ File Location:

📂 examples/jsm/objects/Lensflare.js

📦 Imports

Name Source
AdditiveBlending three
Box2 three
BufferGeometry three
Color three
FramebufferTexture three
InterleavedBuffer three
InterleavedBufferAttribute three
Mesh three
MeshBasicMaterial three
RawShaderMaterial three
UnsignedByteType three
Vector2 three
Vector3 three
Vector4 three

Variables & Constants

Name Type Kind Value Exported
positionScreen any let/var new Vector3()
positionView any let/var new Vector3()
tempMap any let/var new FramebufferTexture( 16, 16 )
occlusionMap any let/var new FramebufferTexture( 16, 16 )
currentType any let/var UnsignedByteType
geometry any let/var Lensflare.Geometry
material1a any let/var new RawShaderMaterial( { uniforms: { 'scale': { value: null }, 'screenPositio...
material1b any let/var new RawShaderMaterial( { uniforms: { 'map': { value: tempMap }, 'scale': { va...
mesh1 any let/var new Mesh( geometry, material1a )
elements any[] let/var []
shader { name: string; uniforms: { map: { va... let/var LensflareElement.Shader
material2 any let/var new RawShaderMaterial( { name: shader.name, uniforms: { 'map': { value: null ...
mesh2 any let/var new Mesh( geometry, material2 )
scale any let/var new Vector2()
screenPositionPixels any let/var new Vector2()
validArea any let/var new Box2()
viewport any let/var new Vector4()
type any let/var ( renderTarget !== null ) ? renderTarget.texture.type : UnsignedByteType
invAspect number let/var viewport.w / viewport.z
halfViewportWidth number let/var viewport.z / 2.0
halfViewportHeight number let/var viewport.w / 2.0
size number let/var 16 / viewport.w
uniforms any let/var material1a.uniforms
vecX number let/var - positionScreen.x * 2
vecY number let/var - positionScreen.y * 2
element any let/var elements[ i ]
uniforms any let/var material2.uniforms
invAspect number let/var viewport.w / viewport.z
geometry any let/var new BufferGeometry()
float32Array Float32Array<ArrayBuffer> let/var new Float32Array( [ - 1, - 1, 0, 0, 0, 1, - 1, 0, 1, 0, 1, 1, 0, 1, 1, - 1, 1...
interleavedBuffer any let/var new InterleavedBuffer( float32Array, 5 )

Classes

Lensflare

Class Code
class Lensflare extends Mesh {

    /**
     * Constructs a new lensflare.
     */
    constructor() {

        super( Lensflare.Geometry, new MeshBasicMaterial( { opacity: 0, transparent: true } ) );

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

        this.type = 'Lensflare';

        /**
         * Overwritten to disable view-frustum culling by default.
         *
         * @type {boolean}
         * @default false
         */
        this.frustumCulled = false;

        /**
         * Overwritten to make sure lensflares a rendered last.
         *
         * @type {number}
         * @default Infinity
         */
        this.renderOrder = Infinity;

        //

        const positionScreen = new Vector3();
        const positionView = new Vector3();

        // textures

        const tempMap = new FramebufferTexture( 16, 16 );
        const occlusionMap = new FramebufferTexture( 16, 16 );

        let currentType = UnsignedByteType;

        // material

        const geometry = Lensflare.Geometry;

        const material1a = new RawShaderMaterial( {
            uniforms: {
                'scale': { value: null },
                'screenPosition': { value: null }
            },
            vertexShader: /* glsl */`

                precision highp float;

                uniform vec3 screenPosition;
                uniform vec2 scale;

                attribute vec3 position;

                void main() {

                    gl_Position = vec4( position.xy * scale + screenPosition.xy, screenPosition.z, 1.0 );

                }`,

            fragmentShader: /* glsl */`

                precision highp float;

                void main() {

                    gl_FragColor = vec4( 1.0, 0.0, 1.0, 1.0 );

                }`,
            depthTest: true,
            depthWrite: false,
            transparent: false
        } );

        const material1b = new RawShaderMaterial( {
            uniforms: {
                'map': { value: tempMap },
                'scale': { value: null },
                'screenPosition': { value: null }
            },
            vertexShader: /* glsl */`

                precision highp float;

                uniform vec3 screenPosition;
                uniform vec2 scale;

                attribute vec3 position;
                attribute vec2 uv;

                varying vec2 vUV;

                void main() {

                    vUV = uv;

                    gl_Position = vec4( position.xy * scale + screenPosition.xy, screenPosition.z, 1.0 );

                }`,

            fragmentShader: /* glsl */`

                precision highp float;

                uniform sampler2D map;

                varying vec2 vUV;

                void main() {

                    gl_FragColor = texture2D( map, vUV );

                }`,
            depthTest: false,
            depthWrite: false,
            transparent: false
        } );

        // the following object is used for occlusionMap generation

        const mesh1 = new Mesh( geometry, material1a );

        //

        const elements = [];

        const shader = LensflareElement.Shader;

        const material2 = new RawShaderMaterial( {
            name: shader.name,
            uniforms: {
                'map': { value: null },
                'occlusionMap': { value: occlusionMap },
                'color': { value: new Color( 0xffffff ) },
                'scale': { value: new Vector2() },
                'screenPosition': { value: new Vector3() }
            },
            vertexShader: shader.vertexShader,
            fragmentShader: shader.fragmentShader,
            blending: AdditiveBlending,
            transparent: true,
            depthWrite: false
        } );

        const mesh2 = new Mesh( geometry, material2 );

        /**
         * Adds the given lensflare element to this instance.
         *
         * @param {LensflareElement} element - The element to add.
         */
        this.addElement = function ( element ) {

            elements.push( element );

        };

        //

        const scale = new Vector2();
        const screenPositionPixels = new Vector2();
        const validArea = new Box2();
        const viewport = new Vector4();

        this.onBeforeRender = function ( renderer, scene, camera ) {

            renderer.getCurrentViewport( viewport );

            const renderTarget = renderer.getRenderTarget();
            const type = ( renderTarget !== null ) ? renderTarget.texture.type : UnsignedByteType;

            if ( currentType !== type ) {

                tempMap.dispose();
                occlusionMap.dispose();

                tempMap.type = occlusionMap.type = type;

                currentType = type;

            }

            const invAspect = viewport.w / viewport.z;
            const halfViewportWidth = viewport.z / 2.0;
            const halfViewportHeight = viewport.w / 2.0;

            let size = 16 / viewport.w;
            scale.set( size * invAspect, size );

            validArea.min.set( viewport.x, viewport.y );
            validArea.max.set( viewport.x + ( viewport.z - 16 ), viewport.y + ( viewport.w - 16 ) );

            // calculate position in screen space

            positionView.setFromMatrixPosition( this.matrixWorld );
            positionView.applyMatrix4( camera.matrixWorldInverse );

            if ( positionView.z > 0 ) return; // lensflare is behind the camera

            positionScreen.copy( positionView ).applyMatrix4( camera.projectionMatrix );

            // horizontal and vertical coordinate of the lower left corner of the pixels to copy

            screenPositionPixels.x = viewport.x + ( positionScreen.x * halfViewportWidth ) + halfViewportWidth - 8;
            screenPositionPixels.y = viewport.y + ( positionScreen.y * halfViewportHeight ) + halfViewportHeight - 8;

            // screen cull

            if ( validArea.containsPoint( screenPositionPixels ) ) {

                // save current RGB to temp texture

                renderer.copyFramebufferToTexture( tempMap, screenPositionPixels );

                // render pink quad

                let uniforms = material1a.uniforms;
                uniforms[ 'scale' ].value = scale;
                uniforms[ 'screenPosition' ].value = positionScreen;

                renderer.renderBufferDirect( camera, null, geometry, material1a, mesh1, null );

                // copy result to occlusionMap

                renderer.copyFramebufferToTexture( occlusionMap, screenPositionPixels );

                // restore graphics

                uniforms = material1b.uniforms;
                uniforms[ 'scale' ].value = scale;
                uniforms[ 'screenPosition' ].value = positionScreen;

                renderer.renderBufferDirect( camera, null, geometry, material1b, mesh1, null );

                // render elements

                const vecX = - positionScreen.x * 2;
                const vecY = - positionScreen.y * 2;

                for ( let i = 0, l = elements.length; i < l; i ++ ) {

                    const element = elements[ i ];

                    const uniforms = material2.uniforms;

                    uniforms[ 'color' ].value.copy( element.color );
                    uniforms[ 'map' ].value = element.texture;
                    uniforms[ 'screenPosition' ].value.x = positionScreen.x + vecX * element.distance;
                    uniforms[ 'screenPosition' ].value.y = positionScreen.y + vecY * element.distance;

                    size = element.size / viewport.w;
                    const invAspect = viewport.w / viewport.z;

                    uniforms[ 'scale' ].value.set( size * invAspect, size );

                    material2.uniformsNeedUpdate = true;

                    renderer.renderBufferDirect( camera, null, geometry, material2, mesh2, null );

                }

            }

        };

        /**
         * Frees the GPU-related resources allocated by this instance. Call this
         * method whenever this instance is no longer used in your app.
         */
        this.dispose = function () {

            material1a.dispose();
            material1b.dispose();
            material2.dispose();

            tempMap.dispose();
            occlusionMap.dispose();

            for ( let i = 0, l = elements.length; i < l; i ++ ) {

                elements[ i ].texture.dispose();

            }

        };

    }

}

LensflareElement

Class Code
class LensflareElement {

    /**
     * Constructs a new lensflare element.
     *
     * @param {Texture} texture - The flare's texture.
     * @param {number} [size=1] - The size in pixels.
     * @param {number} [distance=0] - The normalized distance (`[0,1]`) from the light source.
     * A value of `0` means the flare is located at light source.
     * @param {Color} [color] - The flare's color
     */
    constructor( texture, size = 1, distance = 0, color = new Color( 0xffffff ) ) {

        /**
         * The flare's texture.
         *
         * @type {Texture}
         */
        this.texture = texture;

        /**
         * The size in pixels.
         *
         * @type {number}
         * @default 1
         */
        this.size = size;

        /**
         * The normalized distance (`[0,1]`) from the light source.
         * A value of `0` means the flare is located at light source.
         *
         * @type {number}
         * @default 0
         */
        this.distance = distance;

        /**
         * The flare's color
         *
         * @type {Color}
         * @default (1,1,1)
         */
        this.color = color;

    }

}