diff --git a/examples/files.json b/examples/files.json index 0173905083689c..6341bfa5de2e2f 100644 --- a/examples/files.json +++ b/examples/files.json @@ -485,6 +485,7 @@ "webgpu_volume_cloud", "webgpu_volume_lighting", "webgpu_volume_lighting_rectarea", + "webgpu_volume_lighting_traa", "webgpu_volume_perlin", "webgpu_water", "webgpu_xr_rollercoaster", diff --git a/examples/jsm/tsl/display/RetroPassNode.js b/examples/jsm/tsl/display/RetroPassNode.js index 2f6a9cb10e463f..bcf42e8b9de77d 100644 --- a/examples/jsm/tsl/display/RetroPassNode.js +++ b/examples/jsm/tsl/display/RetroPassNode.js @@ -1,5 +1,5 @@ -import { MeshBasicNodeMaterial, PassNode, UnsignedByteType, NearestFilter } from 'three/webgpu'; -import { float, vec2, vec4, Fn, uv, varying, cameraProjectionMatrix, cameraViewMatrix, positionWorld, screenSize, materialColor, replaceDefaultUV } from 'three/tsl'; +import { MeshBasicNodeMaterial, PassNode, UnsignedByteType, NearestFilter, CubeMapNode, MeshPhongNodeMaterial } from 'three/webgpu'; +import { float, vec2, vec4, Fn, uv, varying, cameraProjectionMatrix, cameraViewMatrix, positionWorld, screenSize, materialColor, uint, texture, uniform, context, reflectVector } from 'three/tsl'; const _affineUv = varying( vec2() ); const _w = varying( float() ); @@ -50,7 +50,8 @@ class RetroPassNode extends PassNode { super( PassNode.COLOR, scene, camera ); const { - affineDistortion = null + affineDistortion = null, + filterTextures = false } = options; this.setResolutionScale( .25 ); @@ -61,6 +62,8 @@ class RetroPassNode extends PassNode { this.affineDistortionNode = affineDistortion; + this.filterTextures = filterTextures; + this._materialCache = new Map(); } @@ -80,36 +83,125 @@ class RetroPassNode extends PassNode { renderer.setRenderObjectFunction( ( object, scene, camera, geometry, material, ...params ) => { - let retroMaterial = this._materialCache.get( material ); + const retroMaterialData = this._materialCache.get( material ); + + let retroMaterial; + + if ( retroMaterialData === undefined || retroMaterialData.version !== material.version ) { + + if ( retroMaterialData !== undefined ) { + + retroMaterialData.material.dispose(); + + } + + if ( material.isMeshBasicMaterial || material.isMeshBasicNodeMaterial ) { + + retroMaterial = new MeshBasicNodeMaterial(); - if ( retroMaterial === undefined ) { + } else { - retroMaterial = new MeshBasicNodeMaterial(); + retroMaterial = new MeshPhongNodeMaterial(); + + } retroMaterial.colorNode = material.colorNode || null; retroMaterial.opacityNode = material.opacityNode || null; retroMaterial.positionNode = material.positionNode || null; retroMaterial.vertexNode = material.vertexNode || _clipSpaceRetro; + let colorNode = material.colorNode || materialColor; + + if ( material.isMeshStandardNodeMaterial || material.isMeshStandardMaterial ) { + + const envMap = material.envMap || scene.environment; + + if ( envMap ) { + + const reflection = new CubeMapNode( texture( envMap ) ); + + let metalness; + + if ( material.metalnessNode ) { + + metalness = material.metalnessNode; + + } else { + + metalness = uniform( material.metalness ).onRenderUpdate( ( { material } ) => material.metalness ); + + if ( material.metalnessMap ) { + + const textureUniform = texture( material.metalnessMap ).onRenderUpdate( ( { material } ) => material.metalnessMap ); + + metalness = metalness.mul( textureUniform.b ); + + } + + } + + colorNode = metalness.mix( colorNode, reflection ); + + } + + } + + retroMaterial.colorNode = colorNode; + + // + + const contextData = {}; + if ( this.affineDistortionNode ) { - retroMaterial.colorNode = replaceDefaultUV( () => { + contextData.getUV = ( texture ) => { + + let finalUV; + + if ( texture.isCubeTextureNode ) { - return this.affineDistortionNode.mix( uv(), _affineUv.div( _w ) ); + finalUV = reflectVector; - }, retroMaterial.colorNode || materialColor ); + } else { + + finalUV = this.affineDistortionNode.mix( uv(), _affineUv.div( _w ) ); + + } + + return finalUV; + + }; } - this._materialCache.set( material, retroMaterial ); + if ( this.filterTextures !== true ) { + + contextData.getTextureLevel = () => uint( 0 ); + + } + + retroMaterial.contextNode = context( contextData ); + + // + + this._materialCache.set( material, { + material: retroMaterial, + version: material.version + } ); + + } else { + + retroMaterial = retroMaterialData.material; } - retroMaterial.map = material.map; - retroMaterial.color = material.color; - retroMaterial.opacity = material.opacity; - retroMaterial.transparent = material.transparent; - retroMaterial.side = material.side; + for ( const property in material ) { + + if ( retroMaterial[ property ] === undefined ) continue; + + retroMaterial[ property ] = material[ property ]; + + } renderer.renderObject( object, scene, camera, geometry, retroMaterial, ...params ); @@ -131,7 +223,12 @@ class RetroPassNode extends PassNode { super.dispose(); - this._materialCache.forEach( material => material.dispose() ); + this._materialCache.forEach( ( data ) => { + + data.material.dispose(); + + } ); + this._materialCache.clear(); } diff --git a/examples/screenshots/webgpu_postprocessing_retro.jpg b/examples/screenshots/webgpu_postprocessing_retro.jpg index 1585d761881f50..39626e76a86680 100644 Binary files a/examples/screenshots/webgpu_postprocessing_retro.jpg and b/examples/screenshots/webgpu_postprocessing_retro.jpg differ diff --git a/examples/screenshots/webgpu_volume_lighting_traa.jpg b/examples/screenshots/webgpu_volume_lighting_traa.jpg new file mode 100644 index 00000000000000..00898d2d2f9b1e Binary files /dev/null and b/examples/screenshots/webgpu_volume_lighting_traa.jpg differ diff --git a/examples/webgpu_postprocessing_retro.html b/examples/webgpu_postprocessing_retro.html index 0b64507a806465..75377766b97b5d 100644 --- a/examples/webgpu_postprocessing_retro.html +++ b/examples/webgpu_postprocessing_retro.html @@ -35,7 +35,7 @@ + + + + + diff --git a/src/nodes/accessors/Camera.js b/src/nodes/accessors/Camera.js index c42d3b6f15ab79..331d331c297a79 100644 --- a/src/nodes/accessors/Camera.js +++ b/src/nodes/accessors/Camera.js @@ -6,6 +6,29 @@ import { uniformArray } from './UniformArrayNode.js'; import { builtin } from './BuiltinNode.js'; import { screenSize } from '../display/ScreenNode.js'; +// Cache node uniforms + +let _cameraProjectionMatrixBase = null; +let _cameraProjectionMatrixArray = null; + +let _cameraProjectionMatrixInverseBase = null; +let _cameraProjectionMatrixInverseArray = null; + +let _cameraViewMatrixBase = null; +let _cameraViewMatrixArray = null; + +let _cameraWorldMatrixBase = null; +let _cameraWorldMatrixArray = null; + +let _cameraNormalMatrixBase = null; +let _cameraNormalMatrixArray = null; + +let _cameraPositionBase = null; +let _cameraPositionArray = null; + +let _cameraViewportBase = null; +let _cameraViewportArray = null; + /** * TSL object that represents the current `index` value of the camera if used ArrayCamera. * @@ -50,13 +73,27 @@ export const cameraProjectionMatrix = /*@__PURE__*/ ( Fn( ( { camera } ) => { } - const cameraProjectionMatrices = uniformArray( matrices ).setGroup( renderGroup ).setName( 'cameraProjectionMatrices' ); + if ( _cameraProjectionMatrixArray === null ) { + + _cameraProjectionMatrixArray = uniformArray( matrices ).setGroup( renderGroup ).setName( 'cameraProjectionMatrices' ); - cameraProjectionMatrix = cameraProjectionMatrices.element( camera.isMultiViewCamera ? builtin( 'gl_ViewID_OVR' ) : cameraIndex ).toConst( 'cameraProjectionMatrix' ); + } else { + + _cameraProjectionMatrixArray.array = matrices; + + } + + cameraProjectionMatrix = _cameraProjectionMatrixArray.element( camera.isMultiViewCamera ? builtin( 'gl_ViewID_OVR' ) : cameraIndex ).toConst( 'cameraProjectionMatrix' ); } else { - cameraProjectionMatrix = uniform( 'mat4' ).setName( 'cameraProjectionMatrix' ).setGroup( renderGroup ).onRenderUpdate( ( { camera } ) => camera.projectionMatrix ); + if ( _cameraProjectionMatrixBase === null ) { + + _cameraProjectionMatrixBase = uniform( camera.projectionMatrix ).setName( 'cameraProjectionMatrix' ).setGroup( renderGroup ).onRenderUpdate( ( { camera } ) => camera.projectionMatrix ); + + } + + cameraProjectionMatrix = _cameraProjectionMatrixBase; } @@ -84,13 +121,27 @@ export const cameraProjectionMatrixInverse = /*@__PURE__*/ ( Fn( ( { camera } ) } - const cameraProjectionMatricesInverse = uniformArray( matrices ).setGroup( renderGroup ).setName( 'cameraProjectionMatricesInverse' ); + if ( _cameraProjectionMatrixInverseArray === null ) { + + _cameraProjectionMatrixInverseArray = uniformArray( matrices ).setGroup( renderGroup ).setName( 'cameraProjectionMatricesInverse' ); - cameraProjectionMatrixInverse = cameraProjectionMatricesInverse.element( camera.isMultiViewCamera ? builtin( 'gl_ViewID_OVR' ) : cameraIndex ).toConst( 'cameraProjectionMatrixInverse' ); + } else { + + _cameraProjectionMatrixInverseArray.array = matrices; + + } + + cameraProjectionMatrixInverse = _cameraProjectionMatrixInverseArray.element( camera.isMultiViewCamera ? builtin( 'gl_ViewID_OVR' ) : cameraIndex ).toConst( 'cameraProjectionMatrixInverse' ); } else { - cameraProjectionMatrixInverse = uniform( 'mat4' ).setName( 'cameraProjectionMatrixInverse' ).setGroup( renderGroup ).onRenderUpdate( ( { camera } ) => camera.projectionMatrixInverse ); + if ( _cameraProjectionMatrixInverseBase === null ) { + + _cameraProjectionMatrixInverseBase = uniform( camera.projectionMatrixInverse ).setName( 'cameraProjectionMatrixInverse' ).setGroup( renderGroup ).onRenderUpdate( ( { camera } ) => camera.projectionMatrixInverse ); + + } + + cameraProjectionMatrixInverse = _cameraProjectionMatrixInverseBase; } @@ -118,13 +169,27 @@ export const cameraViewMatrix = /*@__PURE__*/ ( Fn( ( { camera } ) => { } - const cameraViewMatrices = uniformArray( matrices ).setGroup( renderGroup ).setName( 'cameraViewMatrices' ); + if ( _cameraViewMatrixArray === null ) { + + _cameraViewMatrixArray = uniformArray( matrices ).setGroup( renderGroup ).setName( 'cameraViewMatrices' ); - cameraViewMatrix = cameraViewMatrices.element( camera.isMultiViewCamera ? builtin( 'gl_ViewID_OVR' ) : cameraIndex ).toConst( 'cameraViewMatrix' ); + } else { + + _cameraViewMatrixArray.array = matrices; + + } + + cameraViewMatrix = _cameraViewMatrixArray.element( camera.isMultiViewCamera ? builtin( 'gl_ViewID_OVR' ) : cameraIndex ).toConst( 'cameraViewMatrix' ); } else { - cameraViewMatrix = uniform( 'mat4' ).setName( 'cameraViewMatrix' ).setGroup( renderGroup ).onRenderUpdate( ( { camera } ) => camera.matrixWorldInverse ); + if ( _cameraViewMatrixBase === null ) { + + _cameraViewMatrixBase = uniform( camera.matrixWorldInverse ).setName( 'cameraViewMatrix' ).setGroup( renderGroup ).onRenderUpdate( ( { camera } ) => camera.matrixWorldInverse ); + + } + + cameraViewMatrix = _cameraViewMatrixBase; } @@ -152,13 +217,27 @@ export const cameraWorldMatrix = /*@__PURE__*/ ( Fn( ( { camera } ) => { } - const cameraWorldMatrices = uniformArray( matrices ).setGroup( renderGroup ).setName( 'cameraWorldMatrices' ); + if ( _cameraWorldMatrixArray === null ) { + + _cameraWorldMatrixArray = uniformArray( matrices ).setGroup( renderGroup ).setName( 'cameraWorldMatrices' ); - cameraWorldMatrix = cameraWorldMatrices.element( camera.isMultiViewCamera ? builtin( 'gl_ViewID_OVR' ) : cameraIndex ).toConst( 'cameraWorldMatrix' ); + } else { + + _cameraWorldMatrixArray.array = matrices; + + } + + cameraWorldMatrix = _cameraWorldMatrixArray.element( camera.isMultiViewCamera ? builtin( 'gl_ViewID_OVR' ) : cameraIndex ).toConst( 'cameraWorldMatrix' ); } else { - cameraWorldMatrix = uniform( 'mat4' ).setName( 'cameraWorldMatrix' ).setGroup( renderGroup ).onRenderUpdate( ( { camera } ) => camera.matrixWorld ); + if ( _cameraWorldMatrixBase === null ) { + + _cameraWorldMatrixBase = uniform( camera.matrixWorld ).setName( 'cameraWorldMatrix' ).setGroup( renderGroup ).onRenderUpdate( ( { camera } ) => camera.matrixWorld ); + + } + + cameraWorldMatrix = _cameraWorldMatrixBase; } @@ -186,13 +265,27 @@ export const cameraNormalMatrix = /*@__PURE__*/ ( Fn( ( { camera } ) => { } - const cameraNormalMatrices = uniformArray( matrices ).setGroup( renderGroup ).setName( 'cameraNormalMatrices' ); + if ( _cameraNormalMatrixArray === null ) { + + _cameraNormalMatrixArray = uniformArray( matrices ).setGroup( renderGroup ).setName( 'cameraNormalMatrices' ); - cameraNormalMatrix = cameraNormalMatrices.element( camera.isMultiViewCamera ? builtin( 'gl_ViewID_OVR' ) : cameraIndex ).toConst( 'cameraNormalMatrix' ); + } else { + + _cameraNormalMatrixArray.array = matrices; + + } + + cameraNormalMatrix = _cameraNormalMatrixArray.element( camera.isMultiViewCamera ? builtin( 'gl_ViewID_OVR' ) : cameraIndex ).toConst( 'cameraNormalMatrix' ); } else { - cameraNormalMatrix = uniform( 'mat3' ).setName( 'cameraNormalMatrix' ).setGroup( renderGroup ).onRenderUpdate( ( { camera } ) => camera.normalMatrix ); + if ( _cameraNormalMatrixBase === null ) { + + _cameraNormalMatrixBase = uniform( camera.normalMatrix ).setName( 'cameraNormalMatrix' ).setGroup( renderGroup ).onRenderUpdate( ( { camera } ) => camera.normalMatrix ); + + } + + cameraNormalMatrix = _cameraNormalMatrixBase; } @@ -220,24 +313,38 @@ export const cameraPosition = /*@__PURE__*/ ( Fn( ( { camera } ) => { } - const cameraPositions = uniformArray( positions ).setGroup( renderGroup ).setName( 'cameraPositions' ).onRenderUpdate( ( { camera }, self ) => { + if ( _cameraPositionArray === null ) { + + _cameraPositionArray = uniformArray( positions ).setGroup( renderGroup ).setName( 'cameraPositions' ).onRenderUpdate( ( { camera }, self ) => { - const subCameras = camera.cameras; - const array = self.array; + const subCameras = camera.cameras; + const array = self.array; - for ( let i = 0, l = subCameras.length; i < l; i ++ ) { + for ( let i = 0, l = subCameras.length; i < l; i ++ ) { - array[ i ].setFromMatrixPosition( subCameras[ i ].matrixWorld ); + array[ i ].setFromMatrixPosition( subCameras[ i ].matrixWorld ); - } + } - } ); + } ); - cameraPosition = cameraPositions.element( camera.isMultiViewCamera ? builtin( 'gl_ViewID_OVR' ) : cameraIndex ).toConst( 'cameraPosition' ); + } else { + + _cameraPositionArray.array = positions; + + } + + cameraPosition = _cameraPositionArray.element( camera.isMultiViewCamera ? builtin( 'gl_ViewID_OVR' ) : cameraIndex ).toConst( 'cameraPosition' ); } else { - cameraPosition = uniform( new Vector3() ).setName( 'cameraPosition' ).setGroup( renderGroup ).onRenderUpdate( ( { camera }, self ) => self.value.setFromMatrixPosition( camera.matrixWorld ) ); + if ( _cameraPositionBase === null ) { + + _cameraPositionBase = uniform( new Vector3() ).setName( 'cameraPosition' ).setGroup( renderGroup ).onRenderUpdate( ( { camera }, self ) => self.value.setFromMatrixPosition( camera.matrixWorld ) ); + + } + + cameraPosition = _cameraPositionBase; } @@ -266,14 +373,28 @@ export const cameraViewport = /*@__PURE__*/ ( Fn( ( { camera } ) => { } - const cameraViewports = uniformArray( viewports, 'vec4' ).setGroup( renderGroup ).setName( 'cameraViewports' ); + if ( _cameraViewportArray === null ) { + + _cameraViewportArray = uniformArray( viewports, 'vec4' ).setGroup( renderGroup ).setName( 'cameraViewports' ); - cameraViewport = cameraViewports.element( cameraIndex ).toConst( 'cameraViewport' ); + } else { + + _cameraViewportArray.array = viewports; + + } + + cameraViewport = _cameraViewportArray.element( cameraIndex ).toConst( 'cameraViewport' ); } else { - // Fallback for single camera - cameraViewport = vec4( 0, 0, screenSize.x, screenSize.y ).toConst( 'cameraViewport' ); + if ( _cameraViewportBase === null ) { + + // Fallback for single camera + _cameraViewportBase = vec4( 0, 0, screenSize.x, screenSize.y ).toConst( 'cameraViewport' ); + + } + + cameraViewport = _cameraViewportBase; } diff --git a/src/nodes/accessors/TextureNode.js b/src/nodes/accessors/TextureNode.js index 6eaf2ccf66ae55..1f466c1c797bc8 100644 --- a/src/nodes/accessors/TextureNode.js +++ b/src/nodes/accessors/TextureNode.js @@ -405,14 +405,18 @@ class TextureNode extends UniformNode { } else { - if ( this.value.compareFunction !== null && this.value.compareFunction !== LessCompare ) { + if ( this.value.compareFunction === null || this.value.compareFunction === LessCompare ) { + + compareStepNode = this.compareNode; + + } else { + + compareNode = this.compareNode; warnOnce( 'TSL: Only "LessCompare" is supported for depth texture comparison fallback.' ); } - compareStepNode = this.compareNode; - } } diff --git a/src/nodes/accessors/UniformArrayNode.js b/src/nodes/accessors/UniformArrayNode.js index c21fa795fb812d..e05d8ace7a1fcc 100644 --- a/src/nodes/accessors/UniformArrayNode.js +++ b/src/nodes/accessors/UniformArrayNode.js @@ -315,6 +315,8 @@ class UniformArrayNode extends BufferNode { this.bufferCount = length; this.bufferType = paddedType; + this.update(); // initialize the buffer values + return super.setup( builder ); } diff --git a/test/e2e/puppeteer.js b/test/e2e/puppeteer.js index d17e72cd02c84f..96452f43847399 100644 --- a/test/e2e/puppeteer.js +++ b/test/e2e/puppeteer.js @@ -39,6 +39,7 @@ const exceptionList = [ 'webgpu_test_memory', 'webgpu_texturegrad', 'webgpu_tsl_vfx_flames', + 'webgpu_volume_lighting_traa', // Need more time to render 'css3d_mixed',