sunag 4 rokov pred
rodič
commit
8622549228

+ 21 - 0
examples/jsm/renderers/nodes/accessors/UVNode.js

@@ -0,0 +1,21 @@
+import Node from '../core/Node.js';
+
+class UVNode extends Node {
+
+	constructor( index = 0 ) {
+
+		super( 'vec2' );
+
+		this.index = index;
+
+	}
+
+	generate( builder, output ) {
+		
+		return builder.format( builder.getUV( this.index ), this.getType( builder ), output );
+		
+	}
+
+}
+
+export default UVNode;

+ 4 - 4
examples/jsm/renderers/nodes/core/InputNode.js

@@ -26,9 +26,9 @@ class InputNode extends Node {
 
 	}
 
-	generateConst( /*builder*/ ) {
+	generateConst( builder ) {
 
-		console.warn( "Abstract function" );
+		return builder.getConst( this.getType( builder ), this.value );
 
 	}
 
@@ -43,9 +43,9 @@ class InputNode extends Node {
 		} else {
 
 			const nodeUniform = builder.getUniformFromNode( this, builder.shaderStage, this.getType( builder ) );
-			const nsName = builder.getUniformNSName( nodeUniform );
+			const propertyName = builder.getPropertyName( nodeUniform );
 
-			return builder.format( nsName, type, output );
+			return builder.format( propertyName, type, output );
 
 		}
 

+ 5 - 1
examples/jsm/renderers/nodes/core/Node.js

@@ -1,9 +1,11 @@
 class Node {
 
-	constructor( type ) {
+	constructor( type = null ) {
 
 		this.type = type;
 
+		//this.onBeforeUpdate = function ( /*object, scene, camera, geometry, material, group*/ ) {};
+
 		Object.defineProperty( this, 'isNode', { value: true } );
 
 	}
@@ -22,6 +24,8 @@ class Node {
 
 	build( builder, output ) {
 
+		builder.addNode( this );
+
 		return this.generate( builder, output );
 
 	}

+ 82 - 42
examples/jsm/renderers/nodes/core/NodeBuilder.js

@@ -9,6 +9,8 @@ class NodeBuilder {
 		this.material = material;
 		this.renderer = renderer;
 
+		this.nodes = [];
+
 		this.slots = { vertex: [], fragment: [] };
 		this.defines = { vertex: {}, fragment: {} };
 		this.uniforms = { vertex: [], fragment: [] };
@@ -19,43 +21,51 @@ class NodeBuilder {
 
 	}
 
-	addSlot( shader, slot ) {
-
-		this.slots[ shader ].push( slot );
-
+	addNode( node ) {
+		
+		if ( this.nodes.indexOf( node ) === -1 ) {
+			
+			this.nodes.push( node );
+			
+		}
+		
 	}
 
-	define( shader, name, value = '' ) {
+	addSlot( shaderStage, slot ) {
 
-		this.defines[ shader ][ name ] = value;
+		this.slots[ shaderStage ].push( slot );
 
 	}
 
-	generateVec2( x, y ) {
+	define( shaderStage, name, value = '' ) {
 
-		return `vec2( ${x}, ${y})`;
+		this.defines[ shaderStage ][ name ] = value;
 
 	}
 
-	generateVec3( x, y, z ) {
-
-		return `vec3( ${x}, ${y}, ${z} )`;
-
+	getTexture( textureSnippet, uvSnippet ) {
+		
+		
+		
 	}
 
-	generateVec4( x, y, z, w ) {
-
-		return `vec4( ${x}, ${y}, ${z}, ${w} )`;
-
+	getConst( type, value ) {
+		
+		if ( type === 'float' ) return value + ( value % 1 ? '' : '.0' );
+		if ( type === 'vec2' ) return `vec2( ${value.x}, ${value.y} )`;
+		if ( type === 'vec3' ) return `vec3( ${value.x}, ${value.y}, ${value.z} )`;
+		if ( type === 'vec4' ) return `vec4( ${value.x}, ${value.y}, ${value.z}, ${value.w} )`;
+		
 	}
-
-	generateFloat( value ) {
-
-		return value + ( value % 1 ? '' : '.0' );
-
+	
+	getUV( /*index*/ ) {
+		
+		// uv1 only for now
+		return 'vUv';
+		
 	}
 
-	getUniformNSName( nodeUniform ) {
+	getPropertyName( nodeUniform ) {
 
 		return nodeUniform.name;
 
@@ -127,7 +137,7 @@ class NodeBuilder {
 
 	}
 
-	buildDefines( shader ) {
+	_buildDefines( shader ) {
 
 		const defines = this.defines[ shader ];
 
@@ -143,13 +153,30 @@ class NodeBuilder {
 
 	}
 
+	getUniformsOutput( shaderStage ) {
+		
+		const uniforms = this.uniforms[ shaderStage ];
+		
+		let uniformsCode = '';
+
+		for ( let i = 0; i < uniforms.length; i ++ ) {
+
+			let uniform = uniforms[ i ];
+
+			uniformsCode += `${uniform.type} ${uniform.name}; `;
+
+		}
+		
+		return uniformsCode;
+		
+	}
+
 	build( shaderStage ) {
 
 		this.shaderStage = shaderStage;
 
 		const slots = this.slots[ shaderStage ];
-		const uniforms = this.uniforms[ shaderStage ];
-
+		
 		if ( slots.length ) {
 
 			this.define( shaderStage, 'NODE', VERSION );
@@ -164,41 +191,54 @@ class NodeBuilder {
 
 			}
 
-			let uniformsCode = '';
-
-			for ( let i = 0; i < uniforms.length; i ++ ) {
-
-				let uniform = uniforms[ i ];
-
-				uniformsCode += `${uniform.type} ${uniform.name}; `;
-
-			}
-
-			this.define( shaderStage, 'NODE_UNIFORMS', uniformsCode );
+			this.define( shaderStage, 'NODE_UNIFORMS', this.getUniformsOutput( shaderStage ) );
 
 		}
 
-		let defines = this.buildDefines( shaderStage );
+		let defines = this._buildDefines( shaderStage );
 
 		return {
 			defines
 		};
 
 	}
+	
+	getVectorType( type ) {
+		
+		if ( type === 'texture' ) return 'vec4';
+		
+		return type;
+		
+	}
+	
+	format( snippet, fromType, toType ) {
 
-	format( code, fromType, toType ) {
+		fromType = this.getVectorType( fromType );
+		toType = this.getVectorType( toType );
 
-		const typeToType = `${fromType} -> ${toType}`;
+		const typeToType = `${fromType} to ${toType}`;
 
 		switch ( typeToType ) {
 
-			case 'float -> vec3' : return `vec3( ${code} )`;
+			case 'float to vec2' : return `vec2( ${snippet} )`;
+			case 'float to vec3' : return `vec3( ${snippet} )`;
+			case 'float to vec4' : return `vec4( vec3( ${snippet} ), 1.0 )`;
+
+			case 'vec2 to float' : return `${snippet}.x`;
+			case 'vec2 to vec3' : return `vec3( ${snippet}.x, ${snippet}.y, 0.0 )`;
+			case 'vec2 to vec4' : return `vec4( ${snippet}.x, ${snippet}.y, 0.0, 1.0 )`;
 
-			case 'vec3 -> float' : return `${code}.x`;
+			case 'vec3 to float' : return `${snippet}.x`;
+			case 'vec3 to vec2' : return `${snippet}.xy`;
+			case 'vec3 to vec4' : return `vec4( ${snippet}.x, ${snippet}.y, ${snippet}.z, 1.0 )`;
+			
+			case 'vec4 to float' : return `${snippet}.x`;
+			case 'vec4 to vec2' : return `${snippet}.xy`;
+			case 'vec4 to vec3' : return `${snippet}.xyz`;
 
 		}
 
-		return code;
+		return snippet;
 
 	}
 

+ 1 - 7
examples/jsm/renderers/nodes/inputs/FloatNode.js

@@ -2,7 +2,7 @@ import InputNode from '../core/InputNode.js';
 
 class FloatNode extends InputNode {
 
-	constructor( value ) {
+	constructor( value = 0 ) {
 
 		super( 'float' );
 
@@ -10,12 +10,6 @@ class FloatNode extends InputNode {
 
 	}
 
-	generateConst( builder ) {
-
-		return builder.generateFloat( this.value );
-
-	}
-
 }
 
 export default FloatNode;

+ 28 - 0
examples/jsm/renderers/nodes/inputs/TextureNode.js

@@ -0,0 +1,28 @@
+import InputNode from '../core/InputNode.js';
+import UVNode from '../accessors/UVNode.js';
+
+class TextureNode extends InputNode {
+
+	constructor( value, uv = new UVNode() ) {
+
+		super( 'texture' );
+
+		this.value = value;
+		this.uv = uv;
+
+	}
+	
+	generate( builder, output ) {
+	
+		const textureSnippet = super.generate( builder );
+		const uvSnippet = this.uv.build( builder );
+
+		const textureCall = builder.getTexture( textureSnippet, uvSnippet );
+
+		return builder.format( textureCall, 'vec4', output );
+		
+	}
+
+}
+
+export default TextureNode;

+ 0 - 6
examples/jsm/renderers/nodes/inputs/Vector3Node.js

@@ -10,12 +10,6 @@ class Vector3Node extends InputNode {
 
 	}
 
-	generateConst( builder ) {
-
-		return builder.generateVec3( this.value.x, this.value.y, this.value.z );
-
-	}
-
 }
 
 export default Vector3Node;

+ 25 - 0
examples/jsm/renderers/nodes/utils/TimerNode.js

@@ -0,0 +1,25 @@
+import FloatNode from '../inputs/FloatNode.js';
+
+class TimerNode extends FloatNode {
+
+	constructor() {
+
+		super();
+
+		this.time = performance.now();
+
+	}
+	
+	update() {
+		
+		const time = performance.now();
+		
+		this.value += ( time - this.time ) / 1000;
+		
+		this.time = time;
+		
+	}
+
+}
+
+export default TimerNode;

+ 2 - 17
examples/jsm/renderers/webgpu/ShaderLib.js

@@ -23,31 +23,18 @@ const ShaderLib = {
 			gl_Position = cameraUniforms.projectionMatrix * modelUniforms.modelViewMatrix * vec4( position, 1.0 );
 		}`,
 		fragmentShader: `#version 450
-		layout(set = 0, binding = 2) uniform OpacityUniforms {
-			float opacity;
-		} opacityUniforms;
 
-		layout(set = 0, binding = 3) uniform sampler mySampler;
-		layout(set = 0, binding = 4) uniform texture2D myTexture;
-
-		#ifdef NODE_UNIFORMS
-
-		layout(set = 0, binding = 5) uniform NodeUniforms {
-			NODE_UNIFORMS
-		} nodeUniforms;
-
-		#endif
+		NODE_UNIFORMS
 
 		layout(location = 0) in vec2 vUv;
 		layout(location = 0) out vec4 outColor;
 
 		void main() {
 
-			outColor = texture( sampler2D( myTexture, mySampler ), vUv );
+			outColor = vec4( 1.0, 1.0, 1.0, 1.0 );
 
 			#ifdef NODE_COLOR
 
-				/* NODE_COLOR_CODE ignore (node code group) for now */
 				outColor.rgb *= NODE_COLOR;
 
 			#endif
@@ -58,8 +45,6 @@ const ShaderLib = {
 
 			#endif
 
-			outColor.a *= opacityUniforms.opacity;
-
 		}`
 	},
 	pointsBasic: {

+ 19 - 35
examples/jsm/renderers/webgpu/WebGPUBindings.js

@@ -39,7 +39,7 @@ class WebGPUBindings {
 
 			if ( material.isMeshBasicMaterial ) {
 
-				bindings = this._getMeshBasicBindings();
+				bindings = this._getMeshBasicBindings( object );
 
 			} else if ( material.isPointsMaterial ) {
 
@@ -57,7 +57,7 @@ class WebGPUBindings {
 
 			// append node bindings
 
-			bindings = bindings.concat( this.pipelines.getBindings( object ) );
+//			bindings = bindings.concat( this.pipelines.getBindings( object ) );
 
 			// setup (static) binding layout and (dynamic) binding group
 
@@ -155,39 +155,31 @@ class WebGPUBindings {
 			} else if ( binding.isSampler ) {
 
 				const material = object.material;
-				const texture = material[ binding.name ];
+				const texture = binding.texture;
 
-				if ( texture !== null ) {
+				textures.updateSampler( texture );
 
-					textures.updateSampler( texture );
+				const samplerGPU = textures.getSampler( texture );
 
-					const samplerGPU = textures.getSampler( texture );
+				if ( binding.samplerGPU !== samplerGPU ) {
 
-					if ( binding.samplerGPU !== samplerGPU ) {
-
-						binding.samplerGPU = samplerGPU;
-						needsBindGroupRefresh = true;
-
-					}
+					binding.samplerGPU = samplerGPU;
+					needsBindGroupRefresh = true;
 
 				}
 
 			} else if ( binding.isSampledTexture ) {
 
 				const material = object.material;
-				const texture = material[ binding.name ];
+				const texture = binding.texture;
 
-				if ( texture !== null ) {
+				const forceUpdate = textures.updateTexture( texture );
+				const textureGPU = textures.getTextureGPU( texture );
 
-					const forceUpdate = textures.updateTexture( texture );
-					const textureGPU = textures.getTextureGPU( texture );
+				if ( binding.textureGPU !== textureGPU || forceUpdate === true ) {
 
-					if ( binding.textureGPU !== textureGPU || forceUpdate === true ) {
-
-						binding.textureGPU = textureGPU;
-						needsBindGroupRefresh = true;
-
-					}
+					binding.textureGPU = textureGPU;
+					needsBindGroupRefresh = true;
 
 				}
 
@@ -288,9 +280,9 @@ class WebGPUBindings {
 
 	}
 
-	_getMeshBasicBindings() {
+	_getMeshBasicBindings( object ) {
 
-		const bindings = [];
+		let bindings = [];
 
 		// UBOs
 
@@ -332,21 +324,13 @@ class WebGPUBindings {
 
 		} );
 
-		// sampler
-
-		const diffuseSampler = new WebGPUSampler( 'map' );
-
-		// texture
-
-		const diffuseTexture = new WebGPUSampledTexture( 'map' );
-
 		// the order of WebGPUBinding objects must match the binding order in the shader
 
 		bindings.push( modelGroup );
 		bindings.push( cameraGroup );
-		bindings.push( opacityGroup );
-		bindings.push( diffuseSampler );
-		bindings.push( diffuseTexture );
+		bindings = bindings.concat( this.pipelines.getBindings( object ) );
+		//bindings.push( diffuseSampler );
+		//bindings.push( diffuseTexture );
 
 		return bindings;
 

+ 95 - 25
examples/jsm/renderers/webgpu/WebGPUNodeBuilder.js

@@ -1,5 +1,7 @@
 import WebGPUUniformsGroup from './WebGPUUniformsGroup.js';
 import { FloatUniform, Vector3Uniform } from './WebGPUUniform.js';
+import WebGPUSampler from './WebGPUSampler.js';
+import { WebGPUSampledTexture } from './WebGPUSampledTexture.js';
 
 import NodeSlot from '../nodes/core/NodeSlot.js';
 import NodeBuilder from '../nodes/core/NodeBuilder.js';
@@ -36,6 +38,9 @@ class WebGPUNodeBuilder extends NodeBuilder {
 
 		super( material, renderer );
 
+		this.bindingIndex = 2;
+		this.bindings = { vertex: [], fragment: [] };
+
 		this.uniformsGroup = {};
 
 		this._parseMaterial();
@@ -64,23 +69,29 @@ class WebGPUNodeBuilder extends NodeBuilder {
 
 	}
 
-	getUniformNSName( nodeUniform ) {
-
-		return `nodeUniforms.${nodeUniform.name}`;
-
+	getTexture( textureSnippet, uvSnippet ) {
+		
+		return `texture( sampler2D( ${textureSnippet}, ${textureSnippet}_sampler ), ${uvSnippet} )`;
+		
 	}
 
-	getBindings() {
+	getPropertyName( nodeUniform ) {
 
-		const bindings = [];
+		if ( nodeUniform.type === 'texture' ) {
 
-		const uniformsVertexGroup = this.uniformsGroup[ 'vertex' ];
-		const uniformsFragmentGroup = this.uniformsGroup[ 'fragment' ];
+			return nodeUniform.name;
+			
+		} else {
+			
+			return `nodeUniforms.${nodeUniform.name}`;
+			
+		}
 
-		if ( uniformsVertexGroup ) bindings.push( uniformsVertexGroup );
-		if ( uniformsFragmentGroup ) bindings.push( uniformsFragmentGroup );
+	}
+
+	getBindings( shaderStage ) {
 
-		return bindings;
+		return this.bindings[ shaderStage ];
 
 	}
 
@@ -91,34 +102,49 @@ class WebGPUNodeBuilder extends NodeBuilder {
 
 		if ( nodeData.webgpuUniform === undefined ) {
 
-			let uniformsGroup = this.uniformsGroup[ shaderStage ];
+			let uniform;
 
-			if ( uniformsGroup === undefined ) {
+			if ( type === 'texture' ) {
+				
+				const sampler = new WebGPUSampler( `${uniformNode.name}_sampler`, uniformNode.value );
+				const texture = new WebGPUSampledTexture( uniformNode.name, uniformNode.value );
+				
+				// Array.unshift: add first textures in sequence
+				
+				this.bindings[ shaderStage ].unshift( sampler, texture );
+				
+			} else {
 
-				uniformsGroup = new WebGPUNodeUniformsGroup( shaderStage );
+				let uniformsGroup = this.uniformsGroup[ shaderStage ];
 
-				this.uniformsGroup[ shaderStage ] = uniformsGroup;
+				if ( uniformsGroup === undefined ) {
 
-			}
+					uniformsGroup = new WebGPUNodeUniformsGroup( shaderStage );
 
-			let uniform;
+					this.uniformsGroup[ shaderStage ] = uniformsGroup;
 
-			if ( type === 'float' ) {
+					this.bindings[ shaderStage ].push( uniformsGroup );
 
-				uniform = new FloatUniform( uniformNode.name, uniformNode.value );
+				}
 
-			} else if ( type === 'vec3' ) {
+				if ( type === 'float' ) {
 
-				uniform = new Vector3Uniform( uniformNode.name, uniformNode.value );
+					uniform = new FloatUniform( uniformNode );
 
-			} else {
+				} else if ( type === 'vec3' ) {
 
-				console.error( `Uniform "${type}" not declared.` );
+					uniform = new Vector3Uniform( uniformNode.name, uniformNode.value );
 
-			}
+				} else {
+
+					throw new Error( `Uniform "${type}" not declared.` );
 
-			uniformsGroup.addUniform( uniform );
+				}
 
+				uniformsGroup.addUniform( uniform );
+
+			}
+			
 			nodeData.webgpuUniform = uniform;
 
 		}
@@ -127,6 +153,50 @@ class WebGPUNodeBuilder extends NodeBuilder {
 
 	}
 
+	getUniformsOutput( shaderStage ) {
+		
+		const uniforms = this.uniforms[ shaderStage ];
+		
+		let uniformsCode = '';
+		let uniformGroupCode = '';
+		
+		let bindingIndex = this.bindingIndex;
+
+		for ( let uniform of uniforms ) {
+
+			if (uniform.type === 'texture') {
+
+				uniformsCode += `layout(set = 0, binding = ${bindingIndex++}) uniform sampler ${uniform.name}_sampler;`;
+				uniformsCode += `layout(set = 0, binding = ${bindingIndex++}) uniform texture2D ${uniform.name};`;
+
+			} else {
+		
+				if (!uniformGroupCode) {
+					
+					uniformGroupCode = `layout(set = 0, binding = ${bindingIndex++}) uniform NodeUniforms {`;
+					
+				}
+				
+				uniformGroupCode += `uniform ${uniform.type} ${uniform.name};`;
+				
+			}
+
+		}
+		
+		if (uniformGroupCode) {
+			
+			uniformGroupCode += `} nodeUniforms;`;
+			
+			uniformsCode += uniformGroupCode;
+			
+		}
+		
+		console.log( uniformsCode );
+		
+		return uniformsCode;
+		
+	}
+
 	buildShader( shaderStage, code ) {
 
 		// use regex maybe for security?

+ 38 - 0
examples/jsm/renderers/webgpu/WebGPUNodes.js

@@ -0,0 +1,38 @@
+
+class WebGPUNodes {
+
+	constructor( ) {
+
+		this.uniformsData = new WeakMap();
+
+	}
+
+	get( material ) {
+
+		let data = this.uniformsData.get( object );
+
+		if ( data === undefined ) {
+			
+			this.uniformsData.set( object, data );
+
+		}
+
+		return data;
+
+	}
+
+	update( object, camera ) {
+
+		
+
+	}
+
+	dispose() {
+
+		this.uniformsData = new WeakMap();
+
+	}
+
+}
+
+export default WebGPUNodes;

+ 11 - 2
examples/jsm/renderers/webgpu/WebGPURenderPipelines.js

@@ -23,6 +23,7 @@ class WebGPURenderPipelines {
 		this.glslang = glslang;
 		this.sampleCount = sampleCount;
 
+		this.nodes = new WeakMap();
 		this.bindings = new WeakMap();
 
 		this.pipelines = new WeakMap();
@@ -85,7 +86,9 @@ class WebGPURenderPipelines {
 
 			shader = nodeBuilder.parse( shader.vertexShader, shader.fragmentShader );
 
-			this.bindings.set( object, nodeBuilder.getBindings() );
+			this.nodes.set( material, nodeBuilder.nodes );
+
+			this.bindings.set( object, nodeBuilder.getBindings( 'fragment' ) );
 
 			// shader modules
 
@@ -109,7 +112,7 @@ class WebGPURenderPipelines {
 			let moduleFragment = this.shaderModules.fragment.get( shader );
 
 			if ( moduleFragment === undefined ) {
-
+console.log( shader.fragmentShader );
 				const byteCodeFragment = glslang.compileGLSL( shader.fragmentShader, 'fragment' );
 
 				moduleFragment = {
@@ -228,6 +231,12 @@ class WebGPURenderPipelines {
 
 	}
 
+	getNodes( material ) {
+
+		return this.nodes.get( material );
+
+	}
+
 	getBindings( object ) {
 
 		return this.bindings.get( object );

+ 3 - 1
examples/jsm/renderers/webgpu/WebGPUSampledTexture.js

@@ -3,10 +3,12 @@ import { GPUBindingType, GPUTextureViewDimension } from './constants.js';
 
 class WebGPUSampledTexture extends WebGPUBinding {
 
-	constructor( name ) {
+	constructor( name, texture ) {
 
 		super( name );
 
+		this.texture = texture;
+
 		this.dimension = GPUTextureViewDimension.TwoD;
 
 		this.type = GPUBindingType.SampledTexture;

+ 3 - 1
examples/jsm/renderers/webgpu/WebGPUSampler.js

@@ -3,10 +3,12 @@ import { GPUBindingType } from './constants.js';
 
 class WebGPUSampler extends WebGPUBinding {
 
-	constructor( name ) {
+	constructor( name, texture ) {
 
 		super( name );
 
+		this.texture = texture;
+
 		this.type = GPUBindingType.Sampler;
 		this.visibility = GPUShaderStage.FRAGMENT;
 

+ 16 - 2
examples/jsm/renderers/webgpu/WebGPUUniform.js

@@ -19,14 +19,22 @@ class WebGPUUniform {
 		this.value = value;
 
 	}
+	
+	getValue() {
+		
+		return this.value;
+		
+	}
 
 }
 
 class FloatUniform extends WebGPUUniform {
 
-	constructor( name, value = 0 ) {
+	constructor( nodeUniform ) {
 
-		super( name, value );
+		super( nodeUniform.name, nodeUniform.value );
+
+		this.nodeUniform = nodeUniform;
 
 		this.boundary = 4;
 		this.itemSize = 1;
@@ -34,6 +42,12 @@ class FloatUniform extends WebGPUUniform {
 		Object.defineProperty( this, 'isFloatUniform', { value: true } );
 
 	}
+	
+	getValue() {
+		
+		return this.nodeUniform.value;
+		
+	}
 
 }
 

+ 1 - 1
examples/jsm/renderers/webgpu/WebGPUUniformsGroup.js

@@ -127,7 +127,7 @@ class WebGPUUniformsGroup extends WebGPUBinding {
 		let updated = false;
 
 		const a = this.array;
-		const v = uniform.value;
+		const v = uniform.getValue();
 		const offset = uniform.offset;
 
 		if ( a[ offset ] !== v ) {

+ 24 - 7
examples/webgpu_sandbox.html

@@ -21,9 +21,14 @@
 
 			import FloatNode from './jsm/renderers/nodes/inputs/FloatNode.js';
 			import Vector3Node from './jsm/renderers/nodes/inputs/Vector3Node.js';
+			import TextureNode from './jsm/renderers/nodes/inputs/TextureNode.js';
+			import UVNode from './jsm/renderers/nodes/accessors/UVNode.js';
 			import OperatorNode from './jsm/renderers/nodes/math/OperatorNode.js';
+			import TimerNode from './jsm/renderers/nodes/utils/TimerNode.js';
 
 			let camera, scene, renderer;
+			
+			let timerNode;
 
 			let box;
 
@@ -49,13 +54,23 @@
 
 				const textureLoader = new THREE.TextureLoader();
 				const texture = textureLoader.load( './textures/uv_grid_opengl.jpg' );
+				texture.wrapS = THREE.RepeatWrapping;
+				texture.wrapT = THREE.RepeatWrapping;
 
 				const geometryBox = new THREE.BoxBufferGeometry();
 				const materialBox = new THREE.MeshBasicMaterial( { map: texture } );
 
-				materialBox.colorNode = new OperatorNode( '+', new FloatNode( .5 ).setConst( true ), new Vector3Node( new THREE.Vector3( 0, 0, 1 ) ) );
-				materialBox.opacityNode = new FloatNode( .5 );
-				//materialBox.transparent = true;
+				timerNode = new TimerNode();
+				
+				const timerScaleNode = new OperatorNode( '*', timerNode, new FloatNode( .5 ).setConst( true ) );
+				const animateUV = new OperatorNode( '+', new UVNode(), timerScaleNode );
+
+				//materialBox.colorNode = new OperatorNode( '+', new FloatNode( .5 ).setConst( true ), new Vector3Node( new THREE.Vector3( 0, 0, 1 ) ) );
+				materialBox.colorNode = new OperatorNode( '+', new TextureNode( texture, animateUV ), new Vector3Node( new THREE.Vector3( 0, 0, 1 ) ) );
+				//materialBox.colorNode = new Vector3Node( new THREE.Vector3( 1, 1, 1 ) );
+				//materialBox.colorNode = new TextureNode( texture );
+				//materialBox.opacityNode = timerNode;
+				materialBox.transparent = true;
 
 				box = new THREE.Mesh( geometryBox, materialBox );
 				box.position.set( 0, 1, 0 );
@@ -68,7 +83,7 @@
 
 				const plane = new THREE.Mesh( geometryPlane, materialPlane );
 				plane.position.set( 0, - 1, 0 );
-				scene.add( plane );
+				//scene.add( plane );
 
 				// compressed texture
 
@@ -79,7 +94,7 @@
 
 				const boxCompressed = new THREE.Mesh( geometryBox, materialCompressed );
 				boxCompressed.position.set( - 2, 1, 0 );
-				scene.add( boxCompressed );
+				//scene.add( boxCompressed );
 
 				// points
 
@@ -99,7 +114,7 @@
 
 				const pointCloud = new THREE.Points( geometryPoints, materialPoints );
 				pointCloud.position.set( 2, - 1, 0 );
-				scene.add( pointCloud );
+				//scene.add( pointCloud );
 
 				// lines
 
@@ -112,7 +127,7 @@
 				const materialLine = new THREE.LineBasicMaterial();
 				const line = new THREE.Line( geometryLine, materialLine );
 				line.position.set( 2, 1, 0 );
-				scene.add( line );
+				//scene.add( line );
 
 				//
 
@@ -140,6 +155,8 @@
 
 				requestAnimationFrame( animate );
 
+				timerNode.update();
+
 				box.rotation.x += 0.01;
 				box.rotation.y += 0.02;