UNPKG

three

Version:

JavaScript 3D library

411 lines (241 loc) • 10.9 kB
import { uniform } from '../core/UniformNode.js'; import { renderGroup, sharedUniformGroup } from '../core/UniformGroupNode.js'; import { Vector3 } from '../../math/Vector3.js'; import { Fn, vec4 } from '../tsl/TSLBase.js'; import { uniformArray } from './UniformArrayNode.js'; import { builtin } from './BuiltinNode.js'; import { screenSize } from '../display/ScreenNode.js'; // Cache single-camera uniforms globally. Array uniforms are cached per renderer and // camera count so cameras with different array sizes cannot overwrite each other's // buffers, while builders with matching counts still share them. const _cameraArrays = /*@__PURE__*/ new WeakMap(); function getCameraArray( renderer, camera, name, create ) { let arrays = _cameraArrays.get( renderer ); if ( arrays === undefined ) { arrays = new Map(); _cameraArrays.set( renderer, arrays ); } const key = name + ':' + camera.cameras.length; let array = arrays.get( key ); if ( array === undefined ) { array = create( camera ); arrays.set( key, array ); } return array; } let _cameraProjectionMatrixBase = null; let _cameraProjectionMatrixInverseBase = null; let _cameraViewMatrixBase = null; let _cameraWorldMatrixBase = null; let _cameraNormalMatrixBase = null; let _cameraPositionBase = null; let _cameraViewportBase = null; /** * TSL object that represents the current `index` value of the camera if used ArrayCamera. * * @tsl * @type {UniformNode<uint>} */ export const cameraIndex = /*@__PURE__*/ uniform( 0, 'uint' ).setName( 'u_cameraIndex' ).setGroup( sharedUniformGroup( 'cameraIndex' ) ).toVarying( 'v_cameraIndex' ); /** * TSL object that represents the `near` value of the camera used for the current render. * * @tsl * @type {UniformNode<float>} */ export const cameraNear = /*@__PURE__*/ uniform( 'float' ).setName( 'cameraNear' ).setGroup( renderGroup ).onRenderUpdate( ( { camera } ) => camera.near ); /** * TSL object that represents the `far` value of the camera used for the current render. * * @tsl * @type {UniformNode<float>} */ export const cameraFar = /*@__PURE__*/ uniform( 'float' ).setName( 'cameraFar' ).setGroup( renderGroup ).onRenderUpdate( ( { camera } ) => camera.far ); /** * TSL object that represents the projection matrix of the camera used for the current render. * * @tsl * @type {UniformNode<mat4>} */ export const cameraProjectionMatrix = /*@__PURE__*/ ( Fn( ( { camera, renderer } ) => { let cameraProjectionMatrix; if ( camera.isArrayCamera && camera.cameras.length > 0 ) { const cameraArray = getCameraArray( renderer, camera, 'cameraProjectionMatrices', ( camera ) => { const values = camera.cameras.map( ( subCamera ) => subCamera.projectionMatrix ); return uniformArray( values ).setGroup( renderGroup ).setName( 'cameraProjectionMatrices' ).onRenderUpdate( ( { camera }, self ) => { for ( let i = 0; i < camera.cameras.length; i ++ ) { self.array[ i ] = camera.cameras[ i ].projectionMatrix; } } ); } ); cameraProjectionMatrix = cameraArray.element( camera.isMultiViewCamera ? builtin( 'gl_ViewID_OVR' ) : cameraIndex ); } else { if ( _cameraProjectionMatrixBase === null ) { _cameraProjectionMatrixBase = uniform( camera.projectionMatrix ).setName( 'cameraProjectionMatrix' ).setGroup( renderGroup ).onRenderUpdate( ( { camera } ) => camera.projectionMatrix ); } cameraProjectionMatrix = _cameraProjectionMatrixBase; } return cameraProjectionMatrix; } ).once() )(); /** * TSL object that represents the inverse projection matrix of the camera used for the current render. * * @tsl * @type {UniformNode<mat4>} */ export const cameraProjectionMatrixInverse = /*@__PURE__*/ ( Fn( ( { camera, renderer } ) => { let cameraProjectionMatrixInverse; if ( camera.isArrayCamera && camera.cameras.length > 0 ) { const cameraArray = getCameraArray( renderer, camera, 'cameraProjectionMatricesInverse', ( camera ) => { const values = camera.cameras.map( ( subCamera ) => subCamera.projectionMatrixInverse ); return uniformArray( values ).setGroup( renderGroup ).setName( 'cameraProjectionMatricesInverse' ).onRenderUpdate( ( { camera }, self ) => { for ( let i = 0; i < camera.cameras.length; i ++ ) { self.array[ i ] = camera.cameras[ i ].projectionMatrixInverse; } } ); } ); cameraProjectionMatrixInverse = cameraArray.element( camera.isMultiViewCamera ? builtin( 'gl_ViewID_OVR' ) : cameraIndex ); } else { if ( _cameraProjectionMatrixInverseBase === null ) { _cameraProjectionMatrixInverseBase = uniform( camera.projectionMatrixInverse ).setName( 'cameraProjectionMatrixInverse' ).setGroup( renderGroup ).onRenderUpdate( ( { camera } ) => camera.projectionMatrixInverse ); } cameraProjectionMatrixInverse = _cameraProjectionMatrixInverseBase; } return cameraProjectionMatrixInverse; } ).once() )(); /** * TSL object that represents the view matrix of the camera used for the current render. * * @tsl * @type {UniformNode<mat4>} */ export const cameraViewMatrix = /*@__PURE__*/ ( Fn( ( { camera, renderer } ) => { let cameraViewMatrix; if ( camera.isArrayCamera && camera.cameras.length > 0 ) { const cameraArray = getCameraArray( renderer, camera, 'cameraViewMatrices', ( camera ) => { const values = camera.cameras.map( ( subCamera ) => subCamera.matrixWorldInverse ); return uniformArray( values ).setGroup( renderGroup ).setName( 'cameraViewMatrices' ).onRenderUpdate( ( { camera }, self ) => { for ( let i = 0; i < camera.cameras.length; i ++ ) { self.array[ i ] = camera.cameras[ i ].matrixWorldInverse; } } ); } ); cameraViewMatrix = cameraArray.element( camera.isMultiViewCamera ? builtin( 'gl_ViewID_OVR' ) : cameraIndex ); } else { if ( _cameraViewMatrixBase === null ) { _cameraViewMatrixBase = uniform( camera.matrixWorldInverse ).setName( 'cameraViewMatrix' ).setGroup( renderGroup ).onRenderUpdate( ( { camera } ) => camera.matrixWorldInverse ); } cameraViewMatrix = _cameraViewMatrixBase; } return cameraViewMatrix; } ).once() )(); /** * TSL object that represents the world matrix of the camera used for the current render. * * @tsl * @type {UniformNode<mat4>} */ export const cameraWorldMatrix = /*@__PURE__*/ ( Fn( ( { camera, renderer } ) => { let cameraWorldMatrix; if ( camera.isArrayCamera && camera.cameras.length > 0 ) { const cameraArray = getCameraArray( renderer, camera, 'cameraWorldMatrices', ( camera ) => { const values = camera.cameras.map( ( subCamera ) => subCamera.matrixWorld ); return uniformArray( values ).setGroup( renderGroup ).setName( 'cameraWorldMatrices' ).onRenderUpdate( ( { camera }, self ) => { for ( let i = 0; i < camera.cameras.length; i ++ ) { self.array[ i ] = camera.cameras[ i ].matrixWorld; } } ); } ); cameraWorldMatrix = cameraArray.element( camera.isMultiViewCamera ? builtin( 'gl_ViewID_OVR' ) : cameraIndex ); } else { if ( _cameraWorldMatrixBase === null ) { _cameraWorldMatrixBase = uniform( camera.matrixWorld ).setName( 'cameraWorldMatrix' ).setGroup( renderGroup ).onRenderUpdate( ( { camera } ) => camera.matrixWorld ); } cameraWorldMatrix = _cameraWorldMatrixBase; } return cameraWorldMatrix; } ).once() )(); /** * TSL object that represents the normal matrix of the camera used for the current render. * * @tsl * @type {UniformNode<mat3>} */ export const cameraNormalMatrix = /*@__PURE__*/ ( Fn( ( { camera, renderer } ) => { let cameraNormalMatrix; if ( camera.isArrayCamera && camera.cameras.length > 0 ) { const cameraArray = getCameraArray( renderer, camera, 'cameraNormalMatrices', ( camera ) => { const values = camera.cameras.map( ( subCamera ) => subCamera.normalMatrix ); return uniformArray( values ).setGroup( renderGroup ).setName( 'cameraNormalMatrices' ).onRenderUpdate( ( { camera }, self ) => { for ( let i = 0; i < camera.cameras.length; i ++ ) { self.array[ i ] = camera.cameras[ i ].normalMatrix; } } ); } ); cameraNormalMatrix = cameraArray.element( camera.isMultiViewCamera ? builtin( 'gl_ViewID_OVR' ) : cameraIndex ); } else { if ( _cameraNormalMatrixBase === null ) { _cameraNormalMatrixBase = uniform( camera.normalMatrix ).setName( 'cameraNormalMatrix' ).setGroup( renderGroup ).onRenderUpdate( ( { camera } ) => camera.normalMatrix ); } cameraNormalMatrix = _cameraNormalMatrixBase; } return cameraNormalMatrix; } ).once() )(); /** * TSL object that represents the position in world space of the camera used for the current render. * * @tsl * @type {UniformNode<vec3>} */ export const cameraPosition = /*@__PURE__*/ ( Fn( ( { camera, renderer } ) => { let cameraPosition; if ( camera.isArrayCamera && camera.cameras.length > 0 ) { const cameraArray = getCameraArray( renderer, camera, 'cameraPositions', ( camera ) => { const positions = []; for ( let i = 0, l = camera.cameras.length; i < l; i ++ ) { positions.push( new Vector3() ); } return 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 = cameraArray.element( camera.isMultiViewCamera ? builtin( 'gl_ViewID_OVR' ) : cameraIndex ); } else { if ( _cameraPositionBase === null ) { _cameraPositionBase = uniform( new Vector3() ).setName( 'cameraPosition' ).setGroup( renderGroup ).onRenderUpdate( ( { camera }, self ) => self.value.setFromMatrixPosition( camera.matrixWorld ) ); } cameraPosition = _cameraPositionBase; } return cameraPosition; } ).once() )(); /** * TSL object that represents the viewport of the camera used for the current render. * * @tsl * @type {UniformNode<vec4>} */ export const cameraViewport = /*@__PURE__*/ ( Fn( ( { camera, renderer } ) => { let cameraViewport; if ( camera.isArrayCamera && camera.cameras.length > 0 ) { const cameraArray = getCameraArray( renderer, camera, 'cameraViewports', ( camera ) => { const values = camera.cameras.map( ( subCamera ) => subCamera.viewport ); return uniformArray( values, 'vec4' ).setGroup( renderGroup ).setName( 'cameraViewports' ).onRenderUpdate( ( { camera }, self ) => { for ( let i = 0; i < camera.cameras.length; i ++ ) { self.array[ i ] = camera.cameras[ i ].viewport; } } ); } ); cameraViewport = cameraArray.element( cameraIndex ); } else { if ( _cameraViewportBase === null ) { // Fallback for single camera _cameraViewportBase = vec4( 0, 0, screenSize.x, screenSize.y ).toConst( 'cameraViewport' ); } cameraViewport = _cameraViewportBase; } return cameraViewport; } ).once() )();