2
0
Эх сурвалжийг харах

WebGPURenderer: Backdrop Node - Part 2/3 (#26196)

* WebGPURenderer: Depth buffer improvements

* CameraNode: Added cameraNear, cameraFar

* Object3DNode: Added modelScale, objectScale()

* Added TextureBicubicNode, textureBicubic

* TextureNode: Added .size(), level() and uv()

* Added ViewportDepthTextureNode: viewportDepthTexture()

* Added ViewportDepthNode: depth, depthTexture()

* Added viewportMipTexture()

* Update imports

* Add webgpu_backdrop_area example

* update title

* CameraNode: Fix VIEW_MATRIX
sunag 2 жил өмнө
parent
commit
df27d3be03

+ 1 - 0
examples/files.json

@@ -307,6 +307,7 @@
 	"webgpu": [
 	"webgpu": [
 		"webgpu_audio_processing",
 		"webgpu_audio_processing",
 		"webgpu_backdrop",
 		"webgpu_backdrop",
+		"webgpu_backdrop_area",
 		"webgpu_compute",
 		"webgpu_compute",
 		"webgpu_cubemap_adjustments",
 		"webgpu_cubemap_adjustments",
 		"webgpu_cubemap_dynamic",
 		"webgpu_cubemap_dynamic",

+ 8 - 5
examples/jsm/nodes/Nodes.js

@@ -65,16 +65,17 @@ export * from './shadernode/ShaderNode.js';
 export { default as BitangentNode, bitangentGeometry, bitangentLocal, bitangentView, bitangentWorld, transformedBitangentView, transformedBitangentWorld } from './accessors/BitangentNode.js';
 export { default as BitangentNode, bitangentGeometry, bitangentLocal, bitangentView, bitangentWorld, transformedBitangentView, transformedBitangentWorld } from './accessors/BitangentNode.js';
 export { default as BufferAttributeNode, bufferAttribute, dynamicBufferAttribute } from './accessors/BufferAttributeNode.js';
 export { default as BufferAttributeNode, bufferAttribute, dynamicBufferAttribute } from './accessors/BufferAttributeNode.js';
 export { default as BufferNode, buffer } from './accessors/BufferNode.js';
 export { default as BufferNode, buffer } from './accessors/BufferNode.js';
-export { default as CameraNode, cameraProjectionMatrix, cameraViewMatrix, cameraNormalMatrix, cameraWorldMatrix, cameraPosition } from './accessors/CameraNode.js';
+export { default as CameraNode, cameraProjectionMatrix, cameraViewMatrix, cameraNormalMatrix, cameraWorldMatrix, cameraPosition, cameraNear, cameraFar } from './accessors/CameraNode.js';
 export { default as CubeTextureNode, cubeTexture } from './accessors/CubeTextureNode.js';
 export { default as CubeTextureNode, cubeTexture } from './accessors/CubeTextureNode.js';
 export { default as ExtendedMaterialNode, materialNormal } from './accessors/ExtendedMaterialNode.js';
 export { default as ExtendedMaterialNode, materialNormal } from './accessors/ExtendedMaterialNode.js';
 export { default as InstanceNode, instance } from './accessors/InstanceNode.js';
 export { default as InstanceNode, instance } from './accessors/InstanceNode.js';
 export { default as MaterialNode, materialUV, materialAlphaTest, materialColor, materialShininess, materialEmissive, materialOpacity, materialSpecularColor, materialReflectivity, materialRoughness, materialMetalness, materialRotation } from './accessors/MaterialNode.js';
 export { default as MaterialNode, materialUV, materialAlphaTest, materialColor, materialShininess, materialEmissive, materialOpacity, materialSpecularColor, materialReflectivity, materialRoughness, materialMetalness, materialRotation } from './accessors/MaterialNode.js';
 export { default as MaterialReferenceNode, materialReference } from './accessors/MaterialReferenceNode.js';
 export { default as MaterialReferenceNode, materialReference } from './accessors/MaterialReferenceNode.js';
-export { default as ModelNode, modelDirection, modelViewMatrix, modelNormalMatrix, modelWorldMatrix, modelPosition, modelViewPosition } from './accessors/ModelNode.js';
+export { default as TextureBicubicNode, textureBicubic } from './accessors/TextureBicubicNode.js';
+export { default as ModelNode, modelDirection, modelViewMatrix, modelNormalMatrix, modelWorldMatrix, modelPosition, modelViewPosition, modelScale } from './accessors/ModelNode.js';
 export { default as ModelViewProjectionNode, modelViewProjection } from './accessors/ModelViewProjectionNode.js';
 export { default as ModelViewProjectionNode, modelViewProjection } from './accessors/ModelViewProjectionNode.js';
 export { default as NormalNode, normalGeometry, normalLocal, normalView, normalWorld, transformedNormalView, transformedNormalWorld } from './accessors/NormalNode.js';
 export { default as NormalNode, normalGeometry, normalLocal, normalView, normalWorld, transformedNormalView, transformedNormalWorld } from './accessors/NormalNode.js';
-export { default as Object3DNode, objectDirection, objectViewMatrix, objectNormalMatrix, objectWorldMatrix, objectPosition, objectViewPosition } from './accessors/Object3DNode.js';
+export { default as Object3DNode, objectDirection, objectViewMatrix, objectNormalMatrix, objectWorldMatrix, objectPosition, objectScale, objectViewPosition } from './accessors/Object3DNode.js';
 export { default as PointUVNode, pointUV } from './accessors/PointUVNode.js';
 export { default as PointUVNode, pointUV } from './accessors/PointUVNode.js';
 export { default as PositionNode, positionGeometry, positionLocal, positionWorld, positionWorldDirection, positionView, positionViewDirection } from './accessors/PositionNode.js';
 export { default as PositionNode, positionGeometry, positionLocal, positionWorld, positionWorldDirection, positionView, positionViewDirection } from './accessors/PositionNode.js';
 export { default as ReferenceNode, reference } from './accessors/ReferenceNode.js';
 export { default as ReferenceNode, reference } from './accessors/ReferenceNode.js';
@@ -82,7 +83,7 @@ export { default as ReflectVectorNode, reflectVector } from './accessors/Reflect
 export { default as SkinningNode, skinning } from './accessors/SkinningNode.js';
 export { default as SkinningNode, skinning } from './accessors/SkinningNode.js';
 export { default as StorageBufferNode, storage } from './accessors/StorageBufferNode.js';
 export { default as StorageBufferNode, storage } from './accessors/StorageBufferNode.js';
 export { default as TangentNode, tangentGeometry, tangentLocal, tangentView, tangentWorld, transformedTangentView, transformedTangentWorld } from './accessors/TangentNode.js';
 export { default as TangentNode, tangentGeometry, tangentLocal, tangentView, tangentWorld, transformedTangentView, transformedTangentWorld } from './accessors/TangentNode.js';
-export { default as TextureNode, texture, sampler } from './accessors/TextureNode.js';
+export { default as TextureNode, texture, /*textureLevel,*/ sampler } from './accessors/TextureNode.js';
 export { default as UVNode, uv } from './accessors/UVNode.js';
 export { default as UVNode, uv } from './accessors/UVNode.js';
 export { default as UserDataNode, userData } from './accessors/UserDataNode.js';
 export { default as UserDataNode, userData } from './accessors/UserDataNode.js';
 
 
@@ -95,8 +96,10 @@ export { default as NormalMapNode, normalMap, TBNViewMatrix } from './display/No
 export { default as PosterizeNode, posterize } from './display/PosterizeNode.js';
 export { default as PosterizeNode, posterize } from './display/PosterizeNode.js';
 export { default as ToneMappingNode, toneMapping } from './display/ToneMappingNode.js';
 export { default as ToneMappingNode, toneMapping } from './display/ToneMappingNode.js';
 export { default as ViewportNode, viewportCoordinate, viewportResolution, viewportTopLeft, viewportBottomLeft, viewportTopRight, viewportBottomRight } from './display/ViewportNode.js';
 export { default as ViewportNode, viewportCoordinate, viewportResolution, viewportTopLeft, viewportBottomLeft, viewportTopRight, viewportBottomRight } from './display/ViewportNode.js';
-export { default as ViewportTextureNode, viewportTexture } from './display/ViewportTextureNode.js';
+export { default as ViewportTextureNode, viewportTexture, viewportMipTexture } from './display/ViewportTextureNode.js';
 export { default as ViewportSharedTextureNode, viewportSharedTexture } from './display/ViewportSharedTextureNode.js';
 export { default as ViewportSharedTextureNode, viewportSharedTexture } from './display/ViewportSharedTextureNode.js';
+export { default as ViewportDepthTextureNode, viewportDepthTexture } from './display/ViewportDepthTextureNode.js';
+export { default as ViewportDepthNode, viewZToOrthographicDepth, orthographicDepthToViewZ, viewZToPerspectiveDepth, perspectiveDepthToViewZ, depth, depthTexture } from './display/ViewportDepthNode.js';
 
 
 // code
 // code
 export { default as ExpressionNode, expression } from './code/ExpressionNode.js';
 export { default as ExpressionNode, expression } from './code/ExpressionNode.js';

+ 23 - 3
examples/jsm/nodes/accessors/CameraNode.js

@@ -18,6 +18,10 @@ class CameraNode extends Object3DNode {
 
 
 			return 'mat4';
 			return 'mat4';
 
 
+		} else if ( scope === CameraNode.NEAR || scope === CameraNode.FAR ) {
+
+			return 'float';
+
 		}
 		}
 
 
 		return super.getNodeType( builder );
 		return super.getNodeType( builder );
@@ -30,13 +34,21 @@ class CameraNode extends Object3DNode {
 		const uniformNode = this._uniformNode;
 		const uniformNode = this._uniformNode;
 		const scope = this.scope;
 		const scope = this.scope;
 
 
-		if ( scope === CameraNode.PROJECTION_MATRIX ) {
+		if ( scope === CameraNode.VIEW_MATRIX ) {
+
+			uniformNode.value = camera.matrixWorldInverse;
+
+		} else if ( scope === CameraNode.PROJECTION_MATRIX ) {
 
 
 			uniformNode.value = camera.projectionMatrix;
 			uniformNode.value = camera.projectionMatrix;
 
 
-		} else if ( scope === CameraNode.VIEW_MATRIX ) {
+		} else if ( scope === CameraNode.NEAR ) {
 
 
-			uniformNode.value = camera.matrixWorldInverse;
+			uniformNode.value = camera.near;
+
+		} else if ( scope === CameraNode.FAR ) {
+
+			uniformNode.value = camera.far;
 
 
 		} else {
 		} else {
 
 
@@ -56,6 +68,10 @@ class CameraNode extends Object3DNode {
 
 
 			this._uniformNode.nodeType = 'mat4';
 			this._uniformNode.nodeType = 'mat4';
 
 
+		} else if ( scope === CameraNode.NEAR || scope === CameraNode.FAR ) {
+
+			this._uniformNode.nodeType = 'float';
+
 		}
 		}
 
 
 		return super.generate( builder );
 		return super.generate( builder );
@@ -65,10 +81,14 @@ class CameraNode extends Object3DNode {
 }
 }
 
 
 CameraNode.PROJECTION_MATRIX = 'projectionMatrix';
 CameraNode.PROJECTION_MATRIX = 'projectionMatrix';
+CameraNode.NEAR = 'near';
+CameraNode.FAR = 'far';
 
 
 export default CameraNode;
 export default CameraNode;
 
 
 export const cameraProjectionMatrix = nodeImmutable( CameraNode, CameraNode.PROJECTION_MATRIX );
 export const cameraProjectionMatrix = nodeImmutable( CameraNode, CameraNode.PROJECTION_MATRIX );
+export const cameraNear = nodeImmutable( CameraNode, CameraNode.NEAR );
+export const cameraFar = nodeImmutable( CameraNode, CameraNode.FAR );
 export const cameraViewMatrix = nodeImmutable( CameraNode, CameraNode.VIEW_MATRIX );
 export const cameraViewMatrix = nodeImmutable( CameraNode, CameraNode.VIEW_MATRIX );
 export const cameraNormalMatrix = nodeImmutable( CameraNode, CameraNode.NORMAL_MATRIX );
 export const cameraNormalMatrix = nodeImmutable( CameraNode, CameraNode.NORMAL_MATRIX );
 export const cameraWorldMatrix = nodeImmutable( CameraNode, CameraNode.WORLD_MATRIX );
 export const cameraWorldMatrix = nodeImmutable( CameraNode, CameraNode.WORLD_MATRIX );

+ 1 - 0
examples/jsm/nodes/accessors/ModelNode.js

@@ -27,6 +27,7 @@ export const modelViewMatrix = nodeImmutable( ModelNode, ModelNode.VIEW_MATRIX )
 export const modelNormalMatrix = nodeImmutable( ModelNode, ModelNode.NORMAL_MATRIX );
 export const modelNormalMatrix = nodeImmutable( ModelNode, ModelNode.NORMAL_MATRIX );
 export const modelWorldMatrix = nodeImmutable( ModelNode, ModelNode.WORLD_MATRIX );
 export const modelWorldMatrix = nodeImmutable( ModelNode, ModelNode.WORLD_MATRIX );
 export const modelPosition = nodeImmutable( ModelNode, ModelNode.POSITION );
 export const modelPosition = nodeImmutable( ModelNode, ModelNode.POSITION );
+export const modelScale = nodeImmutable( ModelNode, ModelNode.SCALE );
 export const modelViewPosition = nodeImmutable( ModelNode, ModelNode.VIEW_POSITION );
 export const modelViewPosition = nodeImmutable( ModelNode, ModelNode.VIEW_POSITION );
 
 
 addNodeClass( ModelNode );
 addNodeClass( ModelNode );

+ 10 - 2
examples/jsm/nodes/accessors/Object3DNode.js

@@ -32,7 +32,7 @@ class Object3DNode extends Node {
 
 
 			return 'mat3';
 			return 'mat3';
 
 
-		} else if ( scope === Object3DNode.POSITION || scope === Object3DNode.VIEW_POSITION || scope === Object3DNode.DIRECTION ) {
+		} else if ( scope === Object3DNode.POSITION || scope === Object3DNode.VIEW_POSITION || scope === Object3DNode.DIRECTION || scope === Object3DNode.SCALE ) {
 
 
 			return 'vec3';
 			return 'vec3';
 
 
@@ -64,6 +64,12 @@ class Object3DNode extends Node {
 
 
 			uniformNode.value.setFromMatrixPosition( object.matrixWorld );
 			uniformNode.value.setFromMatrixPosition( object.matrixWorld );
 
 
+		} else if ( scope === Object3DNode.SCALE ) {
+
+			uniformNode.value = uniformNode.value || new Vector3();
+
+			uniformNode.value.setFromMatrixScale( object.matrixWorld );
+
 		} else if ( scope === Object3DNode.DIRECTION ) {
 		} else if ( scope === Object3DNode.DIRECTION ) {
 
 
 			uniformNode.value = uniformNode.value || new Vector3();
 			uniformNode.value = uniformNode.value || new Vector3();
@@ -95,7 +101,7 @@ class Object3DNode extends Node {
 
 
 			this._uniformNode.nodeType = 'mat3';
 			this._uniformNode.nodeType = 'mat3';
 
 
-		} else if ( scope === Object3DNode.POSITION || scope === Object3DNode.VIEW_POSITION || scope === Object3DNode.DIRECTION ) {
+		} else if ( scope === Object3DNode.POSITION || scope === Object3DNode.VIEW_POSITION || scope === Object3DNode.DIRECTION || scope === Object3DNode.SCALE ) {
 
 
 			this._uniformNode.nodeType = 'vec3';
 			this._uniformNode.nodeType = 'vec3';
 
 
@@ -127,6 +133,7 @@ Object3DNode.VIEW_MATRIX = 'viewMatrix';
 Object3DNode.NORMAL_MATRIX = 'normalMatrix';
 Object3DNode.NORMAL_MATRIX = 'normalMatrix';
 Object3DNode.WORLD_MATRIX = 'worldMatrix';
 Object3DNode.WORLD_MATRIX = 'worldMatrix';
 Object3DNode.POSITION = 'position';
 Object3DNode.POSITION = 'position';
+Object3DNode.SCALE = 'scale';
 Object3DNode.VIEW_POSITION = 'viewPosition';
 Object3DNode.VIEW_POSITION = 'viewPosition';
 Object3DNode.DIRECTION = 'direction';
 Object3DNode.DIRECTION = 'direction';
 
 
@@ -137,6 +144,7 @@ export const objectViewMatrix = nodeProxy( Object3DNode, Object3DNode.VIEW_MATRI
 export const objectNormalMatrix = nodeProxy( Object3DNode, Object3DNode.NORMAL_MATRIX );
 export const objectNormalMatrix = nodeProxy( Object3DNode, Object3DNode.NORMAL_MATRIX );
 export const objectWorldMatrix = nodeProxy( Object3DNode, Object3DNode.WORLD_MATRIX );
 export const objectWorldMatrix = nodeProxy( Object3DNode, Object3DNode.WORLD_MATRIX );
 export const objectPosition = nodeProxy( Object3DNode, Object3DNode.POSITION );
 export const objectPosition = nodeProxy( Object3DNode, Object3DNode.POSITION );
+export const objectScale = nodeProxy( Object3DNode, Object3DNode.SCALE );
 export const objectViewPosition = nodeProxy( Object3DNode, Object3DNode.VIEW_POSITION );
 export const objectViewPosition = nodeProxy( Object3DNode, Object3DNode.VIEW_POSITION );
 
 
 addNodeClass( Object3DNode );
 addNodeClass( Object3DNode );

+ 94 - 0
examples/jsm/nodes/accessors/TextureBicubicNode.js

@@ -0,0 +1,94 @@
+import TempNode from '../core/TempNode.js';
+import { addNodeClass } from '../core/Node.js';
+import { add, mul, div } from '../math/OperatorNode.js';
+import { floor, ceil, fract, pow } from '../math/MathNode.js';
+import { nodeProxy, addNodeElement, float, vec2, vec4, int } from '../shadernode/ShaderNode.js';
+
+// Mipped Bicubic Texture Filtering by N8
+// https://www.shadertoy.com/view/Dl2SDW
+
+const bC = 1.0 / 6.0;
+
+const w0 = ( a ) => mul( bC, mul( a, mul( a, a.negate().add( 3.0 ) ).sub( 3.0 ) ).add( 1.0 ) );
+
+const w1 = ( a ) => mul( bC, mul( a, mul( a, mul( 3.0, a ).sub( 6.0 ) ) ).add( 4.0 ) );
+
+const w2 = ( a ) => mul( bC, mul( a, mul( a, mul( - 3.0, a ).add( 3.0 ) ).add( 3.0 ) ).add( 1.0 ) );
+
+const w3 = ( a ) => mul( bC, pow( a, 3 ) );
+
+const g0 = ( a ) => w0( a ).add( w1( a ) );
+
+const g1 = ( a ) => w2( a ).add( w3( a ) );
+
+// h0 and h1 are the two offset functions
+const h0 = ( a ) => add( - 1.0, w1( a ).div( w0( a ).add( w1( a ) ) ) );
+
+const h1 = ( a ) => add( 1.0, w3( a ).div( w2( a ).add( w3( a ) ) ) );
+
+const bicubic = ( textureNode, texelSize, lod ) => {
+
+	const uv = textureNode.uvNode;
+	const uvScaled = mul( uv, texelSize.zw ).add( 0.5 );
+
+	const iuv = floor( uvScaled );
+	const fuv = fract( uvScaled );
+
+	const g0x = g0( fuv.x );
+	const g1x = g1( fuv.x );
+	const h0x = h0( fuv.x );
+	const h1x = h1( fuv.x );
+	const h0y = h0( fuv.y );
+	const h1y = h1( fuv.y );
+
+	const p0 = vec2( iuv.x.add( h0x ), iuv.y.add( h0y ) ).sub( 0.5 ).mul( texelSize.xy );
+	const p1 = vec2( iuv.x.add( h1x ), iuv.y.add( h0y ) ).sub( 0.5 ).mul( texelSize.xy );
+	const p2 = vec2( iuv.x.add( h0x ), iuv.y.add( h1y ) ).sub( 0.5 ).mul( texelSize.xy );
+	const p3 = vec2( iuv.x.add( h1x ), iuv.y.add( h1y ) ).sub( 0.5 ).mul( texelSize.xy );
+
+	const a = g0( fuv.y ).mul( add( g0x.mul( textureNode.uv( p0 ).level( lod ) ), g1x.mul( textureNode.uv( p1 ).level( lod ) ) ) );
+	const b = g1( fuv.y ).mul( add( g0x.mul( textureNode.uv( p2 ).level( lod ) ), g1x.mul( textureNode.uv( p3 ).level( lod ) ) ) );
+
+	return a.add( b );
+
+};
+
+const textureBicubicMethod = ( textureNode, lodNode ) => {
+
+	const fLodSize = vec2( textureNode.size( int( lodNode ) ) );
+	const cLodSize = vec2( textureNode.size( int( lodNode.add( 1.0 ) ) ) );
+	const fLodSizeInv = div( 1.0, fLodSize );
+	const cLodSizeInv = div( 1.0, cLodSize );
+	const fSample = bicubic( textureNode, vec4( fLodSizeInv, fLodSize ), floor( lodNode ) );
+	const cSample = bicubic( textureNode, vec4( cLodSizeInv, cLodSize ), ceil( lodNode ) );
+
+	return fract( lodNode ).mix( fSample, cSample );
+
+};
+
+class TextureBicubicNode extends TempNode {
+
+	constructor( textureNode, blurNode = float( 3 ) ) {
+
+		super( 'vec4' );
+
+		this.textureNode = textureNode;
+		this.blurNode = blurNode;
+
+	}
+
+	construct() {
+
+		return textureBicubicMethod( this.textureNode, this.blurNode );
+
+	}
+
+}
+
+export default TextureBicubicNode;
+
+export const textureBicubic = nodeProxy( TextureBicubicNode );
+
+addNodeElement( 'bicubic', textureBicubic );
+
+addNodeClass( TextureBicubicNode );

+ 37 - 0
examples/jsm/nodes/accessors/TextureNode.js

@@ -1,5 +1,7 @@
 import UniformNode from '../core/UniformNode.js';
 import UniformNode from '../core/UniformNode.js';
 import { uv } from './UVNode.js';
 import { uv } from './UVNode.js';
+import { textureSize } from './TextureSizeNode.js';
+import { context } from '../core/ContextNode.js';
 import { addNodeClass } from '../core/Node.js';
 import { addNodeClass } from '../core/Node.js';
 import { addNodeElement, nodeProxy } from '../shadernode/ShaderNode.js';
 import { addNodeElement, nodeProxy } from '../shadernode/ShaderNode.js';
 
 
@@ -140,6 +142,32 @@ class TextureNode extends UniformNode {
 
 
 	}
 	}
 
 
+	uv( uvNode ) {
+
+		const textureNode = this.clone();
+		textureNode.uvNode = uvNode;
+
+		return textureNode;
+
+	}
+
+	level( levelNode ) {
+
+		const textureNode = this.clone();
+		textureNode.levelNode = levelNode;
+
+		return context( textureNode, {
+			getMIPLevelAlgorithmNode: ( textureNode, levelNode ) => levelNode
+		} );
+
+	}
+
+	size( levelNode ) {
+
+		return textureSize( this, levelNode );
+
+	}
+
 	serialize( data ) {
 	serialize( data ) {
 
 
 		super.serialize( data );
 		super.serialize( data );
@@ -156,13 +184,22 @@ class TextureNode extends UniformNode {
 
 
 	}
 	}
 
 
+	clone() {
+
+		return new this.constructor( this.value, this.uvNode, this.levelNode );
+
+	}
+
 }
 }
 
 
 export default TextureNode;
 export default TextureNode;
 
 
 export const texture = nodeProxy( TextureNode );
 export const texture = nodeProxy( TextureNode );
+//export const textureLevel = ( value, uv, level ) => texture( value, uv ).level( level );
+
 export const sampler = ( aTexture ) => ( aTexture.isNode === true ? aTexture : texture( aTexture ) ).convert( 'sampler' );
 export const sampler = ( aTexture ) => ( aTexture.isNode === true ? aTexture : texture( aTexture ) ).convert( 'sampler' );
 
 
 addNodeElement( 'texture', texture );
 addNodeElement( 'texture', texture );
+//addNodeElement( 'textureLevel', textureLevel );
 
 
 addNodeClass( TextureNode );
 addNodeClass( TextureNode );

+ 35 - 0
examples/jsm/nodes/accessors/TextureSizeNode.js

@@ -0,0 +1,35 @@
+import Node from '../core/Node.js';
+import { addNodeClass } from '../core/Node.js';
+import { addNodeElement, nodeProxy } from '../shadernode/ShaderNode.js';
+
+class TextureSizeNode extends Node {
+
+	constructor( textureNode, levelNode = null ) {
+
+		super( 'uvec2' );
+
+		this.isTextureSizeNode = true;
+
+		this.textureNode = textureNode;
+		this.levelNode = levelNode;
+
+	}
+
+	generate( builder, output ) {
+
+		const textureProperty = this.textureNode.build( builder, 'property' );
+		const levelNode = this.levelNode.build( builder, 'int' );
+
+		return builder.format( `textureDimensions( ${textureProperty}, ${levelNode} )`, this.getNodeType( builder ), output );
+
+	}
+
+}
+
+export default TextureSizeNode;
+
+export const textureSize = nodeProxy( TextureSizeNode );
+
+addNodeElement( 'textureSize', textureSize );
+
+addNodeClass( TextureSizeNode );

+ 69 - 0
examples/jsm/nodes/display/ViewportDepthNode.js

@@ -0,0 +1,69 @@
+import Node, { addNodeClass } from '../core/Node.js';
+import { nodeImmutable, nodeProxy } from '../shadernode/ShaderNode.js';
+import { cameraNear, cameraFar } from '../accessors/CameraNode.js';
+import { positionView } from '../accessors/PositionNode.js';
+import { viewportDepthTexture } from './ViewportDepthTextureNode.js';
+
+class ViewportDepthNode extends Node {
+
+	constructor( scope, textureNode = null ) {
+
+		super( 'float' );
+
+		this.scope = scope;
+		this.textureNode = textureNode;
+
+		this.isViewportDepthNode = true;
+
+	}
+
+	construct( /*builder*/ ) {
+
+		const { scope } = this;
+
+		let node = null;
+
+		if ( scope === ViewportDepthNode.DEPTH ) {
+
+			node = viewZToOrthographicDepth( positionView.z, cameraNear, cameraFar );
+
+		} else if ( scope === ViewportDepthNode.DEPTH_TEXTURE ) {
+
+			const texture = this.textureNode || viewportDepthTexture();
+
+			const viewZ = perspectiveDepthToViewZ( texture, cameraNear, cameraFar );
+			node = viewZToOrthographicDepth( viewZ, cameraNear, cameraFar );
+
+		}
+
+		return node;
+
+	}
+
+}
+
+// NOTE: viewZ, the z-coordinate in camera space, is negative for points in front of the camera
+
+// -near maps to 0; -far maps to 1
+export const viewZToOrthographicDepth = ( viewZ, near, far ) => viewZ.add( near ).div( near.sub( far ) );
+
+// maps orthographic depth in [ 0, 1 ] to viewZ
+export const orthographicDepthToViewZ = ( depth, near, far ) => near.sub( far ).mul( depth ).sub( near );
+
+// NOTE: https://twitter.com/gonnavis/status/1377183786949959682
+
+// -near maps to 0; -far maps to 1
+export const viewZToPerspectiveDepth = ( viewZ, near, far ) => near.add( viewZ ).mul( far ).div( near.sub( far ).mul( viewZ ) );
+
+// maps perspective depth in [ 0, 1 ] to viewZ
+export const perspectiveDepthToViewZ = ( depth, near, far ) => near.mul( far ).div( far.sub( near ).mul( depth ).sub( far ) );
+
+ViewportDepthNode.DEPTH = 'depth';
+ViewportDepthNode.DEPTH_TEXTURE = 'depthTexture';
+
+export default ViewportDepthNode;
+
+export const depth = nodeImmutable( ViewportDepthNode, ViewportDepthNode.DEPTH );
+export const depthTexture = nodeProxy( ViewportDepthNode, ViewportDepthNode.DEPTH_TEXTURE );
+
+addNodeClass( ViewportDepthNode );

+ 34 - 0
examples/jsm/nodes/display/ViewportDepthTextureNode.js

@@ -0,0 +1,34 @@
+import ViewportTextureNode from './ViewportTextureNode.js';
+import { addNodeClass } from '../core/Node.js';
+import { addNodeElement, nodeProxy } from '../shadernode/ShaderNode.js';
+import { viewportTopLeft } from './ViewportNode.js';
+import { DepthTexture, LinearMipmapLinearFilter, DepthFormat, UnsignedIntType } from 'three';
+
+let sharedDepthbuffer = null;
+
+class ViewportDepthTextureNode extends ViewportTextureNode {
+
+	constructor( uvNode = viewportTopLeft, levelNode = null ) {
+
+		if ( sharedDepthbuffer === null ) {
+
+			sharedDepthbuffer = new DepthTexture();
+			sharedDepthbuffer.minFilter = LinearMipmapLinearFilter;
+			sharedDepthbuffer.type = UnsignedIntType;
+			sharedDepthbuffer.format = DepthFormat;
+
+		}
+
+		super( uvNode, levelNode, sharedDepthbuffer );
+
+	}
+
+}
+
+export default ViewportDepthTextureNode;
+
+export const viewportDepthTexture = nodeProxy( ViewportDepthTextureNode );
+
+addNodeElement( 'viewportDepthTexture', viewportDepthTexture );
+
+addNodeClass( ViewportDepthTextureNode );

+ 6 - 5
examples/jsm/nodes/display/ViewportSharedTextureNode.js

@@ -2,20 +2,21 @@ import ViewportTextureNode from './ViewportTextureNode.js';
 import { addNodeClass } from '../core/Node.js';
 import { addNodeClass } from '../core/Node.js';
 import { addNodeElement, nodeProxy } from '../shadernode/ShaderNode.js';
 import { addNodeElement, nodeProxy } from '../shadernode/ShaderNode.js';
 import { viewportTopLeft } from './ViewportNode.js';
 import { viewportTopLeft } from './ViewportNode.js';
+import { FramebufferTexture } from 'three';
 
 
 let sharedFramebuffer = null;
 let sharedFramebuffer = null;
 
 
 class ViewportSharedTextureNode extends ViewportTextureNode {
 class ViewportSharedTextureNode extends ViewportTextureNode {
 
 
-	constructor( uv = viewportTopLeft ) {
+	constructor( uvNode = viewportTopLeft, levelNode = null ) {
 
 
-		super( uv );
+		if ( sharedFramebuffer === null ) {
 
 
-	}
+			sharedFramebuffer = new FramebufferTexture();
 
 
-	constructFramebuffer( builder ) {
+		}
 
 
-		return sharedFramebuffer || ( sharedFramebuffer = super.constructFramebuffer( builder ) );
+		super( uvNode, levelNode, sharedFramebuffer );
 
 
 	}
 	}
 
 

+ 23 - 15
examples/jsm/nodes/display/ViewportTextureNode.js

@@ -3,33 +3,28 @@ import { NodeUpdateType } from '../core/constants.js';
 import { addNodeClass } from '../core/Node.js';
 import { addNodeClass } from '../core/Node.js';
 import { addNodeElement, nodeProxy } from '../shadernode/ShaderNode.js';
 import { addNodeElement, nodeProxy } from '../shadernode/ShaderNode.js';
 import { viewportTopLeft } from './ViewportNode.js';
 import { viewportTopLeft } from './ViewportNode.js';
-import { Vector2, FramebufferTexture } from 'three';
+import { Vector2, FramebufferTexture, LinearMipmapLinearFilter } from 'three';
 
 
 const _size = new Vector2();
 const _size = new Vector2();
 
 
 class ViewportTextureNode extends TextureNode {
 class ViewportTextureNode extends TextureNode {
 
 
-	constructor( uv = viewportTopLeft, level = null ) {
+	constructor( uvNode = viewportTopLeft, levelNode = null, framebufferTexture = null ) {
 
 
-		super( null, uv, level );
+		if ( framebufferTexture === null ) {
 
 
-		this.isOutputTextureNode = true;
-
-		this.updateBeforeType = NodeUpdateType.FRAME;
-
-	}
-
-	constructFramebuffer( /*builder*/ ) {
+			framebufferTexture = new FramebufferTexture();
+			framebufferTexture.minFilter = LinearMipmapLinearFilter;
 
 
-		return new FramebufferTexture();
+		}
 
 
-	}
+		super( framebufferTexture, uvNode, levelNode );
 
 
-	construct( builder ) {
+		this.generateMipmaps = false;
 
 
-		if ( this.value === null ) this.value = this.constructFramebuffer( builder );
+		this.isOutputTextureNode = true;
 
 
-		return super.construct( builder );
+		this.updateBeforeType = NodeUpdateType.FRAME;
 
 
 	}
 	}
 
 
@@ -52,8 +47,19 @@ class ViewportTextureNode extends TextureNode {
 
 
 		//
 		//
 
 
+		const currentGenerateMipmaps = framebufferTexture.generateMipmaps;
+		framebufferTexture.generateMipmaps = this.generateMipmaps;
+
 		renderer.copyFramebufferToTexture( framebufferTexture );
 		renderer.copyFramebufferToTexture( framebufferTexture );
 
 
+		framebufferTexture.generateMipmaps = currentGenerateMipmaps;
+
+	}
+
+	clone() {
+
+		return new this.constructor( this.uvNode, this.levelNode, this.value );
+
 	}
 	}
 
 
 }
 }
@@ -61,7 +67,9 @@ class ViewportTextureNode extends TextureNode {
 export default ViewportTextureNode;
 export default ViewportTextureNode;
 
 
 export const viewportTexture = nodeProxy( ViewportTextureNode );
 export const viewportTexture = nodeProxy( ViewportTextureNode );
+export const viewportMipTexture = nodeProxy( ViewportTextureNode, null, null, { generateMipmaps: true } );
 
 
 addNodeElement( 'viewportTexture', viewportTexture );
 addNodeElement( 'viewportTexture', viewportTexture );
+addNodeElement( 'viewportMipTexture', viewportMipTexture );
 
 
 addNodeClass( ViewportTextureNode );
 addNodeClass( ViewportTextureNode );

+ 6 - 0
examples/jsm/renderers/common/Renderer.js

@@ -42,6 +42,9 @@ class Renderer {
 
 
 		this.sortObjects = true;
 		this.sortObjects = true;
 
 
+		this.depth = true;
+		this.stencil = true;
+
 		// internals
 		// internals
 
 
 		this._pixelRatio = 1;
 		this._pixelRatio = 1;
@@ -239,6 +242,9 @@ class Renderer {
 		renderContext.scissorValue.copy( scissor ).multiplyScalar( pixelRatio ).floor();
 		renderContext.scissorValue.copy( scissor ).multiplyScalar( pixelRatio ).floor();
 		renderContext.scissor = this._scissorTest && renderContext.scissorValue.equals( _screen ) === false;
 		renderContext.scissor = this._scissorTest && renderContext.scissorValue.equals( _screen ) === false;
 
 
+		renderContext.depth = this.depth;
+		renderContext.stencil = this.stencil;
+
 		//
 		//
 
 
 		_projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse );
 		_projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse );

+ 85 - 32
examples/jsm/renderers/webgpu/WebGPUBackend.js

@@ -7,7 +7,7 @@ import { GPUFeatureName, GPUTextureFormat, GPULoadOp, GPUStoreOp, GPUIndexFormat
 import WebGPUNodeBuilder from './nodes/WGSLNodeBuilder.js';
 import WebGPUNodeBuilder from './nodes/WGSLNodeBuilder.js';
 import Backend from '../common/Backend.js';
 import Backend from '../common/Backend.js';
 
 
-import { DepthFormat, WebGPUCoordinateSystem } from 'three';
+import { DepthTexture, DepthFormat, DepthStencilFormat, UnsignedInt248Type, UnsignedIntType, WebGPUCoordinateSystem } from 'three';
 
 
 import WebGPUUtils from './utils/WebGPUUtils.js';
 import WebGPUUtils from './utils/WebGPUUtils.js';
 import WebGPUAttributeUtils from './utils/WebGPUAttributeUtils.js';
 import WebGPUAttributeUtils from './utils/WebGPUAttributeUtils.js';
@@ -53,7 +53,8 @@ class WebGPUBackend extends Backend {
 		this.device = null;
 		this.device = null;
 		this.context = null;
 		this.context = null;
 		this.colorBuffer = null;
 		this.colorBuffer = null;
-		this.depthBuffer = null;
+
+		this.depthBuffers = new WeakMap();
 
 
 		this.utils = new WebGPUUtils( this );
 		this.utils = new WebGPUUtils( this );
 		this.attributeUtils = new WebGPUAttributeUtils( this );
 		this.attributeUtils = new WebGPUAttributeUtils( this );
@@ -184,7 +185,7 @@ class WebGPUBackend extends Backend {
 
 
 			}
 			}
 
 
-			depthStencilAttachment.view = this.depthBuffer.createView();
+			depthStencilAttachment.view = this._getDepthBufferGPU( renderContext ).createView();
 
 
 		}
 		}
 
 
@@ -321,7 +322,7 @@ class WebGPUBackend extends Backend {
 
 
 		}
 		}
 
 
-		descriptor.depthStencilAttachment.view = this.depthBuffer.createView();
+		descriptor.depthStencilAttachment.view = this._getDepthBufferGPU( renderContext ).createView();
 
 
 		if ( color ) {
 		if ( color ) {
 
 
@@ -670,7 +671,6 @@ class WebGPUBackend extends Backend {
 
 
 		this._configureContext();
 		this._configureContext();
 		this._setupColorBuffer();
 		this._setupColorBuffer();
-		this._setupDepthBuffer();
 
 
 	}
 	}
 
 
@@ -696,30 +696,44 @@ class WebGPUBackend extends Backend {
 
 
 	}
 	}
 
 
-	copyFramebufferToTexture( framebufferTexture, renderContext ) {
+	copyFramebufferToTexture( texture, renderContext ) {
 
 
 		const renderContextData = this.get( renderContext );
 		const renderContextData = this.get( renderContext );
 
 
 		const { encoder, descriptor } = renderContextData;
 		const { encoder, descriptor } = renderContextData;
 
 
-		const sourceGPU = this.context.getCurrentTexture();
-		const destinationGPU = this.get( framebufferTexture ).texture;
+		let sourceGPU = null;
+
+		if ( texture.isFramebufferTexture ) {
+
+			sourceGPU = this.context.getCurrentTexture();
+
+		} else if ( texture.isDepthTexture ) {
+
+			sourceGPU = this._getDepthBufferGPU( renderContext );
+
+		}
+
+		const destinationGPU = this.get( texture ).texture;
 
 
 		renderContextData.currentPass.end();
 		renderContextData.currentPass.end();
 
 
 		encoder.copyTextureToTexture(
 		encoder.copyTextureToTexture(
 			{
 			{
-			  texture: sourceGPU
+				texture: sourceGPU,
+				origin: { x: 0, y: 0, z: 0 }
 			},
 			},
 			{
 			{
-			  texture: destinationGPU
+				texture: destinationGPU
 			},
 			},
 			[
 			[
-				framebufferTexture.image.width,
-				framebufferTexture.image.height
+				texture.image.width,
+				texture.image.height
 			]
 			]
 		);
 		);
 
 
+		if ( texture.generateMipmaps ) this.textureUtils.generateMipmaps( texture );
+
 		descriptor.colorAttachments[ 0 ].loadOp = GPULoadOp.Load;
 		descriptor.colorAttachments[ 0 ].loadOp = GPULoadOp.Load;
 		if ( renderContext.depth ) descriptor.depthStencilAttachment.depthLoadOp = GPULoadOp.Load;
 		if ( renderContext.depth ) descriptor.depthStencilAttachment.depthLoadOp = GPULoadOp.Load;
 		if ( renderContext.stencil ) descriptor.depthStencilAttachment.stencilLoadOp = GPULoadOp.Load;
 		if ( renderContext.stencil ) descriptor.depthStencilAttachment.stencilLoadOp = GPULoadOp.Load;
@@ -731,6 +745,65 @@ class WebGPUBackend extends Backend {
 
 
 	// utils
 	// utils
 
 
+	_getDepthBufferGPU( renderContext ) {
+
+		const { depthBuffers } = this;
+		const { width, height } = this.getDrawingBufferSize();
+
+		let depthTexture = depthBuffers.get( renderContext );
+
+		if ( depthTexture !== undefined && depthTexture.image.width === width && depthTexture.image.height === height ) {
+
+			return this.get( depthTexture ).texture;
+
+		}
+
+		this._destroyDepthBufferGPU( renderContext );
+
+		depthTexture = new DepthTexture();
+		depthTexture.name = 'depthBuffer';
+
+		if ( renderContext.stencil  ) {
+
+			depthTexture = new DepthTexture();
+			depthTexture.format = DepthStencilFormat;
+			depthTexture.type = UnsignedInt248Type;
+
+		} else if ( renderContext.depth ) {
+
+			depthTexture = new DepthTexture();
+			depthTexture.format = DepthFormat;
+			depthTexture.type = UnsignedIntType;
+
+		}
+
+		depthTexture.image.width = width;
+		depthTexture.image.height = height;
+
+		this.textureUtils.createTexture( depthTexture, { sampleCount: this.parameters.sampleCount } );
+
+		depthBuffers.set( renderContext, depthTexture );
+
+		return this.get( depthTexture ).texture;
+
+	}
+
+	_destroyDepthBufferGPU( renderContext ) {
+
+		const { depthBuffers } = this;
+
+		const depthTexture = depthBuffers.get( renderContext );
+
+		if ( depthTexture !== undefined ) {
+
+			this.textureUtils.destroyTexture( depthTexture );
+
+			depthBuffers.delete( renderContext );
+
+		}
+
+	}
+
 	_configureContext() {
 	_configureContext() {
 
 
 		this.context.configure( {
 		this.context.configure( {
@@ -763,26 +836,6 @@ class WebGPUBackend extends Backend {
 
 
 	}
 	}
 
 
-	_setupDepthBuffer() {
-
-		if ( this.depthBuffer ) this.depthBuffer.destroy();
-
-		const { width, height } = this.getDrawingBufferSize();
-
-		this.depthBuffer = this.device.createTexture( {
-			label: 'depthBuffer',
-			size: {
-				width: width,
-				height: height,
-				depthOrArrayLayers: 1
-			},
-			sampleCount: this.parameters.sampleCount,
-			format: GPUTextureFormat.Depth24PlusStencil8,
-			usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC
-		} );
-
-	}
-
 }
 }
 
 
 export default WebGPUBackend;
 export default WebGPUBackend;

+ 3 - 3
examples/jsm/renderers/webgpu/utils/WebGPUTextureUtils.js

@@ -86,7 +86,7 @@ class WebGPUTextureUtils {
 
 
 	}
 	}
 
 
-	createTexture( texture ) {
+	createTexture( texture, options = {} ) {
 
 
 		const backend = this.backend;
 		const backend = this.backend;
 		const textureData = backend.get( texture );
 		const textureData = backend.get( texture );
@@ -104,9 +104,9 @@ class WebGPUTextureUtils {
 		const mipLevelCount = this._getMipLevelCount( texture, width, height, needsMipmaps );
 		const mipLevelCount = this._getMipLevelCount( texture, width, height, needsMipmaps );
 		const format = texture.internalFormat || this._getFormat( texture );
 		const format = texture.internalFormat || this._getFormat( texture );
 		//const sampleCount = texture.isRenderTargetTexture || texture.isDepthTexture ? backend.utils.getSampleCount( renderContext ) : 1;
 		//const sampleCount = texture.isRenderTargetTexture || texture.isDepthTexture ? backend.utils.getSampleCount( renderContext ) : 1;
-		const sampleCount = 1;
+		const sampleCount = options.sampleCount !== undefined ? options.sampleCount : 1;
 
 
-		let usage = GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST;
+		let usage = GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.COPY_SRC;
 
 
 		if ( texture.isCompressedTexture !== true ) {
 		if ( texture.isCompressedTexture !== true ) {
 
 

+ 5 - 1
examples/jsm/renderers/webgpu/utils/WebGPUUtils.js

@@ -16,10 +16,14 @@ class WebGPUUtils {
 
 
 			format = this.getTextureFormatGPU( renderContext.depthTexture );
 			format = this.getTextureFormatGPU( renderContext.depthTexture );
 
 
-		} else {
+		} else if ( renderContext.depth && renderContext.stencil ) {
 
 
 			format = GPUTextureFormat.Depth24PlusStencil8;
 			format = GPUTextureFormat.Depth24PlusStencil8;
 
 
+		} else if ( renderContext.depth ) {
+
+			format = GPUTextureFormat.Depth24Plus;
+
 		}
 		}
 
 
 		return format;
 		return format;

BIN
examples/screenshots/webgpu_backdrop_area.jpg


+ 181 - 0
examples/webgpu_backdrop_area.html

@@ -0,0 +1,181 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js - WebGPU - Backdrop Area</title>
+		<meta charset="utf-8">
+		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+		<link type="text/css" rel="stylesheet" href="main.css">
+	</head>
+	<body>
+
+		<div id="info">
+			<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> WebGPU - Backdrop Area
+		</div>
+
+		<script async src="https://unpkg.com/[email protected]/dist/es-module-shims.js"></script>
+
+		<script type="importmap">
+			{
+				"imports": {
+					"three": "../build/three.module.js",
+					"three/addons/": "./jsm/",
+					"three/nodes": "./jsm/nodes/Nodes.js"
+				}
+			}
+		</script>
+
+		<script type="module">
+
+			import * as THREE from 'three';
+			import { color, depth, depthTexture, toneMapping, viewportSharedTexture, viewportMipTexture, viewportTopLeft, checker, uv, modelScale, MeshBasicNodeMaterial } from 'three/nodes';
+ 
+			import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
+
+			import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
+
+			import WebGPU from 'three/addons/capabilities/WebGPU.js';
+			import WebGPURenderer from 'three/addons/renderers/webgpu/WebGPURenderer.js';
+
+			import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
+
+			let camera, scene, renderer;
+			let mixer, clock;
+
+			init();
+
+			function init() {
+
+				if ( WebGPU.isAvailable() === false ) {
+
+					document.body.appendChild( WebGPU.getErrorMessage() );
+
+					throw new Error( 'No WebGPU support' );
+
+				}
+
+				camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 0.25, 25 );
+				camera.position.set( 3, 2, 3 );
+
+				scene = new THREE.Scene();
+				scene.background = new THREE.Color( 0x333333 );
+				camera.lookAt( 0, 1, 0 );
+
+				clock = new THREE.Clock();
+
+				// model
+
+				const loader = new GLTFLoader();
+				loader.load( 'models/gltf/Michelle.glb', function ( gltf ) {
+
+					const object = gltf.scene;
+					mixer = new THREE.AnimationMixer( object );
+
+					const action = mixer.clipAction( gltf.animations[ 0 ] );
+					action.play();
+
+					scene.add( object );
+
+				} );
+
+				// volume
+
+				const depthAlphaNode = depthTexture().distance( depth ).oneMinus().smoothstep( .90, 2 ).mul( 20 ).saturate();
+
+				const volumeMaterial = new MeshBasicNodeMaterial();
+				volumeMaterial.colorNode = color( 0x0066ff );
+				volumeMaterial.backdropNode = viewportSharedTexture();
+				volumeMaterial.backdropAlphaNode = depthAlphaNode;
+				volumeMaterial.transparent = true;
+
+				const depthMaterial = new MeshBasicNodeMaterial();
+				depthMaterial.backdropNode = depthAlphaNode;
+				depthMaterial.transparent = true;
+
+				const bicubicMaterial = new MeshBasicNodeMaterial();
+				bicubicMaterial.backdropNode = viewportMipTexture().bicubic( 5 ); // @TODO: Move to alpha value [ 0, 1 ]
+				bicubicMaterial.backdropAlphaNode = checker( uv().mul( 3 ).mul( modelScale.xy ) );
+				bicubicMaterial.opacityNode = bicubicMaterial.backdropAlphaNode;
+				bicubicMaterial.transparent = true;
+
+				const pixelMaterial = new MeshBasicNodeMaterial();
+				pixelMaterial.backdropNode = viewportSharedTexture( viewportTopLeft.mul( 100 ).floor().div( 100 ) ); // @TODO: Move to alpha value [ 0, 1 ]
+				pixelMaterial.transparent = true;
+
+				// box / floor
+
+				const box = new THREE.Mesh( new THREE.BoxGeometry( 2, 2, 2 ), volumeMaterial );
+				box.position.set( 0, 1, 0 );
+				//box.material.side = THREE.DoubleSide; // @TODO: Needed add support to material.forceSinglePass = false;
+				scene.add( box );
+
+				const boxBack = new THREE.Mesh( new THREE.BoxGeometry( 2, 2, 2 ).scale( - 1, - 1, - 1 ), volumeMaterial );
+				boxBack.position.set( 0, 1, 0 );
+				boxBack.renderOrder = - 1;
+				boxBack.visible = false;
+				scene.add( boxBack );
+
+				const floor = new THREE.Mesh( new THREE.BoxGeometry( 1.99, .01, 1.99 ), new MeshBasicNodeMaterial( { color: 0x333333 } ) );
+				floor.position.set( 0, 0, 0 );
+				scene.add( floor );
+
+				// renderer
+
+				renderer = new WebGPURenderer();
+				renderer.stencil = false;
+				renderer.setPixelRatio( window.devicePixelRatio );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				renderer.setAnimationLoop( animate );
+				renderer.toneMappingNode = toneMapping( THREE.LinearToneMapping, .15 );
+				document.body.appendChild( renderer.domElement );
+
+				const controls = new OrbitControls( camera, renderer.domElement );
+				controls.target.set( 0, 1, 0 );
+				controls.update();
+
+				window.addEventListener( 'resize', onWindowResize );
+
+				// gui
+
+				const materials = {
+					'volume': volumeMaterial,
+					'depth': depthMaterial,
+					'bicubic': bicubicMaterial,
+					'pixel': pixelMaterial
+				};
+
+				const gui = new GUI();
+				const options = { material: 'volume' };
+
+				gui.add( box.scale, 'x', 0.1, 2, 0.01 ).onChange( () => boxBack.scale.copy( box.scale ) );
+				gui.add( box.scale, 'z', 0.1, 2, 0.01 ).onChange( () => boxBack.scale.copy( box.scale ) );
+				gui.add( options, 'material', Object.keys( materials ) ).onChange( name => {
+
+					box.material = boxBack.material = materials[ name ];
+					boxBack.visible = ( name === 'bicubic' );
+
+				} );
+
+			}
+
+			function onWindowResize() {
+
+				camera.aspect = window.innerWidth / window.innerHeight;
+				camera.updateProjectionMatrix();
+
+				renderer.setSize( window.innerWidth, window.innerHeight );
+
+			}
+
+			function animate() {
+
+				const delta = clock.getDelta();
+
+				if ( mixer ) mixer.update( delta );
+
+				renderer.render( scene, camera );
+
+			}
+
+		</script>
+	</body>
+</html>

+ 1 - 0
test/e2e/puppeteer.js

@@ -94,6 +94,7 @@ const exceptionList = [
 	// Awaiting for WebGPU support
 	// Awaiting for WebGPU support
 	'webgpu_audio_processing',
 	'webgpu_audio_processing',
 	'webgpu_backdrop',
 	'webgpu_backdrop',
+	'webgpu_backdrop_area',
 	'webgpu_compute',
 	'webgpu_compute',
 	'webgpu_cubemap_adjustments',
 	'webgpu_cubemap_adjustments',
 	'webgpu_cubemap_dynamic',
 	'webgpu_cubemap_dynamic',