diff --git a/examples/files.json b/examples/files.json index f300653323b067..f46006a7b5e663 100644 --- a/examples/files.json +++ b/examples/files.json @@ -381,7 +381,8 @@ "webgpu_mirror", "webgpu_multisampled_renderbuffers", "webgpu_materials_texture_anisotropy", - "webgpu_storage_buffer" + "webgpu_storage_buffer", + "webgpu_instancing_morph" ], "webaudio": [ "webaudio_orientation", diff --git a/examples/jsm/nodes/accessors/InstanceNode.js b/examples/jsm/nodes/accessors/InstanceNode.js index f85d2ff027a195..54adfd0d3efcee 100644 --- a/examples/jsm/nodes/accessors/InstanceNode.js +++ b/examples/jsm/nodes/accessors/InstanceNode.js @@ -1,9 +1,10 @@ import Node, { addNodeClass } from '../core/Node.js'; +import { varyingProperty } from '../core/PropertyNode.js'; import { instancedBufferAttribute, instancedDynamicBufferAttribute } from './BufferAttributeNode.js'; import { normalLocal } from './NormalNode.js'; import { positionLocal } from './PositionNode.js'; import { nodeProxy, vec3, mat3, mat4 } from '../shadernode/ShaderNode.js'; -import { DynamicDrawUsage, InstancedInterleavedBuffer } from 'three'; +import { DynamicDrawUsage, InstancedInterleavedBuffer, InstancedBufferAttribute } from 'three'; class InstanceNode extends Node { @@ -15,15 +16,18 @@ class InstanceNode extends Node { this.instanceMatrixNode = null; + this.instanceColorNode = null; + } setup( /*builder*/ ) { let instanceMatrixNode = this.instanceMatrixNode; + const instanceMesh = this.instanceMesh; + if ( instanceMatrixNode === null ) { - const instanceMesh = this.instanceMesh; const instanceAttribute = instanceMesh.instanceMatrix; const buffer = new InstancedInterleavedBuffer( instanceAttribute.array, 16, 1 ); @@ -43,6 +47,17 @@ class InstanceNode extends Node { } + const instanceColorAttribute = instanceMesh.instanceColor; + + if ( instanceColorAttribute && this.instanceColorNode === null ) { + + const buffer = new InstancedBufferAttribute( instanceColorAttribute.array, 3 ); + const bufferFn = instanceColorAttribute.usage === DynamicDrawUsage ? instancedDynamicBufferAttribute : instancedBufferAttribute; + + this.instanceColorNode = vec3( bufferFn( buffer, 'vec3', 3, 0 ) ); + + } + // POSITION const instancePosition = instanceMatrixNode.mul( positionLocal ).xyz; @@ -60,6 +75,14 @@ class InstanceNode extends Node { positionLocal.assign( instancePosition ); normalLocal.assign( instanceNormal ); + // COLOR + + if ( this.instanceColorNode !== null ) { + + varyingProperty( 'vec3', 'vInstanceColor' ).assign( this.instanceColorNode ); + + } + } } diff --git a/examples/jsm/nodes/accessors/MorphNode.js b/examples/jsm/nodes/accessors/MorphNode.js index 821852acc47a7b..712dcc239056d5 100644 --- a/examples/jsm/nodes/accessors/MorphNode.js +++ b/examples/jsm/nodes/accessors/MorphNode.js @@ -1,12 +1,12 @@ import Node, { addNodeClass } from '../core/Node.js'; import { NodeUpdateType } from '../core/constants.js'; -import { nodeProxy, tslFn } from '../shadernode/ShaderNode.js'; +import { float, nodeProxy, tslFn } from '../shadernode/ShaderNode.js'; import { uniform } from '../core/UniformNode.js'; import { reference } from './ReferenceNode.js'; import { positionLocal } from './PositionNode.js'; import { normalLocal } from './NormalNode.js'; import { textureLoad } from './TextureNode.js'; -import { vertexIndex } from '../core/IndexNode.js'; +import { instanceIndex, vertexIndex } from '../core/IndexNode.js'; import { ivec2, int } from '../shadernode/ShaderNode.js'; import { DataArrayTexture, Vector2, Vector4, FloatType } from 'three'; import { loop } from '../utils/LoopNode.js'; @@ -188,7 +188,17 @@ class MorphNode extends Node { loop( morphTargetsCount, ( { i } ) => { - const influence = reference( 'morphTargetInfluences', 'float' ).element( i ); + const influence = float( 0 ).toVar(); + + if ( this.mesh.isInstancedMesh === true && this.mesh.morphTexture !== null ) { + + influence.assign( textureLoad( this.mesh.morphTexture, ivec2( int( i ).add( 1 ), int( instanceIndex ) ) ).r ); + + } else { + + influence.assign( reference( 'morphTargetInfluences', 'float' ).element( i ).toVar() ); + + } if ( hasMorphPosition === true ) { diff --git a/examples/jsm/nodes/accessors/NormalNode.js b/examples/jsm/nodes/accessors/NormalNode.js index 6b850f5fc606f7..f14086c8ee99af 100644 --- a/examples/jsm/nodes/accessors/NormalNode.js +++ b/examples/jsm/nodes/accessors/NormalNode.js @@ -5,7 +5,7 @@ import { property } from '../core/PropertyNode.js'; import { normalize } from '../math/MathNode.js'; import { cameraViewMatrix } from './CameraNode.js'; import { modelNormalMatrix } from './ModelNode.js'; -import { nodeImmutable } from '../shadernode/ShaderNode.js'; +import { nodeImmutable, vec3 } from '../shadernode/ShaderNode.js'; class NormalNode extends Node { @@ -37,7 +37,17 @@ class NormalNode extends Node { if ( scope === NormalNode.GEOMETRY ) { - outputNode = attribute( 'normal', 'vec3' ); + const geometryAttribute = builder.hasGeometryAttribute( 'normal' ); + + if ( geometryAttribute === false ) { + + outputNode = vec3( 0, 1, 0 ); + + } else { + + outputNode = attribute( 'normal', 'vec3' ); + + } } else if ( scope === NormalNode.LOCAL ) { diff --git a/examples/jsm/nodes/materials/NodeMaterial.js b/examples/jsm/nodes/materials/NodeMaterial.js index 790ebd7acdf111..a22190552eb198 100644 --- a/examples/jsm/nodes/materials/NodeMaterial.js +++ b/examples/jsm/nodes/materials/NodeMaterial.js @@ -1,7 +1,7 @@ import { Material, ShaderMaterial, NoColorSpace, LinearSRGBColorSpace } from 'three'; import { getNodeChildren, getCacheKey } from '../core/NodeUtils.js'; import { attribute } from '../core/AttributeNode.js'; -import { output, diffuseColor } from '../core/PropertyNode.js'; +import { output, diffuseColor, varyingProperty } from '../core/PropertyNode.js'; import { materialAlphaTest, materialColor, materialOpacity, materialEmissive, materialNormal } from '../accessors/MaterialNode.js'; import { modelViewProjection } from '../accessors/ModelViewProjectionNode.js'; import { transformedNormalView } from '../accessors/NormalNode.js'; @@ -220,7 +220,7 @@ class NodeMaterial extends ShaderMaterial { } - setupDiffuseColor( { geometry } ) { + setupDiffuseColor( { object, geometry } ) { let colorNode = this.colorNode ? vec4( this.colorNode ) : materialColor; @@ -232,6 +232,16 @@ class NodeMaterial extends ShaderMaterial { } + // Instanced colors + + if ( object.instanceColor ) { + + const instanceColor = varyingProperty( 'vec3', 'vInstanceColor' ); + + colorNode = instanceColor.mul( colorNode ); + + } + // COLOR diffuseColor.assign( colorNode ); diff --git a/examples/screenshots/webgpu_instancing_morph.jpg b/examples/screenshots/webgpu_instancing_morph.jpg new file mode 100644 index 00000000000000..1cab133b4271c4 Binary files /dev/null and b/examples/screenshots/webgpu_instancing_morph.jpg differ diff --git a/examples/webgpu_instancing_morph.html b/examples/webgpu_instancing_morph.html new file mode 100644 index 00000000000000..3ad2a9cf0e4327 --- /dev/null +++ b/examples/webgpu_instancing_morph.html @@ -0,0 +1,195 @@ + + + + three.js webgpu - instancing - Morph Target Animations + + + + + + + + + + + diff --git a/test/e2e/puppeteer.js b/test/e2e/puppeteer.js index fe01ea5ccd622a..bb0ce13ab17a43 100644 --- a/test/e2e/puppeteer.js +++ b/test/e2e/puppeteer.js @@ -139,6 +139,7 @@ const exceptionList = [ 'webgpu_tsl_transpiler', 'webgpu_portal', 'webgpu_custom_fog', + 'webgpu_instancing_morph', // WebGPU idleTime and parseTime too low 'webgpu_compute_particles',