From e7e1f065cd2521a76083594f55483ccade7ebb18 Mon Sep 17 00:00:00 2001 From: Renaud Rohlinger Date: Sun, 10 Aug 2025 19:58:28 +0900 Subject: [PATCH 1/5] TSL: Improve TSL camera array support and introduce cameraViewport --- src/nodes/accessors/Camera.js | 138 +++++++++++++++++++++++++++++++++- 1 file changed, 135 insertions(+), 3 deletions(-) diff --git a/src/nodes/accessors/Camera.js b/src/nodes/accessors/Camera.js index a2845468de2692..ad9301c5130f96 100644 --- a/src/nodes/accessors/Camera.js +++ b/src/nodes/accessors/Camera.js @@ -4,6 +4,7 @@ import { Vector3 } from '../../math/Vector3.js'; import { Fn } from '../tsl/TSLBase.js'; import { uniformArray } from './UniformArrayNode.js'; import { builtin } from './BuiltinNode.js'; +import { Vector4 } from '../../math/Vector4.js'; /** * TSL object that represents the current `index` value of the camera if used ArrayCamera. @@ -137,7 +138,33 @@ export const cameraViewMatrix = /*@__PURE__*/ ( Fn( ( { camera } ) => { * @tsl * @type {UniformNode} */ -export const cameraWorldMatrix = /*@__PURE__*/ uniform( 'mat4' ).setName( 'cameraWorldMatrix' ).setGroup( renderGroup ).onRenderUpdate( ( { camera } ) => camera.matrixWorld ); +export const cameraWorldMatrix = /*@__PURE__*/ ( Fn( ( { camera } ) => { + + let cameraWorldMatrix; + + if ( camera.isArrayCamera && camera.cameras.length > 0 ) { + + const matrices = []; + + for ( const subCamera of camera.cameras ) { + + matrices.push( subCamera.matrixWorld ); + + } + + const cameraWorldMatrices = uniformArray( matrices ).setGroup( renderGroup ).setName( 'cameraWorldMatrices' ); + + cameraWorldMatrix = cameraWorldMatrices.element( camera.isMultiViewCamera ? builtin( 'gl_ViewID_OVR' ) : cameraIndex ).toVar( 'cameraWorldMatrix' ); + + } else { + + cameraWorldMatrix = uniform( 'mat4' ).setName( 'cameraWorldMatrix' ).setGroup( renderGroup ).onRenderUpdate( ( { camera } ) => camera.matrixWorld ); + + } + + return cameraWorldMatrix; + +} ).once() )(); /** * TSL object that represents the normal matrix of the camera used for the current render. @@ -145,7 +172,33 @@ export const cameraWorldMatrix = /*@__PURE__*/ uniform( 'mat4' ).setName( 'camer * @tsl * @type {UniformNode} */ -export const cameraNormalMatrix = /*@__PURE__*/ uniform( 'mat3' ).setName( 'cameraNormalMatrix' ).setGroup( renderGroup ).onRenderUpdate( ( { camera } ) => camera.normalMatrix ); +export const cameraNormalMatrix = /*@__PURE__*/ ( Fn( ( { camera } ) => { + + let cameraNormalMatrix; + + if ( camera.isArrayCamera && camera.cameras.length > 0 ) { + + const matrices = []; + + for ( const subCamera of camera.cameras ) { + + matrices.push( subCamera.normalMatrix ); + + } + + const cameraNormalMatrices = uniformArray( matrices ).setGroup( renderGroup ).setName( 'cameraNormalMatrices' ); + + cameraNormalMatrix = cameraNormalMatrices.element( camera.isMultiViewCamera ? builtin( 'gl_ViewID_OVR' ) : cameraIndex ).toVar( 'cameraNormalMatrix' ); + + } else { + + cameraNormalMatrix = uniform( 'mat3' ).setName( 'cameraNormalMatrix' ).setGroup( renderGroup ).onRenderUpdate( ( { camera } ) => camera.normalMatrix ); + + } + + return cameraNormalMatrix; + +} ).once() )(); /** * TSL object that represents the position in world space of the camera used for the current render. @@ -153,4 +206,83 @@ export const cameraNormalMatrix = /*@__PURE__*/ uniform( 'mat3' ).setName( 'came * @tsl * @type {UniformNode} */ -export const cameraPosition = /*@__PURE__*/ uniform( new Vector3() ).setName( 'cameraPosition' ).setGroup( renderGroup ).onRenderUpdate( ( { camera }, self ) => self.value.setFromMatrixPosition( camera.matrixWorld ) ); +export const cameraPosition = /*@__PURE__*/ ( Fn( ( { camera } ) => { + + let cameraPosition; + + if ( camera.isArrayCamera && camera.cameras.length > 0 ) { + + const positions = []; + + for ( let i = 0, l = camera.cameras.length; i < l; i ++ ) { + + positions.push( new Vector3() ); + + } + + const cameraPositions = uniformArray( positions ).setGroup( renderGroup ).setName( 'cameraPositions' ).onRenderUpdate( ( { camera }, self ) => { + + const subCameras = camera.cameras; + const array = self.array; + + for ( let i = 0, l = subCameras.length; i < l; i ++ ) { + + array[ i ].setFromMatrixPosition( subCameras[ i ].matrixWorld ); + + } + + } ); + + cameraPosition = cameraPositions.element( camera.isMultiViewCamera ? builtin( 'gl_ViewID_OVR' ) : cameraIndex ).toVar( 'cameraPosition' ); + + } else { + + cameraPosition = uniform( new Vector3() ).setName( 'cameraPosition' ).setGroup( renderGroup ).onRenderUpdate( ( { camera }, self ) => self.value.setFromMatrixPosition( camera.matrixWorld ) ); + + } + + return cameraPosition; + +} ).once() )(); + + +/** + * TSL object that represents the viewport of the camera used for the current render. + * + * @tsl + * @type {UniformNode} + */ +export const cameraViewport = /*@__PURE__*/ Fn( ( { camera } ) => { + + let cameraViewport; + + if ( camera.isArrayCamera && camera.cameras.length > 0 ) { + + const viewports = []; + + for ( const subCamera of camera.cameras ) { + + viewports.push( subCamera.viewport ); + + } + + const cameraViewports = uniformArray( viewports, 'vec4' ) + .setGroup( renderGroup ) + .setName( 'cameraViewports' ); + + cameraViewport = cameraViewports + .element( cameraIndex ) + .toVar( 'cameraViewport' ); + + } else { + + // Fallback for single camera + cameraViewport = uniform( new Vector4( 0, 0, 1, 1 ) ).setName( + 'cameraViewport' + ); + + } + + return cameraViewport; + +} ).once()(); From 1bfedb7fa97a4fe79905fb2f32fe692627252c58 Mon Sep 17 00:00:00 2001 From: Renaud Rohlinger Date: Sun, 10 Aug 2025 20:00:39 +0900 Subject: [PATCH 2/5] Expose to userland --- src/Three.TSL.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Three.TSL.js b/src/Three.TSL.js index 83f5113d915cb2..84713cd35adf25 100644 --- a/src/Three.TSL.js +++ b/src/Three.TSL.js @@ -97,6 +97,7 @@ export const buffer = TSL.buffer; export const bufferAttribute = TSL.bufferAttribute; export const bumpMap = TSL.bumpMap; export const burn = TSL.burn; +export const builtin = TSL.builtin; export const bvec2 = TSL.bvec2; export const bvec3 = TSL.bvec3; export const bvec4 = TSL.bvec4; @@ -111,6 +112,7 @@ export const cameraPosition = TSL.cameraPosition; export const cameraProjectionMatrix = TSL.cameraProjectionMatrix; export const cameraProjectionMatrixInverse = TSL.cameraProjectionMatrixInverse; export const cameraViewMatrix = TSL.cameraViewMatrix; +export const cameraViewport = TSL.cameraViewport; export const cameraWorldMatrix = TSL.cameraWorldMatrix; export const cbrt = TSL.cbrt; export const cdl = TSL.cdl; From 0d7e352e55244b20326a24ad8c358a4ac5b8dc50 Mon Sep 17 00:00:00 2001 From: Renaud Rohlinger Date: Sun, 10 Aug 2025 20:29:37 +0900 Subject: [PATCH 3/5] better viewport fallback working OOTB --- src/nodes/accessors/Camera.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/nodes/accessors/Camera.js b/src/nodes/accessors/Camera.js index ad9301c5130f96..aa1f70e7e8cf55 100644 --- a/src/nodes/accessors/Camera.js +++ b/src/nodes/accessors/Camera.js @@ -1,10 +1,10 @@ import { uniform } from '../core/UniformNode.js'; import { renderGroup, sharedUniformGroup } from '../core/UniformGroupNode.js'; import { Vector3 } from '../../math/Vector3.js'; -import { Fn } from '../tsl/TSLBase.js'; +import { Fn, vec4 } from '../tsl/TSLBase.js'; import { uniformArray } from './UniformArrayNode.js'; import { builtin } from './BuiltinNode.js'; -import { Vector4 } from '../../math/Vector4.js'; +import { screenSize } from '../display/ScreenNode.js'; /** * TSL object that represents the current `index` value of the camera if used ArrayCamera. @@ -277,7 +277,7 @@ export const cameraViewport = /*@__PURE__*/ Fn( ( { camera } ) => { } else { // Fallback for single camera - cameraViewport = uniform( new Vector4( 0, 0, 1, 1 ) ).setName( + cameraViewport = vec4( 0, 0, screenSize.x, screenSize.y ).setName( 'cameraViewport' ); From a3a2f0fe6c04823a94aaf6b8f7f0a0c1de47bdca Mon Sep 17 00:00:00 2001 From: Renaud Rohlinger Date: Sun, 10 Aug 2025 20:34:33 +0900 Subject: [PATCH 4/5] cleanup --- src/nodes/accessors/Camera.js | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/nodes/accessors/Camera.js b/src/nodes/accessors/Camera.js index aa1f70e7e8cf55..bcdec6b7300f7f 100644 --- a/src/nodes/accessors/Camera.js +++ b/src/nodes/accessors/Camera.js @@ -52,7 +52,7 @@ export const cameraProjectionMatrix = /*@__PURE__*/ ( Fn( ( { camera } ) => { const cameraProjectionMatrices = uniformArray( matrices ).setGroup( renderGroup ).setName( 'cameraProjectionMatrices' ); - cameraProjectionMatrix = cameraProjectionMatrices.element( camera.isMultiViewCamera ? builtin( 'gl_ViewID_OVR' ) : cameraIndex ).toVar( 'cameraProjectionMatrix' ); + cameraProjectionMatrix = cameraProjectionMatrices.element( camera.isMultiViewCamera ? builtin( 'gl_ViewID_OVR' ) : cameraIndex ).toConst( 'cameraProjectionMatrix' ); } else { @@ -86,7 +86,7 @@ export const cameraProjectionMatrixInverse = /*@__PURE__*/ ( Fn( ( { camera } ) const cameraProjectionMatricesInverse = uniformArray( matrices ).setGroup( renderGroup ).setName( 'cameraProjectionMatricesInverse' ); - cameraProjectionMatrixInverse = cameraProjectionMatricesInverse.element( camera.isMultiViewCamera ? builtin( 'gl_ViewID_OVR' ) : cameraIndex ).toVar( 'cameraProjectionMatrixInverse' ); + cameraProjectionMatrixInverse = cameraProjectionMatricesInverse.element( camera.isMultiViewCamera ? builtin( 'gl_ViewID_OVR' ) : cameraIndex ).toConst( 'cameraProjectionMatrixInverse' ); } else { @@ -120,7 +120,7 @@ export const cameraViewMatrix = /*@__PURE__*/ ( Fn( ( { camera } ) => { const cameraViewMatrices = uniformArray( matrices ).setGroup( renderGroup ).setName( 'cameraViewMatrices' ); - cameraViewMatrix = cameraViewMatrices.element( camera.isMultiViewCamera ? builtin( 'gl_ViewID_OVR' ) : cameraIndex ).toVar( 'cameraViewMatrix' ); + cameraViewMatrix = cameraViewMatrices.element( camera.isMultiViewCamera ? builtin( 'gl_ViewID_OVR' ) : cameraIndex ).toConst( 'cameraViewMatrix' ); } else { @@ -154,7 +154,7 @@ export const cameraWorldMatrix = /*@__PURE__*/ ( Fn( ( { camera } ) => { const cameraWorldMatrices = uniformArray( matrices ).setGroup( renderGroup ).setName( 'cameraWorldMatrices' ); - cameraWorldMatrix = cameraWorldMatrices.element( camera.isMultiViewCamera ? builtin( 'gl_ViewID_OVR' ) : cameraIndex ).toVar( 'cameraWorldMatrix' ); + cameraWorldMatrix = cameraWorldMatrices.element( camera.isMultiViewCamera ? builtin( 'gl_ViewID_OVR' ) : cameraIndex ).toConst( 'cameraWorldMatrix' ); } else { @@ -188,7 +188,7 @@ export const cameraNormalMatrix = /*@__PURE__*/ ( Fn( ( { camera } ) => { const cameraNormalMatrices = uniformArray( matrices ).setGroup( renderGroup ).setName( 'cameraNormalMatrices' ); - cameraNormalMatrix = cameraNormalMatrices.element( camera.isMultiViewCamera ? builtin( 'gl_ViewID_OVR' ) : cameraIndex ).toVar( 'cameraNormalMatrix' ); + cameraNormalMatrix = cameraNormalMatrices.element( camera.isMultiViewCamera ? builtin( 'gl_ViewID_OVR' ) : cameraIndex ).toConst( 'cameraNormalMatrix' ); } else { @@ -233,7 +233,7 @@ export const cameraPosition = /*@__PURE__*/ ( Fn( ( { camera } ) => { } ); - cameraPosition = cameraPositions.element( camera.isMultiViewCamera ? builtin( 'gl_ViewID_OVR' ) : cameraIndex ).toVar( 'cameraPosition' ); + cameraPosition = cameraPositions.element( camera.isMultiViewCamera ? builtin( 'gl_ViewID_OVR' ) : cameraIndex ).toConst( 'cameraPosition' ); } else { @@ -272,14 +272,12 @@ export const cameraViewport = /*@__PURE__*/ Fn( ( { camera } ) => { cameraViewport = cameraViewports .element( cameraIndex ) - .toVar( 'cameraViewport' ); + .toConst( 'cameraViewport' ); } else { // Fallback for single camera - cameraViewport = vec4( 0, 0, screenSize.x, screenSize.y ).setName( - 'cameraViewport' - ); + cameraViewport = vec4( 0, 0, screenSize.x, screenSize.y ).toConst( 'cameraViewport' ); } From 59969278addc513292d7f731672a90a2c573d8c8 Mon Sep 17 00:00:00 2001 From: Renaud Rohlinger Date: Mon, 11 Aug 2025 15:17:58 +0900 Subject: [PATCH 5/5] cleanup --- src/nodes/accessors/Camera.js | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/src/nodes/accessors/Camera.js b/src/nodes/accessors/Camera.js index bcdec6b7300f7f..c42d3b6f15ab79 100644 --- a/src/nodes/accessors/Camera.js +++ b/src/nodes/accessors/Camera.js @@ -252,35 +252,31 @@ export const cameraPosition = /*@__PURE__*/ ( Fn( ( { camera } ) => { * @tsl * @type {UniformNode} */ -export const cameraViewport = /*@__PURE__*/ Fn( ( { camera } ) => { +export const cameraViewport = /*@__PURE__*/ ( Fn( ( { camera } ) => { let cameraViewport; if ( camera.isArrayCamera && camera.cameras.length > 0 ) { - const viewports = []; + const viewports = []; - for ( const subCamera of camera.cameras ) { + for ( const subCamera of camera.cameras ) { viewports.push( subCamera.viewport ); } - const cameraViewports = uniformArray( viewports, 'vec4' ) - .setGroup( renderGroup ) - .setName( 'cameraViewports' ); + const cameraViewports = uniformArray( viewports, 'vec4' ).setGroup( renderGroup ).setName( 'cameraViewports' ); - cameraViewport = cameraViewports - .element( cameraIndex ) - .toConst( 'cameraViewport' ); + cameraViewport = cameraViewports.element( cameraIndex ).toConst( 'cameraViewport' ); } else { - // Fallback for single camera - cameraViewport = vec4( 0, 0, screenSize.x, screenSize.y ).toConst( 'cameraViewport' ); + // Fallback for single camera + cameraViewport = vec4( 0, 0, screenSize.x, screenSize.y ).toConst( 'cameraViewport' ); } return cameraViewport; -} ).once()(); +} ).once() )();