From 84569eb27a3e070e6a4844e59e906ea0ee26c4cb Mon Sep 17 00:00:00 2001 From: "Mr.doob" Date: Thu, 2 Jul 2026 11:10:38 +0900 Subject: [PATCH 1/2] Examples: Skip TRAA for SSAO in webgpu_postprocessing_ao. SSAO is self-denoised so the SSAO path now uses 4x MSAA on the scene pass instead of TRAA. The resolution scale control is now shared by both AO types with per-type defaults (GTAO 0.5, SSAO 1). Co-Authored-By: Claude Fable 5 --- examples/webgpu_postprocessing_ao.html | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/examples/webgpu_postprocessing_ao.html b/examples/webgpu_postprocessing_ao.html index 9db3bb492cf48c..876199d54422ff 100644 --- a/examples/webgpu_postprocessing_ao.html +++ b/examples/webgpu_postprocessing_ao.html @@ -60,6 +60,7 @@ aoType: 'GTAO', samples: 16, radius: 0.5, + resolutionScale: 0.5, scale: 0.8, // GTAO thickness: 1, // GTAO temporalFiltering: true, // GTAO @@ -142,7 +143,7 @@ const scenePass = pass( scene, camera ).toInspector( 'Color' ); - // final output + traa + // traa ( resolves the temporal noise of GTAO ) traaPass = traa( scenePass, prePassDepth, prePassVelocity, camera ); traaPass.useSubpixelCorrection = false; @@ -153,7 +154,13 @@ function updateOutput() { - renderPipeline.outputNode = params.aoOnly ? vec4( vec3( aoPass.getTextureNode().sample( screenUV ).r ), 1 ) : traaPass; + // SSAO is self-denoised so it can skip TRAA and use MSAA for edge anti-aliasing instead + + const useTRAA = ( params.aoType === 'GTAO' ); + + scenePass.options.samples = useTRAA ? 0 : 4; + + renderPipeline.outputNode = params.aoOnly ? vec4( vec3( aoPass.getTextureNode().sample( screenUV ).r ), 1 ) : ( useTRAA ? traaPass : scenePass ); renderer.toneMapping = params.aoOnly ? THREE.NoToneMapping : THREE.NeutralToneMapping; renderPipeline.needsUpdate = true; @@ -167,8 +174,6 @@ ? ssao( prePassDepth, prePassNormal, camera ).toInspector( 'SSAO', ( inspectNode ) => inspectNode.r ) : ao( prePassDepth, prePassNormal, camera ).toInspector( 'GTAO', ( inspectNode ) => inspectNode.r ); - aoPass.resolutionScale = 0.5; // running AO in half resolution is often sufficient - updateParameters(); scenePass.contextNode = builtinAOContext( aoPass.getTextureNode().sample( screenUV ).r ); @@ -524,17 +529,21 @@ const gui = renderer.inspector.createParameters( 'Settings' ); gui.add( params, 'aoType', [ 'GTAO', 'SSAO' ] ).name( 'AO type' ).onChange( () => { + // half resolution suffices for GTAO since TRAA smooths it, SSAO looks best at full resolution + + resolutionScaleControl.setValue( params.aoType === 'SSAO' ? 1 : 0.5 ); + createAO(); updateControls(); } ); + const resolutionScaleControl = gui.add( params, 'resolutionScale', 0, 1 ).name( 'resolution' ).onChange( updateParameters ); gui.add( params, 'samples', 4, 32, 1 ).onChange( updateParameters ); gui.add( params, 'radius', 0.1, 1 ).onChange( updateParameters ); const gtaoControls = [ gui.add( params, 'scale', 0.01, 1 ).onChange( updateParameters ), gui.add( params, 'thickness', 0.01, 2 ).onChange( updateParameters ), - gui.add( aoPass, 'resolutionScale', 0, 1 ).name( 'resolution scale' ), gui.add( params, 'temporalFiltering' ).name( 'temporal filtering' ).onChange( updateParameters ) ]; @@ -546,7 +555,7 @@ ]; gui.add( transparentMesh, 'visible' ).name( 'show transparent mesh' ); - gui.add( params, 'transparentOpacity', 0, 1, 0.01 ).name( 'transparent opacity' ).onChange( ( value ) => { + gui.add( params, 'transparentOpacity', 0, 1, 0.01 ).name( 'mesh opacity' ).onChange( ( value ) => { transparentMesh.material.opacity = value; @@ -579,6 +588,7 @@ aoPass.samples.value = params.samples; aoPass.radius.value = params.radius; + aoPass.resolutionScale = params.resolutionScale; if ( params.aoType === 'SSAO' ) { From d5ee8bfa466d24fc95ac3f1a85a119e7aee81523 Mon Sep 17 00:00:00 2001 From: sunag Date: Thu, 2 Jul 2026 00:16:57 -0300 Subject: [PATCH 2/2] TSL: Support primitive values in `array()` (#33929) --- examples/jsm/tsl/display/BloomNode.js | 17 +++++------------ src/nodes/core/ArrayNode.js | 4 ++-- src/renderers/common/QuadMesh.js | 6 +++--- 3 files changed, 10 insertions(+), 17 deletions(-) diff --git a/examples/jsm/tsl/display/BloomNode.js b/examples/jsm/tsl/display/BloomNode.js index 8cb4b3b16a392a..f96b43f02c9628 100644 --- a/examples/jsm/tsl/display/BloomNode.js +++ b/examples/jsm/tsl/display/BloomNode.js @@ -429,7 +429,7 @@ class BloomNode extends TempNode { // composite material - const bloomFactors = array( [ float( 1.0 ), float( 0.8 ), float( 0.6 ), float( 0.4 ), float( 0.2 ) ] ); + const bloomFactors = array( [ 1.0, 0.8, 0.6, 0.4, 0.2 ] ); const bloomTintColors = uniformArray( this.bloomTintColors ); const compositePass = Fn( () => { @@ -527,8 +527,8 @@ class BloomNode extends TempNode { // const colorTexture = texture( null ); - const gaussianOffsets = array( offsets.map( ( value ) => float( value ) ) ); - const gaussianWeights = array( weights.map( ( value ) => float( value ) ) ); + const gaussianOffsets = array( offsets ); + const gaussianWeights = array( weights ); const invSize = uniform( new Vector2() ); const direction = uniform( new Vector2( 0.5, 0.5 ) ); @@ -569,19 +569,12 @@ class BloomNode extends TempNode { } -const lerpBloomFactor = Fn( ( [ factor, radius ] ) => { +const lerpBloomFactor = Fn( ( { factor, radius } ) => { const mirrorFactor = float( 1.2 ).sub( factor ); return mix( factor, mirrorFactor, radius ); -} ).setLayout( { - name: 'lerpBloomFactor', - type: 'float', - inputs: [ - { name: 'factor', type: 'float' }, - { name: 'radius', type: 'float' }, - ] -} ); +}, { factor: 'float', radius: 'float', return: 'float' } ); /** * TSL function for creating a bloom effect. diff --git a/src/nodes/core/ArrayNode.js b/src/nodes/core/ArrayNode.js index 9011caf708be79..7a907c553d3da1 100644 --- a/src/nodes/core/ArrayNode.js +++ b/src/nodes/core/ArrayNode.js @@ -154,7 +154,7 @@ export const array = ( ...params ) => { if ( params.length === 1 ) { - const values = params[ 0 ]; + const values = params[ 0 ].map( ( value ) => nodeObject( value ) ); node = new ArrayNode( null, values.length, values ); @@ -167,7 +167,7 @@ export const array = ( ...params ) => { } - return nodeObject( node ); + return node; }; diff --git a/src/renderers/common/QuadMesh.js b/src/renderers/common/QuadMesh.js index 5d7ffce8e819d4..e791fd15d2ec46 100644 --- a/src/renderers/common/QuadMesh.js +++ b/src/renderers/common/QuadMesh.js @@ -2,7 +2,7 @@ import { BufferGeometry } from '../../core/BufferGeometry.js'; import { Float32BufferAttribute } from '../../core/BufferAttribute.js'; import { Mesh } from '../../objects/Mesh.js'; import { OrthographicCamera } from '../../cameras/OrthographicCamera.js'; -import { vec4, array, float } from '../../nodes/tsl/TSLBase.js'; +import { vec4, array } from '../../nodes/tsl/TSLBase.js'; import { vertexIndex } from '../../nodes/core/IndexNode.js'; import { warnOnce } from '../../utils.js'; @@ -39,8 +39,8 @@ class QuadGeometry extends BufferGeometry { const _geometry = /*@__PURE__*/ new QuadGeometry(); const _vertexNode = /*@__PURE__*/ vec4( - array( [ float( - 1.0 ), float( - 1.0 ), float( 3.0 ) ] ).element( vertexIndex ), - array( [ float( 3.0 ), float( - 1.0 ), float( - 1.0 ) ] ).element( vertexIndex ), + array( [ - 1.0, - 1.0, 3.0 ] ).element( vertexIndex ), + array( [ 3.0, - 1.0, - 1.0 ] ).element( vertexIndex ), 0.0, 1.0 );