Sfoglia il codice sorgente

WebGPURenderer: Backdrop Node - Part 1/3 (#25903)

* WebGPURenderer: Fix re-configure context

* WebGPURenderer: Improve state in favor of access WebGPU encoders

* NodeMaterial: Reduce duplicate initialization of properties

* WebGPUTextures: Support RenderTargetTexture mipmaps if necessary.

* WebGPUBackground: Move to renderState

* WebGPUNodes: .updateBefore() fixes

* WebGPURenderer: Added .copyFramebufferToRenderTarget()

* NodeMaterial: Added backdropNode

* Added webgpu_backdrop example

* update title

* cleanup

* rotate just on idle

* WebGPURenderList: Update include
sunag 2 anni fa
parent
commit
2d1621f76a

+ 1 - 0
examples/files.json

@@ -327,6 +327,7 @@
 	],
 	"webgpu": [
 		"webgpu_audio_processing",
+		"webgpu_backdrop",
 		"webgpu_compute",
 		"webgpu_cubemap_adjustments",
 		"webgpu_cubemap_mix",

+ 2 - 0
examples/jsm/nodes/Nodes.js

@@ -93,6 +93,8 @@ export { default as NormalMapNode, normalMap, TBNViewMatrix } from './display/No
 export { default as PosterizeNode, posterize } from './display/PosterizeNode.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 ViewportTextureNode, viewportTexture } from './display/ViewportTextureNode.js';
+export { default as ViewportSharedTextureNode, viewportSharedTexture } from './display/ViewportSharedTextureNode.js';
 
 // code
 export { default as ExpressionNode, expression } from './code/ExpressionNode.js';

+ 8 - 0
examples/jsm/nodes/core/NodeBuilder.js

@@ -39,6 +39,7 @@ class NodeBuilder {
 
 		this.nodes = [];
 		this.updateNodes = [];
+		this.updateBeforeNodes = [];
 		this.hashNodes = {};
 
 		this.lightsNode = null;
@@ -89,6 +90,7 @@ class NodeBuilder {
 		if ( this.nodes.indexOf( node ) === - 1 ) {
 
 			const updateType = node.getUpdateType();
+			const updateBeforeType = node.getUpdateBeforeType();
 
 			if ( updateType !== NodeUpdateType.NONE ) {
 
@@ -96,6 +98,12 @@ class NodeBuilder {
 
 			}
 
+			if ( updateBeforeType !== NodeUpdateType.NONE ) {
+
+				this.updateBeforeNodes.push( node );
+
+			}
+
 			this.nodes.push( node );
 
 			this.setHashNode( node, node.getHash( this ) );

+ 1 - 1
examples/jsm/nodes/display/ViewportNode.js

@@ -43,7 +43,7 @@ class ViewportNode extends Node {
 
 	update( { renderer } ) {
 
-		renderer.getSize( resolution );
+		renderer.getDrawingBufferSize( resolution );
 
 	}
 

+ 30 - 0
examples/jsm/nodes/display/ViewportSharedTextureNode.js

@@ -0,0 +1,30 @@
+import ViewportTextureNode from './ViewportTextureNode.js';
+import { addNodeClass } from '../core/Node.js';
+import { addNodeElement, nodeProxy } from '../shadernode/ShaderNode.js';
+import { viewportTopLeft } from './ViewportNode.js';
+
+let rtt = null;
+
+class ViewportSharedTextureNode extends ViewportTextureNode {
+
+	constructor( uv = viewportTopLeft ) {
+
+		super( uv );
+
+	}
+
+	constructRTT( builder ) {
+
+		return rtt || ( rtt = builder.getRenderTarget() );
+
+	}
+
+}
+
+export default ViewportSharedTextureNode;
+
+export const viewportSharedTexture = nodeProxy( ViewportSharedTextureNode );
+
+addNodeElement( 'viewportSharedTexture', viewportSharedTexture );
+
+addNodeClass( ViewportSharedTextureNode );

+ 61 - 0
examples/jsm/nodes/display/ViewportTextureNode.js

@@ -0,0 +1,61 @@
+import TextureNode from '../accessors/TextureNode.js';
+import { NodeUpdateType } from '../core/constants.js';
+import { addNodeClass } from '../core/Node.js';
+import { addNodeElement, nodeProxy } from '../shadernode/ShaderNode.js';
+import { viewportTopLeft } from './ViewportNode.js';
+import { Vector2 } from 'three';
+
+let size = new Vector2();
+
+class ViewportTextureNode extends TextureNode {
+
+	constructor( uv = viewportTopLeft, level = null ) {
+
+		super( null, uv, level );
+
+		this.rtt = null;
+
+		this.isOutputTextureNode = true;
+
+		this.updateBeforeType = NodeUpdateType.FRAME;
+
+	}
+
+	constructRTT( builder ) {
+
+		return builder.getRenderTarget();
+
+	}
+
+	construct( builder ) {
+
+		if ( this.rtt === null ) this.rtt = this.constructRTT( builder );
+
+		this.value = this.rtt.texture;
+
+		return super.construct( builder );
+
+	}
+
+	updateBefore( frame ) {
+
+		const rtt = this.rtt;
+
+		const renderer = frame.renderer;
+		renderer.getDrawingBufferSize( size );
+
+		rtt.setSize( size.width, size.height );
+
+		renderer.copyFramebufferToRenderTarget( rtt );
+
+	}
+
+}
+
+export default ViewportTextureNode;
+
+export const viewportTexture = nodeProxy( ViewportTextureNode );
+
+addNodeElement( 'viewportTexture', viewportTexture );
+
+addNodeClass( ViewportTextureNode );

+ 17 - 4
examples/jsm/nodes/lighting/LightingContextNode.js

@@ -1,16 +1,19 @@
 import ContextNode from '../core/ContextNode.js';
 import { temp } from '../core/VarNode.js';
 import { add } from '../math/OperatorNode.js';
+import { mix } from '../math/MathNode.js';
 import { addNodeClass } from '../core/Node.js';
 import { addNodeElement, nodeProxy, float, vec3 } from '../shadernode/ShaderNode.js';
 
 class LightingContextNode extends ContextNode {
 
-	constructor( node, lightingModelNode = null ) {
+	constructor( node, lightingModelNode = null, backdropNode = null, backdropAlphaNode = null ) {
 
 		super( node );
 
 		this.lightingModelNode = lightingModelNode;
+		this.backdropNode = backdropNode;
+		this.backdropAlphaNode = backdropAlphaNode;
 
 	}
 
@@ -22,7 +25,7 @@ class LightingContextNode extends ContextNode {
 
 	construct( builder ) {
 
-		const { lightingModelNode } = this;
+		const { lightingModelNode, backdropNode, backdropAlphaNode } = this;
 
 		const context = this.context = {}; // reset context
 		const properties = builder.getNodeProperties( this );
@@ -30,8 +33,18 @@ class LightingContextNode extends ContextNode {
 		const directDiffuse = temp( vec3() ),
 			directSpecular = temp( vec3() ),
 			indirectDiffuse = temp( vec3() ),
-			indirectSpecular = temp( vec3() ),
-			total = add( directDiffuse, directSpecular, indirectDiffuse, indirectSpecular );
+			indirectSpecular = temp( vec3() );
+
+		let totalDiffuse = add( directDiffuse, indirectDiffuse );
+
+		if ( backdropNode !== null ) {
+
+			totalDiffuse = vec3( backdropAlphaNode !== null ? mix( totalDiffuse, backdropNode, backdropAlphaNode ) : backdropNode );
+
+		}
+
+		const totalSpecular = add( directSpecular, indirectSpecular );
+		const total = add( totalDiffuse, totalSpecular );
 
 		const reflectedLight = {
 			directDiffuse,

+ 0 - 9
examples/jsm/nodes/materials/LineBasicNodeMaterial.js

@@ -15,15 +15,6 @@ class LineBasicNodeMaterial extends NodeMaterial {
 		this.lights = false;
 		this.normals = false;
 
-		this.colorNode = null;
-		this.opacityNode = null;
-
-		this.alphaTestNode = null;
-
-		this.lightNode = null;
-
-		this.positionNode = null;
-
 		this.setDefaultValues( defaultValues );
 
 		this.setValues( parameters );

+ 0 - 9
examples/jsm/nodes/materials/MeshBasicNodeMaterial.js

@@ -14,15 +14,6 @@ class MeshBasicNodeMaterial extends NodeMaterial {
 
 		this.lights = false;
 
-		this.colorNode = null;
-		this.opacityNode = null;
-
-		this.alphaTestNode = null;
-
-		this.lightNode = null;
-
-		this.positionNode = null;
-
 		this.setDefaultValues( defaultValues );
 
 		this.setValues( parameters );

+ 0 - 4
examples/jsm/nodes/materials/MeshNormalNodeMaterial.js

@@ -17,10 +17,6 @@ class MeshNormalNodeMaterial extends NodeMaterial {
 
 		this.isMeshNormalNodeMaterial = true;
 
-		this.opacityNode = null;
-
-		this.positionNode = null;
-
 		this.setDefaultValues( defaultValues );
 
 		this.setValues( parameters );

+ 0 - 9
examples/jsm/nodes/materials/MeshPhongNodeMaterial.js

@@ -18,18 +18,9 @@ class MeshPhongNodeMaterial extends NodeMaterial {
 
 		this.lights = true;
 
-		this.colorNode = null;
-		this.opacityNode = null;
-
 		this.shininessNode = null;
 		this.specularNode = null;
 
-		this.alphaTestNode = null;
-
-		this.lightNode = null;
-
-		this.positionNode = null;
-
 		this.setDefaultValues( defaultValues );
 
 		this.setValues( parameters );

+ 0 - 13
examples/jsm/nodes/materials/MeshStandardNodeMaterial.js

@@ -18,24 +18,11 @@ class MeshStandardNodeMaterial extends NodeMaterial {
 
 		this.isMeshStandardNodeMaterial = true;
 
-		this.colorNode = null;
-		this.opacityNode = null;
-
-		this.alphaTestNode = null;
-
-		this.normalNode = null;
-
 		this.emissiveNode = null;
 
 		this.metalnessNode = null;
 		this.roughnessNode = null;
 
-		this.envNode = null;
-
-		this.lightsNode = null;
-
-		this.positionNode = null;
-
 		this.setDefaultValues( defaultValues );
 
 		this.setValues( parameters );

+ 20 - 4
examples/jsm/nodes/materials/NodeMaterial.js

@@ -11,9 +11,10 @@ import { positionLocal } from '../accessors/PositionNode.js';
 import { skinning } from '../accessors/SkinningNode.js';
 import { texture } from '../accessors/TextureNode.js';
 import { lightsWithoutWrap } from '../lighting/LightsNode.js';
+import { mix } from '../math/MathNode.js';
+import { float, vec3, vec4 } from '../shadernode/ShaderNode.js';
 import AONode from '../lighting/AONode.js';
 import EnvironmentNode from '../lighting/EnvironmentNode.js';
-import { float, vec3, vec4 } from '../shadernode/ShaderNode.js';
 
 const NodeMaterials = new Map();
 
@@ -31,6 +32,16 @@ class NodeMaterial extends ShaderMaterial {
 		this.normals = true;
 
 		this.lightsNode = null;
+		this.envNode = null;
+
+		this.colorNode = null;
+		this.normalNode = null;
+		this.opacityNode = null;
+		this.backdropNode = null;
+		this.backdropAlphaNode = null;
+		this.alphaTestNode = null;
+
+		this.positionNode = null;
 
 	}
 
@@ -193,6 +204,7 @@ class NodeMaterial extends ShaderMaterial {
 	constructLighting( builder ) {
 
 		const { material } = builder;
+		const { backdropNode, backdropAlphaNode, emissiveNode } = this;
 
 		// OUTGOING LIGHT
 
@@ -205,15 +217,19 @@ class NodeMaterial extends ShaderMaterial {
 
 		if ( lightsNode && lightsNode.hasLight !== false ) {
 
-			outgoingLightNode = lightsNode.lightingContext( lightingModelNode );
+			outgoingLightNode = lightsNode.lightingContext( lightingModelNode, backdropNode, backdropAlphaNode );
+
+		} else if ( backdropNode !== null ) {
+
+			outgoingLightNode = vec3( backdropAlphaNode !== null ? mix( outgoingLightNode, backdropNode, backdropAlphaNode ) : backdropNode );
 
 		}
 
 		// EMISSIVE
 
-		if ( ( this.emissiveNode && this.emissiveNode.isNode === true ) || ( material.emissive && material.emissive.isColor === true ) ) {
+		if ( ( emissiveNode && emissiveNode.isNode === true ) || ( material.emissive && material.emissive.isColor === true ) ) {
 
-			outgoingLightNode = outgoingLightNode.add( this.emissiveNode ? vec3( this.emissiveNode ) : materialEmissive );
+			outgoingLightNode = outgoingLightNode.add( emissiveNode ? vec3( emissiveNode ) : materialEmissive );
 
 		}
 

+ 7 - 7
examples/jsm/renderers/webgpu/WebGPUBackground.js

@@ -25,7 +25,7 @@ class WebGPUBackground {
 
 	}
 
-	update( renderList, scene, renderPassDescriptor, renderAttachments ) {
+	update( scene, renderList, renderState ) {
 
 		const renderer = this.renderer;
 		const background = ( scene.isScene === true ) ? scene.backgroundNode || this.properties.get( scene ).backgroundNode || scene.background : null;
@@ -103,8 +103,8 @@ class WebGPUBackground {
 
 		// configure render pass descriptor
 
-		const colorAttachment = renderPassDescriptor.colorAttachments[ 0 ];
-		const depthStencilAttachment = renderPassDescriptor.depthStencilAttachment;
+		const colorAttachment = renderState.descriptorGPU.colorAttachments[ 0 ];
+		const depthStencilAttachment = renderState.descriptorGPU.depthStencilAttachment;
 
 		if ( renderer.autoClear === true || forceClear === true ) {
 
@@ -123,7 +123,7 @@ class WebGPUBackground {
 
 			}
 
-			if ( renderAttachments.depth ) {
+			if ( renderState.depth ) {
 
 				if ( renderer.autoClearDepth === true ) {
 
@@ -140,7 +140,7 @@ class WebGPUBackground {
 
 			}
 
-			if ( renderAttachments.stencil ) {
+			if ( renderState.stencil ) {
 
 				if ( renderer.autoClearStencil === true ) {
 
@@ -162,14 +162,14 @@ class WebGPUBackground {
 			colorAttachment.loadOp = GPULoadOp.Load;
 			colorAttachment.storeOp = GPUStoreOp.Store;
 
-			if ( renderAttachments.depth ) {
+			if ( renderState.depth ) {
 
 				depthStencilAttachment.depthLoadOp = GPULoadOp.Load;
 				depthStencilAttachment.depthStoreOp = GPUStoreOp.Store;
 
 			}
 
-			if ( renderAttachments.stencil ) {
+			if ( renderState.stencil ) {
 
 				depthStencilAttachment.stencilLoadOp = GPULoadOp.Load;
 				depthStencilAttachment.stencilStoreOp = GPUStoreOp.Store;

+ 23 - 0
examples/jsm/renderers/webgpu/WebGPURenderLists.js

@@ -1,4 +1,5 @@
 import WebGPUWeakMap from './WebGPUWeakMap.js';
+import { lights } from '../../nodes/Nodes.js';
 
 function painterSortStable( a, b ) {
 
@@ -58,6 +59,9 @@ class WebGPURenderList {
 		this.opaque = [];
 		this.transparent = [];
 
+		this.lightsNode = lights( [] );
+		this.lightsArray = [];
+
 	}
 
 	init() {
@@ -66,6 +70,9 @@ class WebGPURenderList {
 
 		this.opaque.length = 0;
 		this.transparent.length = 0;
+		this.lightsArray.length = 0;
+
+		return this;
 
 	}
 
@@ -123,6 +130,18 @@ class WebGPURenderList {
 
 	}
 
+	pushLight( light ) {
+
+		this.lightsArray.push( light );
+
+	}
+
+	getLightsNode() {
+
+		return this.lightsNode.fromLights( this.lightsArray );
+
+	}
+
 	sort( customOpaqueSort, customTransparentSort ) {
 
 		if ( this.opaque.length > 1 ) this.opaque.sort( customOpaqueSort || painterSortStable );
@@ -132,6 +151,10 @@ class WebGPURenderList {
 
 	finish() {
 
+		// update lights
+
+		this.lightsNode.fromLights( this.lightsArray );
+
 		// Clear references from inactive renderItems in the list
 
 		for ( let i = this.renderItemsIndex, il = this.renderItems.length; i < il; i ++ ) {

+ 6 - 20
examples/jsm/renderers/webgpu/WebGPURenderStates.js

@@ -1,31 +1,17 @@
 import WebGPUWeakMap from './WebGPUWeakMap.js';
-import { lights } from '../../nodes/Nodes.js';
 
 class WebGPURenderState {
 
 	constructor() {
 
-		this.lightsNode = lights( [] );
+		this.depth = true;
+		this.stencil = true;
 
-		this.lightsArray = [];
+		// defined by renderer(backend)
 
-	}
-
-	init() {
-
-		this.lightsArray.length = 0;
-
-	}
-
-	pushLight( light ) {
-
-		this.lightsArray.push( light );
-
-	}
-
-	getLightsNode() {
-
-		return this.lightsNode.fromLights( this.lightsArray );
+		this.descriptorGPU = null;
+		this.encoderGPU = null;
+		this.currentPassGPU = null;
 
 	}
 

+ 96 - 69
examples/jsm/renderers/webgpu/WebGPURenderer.js

@@ -1,4 +1,4 @@
-import { GPUIndexFormat, GPUTextureFormat, GPUFeatureName } from './constants.js';
+import { GPUIndexFormat, GPUTextureFormat, GPUFeatureName, GPULoadOp } from './constants.js';
 import WebGPUAnimation from './WebGPUAnimation.js';
 import WebGPURenderObjects from './WebGPURenderObjects.js';
 import WebGPUAttributes from './WebGPUAttributes.js';
@@ -14,7 +14,6 @@ import WebGPUTextures from './WebGPUTextures.js';
 import WebGPUBackground from './WebGPUBackground.js';
 import WebGPUNodes from './nodes/WebGPUNodes.js';
 import WebGPUUtils from './WebGPUUtils.js';
-
 import { Frustum, Matrix4, Vector3, Color, SRGBColorSpace, NoToneMapping, DepthFormat } from 'three';
 
 console.info( 'THREE.WebGPURenderer: Modified Matrix4.makePerspective() and Matrix4.makeOrtographic() to work with WebGPU, see https://github.com/mrdoob/three.js/issues/20276.' );
@@ -140,7 +139,6 @@ class WebGPURenderer {
 
 		this._currentRenderState = null;
 
-		this._currentRenderList = null;
 		this._opaqueSort = null;
 		this._transparentSort = null;
 
@@ -224,16 +222,12 @@ class WebGPURenderer {
 
 		const context = ( parameters.context !== undefined ) ? parameters.context : this.domElement.getContext( 'webgpu' );
 
-		context.configure( {
-			device,
-			format: GPUTextureFormat.BGRA8Unorm, // this is the only valid context format right now (r121)
-			alphaMode: 'premultiplied'
-		} );
-
 		this._adapter = adapter;
 		this._device = device;
 		this._context = context;
 
+		this._configureContext();
+
 		this._info = new WebGPUInfo();
 		this._properties = new WebGPUProperties();
 		this._attributes = new WebGPUAttributes( device );
@@ -265,13 +259,24 @@ class WebGPURenderer {
 
 		if ( this._initialized === false ) await this.init();
 
-		//
+		// preserve render tree
 
 		const nodeFrame = this._nodes.nodeFrame;
 
 		const previousRenderId = nodeFrame.renderId;
+		const previousRenderState = this._currentRenderState;
+
+		//
+
+		const renderState = this._renderStates.get( scene, camera );
+		const renderTarget = this._renderTarget;
+
+		this._currentRenderState = renderState;
+
 		nodeFrame.renderId ++;
 
+		//
+
 		if ( this._animation.isAnimating === false ) nodeFrame.update();
 
 		if ( scene.matrixWorldAutoUpdate === true ) scene.updateMatrixWorld();
@@ -283,25 +288,22 @@ class WebGPURenderer {
 		_projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse );
 		_frustum.setFromProjectionMatrix( _projScreenMatrix );
 
-		this._currentRenderList = this._renderLists.get( scene, camera );
-		this._currentRenderList.init();
-
-		this._currentRenderState = this._renderStates.get( scene, camera );
-		this._currentRenderState.init();
+		const renderList = this._renderLists.get( scene, camera );
+		renderList.init();
 
-		this._projectObject( scene, camera, 0 );
+		this._projectObject( scene, camera, 0, renderList );
 
-		this._currentRenderList.finish();
+		renderList.finish();
 
 		if ( this.sortObjects === true ) {
 
-			this._currentRenderList.sort( this._opaqueSort, this._transparentSort );
+			renderList.sort( this._opaqueSort, this._transparentSort );
 
 		}
 
 		// prepare render pass descriptor
 
-		const renderPassDescriptor = {
+		renderState.descriptorGPU = {
 			colorAttachments: [ {
 				view: null
 			} ],
@@ -310,15 +312,8 @@ class WebGPURenderer {
 			}
 		};
 
-		const renderAttachments = {
-			depth: true,
-			stencil: true
-		};
-
-		const colorAttachment = renderPassDescriptor.colorAttachments[ 0 ];
-		const depthStencilAttachment = renderPassDescriptor.depthStencilAttachment;
-
-		const renderTarget = this._renderTarget;
+		const colorAttachment = renderState.descriptorGPU.colorAttachments[ 0 ];
+		const depthStencilAttachment = renderState.descriptorGPU.depthStencilAttachment;
 
 		if ( renderTarget !== null ) {
 
@@ -331,7 +326,7 @@ class WebGPURenderer {
 			colorAttachment.view = renderTargetProperties.colorTextureGPU.createView();
 			depthStencilAttachment.view = renderTargetProperties.depthTextureGPU.createView();
 
-			renderAttachments.stencil = renderTarget.depthTexture ? renderTarget.depthTexture.format !== DepthFormat : true;
+			renderState.stencil = renderTarget.depthTexture ? renderTarget.depthTexture.format !== DepthFormat : true;
 
 		} else {
 
@@ -360,13 +355,14 @@ class WebGPURenderer {
 
 		//
 
-		this._background.update( this._currentRenderList, scene, renderPassDescriptor, renderAttachments );
+		this._background.update( scene, renderList, renderState );
 
 		// start render pass
 
 		const device = this._device;
-		const cmdEncoder = device.createCommandEncoder( {} );
-		const passEncoder = cmdEncoder.beginRenderPass( renderPassDescriptor );
+
+		renderState.encoderGPU = device.createCommandEncoder( {} );
+		renderState.currentPassGPU = renderState.encoderGPU.beginRenderPass( renderState.descriptorGPU );
 
 		// global rasterization settings for all renderable objects
 
@@ -377,7 +373,7 @@ class WebGPURenderer {
 			const width = Math.floor( vp.width * this._pixelRatio );
 			const height = Math.floor( vp.height * this._pixelRatio );
 
-			passEncoder.setViewport( vp.x, vp.y, width, height, vp.minDepth, vp.maxDepth );
+			renderState.currentPassGPU.setViewport( vp.x, vp.y, width, height, vp.minDepth, vp.maxDepth );
 
 		}
 
@@ -388,28 +384,29 @@ class WebGPURenderer {
 			const width = Math.floor( sc.width * this._pixelRatio );
 			const height = Math.floor( sc.height * this._pixelRatio );
 
-			passEncoder.setScissorRect( sc.x, sc.y, width, height );
+			renderState.currentPassGPU.setScissorRect( sc.x, sc.y, width, height );
 
 		}
 
-		// lights node
-
-		const lightsNode = this._currentRenderState.getLightsNode();
-
 		// process render lists
 
-		const opaqueObjects = this._currentRenderList.opaque;
-		const transparentObjects = this._currentRenderList.transparent;
+		const opaqueObjects = renderList.opaque;
+		const transparentObjects = renderList.transparent;
+		const lightsNode = renderList.lightsNode;
 
-		if ( opaqueObjects.length > 0 ) this._renderObjects( opaqueObjects, camera, scene, lightsNode, passEncoder );
-		if ( transparentObjects.length > 0 ) this._renderObjects( transparentObjects, camera, scene, lightsNode, passEncoder );
+		if ( opaqueObjects.length > 0 ) this._renderObjects( opaqueObjects, camera, scene, lightsNode, renderState );
+		if ( transparentObjects.length > 0 ) this._renderObjects( transparentObjects, camera, scene, lightsNode, renderState );
 
 		// finish render pass
 
-		passEncoder.end();
-		device.queue.submit( [ cmdEncoder.finish() ] );
+		renderState.currentPassGPU.end();
+
+		device.queue.submit( [ renderState.encoderGPU.finish() ] );
+
+		// restore render tree
 
 		nodeFrame.renderId = previousRenderId;
+		this._currentRenderState = previousRenderState;
 
 	}
 
@@ -544,6 +541,42 @@ class WebGPURenderer {
 
 	}
 
+	copyFramebufferToRenderTarget( renderTarget ) {
+
+		const renderState = this._currentRenderState;
+		const { encoderGPU, descriptorGPU } = renderState;
+
+		const texture = renderTarget.texture;
+		texture.internalFormat = GPUTextureFormat.BGRA8Unorm;
+
+		this._textures.initRenderTarget( renderTarget );
+
+		const sourceGPU = this._context.getCurrentTexture();
+		const destinationGPU = this._textures.getTextureGPU( texture );
+
+		renderState.currentPassGPU.end();
+
+		encoderGPU.copyTextureToTexture(
+			{
+			  texture: sourceGPU
+			},
+			{
+			  texture: destinationGPU
+			},
+			[
+				texture.image.width,
+				texture.image.height
+			]
+		);
+
+		descriptorGPU.colorAttachments[ 0 ].loadOp = GPULoadOp.Load;
+		if ( renderState.depth ) descriptorGPU.depthStencilAttachment.depthLoadOp = GPULoadOp.Load;
+		if ( renderState.stencil ) descriptorGPU.depthStencilAttachment.stencilLoadOp = GPULoadOp.Load;
+
+		renderState.currentPassGPU = encoderGPU.beginRenderPass( descriptorGPU );
+
+	}
+
 	getViewport( target ) {
 
 		const viewport = this._viewport;
@@ -733,10 +766,7 @@ class WebGPURenderer {
 
 	}
 
-	_projectObject( object, camera, groupOrder ) {
-
-		const currentRenderList = this._currentRenderList;
-		const currentRenderState = this._currentRenderState;
+	_projectObject( object, camera, groupOrder, renderList ) {
 
 		if ( object.visible === false ) return;
 
@@ -754,13 +784,7 @@ class WebGPURenderer {
 
 			} else if ( object.isLight ) {
 
-				currentRenderState.pushLight( object );
-
-				if ( object.castShadow ) {
-
-					//currentRenderState.pushShadow( object );
-
-				}
+				renderList.pushLight( object );
 
 			} else if ( object.isSprite ) {
 
@@ -777,7 +801,7 @@ class WebGPURenderer {
 
 					if ( material.visible ) {
 
-						currentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null );
+						renderList.push( object, geometry, material, groupOrder, _vector3.z, null );
 
 					}
 
@@ -811,7 +835,7 @@ class WebGPURenderer {
 
 							if ( groupMaterial && groupMaterial.visible ) {
 
-								currentRenderList.push( object, geometry, groupMaterial, groupOrder, _vector3.z, group );
+								renderList.push( object, geometry, groupMaterial, groupOrder, _vector3.z, group );
 
 							}
 
@@ -819,7 +843,7 @@ class WebGPURenderer {
 
 					} else if ( material.visible ) {
 
-						currentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null );
+						renderList.push( object, geometry, material, groupOrder, _vector3.z, null );
 
 					}
 
@@ -833,13 +857,13 @@ class WebGPURenderer {
 
 		for ( let i = 0, l = children.length; i < l; i ++ ) {
 
-			this._projectObject( children[ i ], camera, groupOrder );
+			this._projectObject( children[ i ], camera, groupOrder, renderList );
 
 		}
 
 	}
 
-	_renderObjects( renderList, camera, scene, lightsNode, passEncoder ) {
+	_renderObjects( renderList, camera, scene, lightsNode ) {
 
 		// process renderable objects
 
@@ -866,9 +890,9 @@ class WebGPURenderer {
 						const minDepth = ( vp.minDepth === undefined ) ? 0 : vp.minDepth;
 						const maxDepth = ( vp.maxDepth === undefined ) ? 1 : vp.maxDepth;
 
-						passEncoder.setViewport( vp.x, vp.y, vp.width, vp.height, minDepth, maxDepth );
+						this._currentRenderState.currentPassGPU.setViewport( vp.x, vp.y, vp.width, vp.height, minDepth, maxDepth );
 
-						this._renderObject( object, scene, camera2, geometry, material, group, lightsNode, passEncoder );
+						this._renderObject( object, scene, camera2, geometry, material, group, lightsNode );
 
 					}
 
@@ -876,7 +900,7 @@ class WebGPURenderer {
 
 			} else {
 
-				this._renderObject( object, scene, camera, geometry, material, group, lightsNode, passEncoder );
+				this._renderObject( object, scene, camera, geometry, material, group, lightsNode );
 
 			}
 
@@ -884,9 +908,7 @@ class WebGPURenderer {
 
 	}
 
-	_renderObject( object, scene, camera, geometry, material, group, lightsNode, passEncoder ) {
-
-		const info = this._info;
+	_renderObject( object, scene, camera, geometry, material, group, lightsNode ) {
 
 		material = scene.overrideMaterial !== null ? scene.overrideMaterial : material;
 
@@ -904,6 +926,11 @@ class WebGPURenderer {
 
 		//
 
+		const passEncoder = this._currentRenderState.currentPassGPU;
+		const info = this._info;
+
+		//
+
 		object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld );
 		object.normalMatrix.getNormalMatrix( object.modelViewMatrix );
 
@@ -1055,7 +1082,7 @@ class WebGPURenderer {
 				},
 				sampleCount: this._parameters.sampleCount,
 				format: GPUTextureFormat.BGRA8Unorm,
-				usage: GPUTextureUsage.RENDER_ATTACHMENT
+				usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC
 			} );
 
 		}
@@ -1079,7 +1106,7 @@ class WebGPURenderer {
 				},
 				sampleCount: this._parameters.sampleCount,
 				format: GPUTextureFormat.Depth24PlusStencil8,
-				usage: GPUTextureUsage.RENDER_ATTACHMENT
+				usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC
 			} );
 
 		}
@@ -1095,7 +1122,7 @@ class WebGPURenderer {
 			this._context.configure( {
 				device: device,
 				format: GPUTextureFormat.BGRA8Unorm,
-				usage: GPUTextureUsage.RENDER_ATTACHMENT,
+				usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC,
 				alphaMode: 'premultiplied'
 			} );
 

+ 18 - 7
examples/jsm/renderers/webgpu/WebGPUTextures.js

@@ -238,8 +238,13 @@ class WebGPUTextures {
 
 			const width = renderTarget.width;
 			const height = renderTarget.height;
-			const colorTextureFormat = this._getFormat( renderTarget.texture );
-			const label = renderTarget.texture.name ? '_' + renderTarget.texture.name : '';
+
+			const texture = renderTarget.texture;
+
+			const colorTextureFormat = texture.internalFormat || this._getFormat( texture );
+			const label = texture.name ? '_' + texture.name : '';
+			const needsMipmaps = this._needsMipmaps( texture );
+			const mipLevelCount = this._getMipLevelCount( texture, width, height, needsMipmaps );
 
 			const colorTextureGPU = device.createTexture( {
 				label: 'renderTarget' + label,
@@ -248,8 +253,9 @@ class WebGPUTextures {
 					height: height,
 					depthOrArrayLayers: 1
 				},
+				mipLevelCount: mipLevelCount,
 				format: colorTextureFormat,
-				usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING
+				usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST
 			} );
 
 			this.info.memory.textures ++;
@@ -262,7 +268,7 @@ class WebGPUTextures {
 			// Since it's not possible to see just from a texture object whether it belongs to a render
 			// target or not, we need the initializedRTT flag.
 
-			const textureProperties = properties.get( renderTarget.texture );
+			const textureProperties = properties.get( texture );
 			textureProperties.textureGPU = colorTextureGPU;
 			textureProperties.initializedRTT = false;
 
@@ -278,7 +284,7 @@ class WebGPUTextures {
 						depthOrArrayLayers: 1
 					},
 					format: depthTextureFormat,
-					usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING
+					usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST
 				} );
 
 				this.info.memory.textures ++;
@@ -379,7 +385,7 @@ class WebGPUTextures {
 		const needsMipmaps = this._needsMipmaps( texture );
 		const dimension = this._getDimension( texture );
 		const mipLevelCount = this._getMipLevelCount( texture, width, height, needsMipmaps );
-		const format = this._getFormat( texture );
+		const format = texture.internalFormat || this._getFormat( texture );
 
 		let usage = GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST;
 
@@ -435,6 +441,10 @@ class WebGPUTextures {
 
 			}
 
+		} else if ( texture.isRenderTargetTexture ) {
+
+			if ( needsMipmaps === true ) this._generateMipmaps( textureGPU, textureGPUDescriptor );
+
 		} else if ( texture.isDepthTexture !== true && image !== null ) {
 
 			this._copyImageToTexture( image, texture, textureGPU, textureGPUDescriptor, needsMipmaps );
@@ -550,7 +560,8 @@ class WebGPUTextures {
 					width: Math.ceil( width / blockData.width ) * blockData.width,
 					height: Math.ceil( height / blockData.width ) * blockData.width,
 					depthOrArrayLayers: 1
-				} );
+				}
+			);
 
 		}
 

+ 8 - 9
examples/jsm/renderers/webgpu/nodes/WebGPUNodes.js

@@ -82,7 +82,6 @@ class WebGPUNodes {
 
 	}
 
-
 	getCacheKey( scene, lightsNode ) {
 
 		const environmentNode = this.getEnvironmentNode( scene );
@@ -253,26 +252,25 @@ class WebGPUNodes {
 
 	}
 
-	getUpdateNodes( renderObject ) {
+	getNodeFrame( renderObject ) {
 
-		const nodeBuilder = this.get( renderObject );
 		const nodeFrame = this.nodeFrame;
-
 		nodeFrame.scene = renderObject.scene;
 		nodeFrame.object = renderObject.object;
 		nodeFrame.camera = renderObject.camera;
 		nodeFrame.renderer = renderObject.renderer;
 		nodeFrame.material = renderObject.material;
 
-		return nodeBuilder.updateNodes;
+		return nodeFrame;
 
 	}
 
 	updateBefore( renderObject ) {
 
-		const nodeFrame = this.nodeFrame;
+		const nodeFrame = this.getNodeFrame( renderObject );
+		const nodeBuilder = this.get( renderObject );
 
-		for ( const node of this.getUpdateNodes( renderObject ) ) {
+		for ( const node of nodeBuilder.updateBeforeNodes ) {
 
 			nodeFrame.updateBeforeNode( node );
 
@@ -282,9 +280,10 @@ class WebGPUNodes {
 
 	update( renderObject ) {
 
-		const nodeFrame = this.nodeFrame;
+		const nodeFrame = this.getNodeFrame( renderObject );
+		const nodeBuilder = this.get( renderObject );
 
-		for ( const node of this.getUpdateNodes( renderObject ) ) {
+		for ( const node of nodeBuilder.updateNodes ) {
 
 			nodeFrame.updateNode( node );
 

BIN
examples/screenshots/webgpu_backdrop.jpg


+ 166 - 0
examples/webgpu_backdrop.html

@@ -0,0 +1,166 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js - WebGPU - Backdrop</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
+		</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 { float, vec3, color, toneMapping, viewportSharedTexture, viewportTopLeft, checker, uv, oscSine, MeshStandardNodeMaterial } from 'three/nodes';
+
+			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 portals, rotate = true;
+			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.01, 100 );
+				camera.position.set( 1, 2, 3 );
+
+				scene = new THREE.Scene();
+				scene.background = new THREE.Color( 'lightblue' );
+				camera.lookAt( 0, 1, 0 );
+
+				clock = new THREE.Clock();
+
+				//lights
+
+				const light = new THREE.SpotLight( 0xffffff, 1 );
+				light.power = 2000;
+				camera.add( light );
+				scene.add( camera );
+
+				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 );
+
+				} );
+
+				// portals
+
+				portals = new THREE.Group();
+				scene.add( portals );
+
+				function addBackdropSphere( backdropNode, backdropAlphaNode = null ) {
+
+					const distance = 1;
+					const id = portals.children.length;
+					const rotation = THREE.MathUtils.degToRad( id * 45 );
+
+					const geometry = new THREE.SphereGeometry( .3, 32, 16 );
+
+					const material = new MeshStandardNodeMaterial( { color: 0x0066ff } );
+					material.roughnessNode = float( .2 );
+					material.metalnessNode = float( 0 );
+					material.backdropNode = backdropNode;
+					material.backdropAlphaNode = backdropAlphaNode;
+					material.transparent = true;
+
+					const mesh = new THREE.Mesh( geometry, material );
+					mesh.position.set(
+						Math.cos( rotation ) * distance,
+						1,
+						Math.sin( rotation ) * distance
+					);
+
+					portals.add( mesh );
+
+				}
+
+				addBackdropSphere( viewportSharedTexture().bgr.hue( oscSine().mul( Math.PI )  ) );
+				addBackdropSphere( viewportSharedTexture().rgb.oneMinus() );
+				addBackdropSphere( viewportSharedTexture().rgb.saturation( 0 ) );
+				addBackdropSphere( viewportSharedTexture().rgb.saturation( 10 ), oscSine() );
+				addBackdropSphere( viewportSharedTexture().rgb.overlay( checker( uv().mul( 10 ) ) ) );
+				addBackdropSphere( viewportSharedTexture( viewportTopLeft.mul( 40 ).floor().div( 40 ) ) );
+				addBackdropSphere( viewportSharedTexture( viewportTopLeft.mul( 80 ).floor().div( 80 ) ).add( color( 0x0033ff ) ) );
+				addBackdropSphere( vec3( 0, 0, viewportSharedTexture().b ) );
+
+				//renderer
+
+				renderer = new WebGPURenderer();
+				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.addEventListener( 'start', () => rotate = false );
+				controls.addEventListener( 'end', () => rotate = true );
+				controls.update();
+
+				window.addEventListener( 'resize', onWindowResize );
+
+			}
+
+			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 );
+
+				if ( rotate ) portals.rotation.y += delta * 0.5;
+
+				renderer.render( scene, camera );
+
+			}
+
+		</script>
+	</body>
+</html>