Просмотр исходного кода

NodeMaterial: Playground ( WebGPU version ) (#22901)

* update origin-trial token (threejs.org)

* fix webgpu warn() utf8 decoding

* ColorSpaceNode: Add support to LinearTosRGB

* Sync function name with core

* Create PhysicalMaterialFunctions for some PBR functions

* use builder.getMethod() in case of non-webgl renderer

* ShaderNode: add normalGeometry

* add 'lessThanEqual' function for WebGPU

* add bvec3 support to webgpu

* fixes and improves on PBR support to webgpu

* add outputEncoding ( sRGBEncoding ) support to webgpu

* cleanup function call

* add WebGPUBindings.remove( object )

* add material.dispose() support to WebGPU

* add webgpu_nodes_playground example

* fit webgl node-editor lights with webgpu lights

* cleanup
sunag 3 лет назад
Родитель
Сommit
35dfa476dc

+ 1 - 0
examples/files.json

@@ -323,6 +323,7 @@
 		"webgpu_lights_custom",
 		"webgpu_lights_selective",
 		"webgpu_materials",
+		"webgpu_nodes_playground",
 		"webgpu_rtt",
 		"webgpu_sandbox",
 		"webgpu_skinning",

+ 1 - 0
examples/jsm/renderers/nodes/ShaderNode.js

@@ -293,6 +293,7 @@ export const and = ShaderNodeProxy( OperatorNode, '&&' );
 
 export const element = ShaderNodeProxy( ArrayElementNode );
 
+export const normalGeometry = new NormalNode( NormalNode.GEOMETRY );
 export const normalLocal = new NormalNode( NormalNode.LOCAL );
 export const normalWorld = new NormalNode( NormalNode.WORLD );
 export const normalView = new NormalNode( NormalNode.VIEW );

+ 1 - 1
examples/jsm/renderers/nodes/core/NodeBuilder.js

@@ -624,7 +624,7 @@ class NodeBuilder {
 
 	getSignature() {
 
-		return `// Three.js r${ REVISION }  NodeMaterial System\n`;
+		return `// Three.js r${ REVISION } - NodeMaterial System\n`;
 
 	}
 

+ 23 - 3
examples/jsm/renderers/nodes/display/ColorSpaceNode.js

@@ -1,5 +1,8 @@
 import TempNode from '../core/Node.js';
-import { ShaderNode, vec3, pow, mul, add, mix, join, lessThanEqual } from '../ShaderNode.js';
+import { ShaderNode,
+	vec3,
+	pow, mul, add, sub, mix, join,
+	lessThanEqual } from '../ShaderNode.js';
 
 import { LinearEncoding,
 	sRGBEncoding/*, RGBEEncoding, RGBM7Encoding, RGBM16Encoding,
@@ -27,9 +30,26 @@ export const sRGBToLinear = new ShaderNode( ( inputs ) => {
 
 } );
 
+export const LinearTosRGB = new ShaderNode( ( inputs ) => {
+
+	const { value } = inputs;
+
+	const rgb = value.rgb;
+
+	const a = sub( mul( pow( value.rgb, vec3( 0.41666 ) ), 1.055 ), vec3( 0.055 ) );
+	const b = mul( rgb, 12.92 );
+	const factor = vec3( lessThanEqual( rgb, vec3( 0.0031308 ) ) );
+
+	const rgbResult = mix( a, b, factor );
+
+	return join( rgbResult.r, rgbResult.g, rgbResult.b, value.a );
+
+} );
+
 const EncodingLib = {
 	LinearToLinear,
-	sRGBToLinear
+	sRGBToLinear,
+	LinearTosRGB
 };
 
 function getEncodingComponents ( encoding ) {
@@ -117,7 +137,7 @@ class ColorSpaceNode extends TempNode {
 
 		if ( method !== ColorSpaceNode.LINEAR_TO_LINEAR ) {
 
-			const encodingFunctionNode = EncodingLib[ method ];			
+			const encodingFunctionNode = EncodingLib[ method ];
 			const factor = this.factor;
 
 			return encodingFunctionNode( {

+ 2 - 2
examples/jsm/renderers/nodes/functions/BSDFs.js

@@ -75,7 +75,7 @@ export const D_GGX = new ShaderNode( ( inputs ) => {
 
 
 // GGX Distribution, Schlick Fresnel, GGX_SmithCorrelated Visibility
-export const BRDF_Specular_GGX = new ShaderNode( ( inputs ) => {
+export const BRDF_GGX = new ShaderNode( ( inputs ) => {
 
 	const { lightDirection, f0, f90, roughness } = inputs;
 
@@ -109,7 +109,7 @@ export const RE_Direct_Physical = new ShaderNode( ( inputs ) => {
 
 	addTo( directDiffuse, mul( irradiance, BRDF_Lambert( { diffuseColor } ) ) );
 
-	addTo( directSpecular, mul( irradiance, BRDF_Specular_GGX( { lightDirection, f0: specularColor, f90: 1, roughness } ) ) );
+	addTo( directSpecular, mul( irradiance, BRDF_GGX( { lightDirection, f0: specularColor, f90: 1, roughness } ) ) );
 
 } );
 

+ 27 - 0
examples/jsm/renderers/nodes/functions/PhysicalMaterialFunctions.js

@@ -0,0 +1,27 @@
+import { ShaderNode,
+	add, max, min, abs, dFdx, dFdy,
+	normalGeometry
+} from '../ShaderNode.js';
+
+export const getGeometryRoughness = new ShaderNode( () => {
+
+	const dxy = max( abs( dFdx( normalGeometry ) ), abs( dFdy( normalGeometry ) ) );
+	const geometryRoughness = max( max( dxy.x, dxy.y ), dxy.z );
+
+	return geometryRoughness;
+
+} );
+
+export const getRoughness = new ShaderNode( ( inputs ) => {
+
+	const { roughness } = inputs;
+
+	const geometryRoughness = getGeometryRoughness();
+
+	let roughnessFactor = max( roughness, 0.0525 ); // 0.0525 corresponds to the base mip of a 256 cubemap.
+	roughnessFactor = add( roughnessFactor, geometryRoughness );
+	roughnessFactor = min( roughnessFactor, 1.0 );
+
+	return roughnessFactor;
+
+} );

+ 2 - 2
examples/jsm/renderers/nodes/math/OperatorNode.js

@@ -149,11 +149,11 @@ class OperatorNode extends TempNode {
 
 			} else if ( op === '>' && outputLength > 1 ) {
 
-				return `greaterThan( ${a}, ${b} )`;
+				return `${ builder.getMethod( 'greaterThan' ) }( ${a}, ${b} )`;
 
 			} else if ( op === '<=' && outputLength > 1 ) {
 
-				return `lessThanEqual( ${a}, ${b} )`;
+				return `${ builder.getMethod( 'lessThanEqual' ) }( ${a}, ${b} )`;
 
 			} else {
 

+ 7 - 6
examples/jsm/renderers/webgpu/WebGPUBindings.js

@@ -49,6 +49,12 @@ class WebGPUBindings {
 
 	}
 
+	remove( object ) {
+
+		this.uniformsData.delete( object );
+
+	}
+
 	getForCompute( param ) {
 
 		let data = this.uniformsData.get( param );
@@ -108,12 +114,7 @@ class WebGPUBindings {
 
 				if ( needsBufferWrite === true ) {
 
-					this.device.queue.writeBuffer(
-						bufferGPU,
-						0,
-						buffer,
-						0
-					);
+					this.device.queue.writeBuffer( bufferGPU, 0, buffer, 0 );
 
 				}
 

+ 42 - 61
examples/jsm/renderers/webgpu/WebGPURenderPipelines.js

@@ -3,13 +3,13 @@ import WebGPUProgrammableStage from './WebGPUProgrammableStage.js';
 
 class WebGPURenderPipelines {
 
-	constructor( renderer, properties, device, sampleCount, nodes ) {
+	constructor( renderer, device, sampleCount, nodes, bindings = null ) {
 
 		this.renderer = renderer;
-		this.properties = properties;
 		this.device = device;
 		this.sampleCount = sampleCount;
 		this.nodes = nodes;
+		this.bindings = bindings;
 
 		this.pipelines = [];
 		this.objectCache = new WeakMap();
@@ -24,10 +24,7 @@ class WebGPURenderPipelines {
 	get( object ) {
 
 		const device = this.device;
-		const properties = this.properties;
-
 		const material = object.material;
-		const materialProperties = properties.get( material );
 
 		const cache = this._getCache( object );
 
@@ -35,6 +32,14 @@ class WebGPURenderPipelines {
 
 		if ( this._needsUpdate( object, cache ) ) {
 
+			// release previous cache
+
+			if ( cache.currentPipeline !== undefined ) {
+
+				this._releaseObject( object );
+
+			}
+
 			// get shader
 
 			const nodeBuilder = this.nodes.get( object );
@@ -64,37 +69,15 @@ class WebGPURenderPipelines {
 			currentPipeline = this._acquirePipeline( stageVertex, stageFragment, object, nodeBuilder );
 			cache.currentPipeline = currentPipeline;
 
-			// keep track of all pipelines which are used by a material
-
-			let materialPipelines = materialProperties.pipelines;
-
-			if ( materialPipelines === undefined ) {
-
-				materialPipelines = new Set();
-				materialProperties.pipelines = materialPipelines;
-
-			}
-
-			if ( materialPipelines.has( currentPipeline ) === false ) {
+			// keep track of all used times
 
-				materialPipelines.add( currentPipeline );
+			currentPipeline.usedTimes ++;
+			stageVertex.usedTimes ++;
+			stageFragment.usedTimes ++;
 
-				currentPipeline.usedTimes ++;
-				stageVertex.usedTimes ++;
-				stageFragment.usedTimes ++;
+			// events
 
-			}
-
-			// dispose
-
-			if ( materialProperties.disposeCallback === undefined ) {
-
-				const disposeCallback = onMaterialDispose.bind( this );
-				materialProperties.disposeCallback = disposeCallback;
-
-				material.addEventListener( 'dispose', disposeCallback );
-
-			}
+			material.addEventListener( 'dispose', cache.dispose );
 
 		} else {
 
@@ -182,7 +165,20 @@ class WebGPURenderPipelines {
 
 		if ( cache === undefined ) {
 
-			cache = {};
+			cache = {
+
+				dispose: () => {
+
+					this._releaseObject( object );
+
+					this.objectCache.delete( object );
+
+					object.material.removeEventListener( 'dispose', cache.dispose );
+
+				}
+
+			};
+
 			this.objectCache.set( object, cache );
 
 		}
@@ -191,6 +187,18 @@ class WebGPURenderPipelines {
 
 	}
 
+	_releaseObject( object ) {
+
+		const cache = this.objectCache.get( object );
+
+		this._releasePipeline( cache.currentPipeline );
+		delete cache.currentPipeline;
+
+		this.nodes.remove( object );
+		this.bindings.remove( object );
+
+	}
+
 	_releasePipeline( pipeline ) {
 
 		if ( -- pipeline.usedTimes === 0 ) {
@@ -282,31 +290,4 @@ class WebGPURenderPipelines {
 
 }
 
-function onMaterialDispose( event ) {
-
-	const properties = this.properties;
-
-	const material = event.target;
-	const materialProperties = properties.get( material );
-
-	material.removeEventListener( 'dispose', materialProperties.disposeCallback );
-
-	properties.remove( material );
-
-	// remove references to pipelines
-
-	const pipelines = materialProperties.pipelines;
-
-	if ( pipelines !== undefined ) {
-
-		for ( const pipeline of pipelines ) {
-
-			this._releasePipeline( pipeline );
-
-		}
-
-	}
-
-}
-
 export default WebGPURenderPipelines;

+ 2 - 2
examples/jsm/renderers/webgpu/WebGPURenderer.js

@@ -183,9 +183,9 @@ class WebGPURenderer {
 		this._textures = new WebGPUTextures( device, this._properties, this._info );
 		this._objects = new WebGPUObjects( this._geometries, this._info );
 		this._nodes = new WebGPUNodes( this );
-		this._renderPipelines = new WebGPURenderPipelines( this, this._properties, device, parameters.sampleCount, this._nodes );
 		this._computePipelines = new WebGPUComputePipelines( device );
-		this._bindings = new WebGPUBindings( device, this._info, this._properties, this._textures, this._renderPipelines, this._computePipelines, this._attributes, this._nodes );
+		this._renderPipelines = new WebGPURenderPipelines( this, device, parameters.sampleCount, this._nodes );
+		this._bindings = this._renderPipelines.bindings = new WebGPUBindings( device, this._info, this._properties, this._textures, this._renderPipelines, this._computePipelines, this._attributes, this._nodes );
 		this._renderLists = new WebGPURenderLists();
 		this._background = new WebGPUBackground( this );
 

+ 28 - 2
examples/jsm/renderers/webgpu/nodes/WebGPUNodeBuilder.js

@@ -1,3 +1,5 @@
+import { LinearEncoding } from 'three';
+
 import WebGPUNodeUniformsGroup from './WebGPUNodeUniformsGroup.js';
 import {
 	FloatNodeUniform, Vector2NodeUniform, Vector3NodeUniform, Vector4NodeUniform,
@@ -19,9 +21,12 @@ import PositionNode from '../../nodes/accessors/PositionNode.js';
 import NormalNode from '../../nodes/accessors/NormalNode.js';
 import ModelViewProjectionNode from '../../nodes/accessors/ModelViewProjectionNode.js';
 import SkinningNode from '../../nodes/accessors/SkinningNode.js';
+import ColorSpaceNode from '../../nodes/display/ColorSpaceNode.js';
 import LightContextNode from '../../nodes/lights/LightContextNode.js';
 import OperatorNode from '../../nodes/math/OperatorNode.js';
 import WGSLNodeParser from '../../nodes/parsers/WGSLNodeParser.js';
+import { vec4 } from '../../nodes/ShaderNode.js';
+import { getRoughness } from '../../nodes/functions/PhysicalMaterialFunctions.js';
 
 const wgslTypeLib = {
 	float: 'f32',
@@ -30,6 +35,7 @@ const wgslTypeLib = {
 	vec3: 'vec3<f32>',
 	vec4: 'vec4<f32>',
 	uvec4: 'vec4<u32>',
+	bvec3: 'vec3<bool>',
 	mat3: 'mat3x3<f32>',
 	mat4: 'mat4x4<f32>'
 };
@@ -40,6 +46,13 @@ const wgslMethods = {
 };
 
 const wgslPolyfill = {
+	lessThanEqual: new CodeNode( `
+fn lessThanEqual( a : vec3<f32>, b : vec3<f32> ) -> vec3<bool> {
+
+	return vec3<bool>( a.x <= b.x, a.y <= b.y, a.z <= b.z );
+
+}
+` ),
 	mod: new CodeNode( `
 fn mod( x : f32, y : f32 ) -> f32 {
 
@@ -135,7 +148,9 @@ class WebGPUNodeBuilder extends NodeBuilder {
 
 			}
 
-			colorNode = this.addFlow( 'fragment', new VarNode( colorNode, 'DiffuseColor', 'vec4' ) );
+			colorNode = this.addFlow( 'fragment', new VarNode( colorNode, 'Color', 'vec3' ) );
+
+			const diffuseNode = this.addFlow( 'fragment', new VarNode( colorNode, 'DiffuseColor', 'vec4' ) );
 
 			// OPACITY
 
@@ -211,11 +226,13 @@ class WebGPUNodeBuilder extends NodeBuilder {
 
 				}
 
+				roughnessNode = getRoughness( { roughness: roughnessNode } );
+
 				this.addFlow( 'fragment', new VarNode( roughnessNode, 'Roughness', 'float' ) );
 
 				// SPECULAR_TINT
 
-				this.addFlow( 'fragment', new VarNode( new ExpressionNode( 'mix( vec3<f32>( 0.04 ), DiffuseColor.rgb, Metalness )', 'vec3' ), 'SpecularColor', 'color' ) );
+				this.addFlow( 'fragment', new VarNode( new ExpressionNode( 'mix( vec3<f32>( 0.04 ), Color, Metalness )', 'vec3' ), 'SpecularColor', 'color' ) );
 
 				// NORMAL_VIEW
 
@@ -249,6 +266,15 @@ class WebGPUNodeBuilder extends NodeBuilder {
 
 			// RESULT
 
+			const outputEncoding = this.renderer.outputEncoding;
+
+			if ( outputEncoding !== LinearEncoding ) {
+				
+				outputNode = new ColorSpaceNode( ColorSpaceNode.LINEAR_TO_LINEAR, vec4( outputNode ) );
+				outputNode.fromEncoding( outputEncoding );
+				
+			}
+
 			this.addFlow( 'fragment', new VarNode( outputNode, 'Output', 'vec4' ) );
 
 		}

BIN
examples/screenshots/webgpu_nodes_playground.jpg


+ 7 - 12
examples/webgl_materials_nodes_playground.html

@@ -70,18 +70,13 @@
 
 			// Lights
 
-			//scene.add( new THREE.AmbientLight( 0x111111 ) );
-
-			const directionalLight = new THREE.DirectionalLight( 0xF4F6F0, 0.8 );
-			directionalLight.position.set( 0.0, 0.5, 0.5 ).normalize();
-			scene.add( directionalLight );
-
-			const pointLight = new THREE.Mesh( new THREE.SphereGeometry( 4, 8, 8 ), new THREE.MeshBasicMaterial( { color: 0x0c1445 } ) );
-			pointLight.add( new THREE.PointLight( 0x0c1445, 0.7, 500 ) );
-			scene.add( pointLight );
-			pointLight.position.x = - 100;
-			pointLight.position.y = 20;
-			pointLight.position.z = - 260;
+			const topLight = new THREE.PointLight( 0xF4F6F0, 1 );
+			topLight.position.set( 0, 100000, 100000 );
+			scene.add( topLight );
+
+			const backLight = new THREE.PointLight( 0x0c1445, 1.4 );
+			backLight.position.set( - 100, 20, - 260 );
+			scene.add( backLight );
 
 			renderer = new THREE.WebGLRenderer( { antialias: true } );
 			document.body.appendChild( renderer.domElement );

+ 2 - 2
examples/webgpu_compute.html

@@ -4,8 +4,8 @@
 		<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">
-		<!-- WebGPU (For Chrome 94-97), expires 12/13/2021 -->
-		<meta http-equiv="origin-trial" content="Agfr4hEaaoH1kaTtGTZY4OU2lQQOv+gWq+8rYeHZBPWNnLww/smePFCIJOUdRFnQZkO3KAio+SNJapjzaoyFfQQAAABLeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJHUFUiLCJleHBpcnkiOjE2NDMxNTUxOTl9">
+		<!-- WebGPU (For Chrome 94-101), expires 09/01/2022 -->
+		<meta http-equiv="origin-trial" content="AoS1pSJwCV3KRe73TO0YgJkK9FZ/qhmvKeafztp0ofiE8uoGrnKzfxGVKKICvoBfL8dgE0zpkp2g/oEJNS0fDgkAAABeeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJHUFUiLCJleHBpcnkiOjE2NTI4MzE5OTksImlzU3ViZG9tYWluIjp0cnVlfQ==">
 	</head>
 	<body>
 		<div id="info">

+ 2 - 2
examples/webgpu_instance_uniform.html

@@ -5,8 +5,8 @@
 		<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">
-		<!-- WebGPU (For Chrome 94-97), expires 12/13/2021 -->
-		<meta http-equiv="origin-trial" content="Agfr4hEaaoH1kaTtGTZY4OU2lQQOv+gWq+8rYeHZBPWNnLww/smePFCIJOUdRFnQZkO3KAio+SNJapjzaoyFfQQAAABLeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJHUFUiLCJleHBpcnkiOjE2NDMxNTUxOTl9">
+		<!-- WebGPU (For Chrome 94-101), expires 09/01/2022 -->
+		<meta http-equiv="origin-trial" content="AoS1pSJwCV3KRe73TO0YgJkK9FZ/qhmvKeafztp0ofiE8uoGrnKzfxGVKKICvoBfL8dgE0zpkp2g/oEJNS0fDgkAAABeeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJHUFUiLCJleHBpcnkiOjE2NTI4MzE5OTksImlzU3ViZG9tYWluIjp0cnVlfQ==">
 	</head>
 	<body>
 		<div id="info">

+ 2 - 2
examples/webgpu_lights_custom.html

@@ -4,8 +4,8 @@
 		<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">
-		<!-- WebGPU (For Chrome 94-97), expires 12/13/2021 -->
-		<meta http-equiv="origin-trial" content="Agfr4hEaaoH1kaTtGTZY4OU2lQQOv+gWq+8rYeHZBPWNnLww/smePFCIJOUdRFnQZkO3KAio+SNJapjzaoyFfQQAAABLeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJHUFUiLCJleHBpcnkiOjE2NDMxNTUxOTl9">
+		<!-- WebGPU (For Chrome 94-101), expires 09/01/2022 -->
+		<meta http-equiv="origin-trial" content="AoS1pSJwCV3KRe73TO0YgJkK9FZ/qhmvKeafztp0ofiE8uoGrnKzfxGVKKICvoBfL8dgE0zpkp2g/oEJNS0fDgkAAABeeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJHUFUiLCJleHBpcnkiOjE2NTI4MzE5OTksImlzU3ViZG9tYWluIjp0cnVlfQ==">
 	</head>
 	<body>
 		<div id="info">

+ 2 - 2
examples/webgpu_materials.html

@@ -5,8 +5,8 @@
 		<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">
-		<!-- WebGPU (For Chrome 94-97), expires 12/13/2021 -->
-		<meta http-equiv="origin-trial" content="Agfr4hEaaoH1kaTtGTZY4OU2lQQOv+gWq+8rYeHZBPWNnLww/smePFCIJOUdRFnQZkO3KAio+SNJapjzaoyFfQQAAABLeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJHUFUiLCJleHBpcnkiOjE2NDMxNTUxOTl9">
+		<!-- WebGPU (For Chrome 94-101), expires 09/01/2022 -->
+		<meta http-equiv="origin-trial" content="AoS1pSJwCV3KRe73TO0YgJkK9FZ/qhmvKeafztp0ofiE8uoGrnKzfxGVKKICvoBfL8dgE0zpkp2g/oEJNS0fDgkAAABeeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJHUFUiLCJleHBpcnkiOjE2NTI4MzE5OTksImlzU3ViZG9tYWluIjp0cnVlfQ==">
 	</head>
 	<body>
 		<div id="info">

+ 209 - 0
examples/webgpu_nodes_playground.html

@@ -0,0 +1,209 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js - WebGPU - Selective Lights</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">
+		<link rel="stylesheet" href="fonts/open-sans/open-sans.css" type="text/css"/>
+		<link rel="stylesheet" href="fonts/tabler-icons/tabler-icons.min.css" type="text/css"/>
+		<!-- WebGPU (For Chrome 94-101), expires 09/01/2022 -->
+		<meta http-equiv="origin-trial" content="AoS1pSJwCV3KRe73TO0YgJkK9FZ/qhmvKeafztp0ofiE8uoGrnKzfxGVKKICvoBfL8dgE0zpkp2g/oEJNS0fDgkAAABeeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJHUFUiLCJleHBpcnkiOjE2NTI4MzE5OTksImlzU3ViZG9tYWluIjp0cnVlfQ==">
+		<style>
+body {
+	overflow: hidden;
+	width: 100%;
+	height: 100%;
+}
+.renderer {
+	position: absolute;
+	top: 0;
+	left: 0;
+	height: 50%;
+	width: 100%;
+}
+flow {
+	position: absolute;
+	top: 50%;
+	left: 0;
+	height: 50%;
+	width: 100%;
+	background: #222;
+	box-shadow: inset 0 0 20px 0px #000000;
+}
+		</style>
+	</head>
+	<body>
+
+		<div id="info">
+			<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> - WebGPU - Node Editor ( Playground version )<br />
+		</div>
+
+		<script type="importmap">
+		{
+			"imports": {
+				"three": "../build/three.module.js"
+			}
+		}
+		</script>
+		<script type="module">
+
+			import * as THREE from 'three';
+
+			import WebGPURenderer from './jsm/renderers/webgpu/WebGPURenderer.js';
+			import WebGPU from './jsm/renderers/webgpu/WebGPU.js';
+
+			import { NodeEditor } from './jsm/node-editor/NodeEditor.js';
+			import { StandardMaterialEditor } from './jsm/node-editor/materials/StandardMaterialEditor.js';
+
+			import * as Nodes from './jsm/renderers/nodes/Nodes.js';
+
+			import Stats from './jsm/libs/stats.module.js';
+
+			import { OrbitControls } from './jsm/controls/OrbitControls.js';
+			import { FBXLoader } from './jsm/loaders/FBXLoader.js';
+
+			let stats;
+			let camera, scene, renderer;
+			let model;
+			let nodeLights;
+
+			init().then( animate ).catch( error => console.error( error ) );
+
+			async function init() {
+
+				if ( WebGPU.isAvailable() === false ) {
+
+					document.body.appendChild( WebGPU.getErrorMessage() );
+
+					throw 'No WebGPU support';
+
+				}
+
+				camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 5000 );
+				camera.position.set( 0.0, 300, 400 * 3 );
+
+				scene = new THREE.Scene();
+				scene.background = new THREE.Color( 0x333333 );
+
+				// Lights
+
+				const topLight = new THREE.PointLight( 0xF4F6F0, 1 );
+				topLight.position.set( 0, 100000, 100000 );
+				scene.add( topLight );
+
+				const backLight = new THREE.PointLight( 0x0c1445, 1.4 );
+				backLight.position.set( - 100, 20, - 260 );
+				scene.add( backLight );
+
+				nodeLights = Nodes.LightsNode.fromLights( [ topLight, backLight ] );
+
+				renderer = new WebGPURenderer();
+				renderer.setPixelRatio( window.devicePixelRatio );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				document.body.appendChild( renderer.domElement );
+				renderer.outputEncoding = THREE.sRGBEncoding;
+
+				renderer.domElement.className = 'renderer';
+
+				//
+
+				stats = new Stats();
+				document.body.appendChild( stats.dom );
+
+				const controls = new OrbitControls( camera, renderer.domElement );
+				controls.minDistance = 500;
+				controls.maxDistance = 3000;
+
+				window.addEventListener( 'resize', onWindowResize );
+
+				onWindowResize();
+
+				initEditor();
+
+				return renderer.init();
+
+			}
+
+			function initEditor() {
+
+				const nodeEditor = new NodeEditor();
+
+				nodeEditor.addEventListener( 'new', () => {
+
+					const materialEditor = new StandardMaterialEditor();
+					materialEditor.setPosition( ( window.innerWidth / 2 ) - 150, 100 );
+
+					nodeEditor.add( materialEditor );
+
+					model.material = materialEditor.material;
+					model.material.lightNode = nodeLights;
+
+				} );
+
+				nodeEditor.addEventListener( 'load', () => {
+
+					const materialEditor = nodeEditor.nodes[ 0 ];
+					materialEditor.update(); // need move to deserialization
+
+					model.material = materialEditor.material;
+					model.material.lightNode = nodeLights;
+
+				} );
+
+				document.body.appendChild( nodeEditor.domElement );
+
+				const loaderFBX = new FBXLoader();
+				loaderFBX.load( 'models/fbx/stanford-bunny.fbx', ( object ) => {
+
+					const materialEditor = new StandardMaterialEditor();
+					materialEditor.setPosition( ( window.innerWidth / 2 ) - 150, 100 ); // canvas position
+
+					nodeEditor.add( materialEditor );
+
+					model = object.children[ 0 ];
+					model.position.set( 0, 0, 10 );
+					model.scale.setScalar( 1 );
+					model.material = materialEditor.material;
+					model.material.lightNode = nodeLights;
+					scene.add( model );
+
+				} );
+
+			}
+
+			function onWindowResize() {
+
+				const width = window.innerWidth;
+				const height = window.innerHeight / 2;
+
+				camera.aspect = width / height;
+				camera.updateProjectionMatrix();
+
+				renderer.setSize( width, height );
+
+			}
+
+			//
+
+			function animate() {
+
+				requestAnimationFrame( animate );
+
+				render();
+
+				stats.update();
+
+			}
+
+			function render() {
+
+				//if ( model ) model.rotation.y = performance.now() / 5000;
+
+				renderer.render( scene, camera );
+
+			}
+
+		</script>
+	</body>
+</html>

+ 2 - 2
examples/webgpu_rtt.html

@@ -4,8 +4,8 @@
 		<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">
-		<!-- WebGPU (For Chrome 94-97), expires 12/13/2021 -->
-		<meta http-equiv="origin-trial" content="Agfr4hEaaoH1kaTtGTZY4OU2lQQOv+gWq+8rYeHZBPWNnLww/smePFCIJOUdRFnQZkO3KAio+SNJapjzaoyFfQQAAABLeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJHUFUiLCJleHBpcnkiOjE2NDMxNTUxOTl9">
+		<!-- WebGPU (For Chrome 94-101), expires 09/01/2022 -->
+		<meta http-equiv="origin-trial" content="AoS1pSJwCV3KRe73TO0YgJkK9FZ/qhmvKeafztp0ofiE8uoGrnKzfxGVKKICvoBfL8dgE0zpkp2g/oEJNS0fDgkAAABeeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJHUFUiLCJleHBpcnkiOjE2NTI4MzE5OTksImlzU3ViZG9tYWluIjp0cnVlfQ==">
 	</head>
 	<body>
 		<div id="info">

+ 2 - 2
examples/webgpu_sandbox.html

@@ -4,8 +4,8 @@
 		<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">
-		<!-- WebGPU (For Chrome 94-97), expires 12/13/2021 -->
-		<meta http-equiv="origin-trial" content="Agfr4hEaaoH1kaTtGTZY4OU2lQQOv+gWq+8rYeHZBPWNnLww/smePFCIJOUdRFnQZkO3KAio+SNJapjzaoyFfQQAAABLeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJHUFUiLCJleHBpcnkiOjE2NDMxNTUxOTl9">
+		<!-- WebGPU (For Chrome 94-101), expires 09/01/2022 -->
+		<meta http-equiv="origin-trial" content="AoS1pSJwCV3KRe73TO0YgJkK9FZ/qhmvKeafztp0ofiE8uoGrnKzfxGVKKICvoBfL8dgE0zpkp2g/oEJNS0fDgkAAABeeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJHUFUiLCJleHBpcnkiOjE2NTI4MzE5OTksImlzU3ViZG9tYWluIjp0cnVlfQ==">
 	</head>
 	<body>
 		<div id="info">

+ 2 - 2
examples/webgpu_skinning.html

@@ -5,8 +5,8 @@
 		<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">
-		<!-- WebGPU (For Chrome 94-97), expires 12/13/2021 -->
-		<meta http-equiv="origin-trial" content="Agfr4hEaaoH1kaTtGTZY4OU2lQQOv+gWq+8rYeHZBPWNnLww/smePFCIJOUdRFnQZkO3KAio+SNJapjzaoyFfQQAAABLeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJHUFUiLCJleHBpcnkiOjE2NDMxNTUxOTl9">
+		<!-- WebGPU (For Chrome 94-101), expires 09/01/2022 -->
+		<meta http-equiv="origin-trial" content="AoS1pSJwCV3KRe73TO0YgJkK9FZ/qhmvKeafztp0ofiE8uoGrnKzfxGVKKICvoBfL8dgE0zpkp2g/oEJNS0fDgkAAABeeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJHUFUiLCJleHBpcnkiOjE2NTI4MzE5OTksImlzU3ViZG9tYWluIjp0cnVlfQ==">
 	</head>
 	<body>
 

+ 2 - 2
examples/webgpu_skinning_points.html

@@ -5,8 +5,8 @@
 		<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">
-		<!-- WebGPU (For Chrome 94-97), expires 12/13/2021 -->
-		<meta http-equiv="origin-trial" content="Agfr4hEaaoH1kaTtGTZY4OU2lQQOv+gWq+8rYeHZBPWNnLww/smePFCIJOUdRFnQZkO3KAio+SNJapjzaoyFfQQAAABLeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJHUFUiLCJleHBpcnkiOjE2NDMxNTUxOTl9">
+		<!-- WebGPU (For Chrome 94-101), expires 09/01/2022 -->
+		<meta http-equiv="origin-trial" content="AoS1pSJwCV3KRe73TO0YgJkK9FZ/qhmvKeafztp0ofiE8uoGrnKzfxGVKKICvoBfL8dgE0zpkp2g/oEJNS0fDgkAAABeeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJHUFUiLCJleHBpcnkiOjE2NTI4MzE5OTksImlzU3ViZG9tYWluIjp0cnVlfQ==">
 	</head>
 	<body>