Skip to content

⬅️ Back to Table of Contents

📄 FXAANode.js

📊 Analysis Summary

Metric Count
🔧 Functions 8
🧱 Classes 1
📦 Imports 23
📊 Variables & Constants 2

📚 Table of Contents

🛠️ File Location:

📂 examples/jsm/tsl/display/FXAANode.js

📦 Imports

Name Source
Vector2 three/webgpu
TempNode three/webgpu
nodeObject three/tsl
Fn three/tsl
uniformArray three/tsl
select three/tsl
float three/tsl
NodeUpdateType three/tsl
uv three/tsl
dot three/tsl
clamp three/tsl
uniform three/tsl
convertToTexture three/tsl
smoothstep three/tsl
bool three/tsl
vec2 three/tsl
vec3 three/tsl
If three/tsl
Loop three/tsl
max three/tsl
min three/tsl
Break three/tsl
abs three/tsl

Variables & Constants

Name Type Kind Value Exported
map any let/var this.textureNode.value
uvNode any let/var textureNode.uvNode \|\| uv()

Functions

FXAANode.updateBefore(): void

JSDoc:

/**
     * This method is used to update the effect's uniforms once per frame.
     *
     * @param {NodeFrame} frame - The current node frame.
     */

Returns: void

Calls:

  • this._invSize.value.set
Code
updateBefore( /* frame */ ) {

        const map = this.textureNode.value;

        this._invSize.value.set( 1 / map.image.width, 1 / map.image.height );

    }

FXAANode.setup(): ShaderCallNodeInternal

JSDoc:

/**
     * This method is used to setup the effect's TSL code.
     *
     * @param {NodeBuilder} builder - The current node builder.
     * @return {ShaderCallNodeInternal}
     */

Returns: ShaderCallNodeInternal

Calls:

  • this.textureNode.bias
  • uv (from three/tsl)
  • float (from three/tsl)
  • uniformArray (from three/tsl)
  • Fn (from three/tsl)
  • textureNode.sample
  • dot (from three/tsl)
  • Sample
  • vec3 (from three/tsl)
  • uv.add
  • texSize.mul
  • vec2 (from three/tsl)
  • SampleLuminance
  • max (from three/tsl)
  • _RelativeThreshold.mul
  • l.contrast.lessThan
  • SampleLuminanceOffset
  • min (from three/tsl)
  • highest.sub
  • float( 2.0 ).mul
  • l.s.add( l.e ).add( l.n ).add
  • f.add
  • l.se.add( l.sw ).add( l.ne ).add
  • f.mul
  • abs (from three/tsl)
  • f.sub
  • clamp (from three/tsl)
  • f.div
  • smoothstep (from three/tsl)
  • blendFactor.mul( blendFactor ).mul
  • abs( l.s.add( l.n ).sub( l.m.mul( 2.0 ) ) ).mul( 2.0 ).add
  • abs( l.se.add( l.ne ).sub( l.e.mul( 2.0 ) ) ).add
  • l.sw.add( l.nw ).sub
  • l.w.mul
  • abs( l.e.add( l.w ).sub( l.m.mul( 2.0 ) ) ).mul( 2.0 ).add
  • abs( l.se.add( l.sw ).sub( l.s.mul( 2.0 ) ) ).add
  • l.ne.add( l.nw ).sub
  • l.n.mul
  • horizontal.greaterThanEqual
  • select (from three/tsl)
  • pLuminance.sub
  • nLuminance.sub
  • select( isHorizontal, texSize.y, texSize.x ).toVar
  • float().toVar
  • `If( pGradient.lessThan( nGradient ), () => {
            pixelStep.assign( pixelStep.negate() );
            oppositeLuminance.assign( nLuminance );
            gradient.assign( nGradient );
    
        } ).Else`
    
    • oppositeLuminance.assign
    • gradient.assign
    • uv.toVar
    • vec2().toVar
    • `If( e.isHorizontal, () => {

          uvEdge.y.addAssign( e.pixelStep.mul( 0.5 ) );
          edgeStep.assign( vec2( texSize.x, 0.0 ) );
      
      } ).Else`
      
      • uvEdge.x.addAssign
      • e.pixelStep.mul
      • edgeStep.assign
      • l.m.add( e.oppositeLuminance ).mul
      • e.gradient.mul
      • uvEdge.add( edgeStep.mul( EDGE_STEPS.element( 0 ) ) ).toVar
      • SampleLuminance( puv ).sub( edgeLuminance ).toVar
      • abs( pLuminanceDelta ).greaterThanEqual( gradientThreshold ).toVar
      • Loop (from three/tsl)
      • If (from three/tsl)
      • Break (from three/tsl)
      • puv.addAssign
      • edgeStep.mul
      • EDGE_STEPS.element
      • pLuminanceDelta.assign
      • SampleLuminance( puv ).sub
      • pAtEnd.assign
      • abs( pLuminanceDelta ).greaterThanEqual
      • pAtEnd.not
      • uvEdge.sub( edgeStep.mul( EDGE_STEPS.element( 0 ) ) ).toVar
      • SampleLuminance( nuv ).sub( edgeLuminance ).toVar
      • abs( nLuminanceDelta ).greaterThanEqual( gradientThreshold ).toVar
      • nuv.subAssign
      • nLuminanceDelta.assign
      • SampleLuminance( nuv ).sub
      • nAtEnd.assign
      • abs( nLuminanceDelta ).greaterThanEqual
      • nAtEnd.not
      • `If( e.isHorizontal, () => {

        pDistance.assign( puv.x.sub( uv.x ) );
        nDistance.assign( uv.x.sub( nuv.x ) );
        

        } ).Else-pDistance.assign-puv.y.sub-nDistance.assign-uv.y.sub-bool().toVar-If( pDistance.lessThanEqual( nDistance ), () => {

        shortestDistance.assign( pDistance );
        deltaSign.assign( pLuminanceDelta.greaterThanEqual( 0.0 ) );
        

        } ).Else-shortestDistance.assign-deltaSign.assign-nLuminanceDelta.greaterThanEqual-If( deltaSign.equal( l.m.sub( edgeLuminance ).greaterThanEqual( 0.0 ) ), () => {

        blendFactor.assign( 0.0 );
        

        } ).Else-blendFactor.assign-float( 0.5 ).sub-shortestDistance.div-pDistance.add-Fn( ( [ uv, texSize ] ) => {

        const luminance = SampleLuminanceNeighborhood( texSize, uv ); If( ShouldSkipPixel( luminance ), () => {

        return Sample( uv );
        

        } );

        const pixelBlend = DeterminePixelBlendFactor( luminance ); const edge = DetermineEdge( texSize, luminance ); const edgeBlend = DetermineEdgeBlendFactor( texSize, luminance, edge, uv );

        const finalBlend = max( pixelBlend, edgeBlend ); const finalUv = uv.toVar();

        If( edge.isHorizontal, () => {

        finalUv.y.addAssign( edge.pixelStep.mul( finalBlend ) );
        

        } ).Else( () => {

        finalUv.x.addAssign( edge.pixelStep.mul( finalBlend ) );
        

        } );

        return Sample( finalUv );

      } ).setLayout-ApplyFXAA-fxaa`

Code
setup( /* builder */ ) {

        const textureNode = this.textureNode.bias( - 100 );
        const uvNode = textureNode.uvNode || uv();

        const EDGE_STEP_COUNT = float( 6 );
        const EDGE_GUESS = float( 8.0 );
        const EDGE_STEPS = uniformArray( [ 1.0, 1.5, 2.0, 2.0, 2.0, 4.0 ] );

        const _ContrastThreshold = float( 0.0312 );
        const _RelativeThreshold = float( 0.063 );
        const _SubpixelBlending = float( 1.0 );

        const Sample = Fn( ( [ uv ] ) => {

            return textureNode.sample( uv );

        } );

        const SampleLuminance = Fn( ( [ uv ] ) => {

            return dot( Sample( uv ).rgb, vec3( 0.3, 0.59, 0.11 ) );

        } );

        const SampleLuminanceOffset = Fn( ( [ texSize, uv, uOffset, vOffset ] ) => {

            const shiftedUv = uv.add( texSize.mul( vec2( uOffset, vOffset ) ) );
            return SampleLuminance( shiftedUv );

        } );

        const ShouldSkipPixel = ( l ) => {

            const threshold = max( _ContrastThreshold, _RelativeThreshold.mul( l.highest ) );
            return l.contrast.lessThan( threshold );

        };

        const SampleLuminanceNeighborhood = ( texSize, uv ) => {

            const m = SampleLuminance( uv );

            const n = SampleLuminanceOffset( texSize, uv, 0.0, - 1.0 );
            const e = SampleLuminanceOffset( texSize, uv, 1.0, 0.0 );
            const s = SampleLuminanceOffset( texSize, uv, 0.0, 1.0 );
            const w = SampleLuminanceOffset( texSize, uv, - 1.0, 0.0 );

            const ne = SampleLuminanceOffset( texSize, uv, 1.0, - 1.0 );
            const nw = SampleLuminanceOffset( texSize, uv, - 1.0, - 1.0 );
            const se = SampleLuminanceOffset( texSize, uv, 1.0, 1.0 );
            const sw = SampleLuminanceOffset( texSize, uv, - 1.0, 1.0 );

            const highest = max( s, e, n, w, m );
            const lowest = min( s, e, n, w, m );
            const contrast = highest.sub( lowest );

            return { m, n, e, s, w, ne, nw, se, sw, highest, lowest, contrast };

        };

        const DeterminePixelBlendFactor = ( l ) => {

            let f = float( 2.0 ).mul( l.s.add( l.e ).add( l.n ).add( l.w ) );
            f = f.add( l.se.add( l.sw ).add( l.ne ).add( l.nw ) );
            f = f.mul( 1.0 / 12.0 );
            f = abs( f.sub( l.m ) );
            f = clamp( f.div( max( l.contrast, 0 ) ), 0.0, 1.0 );

            const blendFactor = smoothstep( 0.0, 1.0, f );
            return blendFactor.mul( blendFactor ).mul( _SubpixelBlending );

        };

        const DetermineEdge = ( texSize, l ) => {

            const horizontal =
                abs( l.s.add( l.n ).sub( l.m.mul( 2.0 ) ) ).mul( 2.0 ).add(
                    abs( l.se.add( l.ne ).sub( l.e.mul( 2.0 ) ) ).add(
                        abs( l.sw.add( l.nw ).sub( l.w.mul( 2.0 ) ) )
                    )
                );

            const vertical =
                abs( l.e.add( l.w ).sub( l.m.mul( 2.0 ) ) ).mul( 2.0 ).add(
                    abs( l.se.add( l.sw ).sub( l.s.mul( 2.0 ) ) ).add(
                        abs( l.ne.add( l.nw ).sub( l.n.mul( 2.0 ) ) )
                    )
                );

            const isHorizontal = horizontal.greaterThanEqual( vertical );

            const pLuminance = select( isHorizontal, l.s, l.e );
            const nLuminance = select( isHorizontal, l.n, l.w );
            const pGradient = abs( pLuminance.sub( l.m ) );
            const nGradient = abs( nLuminance.sub( l.m ) );

            const pixelStep = select( isHorizontal, texSize.y, texSize.x ).toVar();
            const oppositeLuminance = float().toVar();
            const gradient = float().toVar();

            If( pGradient.lessThan( nGradient ), () => {

                pixelStep.assign( pixelStep.negate() );
                oppositeLuminance.assign( nLuminance );
                gradient.assign( nGradient );

            } ).Else( () => {

                oppositeLuminance.assign( pLuminance );
                gradient.assign( pGradient );

            } );

            return { isHorizontal, pixelStep, oppositeLuminance, gradient };

        };

        const DetermineEdgeBlendFactor = ( texSize, l, e, uv ) => {

            const uvEdge = uv.toVar();
            const edgeStep = vec2().toVar();
            If( e.isHorizontal, () => {

                uvEdge.y.addAssign( e.pixelStep.mul( 0.5 ) );
                edgeStep.assign( vec2( texSize.x, 0.0 ) );

            } ).Else( () => {

                uvEdge.x.addAssign( e.pixelStep.mul( 0.5 ) );
                edgeStep.assign( vec2( 0.0, texSize.y ) );

            } );

            const edgeLuminance = l.m.add( e.oppositeLuminance ).mul( 0.5 );
            const gradientThreshold = e.gradient.mul( 0.25 );

            const puv = uvEdge.add( edgeStep.mul( EDGE_STEPS.element( 0 ) ) ).toVar();
            const pLuminanceDelta = SampleLuminance( puv ).sub( edgeLuminance ).toVar();
            const pAtEnd = abs( pLuminanceDelta ).greaterThanEqual( gradientThreshold ).toVar();

            Loop( { start: 1, end: EDGE_STEP_COUNT }, ( { i } ) => {

                If( pAtEnd, () => {

                    Break();

                } );

                puv.addAssign( edgeStep.mul( EDGE_STEPS.element( i ) ) );
                pLuminanceDelta.assign( SampleLuminance( puv ).sub( edgeLuminance ) );
                pAtEnd.assign( abs( pLuminanceDelta ).greaterThanEqual( gradientThreshold ) );

            } );

            If( pAtEnd.not(), () => {

                puv.addAssign( edgeStep.mul( EDGE_GUESS ) );

            } );

            const nuv = uvEdge.sub( edgeStep.mul( EDGE_STEPS.element( 0 ) ) ).toVar();
            const nLuminanceDelta = SampleLuminance( nuv ).sub( edgeLuminance ).toVar();
            const nAtEnd = abs( nLuminanceDelta ).greaterThanEqual( gradientThreshold ).toVar();

            Loop( { start: 1, end: EDGE_STEP_COUNT }, ( { i } ) => {

                If( nAtEnd, () => {

                    Break();

                } );

                nuv.subAssign( edgeStep.mul( EDGE_STEPS.element( i ) ) );
                nLuminanceDelta.assign( SampleLuminance( nuv ).sub( edgeLuminance ) );
                nAtEnd.assign( abs( nLuminanceDelta ).greaterThanEqual( gradientThreshold ) );

            } );

            If( nAtEnd.not(), () => {

                nuv.subAssign( edgeStep.mul( EDGE_GUESS ) );

            } );

            const pDistance = float().toVar();
            const nDistance = float().toVar();

            If( e.isHorizontal, () => {

                pDistance.assign( puv.x.sub( uv.x ) );
                nDistance.assign( uv.x.sub( nuv.x ) );

            } ).Else( () => {

                pDistance.assign( puv.y.sub( uv.y ) );
                nDistance.assign( uv.y.sub( nuv.y ) );

            } );

            const shortestDistance = float().toVar();
            const deltaSign = bool().toVar();

            If( pDistance.lessThanEqual( nDistance ), () => {

                shortestDistance.assign( pDistance );
                deltaSign.assign( pLuminanceDelta.greaterThanEqual( 0.0 ) );

            } ).Else( () => {

                shortestDistance.assign( nDistance );
                deltaSign.assign( nLuminanceDelta.greaterThanEqual( 0.0 ) );

            } );

            const blendFactor = float().toVar();

            If( deltaSign.equal( l.m.sub( edgeLuminance ).greaterThanEqual( 0.0 ) ), () => {

                blendFactor.assign( 0.0 );

            } ).Else( () => {

                blendFactor.assign( float( 0.5 ).sub( shortestDistance.div( pDistance.add( nDistance ) ) ) );

            } );

            return blendFactor;

        };

        const ApplyFXAA = Fn( ( [ uv, texSize ] ) => {

            const luminance = SampleLuminanceNeighborhood( texSize, uv );
            If( ShouldSkipPixel( luminance ), () => {

                return Sample( uv );

            } );

            const pixelBlend = DeterminePixelBlendFactor( luminance );
            const edge = DetermineEdge( texSize, luminance );
            const edgeBlend = DetermineEdgeBlendFactor( texSize, luminance, edge, uv );

            const finalBlend = max( pixelBlend, edgeBlend );
            const finalUv = uv.toVar();

            If( edge.isHorizontal, () => {

                finalUv.y.addAssign( edge.pixelStep.mul( finalBlend ) );

            } ).Else( () => {

                finalUv.x.addAssign( edge.pixelStep.mul( finalBlend ) );

            } );

            return Sample( finalUv );

        } ).setLayout( {
            name: 'FxaaPixelShader',
            type: 'vec4',
            inputs: [
                { name: 'uv', type: 'vec2' },
                { name: 'texSize', type: 'vec2' },
            ]
        } );

        const fxaa = Fn( () => {

            return ApplyFXAA( uvNode, this._invSize );

        } );

        const outputNode = fxaa();

        return outputNode;

    }

ShouldSkipPixel(l: any): any

Parameters:

  • l any

Returns: any

Calls:

  • max (from three/tsl)
  • _RelativeThreshold.mul
  • l.contrast.lessThan
Code
( l ) => {

            const threshold = max( _ContrastThreshold, _RelativeThreshold.mul( l.highest ) );
            return l.contrast.lessThan( threshold );

        }

SampleLuminanceNeighborhood(texSize: any, uv: any): { m: any; n: any; e: any; s: any; w: any; ne: any; nw: any; se: any; sw: any; highest: any; lowest: any; contrast: any; }

Parameters:

  • texSize any
  • uv any

Returns: { m: any; n: any; e: any; s: any; w: any; ne: any; nw: any; se: any; sw: any; highest: any; lowest: any; contrast: any; }

Calls:

  • SampleLuminance
  • SampleLuminanceOffset
  • max (from three/tsl)
  • min (from three/tsl)
  • highest.sub
Code
( texSize, uv ) => {

            const m = SampleLuminance( uv );

            const n = SampleLuminanceOffset( texSize, uv, 0.0, - 1.0 );
            const e = SampleLuminanceOffset( texSize, uv, 1.0, 0.0 );
            const s = SampleLuminanceOffset( texSize, uv, 0.0, 1.0 );
            const w = SampleLuminanceOffset( texSize, uv, - 1.0, 0.0 );

            const ne = SampleLuminanceOffset( texSize, uv, 1.0, - 1.0 );
            const nw = SampleLuminanceOffset( texSize, uv, - 1.0, - 1.0 );
            const se = SampleLuminanceOffset( texSize, uv, 1.0, 1.0 );
            const sw = SampleLuminanceOffset( texSize, uv, - 1.0, 1.0 );

            const highest = max( s, e, n, w, m );
            const lowest = min( s, e, n, w, m );
            const contrast = highest.sub( lowest );

            return { m, n, e, s, w, ne, nw, se, sw, highest, lowest, contrast };

        }

DeterminePixelBlendFactor(l: any): any

Parameters:

  • l any

Returns: any

Calls:

  • float( 2.0 ).mul
  • l.s.add( l.e ).add( l.n ).add
  • f.add
  • l.se.add( l.sw ).add( l.ne ).add
  • f.mul
  • abs (from three/tsl)
  • f.sub
  • clamp (from three/tsl)
  • f.div
  • max (from three/tsl)
  • smoothstep (from three/tsl)
  • blendFactor.mul( blendFactor ).mul
Code
( l ) => {

            let f = float( 2.0 ).mul( l.s.add( l.e ).add( l.n ).add( l.w ) );
            f = f.add( l.se.add( l.sw ).add( l.ne ).add( l.nw ) );
            f = f.mul( 1.0 / 12.0 );
            f = abs( f.sub( l.m ) );
            f = clamp( f.div( max( l.contrast, 0 ) ), 0.0, 1.0 );

            const blendFactor = smoothstep( 0.0, 1.0, f );
            return blendFactor.mul( blendFactor ).mul( _SubpixelBlending );

        }

DetermineEdge(texSize: any, l: any): { isHorizontal: any; pixelStep: any; oppositeLuminance: any; gradient: any; }

Parameters:

  • texSize any
  • l any

Returns: { isHorizontal: any; pixelStep: any; oppositeLuminance: any; gradient: any; }

Calls:

  • abs( l.s.add( l.n ).sub( l.m.mul( 2.0 ) ) ).mul( 2.0 ).add
  • abs( l.se.add( l.ne ).sub( l.e.mul( 2.0 ) ) ).add
  • abs (from three/tsl)
  • l.sw.add( l.nw ).sub
  • l.w.mul
  • abs( l.e.add( l.w ).sub( l.m.mul( 2.0 ) ) ).mul( 2.0 ).add
  • abs( l.se.add( l.sw ).sub( l.s.mul( 2.0 ) ) ).add
  • l.ne.add( l.nw ).sub
  • l.n.mul
  • horizontal.greaterThanEqual
  • select (from three/tsl)
  • pLuminance.sub
  • nLuminance.sub
  • select( isHorizontal, texSize.y, texSize.x ).toVar
  • float().toVar
  • `If( pGradient.lessThan( nGradient ), () => {
            pixelStep.assign( pixelStep.negate() );
            oppositeLuminance.assign( nLuminance );
            gradient.assign( nGradient );
    
        } ).Else`
    
    • oppositeLuminance.assign
    • gradient.assign
Code
( texSize, l ) => {

            const horizontal =
                abs( l.s.add( l.n ).sub( l.m.mul( 2.0 ) ) ).mul( 2.0 ).add(
                    abs( l.se.add( l.ne ).sub( l.e.mul( 2.0 ) ) ).add(
                        abs( l.sw.add( l.nw ).sub( l.w.mul( 2.0 ) ) )
                    )
                );

            const vertical =
                abs( l.e.add( l.w ).sub( l.m.mul( 2.0 ) ) ).mul( 2.0 ).add(
                    abs( l.se.add( l.sw ).sub( l.s.mul( 2.0 ) ) ).add(
                        abs( l.ne.add( l.nw ).sub( l.n.mul( 2.0 ) ) )
                    )
                );

            const isHorizontal = horizontal.greaterThanEqual( vertical );

            const pLuminance = select( isHorizontal, l.s, l.e );
            const nLuminance = select( isHorizontal, l.n, l.w );
            const pGradient = abs( pLuminance.sub( l.m ) );
            const nGradient = abs( nLuminance.sub( l.m ) );

            const pixelStep = select( isHorizontal, texSize.y, texSize.x ).toVar();
            const oppositeLuminance = float().toVar();
            const gradient = float().toVar();

            If( pGradient.lessThan( nGradient ), () => {

                pixelStep.assign( pixelStep.negate() );
                oppositeLuminance.assign( nLuminance );
                gradient.assign( nGradient );

            } ).Else( () => {

                oppositeLuminance.assign( pLuminance );
                gradient.assign( pGradient );

            } );

            return { isHorizontal, pixelStep, oppositeLuminance, gradient };

        }

DetermineEdgeBlendFactor(texSize: any, l: any, e: any, uv: any): any

Parameters:

  • texSize any
  • l any
  • e any
  • uv any

Returns: any

Calls:

  • uv.toVar
  • vec2().toVar
  • `If( e.isHorizontal, () => {
            uvEdge.y.addAssign( e.pixelStep.mul( 0.5 ) );
            edgeStep.assign( vec2( texSize.x, 0.0 ) );
    
        } ).Else`
    
    • uvEdge.x.addAssign
    • e.pixelStep.mul
    • edgeStep.assign
    • vec2 (from three/tsl)
    • l.m.add( e.oppositeLuminance ).mul
    • e.gradient.mul
    • uvEdge.add( edgeStep.mul( EDGE_STEPS.element( 0 ) ) ).toVar
    • SampleLuminance( puv ).sub( edgeLuminance ).toVar
    • abs( pLuminanceDelta ).greaterThanEqual( gradientThreshold ).toVar
    • Loop (from three/tsl)
    • If (from three/tsl)
    • Break (from three/tsl)
    • puv.addAssign
    • edgeStep.mul
    • EDGE_STEPS.element
    • pLuminanceDelta.assign
    • SampleLuminance( puv ).sub
    • pAtEnd.assign
    • abs( pLuminanceDelta ).greaterThanEqual
    • pAtEnd.not
    • uvEdge.sub( edgeStep.mul( EDGE_STEPS.element( 0 ) ) ).toVar
    • SampleLuminance( nuv ).sub( edgeLuminance ).toVar
    • abs( nLuminanceDelta ).greaterThanEqual( gradientThreshold ).toVar
    • nuv.subAssign
    • nLuminanceDelta.assign
    • SampleLuminance( nuv ).sub
    • nAtEnd.assign
    • abs( nLuminanceDelta ).greaterThanEqual
    • nAtEnd.not
    • float().toVar
    • `If( e.isHorizontal, () => {
          pDistance.assign( puv.x.sub( uv.x ) );
          nDistance.assign( uv.x.sub( nuv.x ) );
      
      } ).Else`
      
      • pDistance.assign
      • puv.y.sub
      • nDistance.assign
      • uv.y.sub
      • bool().toVar
      • `If( pDistance.lessThanEqual( nDistance ), () => {

        shortestDistance.assign( pDistance );
        deltaSign.assign( pLuminanceDelta.greaterThanEqual( 0.0 ) );
        

        } ).Else-shortestDistance.assign-deltaSign.assign-nLuminanceDelta.greaterThanEqual-If( deltaSign.equal( l.m.sub( edgeLuminance ).greaterThanEqual( 0.0 ) ), () => {

        blendFactor.assign( 0.0 );
        

        } ).Else-blendFactor.assign-float( 0.5 ).sub-shortestDistance.div-pDistance.add`

Code
( texSize, l, e, uv ) => {

            const uvEdge = uv.toVar();
            const edgeStep = vec2().toVar();
            If( e.isHorizontal, () => {

                uvEdge.y.addAssign( e.pixelStep.mul( 0.5 ) );
                edgeStep.assign( vec2( texSize.x, 0.0 ) );

            } ).Else( () => {

                uvEdge.x.addAssign( e.pixelStep.mul( 0.5 ) );
                edgeStep.assign( vec2( 0.0, texSize.y ) );

            } );

            const edgeLuminance = l.m.add( e.oppositeLuminance ).mul( 0.5 );
            const gradientThreshold = e.gradient.mul( 0.25 );

            const puv = uvEdge.add( edgeStep.mul( EDGE_STEPS.element( 0 ) ) ).toVar();
            const pLuminanceDelta = SampleLuminance( puv ).sub( edgeLuminance ).toVar();
            const pAtEnd = abs( pLuminanceDelta ).greaterThanEqual( gradientThreshold ).toVar();

            Loop( { start: 1, end: EDGE_STEP_COUNT }, ( { i } ) => {

                If( pAtEnd, () => {

                    Break();

                } );

                puv.addAssign( edgeStep.mul( EDGE_STEPS.element( i ) ) );
                pLuminanceDelta.assign( SampleLuminance( puv ).sub( edgeLuminance ) );
                pAtEnd.assign( abs( pLuminanceDelta ).greaterThanEqual( gradientThreshold ) );

            } );

            If( pAtEnd.not(), () => {

                puv.addAssign( edgeStep.mul( EDGE_GUESS ) );

            } );

            const nuv = uvEdge.sub( edgeStep.mul( EDGE_STEPS.element( 0 ) ) ).toVar();
            const nLuminanceDelta = SampleLuminance( nuv ).sub( edgeLuminance ).toVar();
            const nAtEnd = abs( nLuminanceDelta ).greaterThanEqual( gradientThreshold ).toVar();

            Loop( { start: 1, end: EDGE_STEP_COUNT }, ( { i } ) => {

                If( nAtEnd, () => {

                    Break();

                } );

                nuv.subAssign( edgeStep.mul( EDGE_STEPS.element( i ) ) );
                nLuminanceDelta.assign( SampleLuminance( nuv ).sub( edgeLuminance ) );
                nAtEnd.assign( abs( nLuminanceDelta ).greaterThanEqual( gradientThreshold ) );

            } );

            If( nAtEnd.not(), () => {

                nuv.subAssign( edgeStep.mul( EDGE_GUESS ) );

            } );

            const pDistance = float().toVar();
            const nDistance = float().toVar();

            If( e.isHorizontal, () => {

                pDistance.assign( puv.x.sub( uv.x ) );
                nDistance.assign( uv.x.sub( nuv.x ) );

            } ).Else( () => {

                pDistance.assign( puv.y.sub( uv.y ) );
                nDistance.assign( uv.y.sub( nuv.y ) );

            } );

            const shortestDistance = float().toVar();
            const deltaSign = bool().toVar();

            If( pDistance.lessThanEqual( nDistance ), () => {

                shortestDistance.assign( pDistance );
                deltaSign.assign( pLuminanceDelta.greaterThanEqual( 0.0 ) );

            } ).Else( () => {

                shortestDistance.assign( nDistance );
                deltaSign.assign( nLuminanceDelta.greaterThanEqual( 0.0 ) );

            } );

            const blendFactor = float().toVar();

            If( deltaSign.equal( l.m.sub( edgeLuminance ).greaterThanEqual( 0.0 ) ), () => {

                blendFactor.assign( 0.0 );

            } ).Else( () => {

                blendFactor.assign( float( 0.5 ).sub( shortestDistance.div( pDistance.add( nDistance ) ) ) );

            } );

            return blendFactor;

        }

fxaa(node: any): FXAANode

Parameters:

  • node any

Returns: FXAANode

Calls:

  • nodeObject (from three/tsl)
Code
( node ) => nodeObject( new FXAANode( convertToTexture( node ) ) )

Classes

FXAANode

Class Code
class FXAANode extends TempNode {

    static get type() {

        return 'FXAANode';

    }

    /**
     * Constructs a new FXAA node.
     *
     * @param {TextureNode} textureNode - The texture node that represents the input of the effect.
     */
    constructor( textureNode ) {

        super( 'vec4' );

        /**
         * The texture node that represents the input of the effect.
         *
         * @type {TextureNode}
         */
        this.textureNode = textureNode;

        /**
         * The `updateBeforeType` is set to `NodeUpdateType.FRAME` since the node updates
         * its internal uniforms once per frame in `updateBefore()`.
         *
         * @type {string}
         * @default 'frame'
         */
        this.updateBeforeType = NodeUpdateType.FRAME;

        /**
         * A uniform node holding the inverse resolution value.
         *
         * @private
         * @type {UniformNode<vec2>}
         */
        this._invSize = uniform( new Vector2() );

    }

    /**
     * This method is used to update the effect's uniforms once per frame.
     *
     * @param {NodeFrame} frame - The current node frame.
     */
    updateBefore( /* frame */ ) {

        const map = this.textureNode.value;

        this._invSize.value.set( 1 / map.image.width, 1 / map.image.height );

    }

    /**
     * This method is used to setup the effect's TSL code.
     *
     * @param {NodeBuilder} builder - The current node builder.
     * @return {ShaderCallNodeInternal}
     */
    setup( /* builder */ ) {

        const textureNode = this.textureNode.bias( - 100 );
        const uvNode = textureNode.uvNode || uv();

        const EDGE_STEP_COUNT = float( 6 );
        const EDGE_GUESS = float( 8.0 );
        const EDGE_STEPS = uniformArray( [ 1.0, 1.5, 2.0, 2.0, 2.0, 4.0 ] );

        const _ContrastThreshold = float( 0.0312 );
        const _RelativeThreshold = float( 0.063 );
        const _SubpixelBlending = float( 1.0 );

        const Sample = Fn( ( [ uv ] ) => {

            return textureNode.sample( uv );

        } );

        const SampleLuminance = Fn( ( [ uv ] ) => {

            return dot( Sample( uv ).rgb, vec3( 0.3, 0.59, 0.11 ) );

        } );

        const SampleLuminanceOffset = Fn( ( [ texSize, uv, uOffset, vOffset ] ) => {

            const shiftedUv = uv.add( texSize.mul( vec2( uOffset, vOffset ) ) );
            return SampleLuminance( shiftedUv );

        } );

        const ShouldSkipPixel = ( l ) => {

            const threshold = max( _ContrastThreshold, _RelativeThreshold.mul( l.highest ) );
            return l.contrast.lessThan( threshold );

        };

        const SampleLuminanceNeighborhood = ( texSize, uv ) => {

            const m = SampleLuminance( uv );

            const n = SampleLuminanceOffset( texSize, uv, 0.0, - 1.0 );
            const e = SampleLuminanceOffset( texSize, uv, 1.0, 0.0 );
            const s = SampleLuminanceOffset( texSize, uv, 0.0, 1.0 );
            const w = SampleLuminanceOffset( texSize, uv, - 1.0, 0.0 );

            const ne = SampleLuminanceOffset( texSize, uv, 1.0, - 1.0 );
            const nw = SampleLuminanceOffset( texSize, uv, - 1.0, - 1.0 );
            const se = SampleLuminanceOffset( texSize, uv, 1.0, 1.0 );
            const sw = SampleLuminanceOffset( texSize, uv, - 1.0, 1.0 );

            const highest = max( s, e, n, w, m );
            const lowest = min( s, e, n, w, m );
            const contrast = highest.sub( lowest );

            return { m, n, e, s, w, ne, nw, se, sw, highest, lowest, contrast };

        };

        const DeterminePixelBlendFactor = ( l ) => {

            let f = float( 2.0 ).mul( l.s.add( l.e ).add( l.n ).add( l.w ) );
            f = f.add( l.se.add( l.sw ).add( l.ne ).add( l.nw ) );
            f = f.mul( 1.0 / 12.0 );
            f = abs( f.sub( l.m ) );
            f = clamp( f.div( max( l.contrast, 0 ) ), 0.0, 1.0 );

            const blendFactor = smoothstep( 0.0, 1.0, f );
            return blendFactor.mul( blendFactor ).mul( _SubpixelBlending );

        };

        const DetermineEdge = ( texSize, l ) => {

            const horizontal =
                abs( l.s.add( l.n ).sub( l.m.mul( 2.0 ) ) ).mul( 2.0 ).add(
                    abs( l.se.add( l.ne ).sub( l.e.mul( 2.0 ) ) ).add(
                        abs( l.sw.add( l.nw ).sub( l.w.mul( 2.0 ) ) )
                    )
                );

            const vertical =
                abs( l.e.add( l.w ).sub( l.m.mul( 2.0 ) ) ).mul( 2.0 ).add(
                    abs( l.se.add( l.sw ).sub( l.s.mul( 2.0 ) ) ).add(
                        abs( l.ne.add( l.nw ).sub( l.n.mul( 2.0 ) ) )
                    )
                );

            const isHorizontal = horizontal.greaterThanEqual( vertical );

            const pLuminance = select( isHorizontal, l.s, l.e );
            const nLuminance = select( isHorizontal, l.n, l.w );
            const pGradient = abs( pLuminance.sub( l.m ) );
            const nGradient = abs( nLuminance.sub( l.m ) );

            const pixelStep = select( isHorizontal, texSize.y, texSize.x ).toVar();
            const oppositeLuminance = float().toVar();
            const gradient = float().toVar();

            If( pGradient.lessThan( nGradient ), () => {

                pixelStep.assign( pixelStep.negate() );
                oppositeLuminance.assign( nLuminance );
                gradient.assign( nGradient );

            } ).Else( () => {

                oppositeLuminance.assign( pLuminance );
                gradient.assign( pGradient );

            } );

            return { isHorizontal, pixelStep, oppositeLuminance, gradient };

        };

        const DetermineEdgeBlendFactor = ( texSize, l, e, uv ) => {

            const uvEdge = uv.toVar();
            const edgeStep = vec2().toVar();
            If( e.isHorizontal, () => {

                uvEdge.y.addAssign( e.pixelStep.mul( 0.5 ) );
                edgeStep.assign( vec2( texSize.x, 0.0 ) );

            } ).Else( () => {

                uvEdge.x.addAssign( e.pixelStep.mul( 0.5 ) );
                edgeStep.assign( vec2( 0.0, texSize.y ) );

            } );

            const edgeLuminance = l.m.add( e.oppositeLuminance ).mul( 0.5 );
            const gradientThreshold = e.gradient.mul( 0.25 );

            const puv = uvEdge.add( edgeStep.mul( EDGE_STEPS.element( 0 ) ) ).toVar();
            const pLuminanceDelta = SampleLuminance( puv ).sub( edgeLuminance ).toVar();
            const pAtEnd = abs( pLuminanceDelta ).greaterThanEqual( gradientThreshold ).toVar();

            Loop( { start: 1, end: EDGE_STEP_COUNT }, ( { i } ) => {

                If( pAtEnd, () => {

                    Break();

                } );

                puv.addAssign( edgeStep.mul( EDGE_STEPS.element( i ) ) );
                pLuminanceDelta.assign( SampleLuminance( puv ).sub( edgeLuminance ) );
                pAtEnd.assign( abs( pLuminanceDelta ).greaterThanEqual( gradientThreshold ) );

            } );

            If( pAtEnd.not(), () => {

                puv.addAssign( edgeStep.mul( EDGE_GUESS ) );

            } );

            const nuv = uvEdge.sub( edgeStep.mul( EDGE_STEPS.element( 0 ) ) ).toVar();
            const nLuminanceDelta = SampleLuminance( nuv ).sub( edgeLuminance ).toVar();
            const nAtEnd = abs( nLuminanceDelta ).greaterThanEqual( gradientThreshold ).toVar();

            Loop( { start: 1, end: EDGE_STEP_COUNT }, ( { i } ) => {

                If( nAtEnd, () => {

                    Break();

                } );

                nuv.subAssign( edgeStep.mul( EDGE_STEPS.element( i ) ) );
                nLuminanceDelta.assign( SampleLuminance( nuv ).sub( edgeLuminance ) );
                nAtEnd.assign( abs( nLuminanceDelta ).greaterThanEqual( gradientThreshold ) );

            } );

            If( nAtEnd.not(), () => {

                nuv.subAssign( edgeStep.mul( EDGE_GUESS ) );

            } );

            const pDistance = float().toVar();
            const nDistance = float().toVar();

            If( e.isHorizontal, () => {

                pDistance.assign( puv.x.sub( uv.x ) );
                nDistance.assign( uv.x.sub( nuv.x ) );

            } ).Else( () => {

                pDistance.assign( puv.y.sub( uv.y ) );
                nDistance.assign( uv.y.sub( nuv.y ) );

            } );

            const shortestDistance = float().toVar();
            const deltaSign = bool().toVar();

            If( pDistance.lessThanEqual( nDistance ), () => {

                shortestDistance.assign( pDistance );
                deltaSign.assign( pLuminanceDelta.greaterThanEqual( 0.0 ) );

            } ).Else( () => {

                shortestDistance.assign( nDistance );
                deltaSign.assign( nLuminanceDelta.greaterThanEqual( 0.0 ) );

            } );

            const blendFactor = float().toVar();

            If( deltaSign.equal( l.m.sub( edgeLuminance ).greaterThanEqual( 0.0 ) ), () => {

                blendFactor.assign( 0.0 );

            } ).Else( () => {

                blendFactor.assign( float( 0.5 ).sub( shortestDistance.div( pDistance.add( nDistance ) ) ) );

            } );

            return blendFactor;

        };

        const ApplyFXAA = Fn( ( [ uv, texSize ] ) => {

            const luminance = SampleLuminanceNeighborhood( texSize, uv );
            If( ShouldSkipPixel( luminance ), () => {

                return Sample( uv );

            } );

            const pixelBlend = DeterminePixelBlendFactor( luminance );
            const edge = DetermineEdge( texSize, luminance );
            const edgeBlend = DetermineEdgeBlendFactor( texSize, luminance, edge, uv );

            const finalBlend = max( pixelBlend, edgeBlend );
            const finalUv = uv.toVar();

            If( edge.isHorizontal, () => {

                finalUv.y.addAssign( edge.pixelStep.mul( finalBlend ) );

            } ).Else( () => {

                finalUv.x.addAssign( edge.pixelStep.mul( finalBlend ) );

            } );

            return Sample( finalUv );

        } ).setLayout( {
            name: 'FxaaPixelShader',
            type: 'vec4',
            inputs: [
                { name: 'uv', type: 'vec2' },
                { name: 'texSize', type: 'vec2' },
            ]
        } );

        const fxaa = Fn( () => {

            return ApplyFXAA( uvNode, this._invSize );

        } );

        const outputNode = fxaa();

        return outputNode;

    }

}

Methods

updateBefore(): void
Code
updateBefore( /* frame */ ) {

        const map = this.textureNode.value;

        this._invSize.value.set( 1 / map.image.width, 1 / map.image.height );

    }
setup(): ShaderCallNodeInternal
Code
setup( /* builder */ ) {

        const textureNode = this.textureNode.bias( - 100 );
        const uvNode = textureNode.uvNode || uv();

        const EDGE_STEP_COUNT = float( 6 );
        const EDGE_GUESS = float( 8.0 );
        const EDGE_STEPS = uniformArray( [ 1.0, 1.5, 2.0, 2.0, 2.0, 4.0 ] );

        const _ContrastThreshold = float( 0.0312 );
        const _RelativeThreshold = float( 0.063 );
        const _SubpixelBlending = float( 1.0 );

        const Sample = Fn( ( [ uv ] ) => {

            return textureNode.sample( uv );

        } );

        const SampleLuminance = Fn( ( [ uv ] ) => {

            return dot( Sample( uv ).rgb, vec3( 0.3, 0.59, 0.11 ) );

        } );

        const SampleLuminanceOffset = Fn( ( [ texSize, uv, uOffset, vOffset ] ) => {

            const shiftedUv = uv.add( texSize.mul( vec2( uOffset, vOffset ) ) );
            return SampleLuminance( shiftedUv );

        } );

        const ShouldSkipPixel = ( l ) => {

            const threshold = max( _ContrastThreshold, _RelativeThreshold.mul( l.highest ) );
            return l.contrast.lessThan( threshold );

        };

        const SampleLuminanceNeighborhood = ( texSize, uv ) => {

            const m = SampleLuminance( uv );

            const n = SampleLuminanceOffset( texSize, uv, 0.0, - 1.0 );
            const e = SampleLuminanceOffset( texSize, uv, 1.0, 0.0 );
            const s = SampleLuminanceOffset( texSize, uv, 0.0, 1.0 );
            const w = SampleLuminanceOffset( texSize, uv, - 1.0, 0.0 );

            const ne = SampleLuminanceOffset( texSize, uv, 1.0, - 1.0 );
            const nw = SampleLuminanceOffset( texSize, uv, - 1.0, - 1.0 );
            const se = SampleLuminanceOffset( texSize, uv, 1.0, 1.0 );
            const sw = SampleLuminanceOffset( texSize, uv, - 1.0, 1.0 );

            const highest = max( s, e, n, w, m );
            const lowest = min( s, e, n, w, m );
            const contrast = highest.sub( lowest );

            return { m, n, e, s, w, ne, nw, se, sw, highest, lowest, contrast };

        };

        const DeterminePixelBlendFactor = ( l ) => {

            let f = float( 2.0 ).mul( l.s.add( l.e ).add( l.n ).add( l.w ) );
            f = f.add( l.se.add( l.sw ).add( l.ne ).add( l.nw ) );
            f = f.mul( 1.0 / 12.0 );
            f = abs( f.sub( l.m ) );
            f = clamp( f.div( max( l.contrast, 0 ) ), 0.0, 1.0 );

            const blendFactor = smoothstep( 0.0, 1.0, f );
            return blendFactor.mul( blendFactor ).mul( _SubpixelBlending );

        };

        const DetermineEdge = ( texSize, l ) => {

            const horizontal =
                abs( l.s.add( l.n ).sub( l.m.mul( 2.0 ) ) ).mul( 2.0 ).add(
                    abs( l.se.add( l.ne ).sub( l.e.mul( 2.0 ) ) ).add(
                        abs( l.sw.add( l.nw ).sub( l.w.mul( 2.0 ) ) )
                    )
                );

            const vertical =
                abs( l.e.add( l.w ).sub( l.m.mul( 2.0 ) ) ).mul( 2.0 ).add(
                    abs( l.se.add( l.sw ).sub( l.s.mul( 2.0 ) ) ).add(
                        abs( l.ne.add( l.nw ).sub( l.n.mul( 2.0 ) ) )
                    )
                );

            const isHorizontal = horizontal.greaterThanEqual( vertical );

            const pLuminance = select( isHorizontal, l.s, l.e );
            const nLuminance = select( isHorizontal, l.n, l.w );
            const pGradient = abs( pLuminance.sub( l.m ) );
            const nGradient = abs( nLuminance.sub( l.m ) );

            const pixelStep = select( isHorizontal, texSize.y, texSize.x ).toVar();
            const oppositeLuminance = float().toVar();
            const gradient = float().toVar();

            If( pGradient.lessThan( nGradient ), () => {

                pixelStep.assign( pixelStep.negate() );
                oppositeLuminance.assign( nLuminance );
                gradient.assign( nGradient );

            } ).Else( () => {

                oppositeLuminance.assign( pLuminance );
                gradient.assign( pGradient );

            } );

            return { isHorizontal, pixelStep, oppositeLuminance, gradient };

        };

        const DetermineEdgeBlendFactor = ( texSize, l, e, uv ) => {

            const uvEdge = uv.toVar();
            const edgeStep = vec2().toVar();
            If( e.isHorizontal, () => {

                uvEdge.y.addAssign( e.pixelStep.mul( 0.5 ) );
                edgeStep.assign( vec2( texSize.x, 0.0 ) );

            } ).Else( () => {

                uvEdge.x.addAssign( e.pixelStep.mul( 0.5 ) );
                edgeStep.assign( vec2( 0.0, texSize.y ) );

            } );

            const edgeLuminance = l.m.add( e.oppositeLuminance ).mul( 0.5 );
            const gradientThreshold = e.gradient.mul( 0.25 );

            const puv = uvEdge.add( edgeStep.mul( EDGE_STEPS.element( 0 ) ) ).toVar();
            const pLuminanceDelta = SampleLuminance( puv ).sub( edgeLuminance ).toVar();
            const pAtEnd = abs( pLuminanceDelta ).greaterThanEqual( gradientThreshold ).toVar();

            Loop( { start: 1, end: EDGE_STEP_COUNT }, ( { i } ) => {

                If( pAtEnd, () => {

                    Break();

                } );

                puv.addAssign( edgeStep.mul( EDGE_STEPS.element( i ) ) );
                pLuminanceDelta.assign( SampleLuminance( puv ).sub( edgeLuminance ) );
                pAtEnd.assign( abs( pLuminanceDelta ).greaterThanEqual( gradientThreshold ) );

            } );

            If( pAtEnd.not(), () => {

                puv.addAssign( edgeStep.mul( EDGE_GUESS ) );

            } );

            const nuv = uvEdge.sub( edgeStep.mul( EDGE_STEPS.element( 0 ) ) ).toVar();
            const nLuminanceDelta = SampleLuminance( nuv ).sub( edgeLuminance ).toVar();
            const nAtEnd = abs( nLuminanceDelta ).greaterThanEqual( gradientThreshold ).toVar();

            Loop( { start: 1, end: EDGE_STEP_COUNT }, ( { i } ) => {

                If( nAtEnd, () => {

                    Break();

                } );

                nuv.subAssign( edgeStep.mul( EDGE_STEPS.element( i ) ) );
                nLuminanceDelta.assign( SampleLuminance( nuv ).sub( edgeLuminance ) );
                nAtEnd.assign( abs( nLuminanceDelta ).greaterThanEqual( gradientThreshold ) );

            } );

            If( nAtEnd.not(), () => {

                nuv.subAssign( edgeStep.mul( EDGE_GUESS ) );

            } );

            const pDistance = float().toVar();
            const nDistance = float().toVar();

            If( e.isHorizontal, () => {

                pDistance.assign( puv.x.sub( uv.x ) );
                nDistance.assign( uv.x.sub( nuv.x ) );

            } ).Else( () => {

                pDistance.assign( puv.y.sub( uv.y ) );
                nDistance.assign( uv.y.sub( nuv.y ) );

            } );

            const shortestDistance = float().toVar();
            const deltaSign = bool().toVar();

            If( pDistance.lessThanEqual( nDistance ), () => {

                shortestDistance.assign( pDistance );
                deltaSign.assign( pLuminanceDelta.greaterThanEqual( 0.0 ) );

            } ).Else( () => {

                shortestDistance.assign( nDistance );
                deltaSign.assign( nLuminanceDelta.greaterThanEqual( 0.0 ) );

            } );

            const blendFactor = float().toVar();

            If( deltaSign.equal( l.m.sub( edgeLuminance ).greaterThanEqual( 0.0 ) ), () => {

                blendFactor.assign( 0.0 );

            } ).Else( () => {

                blendFactor.assign( float( 0.5 ).sub( shortestDistance.div( pDistance.add( nDistance ) ) ) );

            } );

            return blendFactor;

        };

        const ApplyFXAA = Fn( ( [ uv, texSize ] ) => {

            const luminance = SampleLuminanceNeighborhood( texSize, uv );
            If( ShouldSkipPixel( luminance ), () => {

                return Sample( uv );

            } );

            const pixelBlend = DeterminePixelBlendFactor( luminance );
            const edge = DetermineEdge( texSize, luminance );
            const edgeBlend = DetermineEdgeBlendFactor( texSize, luminance, edge, uv );

            const finalBlend = max( pixelBlend, edgeBlend );
            const finalUv = uv.toVar();

            If( edge.isHorizontal, () => {

                finalUv.y.addAssign( edge.pixelStep.mul( finalBlend ) );

            } ).Else( () => {

                finalUv.x.addAssign( edge.pixelStep.mul( finalBlend ) );

            } );

            return Sample( finalUv );

        } ).setLayout( {
            name: 'FxaaPixelShader',
            type: 'vec4',
            inputs: [
                { name: 'uv', type: 'vec2' },
                { name: 'texSize', type: 'vec2' },
            ]
        } );

        const fxaa = Fn( () => {

            return ApplyFXAA( uvNode, this._invSize );

        } );

        const outputNode = fxaa();

        return outputNode;

    }