diff --git a/examples/jsm/nodes/Nodes.js b/examples/jsm/nodes/Nodes.js index 9db70a39559618..a751fb54a96592 100644 --- a/examples/jsm/nodes/Nodes.js +++ b/examples/jsm/nodes/Nodes.js @@ -45,6 +45,9 @@ import TextureNode from './accessors/TextureNode.js'; import UVNode from './accessors/UVNode.js'; import UserDataNode from './accessors/UserDataNode.js'; +// geometry +import RangeNode from './geometry/RangeNode.js'; + // gpgpu import ComputeNode from './gpgpu/ComputeNode.js'; @@ -130,7 +133,10 @@ const nodeLib = { VarNode, VaryNode, - // compute + // geometry + RangeNode, + + // gpgpu ComputeNode, // accessors @@ -234,7 +240,10 @@ export { VarNode, VaryNode, - // compute + // geometry + RangeNode, + + // gpgpu ComputeNode, // accessors diff --git a/examples/jsm/nodes/geometry/RangeNode.js b/examples/jsm/nodes/geometry/RangeNode.js new file mode 100644 index 00000000000000..7476665aaac482 --- /dev/null +++ b/examples/jsm/nodes/geometry/RangeNode.js @@ -0,0 +1,101 @@ +import Node from '../core/Node.js'; +import { attribute, float } from '../shadernode/ShaderNodeBaseElements.js'; +import { MathUtils, InstancedBufferAttribute } from 'three'; + +class RangeNode extends Node { + + constructor( min, max ) { + + super(); + + this.min = min; + this.max = max; + + } + + getVectorLength() { + + const min = this.min; + + let length = 1; + + if ( min.isVector2 ) length = 2; + else if ( min.isVector3 ) length = 3; + else if ( min.isVector4 ) length = 4; + else if ( min.isColor ) length = 3; + + return length; + + } + + getNodeType( builder ) { + + return ( builder.object.isInstancedMesh === true ) ? builder.getTypeFromLength( this.getVectorLength() ) : 'float'; + + } + + construct( builder ) { + + const { min, max } = this; + const { object, geometry } = builder; + + const vectorLength = this.getVectorLength(); + const attributeName = 'node' + this.id; + + let output = null; + + if ( object.isInstancedMesh === true ) { + + const length = vectorLength * object.count; + const array = new Float32Array( length ); + + if ( vectorLength === 1 ) { + + for ( let i = 0; i < length; i ++ ) { + + array[ i ] = MathUtils.lerp( min, max, Math.random() ); + + } + + } else if ( min.isColor ) { + + for ( let i = 0; i < length; i += 3 ) { + + array[ i ] = MathUtils.lerp( min.r, max.r, Math.random() ); + array[ i + 1 ] = MathUtils.lerp( min.g, max.g, Math.random() ); + array[ i + 2 ] = MathUtils.lerp( min.b, max.b, Math.random() ); + + } + + } else { + + for ( let i = 0; i < length; i ++ ) { + + const index = i % vectorLength; + + const minValue = min.getComponent( index ); + const maxValue = max.getComponent( index ); + + array[ i ] = MathUtils.lerp( minValue, maxValue, Math.random() ); + + } + + } + + geometry.setAttribute( attributeName, new InstancedBufferAttribute( array, vectorLength ) ); + + output = attribute( attributeName, builder.getTypeFromLength( vectorLength ) ); + + } else { + + output = float( 0 ); + + } + + return output; + + } + +} + +export default RangeNode; diff --git a/examples/jsm/nodes/shadernode/ShaderNodeElements.js b/examples/jsm/nodes/shadernode/ShaderNodeElements.js index e2b5b4c609be57..da7bce360c90ce 100644 --- a/examples/jsm/nodes/shadernode/ShaderNodeElements.js +++ b/examples/jsm/nodes/shadernode/ShaderNodeElements.js @@ -22,6 +22,9 @@ import OscNode from '../utils/OscNode.js'; import SpriteSheetUVNode from '../utils/SpriteSheetUVNode.js'; import TimerNode from '../utils/TimerNode.js'; +// geometry +import RangeNode from '../geometry/RangeNode.js'; + // procedural import CheckerNode from '../procedural/CheckerNode.js'; @@ -101,6 +104,10 @@ export const timerLocal = ( timeScale ) => nodeObject( new TimerNode( TimerNode. export const timerGlobal = ( timeScale ) => nodeObject( new TimerNode( TimerNode.GLOBAL, timeScale ) ); export const timerDelta = ( timeScale ) => nodeObject( new TimerNode( TimerNode.DELTA, timeScale ) ); +// geometry + +export const range = ( min, max ) => nodeObject( new RangeNode( min, max ) ); + // procedural export const checker = nodeProxy( CheckerNode ); diff --git a/examples/jsm/renderers/webgpu/WebGPURenderer.js b/examples/jsm/renderers/webgpu/WebGPURenderer.js index 1de5eaad755cdf..ad7eeab009f09c 100644 --- a/examples/jsm/renderers/webgpu/WebGPURenderer.js +++ b/examples/jsm/renderers/webgpu/WebGPURenderer.js @@ -770,20 +770,6 @@ class WebGPURenderer { const { object, geometry, material, group } = renderItem; - object.onBeforeRender( this, scene, camera, geometry, material, group ); - - object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); - object.normalMatrix.getNormalMatrix( object.modelViewMatrix ); - - this._objects.update( object ); - - // send scene properties to object - - const objectProperties = this._properties.get( object ); - - objectProperties.lightsNode = lightsNode; - objectProperties.scene = scene; - if ( camera.isArrayCamera ) { const cameras = camera.cameras; @@ -800,9 +786,7 @@ class WebGPURenderer { passEncoder.setViewport( vp.x, vp.y, vp.width, vp.height, minDepth, maxDepth ); - this._nodes.update( object, camera2 ); - this._bindings.update( object ); - this._renderObject( object, passEncoder ); + this._renderObject( object, scene, camera2, geometry, material, group, lightsNode, passEncoder ); } @@ -810,9 +794,7 @@ class WebGPURenderer { } else { - this._nodes.update( object, camera ); - this._bindings.update( object ); - this._renderObject( object, passEncoder ); + this._renderObject( object, scene, camera, geometry, material, group, lightsNode, passEncoder ); } @@ -820,10 +802,30 @@ class WebGPURenderer { } - _renderObject( object, passEncoder ) { + _renderObject( object, scene, camera, geometry, material, group, lightsNode, passEncoder ) { const info = this._info; + // send scene properties to object + + const objectProperties = this._properties.get( object ); + + objectProperties.lightsNode = lightsNode; + objectProperties.scene = scene; + + // + + object.onBeforeRender( this, scene, camera, geometry, material, group ); + + object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); + object.normalMatrix.getNormalMatrix( object.modelViewMatrix ); + + // updates + + this._nodes.update( object, camera ); + this._bindings.update( object ); + this._objects.update( object ); + // pipeline const renderPipeline = this._renderPipelines.get( object ); @@ -836,7 +838,6 @@ class WebGPURenderer { // index - const geometry = object.geometry; const index = geometry.index; const hasIndex = ( index !== null ); diff --git a/examples/webgpu_instance_mesh.html b/examples/webgpu_instance_mesh.html index 5423ecdca28272..e0c0034870d35c 100644 --- a/examples/webgpu_instance_mesh.html +++ b/examples/webgpu_instance_mesh.html @@ -30,7 +30,7 @@ import Stats from './jsm/libs/stats.module.js'; import { GUI } from './jsm/libs/lil-gui.module.min.js'; - import { normalWorld } from 'three-nodes/Nodes.js'; + import { mix, range, normalWorld, oscSine, timerLocal } from 'three-nodes/Nodes.js'; import WebGPU from './jsm/capabilities/WebGPU.js'; import WebGPURenderer from './jsm/renderers/webgpu/WebGPURenderer.js'; @@ -61,7 +61,11 @@ scene = new THREE.Scene(); const material = new THREE.MeshBasicMaterial(); - material.colorNode = normalWorld; + + // random colors between instances from 0x000000 to 0xFFFFFF + const randomColors = range( new THREE.Color( 0x000000 ), new THREE.Color( 0xFFFFFF ) ); + + material.colorNode = mix( normalWorld, randomColors, oscSine( timerLocal( .1 ) ) ); const loader = new THREE.BufferGeometryLoader(); loader.load( 'models/json/suzanne_buffergeometry.json', function ( geometry ) { diff --git a/examples/webgpu_skinning_instancing.html b/examples/webgpu_skinning_instancing.html index cc304ac9b35bb1..79db1dc1df9534 100644 --- a/examples/webgpu_skinning_instancing.html +++ b/examples/webgpu_skinning_instancing.html @@ -28,6 +28,8 @@ import * as THREE from 'three'; import * as Nodes from 'three-nodes/Nodes.js'; + import { mix, range, color, oscSine, timerLocal } from 'three-nodes/Nodes.js'; + import { FBXLoader } from './jsm/loaders/FBXLoader.js'; import WebGPU from './jsm/capabilities/WebGPU.js'; @@ -83,8 +85,16 @@ if ( child.isMesh ) { + // random colors between instances from 0x000000 to 0xFFFFFF + const randomColors = range( new THREE.Color( 0x000000 ), new THREE.Color( 0xFFFFFF ) ); + + // random [ 0, 1 ] values between instances + const randomMetalness = range( 0, 1 ); + child.material = new Nodes.MeshStandardNodeMaterial(); child.material.roughness = .1; + child.material.metalnessNode = randomMetalness; + child.material.colorNode = mix( color( 0xFFFFFF ), randomColors, oscSine( timerLocal( .1 ) ) ); child.isInstancedMesh = true; child.instanceMatrix = new THREE.InstancedBufferAttribute( new Float32Array( instanceCount * 16 ), 16 );