Skip to content

Commit

Permalink
WebGPURenderer: MorphNode 1/2 and updates (mrdoob#26325)
Browse files Browse the repository at this point in the history
* Added: instancedBufferAttribute() and instancedDynamicBufferAttribute()

* TSL: vertexIndex

* Background: Fix color conversion

* NodeMaterial: Added flatShading

* Added MorphNode

* Added `webgpu_morphtargets` example

* Update examples/jsm/nodes/accessors/BufferAttributeNode.js

Co-authored-by: Levi Pesin <[email protected]>

---------

Co-authored-by: Levi Pesin <[email protected]>
  • Loading branch information
sunag and LeviPesin committed Jun 25, 2023
1 parent 80118ab commit c944e5a
Show file tree
Hide file tree
Showing 17 changed files with 402 additions and 65 deletions.
1 change: 1 addition & 0 deletions examples/files.json
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,7 @@
"webgpu_loader_gltf_compressed",
"webgpu_materials",
"webgpu_materials_video",
"webgpu_morphtargets",
"webgpu_particles",
"webgpu_rtt",
"webgpu_sandbox",
Expand Down
5 changes: 3 additions & 2 deletions examples/jsm/nodes/Nodes.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export { default as BypassNode, bypass } from './core/BypassNode.js';
export { default as CacheNode, cache } from './core/CacheNode.js';
export { default as ConstNode } from './core/ConstNode.js';
export { default as ContextNode, context, label } from './core/ContextNode.js';
export { default as InstanceIndexNode, instanceIndex } from './core/InstanceIndexNode.js';
export { default as IndexNode, vertexIndex, instanceIndex } from './core/IndexNode.js';
export { default as LightingModel, lightingModel } from './core/LightingModel.js';
export { default as Node, addNodeClass, createNodeFromType } from './core/Node.js';
export { default as NodeAttribute } from './core/NodeAttribute.js';
Expand Down Expand Up @@ -63,14 +63,15 @@ export * from './shadernode/ShaderNode.js';

// accessors
export { default as BitangentNode, bitangentGeometry, bitangentLocal, bitangentView, bitangentWorld, transformedBitangentView, transformedBitangentWorld } from './accessors/BitangentNode.js';
export { default as BufferAttributeNode, bufferAttribute, dynamicBufferAttribute } from './accessors/BufferAttributeNode.js';
export { default as BufferAttributeNode, bufferAttribute, dynamicBufferAttribute, instancedBufferAttribute, instancedDynamicBufferAttribute } from './accessors/BufferAttributeNode.js';
export { default as BufferNode, buffer } from './accessors/BufferNode.js';
export { default as CameraNode, cameraProjectionMatrix, cameraViewMatrix, cameraNormalMatrix, cameraWorldMatrix, cameraPosition, cameraNear, cameraFar } from './accessors/CameraNode.js';
export { default as CubeTextureNode, cubeTexture } from './accessors/CubeTextureNode.js';
export { default as ExtendedMaterialNode, materialNormal } from './accessors/ExtendedMaterialNode.js';
export { default as InstanceNode, instance } from './accessors/InstanceNode.js';
export { default as MaterialNode, materialUV, materialAlphaTest, materialColor, materialShininess, materialEmissive, materialOpacity, materialSpecularColor, materialReflectivity, materialRoughness, materialMetalness, materialRotation } from './accessors/MaterialNode.js';
export { default as MaterialReferenceNode, materialReference } from './accessors/MaterialReferenceNode.js';
export { default as MorphNode, morph } from './accessors/MorphNode.js';
export { default as TextureBicubicNode, textureBicubic } from './accessors/TextureBicubicNode.js';
export { default as ModelNode, modelDirection, modelViewMatrix, modelNormalMatrix, modelWorldMatrix, modelPosition, modelViewPosition, modelScale } from './accessors/ModelNode.js';
export { default as ModelViewProjectionNode, modelViewProjection } from './accessors/ModelViewProjectionNode.js';
Expand Down
29 changes: 21 additions & 8 deletions examples/jsm/nodes/accessors/BufferAttributeNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class BufferAttributeNode extends InputNode {
this.bufferOffset = bufferOffset;

this.usage = StaticDrawUsage;
this.instanced = false;

}

Expand All @@ -34,7 +35,7 @@ class BufferAttributeNode extends InputNode {
buffer.setUsage( this.usage );

this.attribute = bufferAttribute;
this.attribute.isInstancedBufferAttribute = true; // @TODO: Add a possible: InstancedInterleavedBufferAttribute
this.attribute.isInstancedBufferAttribute = this.instanced; // @TODO: Add a possible: InstancedInterleavedBufferAttribute

}

Expand Down Expand Up @@ -69,18 +70,30 @@ class BufferAttributeNode extends InputNode {

}

setUsage( value ) {

this.usage = value;

return this;

}

setInstanced( value ) {

this.instanced = value;

return this;

}

}

export default BufferAttributeNode;

export const bufferAttribute = ( array, type, stride, offset ) => nodeObject( new BufferAttributeNode( array, type, stride, offset ) );
export const dynamicBufferAttribute = ( array, type, stride, offset ) => {

const node = bufferAttribute( array, type, stride, offset );
node.usage = DynamicDrawUsage;

return node;
export const dynamicBufferAttribute = ( array, type, stride, offset ) => bufferAttribute( array, type, stride, offset ).setUsage( DynamicDrawUsage );

};
export const instancedBufferAttribute = ( array, type, stride, offset ) => bufferAttribute( array, type, stride, offset ).setInstanced( true );
export const instancedDynamicBufferAttribute = ( array, type, stride, offset ) => dynamicBufferAttribute( array, type, stride, offset ).setInstanced( true );

addNodeClass( BufferAttributeNode );
4 changes: 2 additions & 2 deletions examples/jsm/nodes/accessors/InstanceNode.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Node, { addNodeClass } from '../core/Node.js';
import { bufferAttribute, dynamicBufferAttribute } from './BufferAttributeNode.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';
Expand Down Expand Up @@ -27,7 +27,7 @@ class InstanceNode extends Node {
const instaceAttribute = instanceMesh.instanceMatrix;
const array = instaceAttribute.array;

const bufferFn = instaceAttribute.usage === DynamicDrawUsage ? dynamicBufferAttribute : bufferAttribute;
const bufferFn = instaceAttribute.usage === DynamicDrawUsage ? instancedDynamicBufferAttribute : instancedBufferAttribute;

const instanceBuffers = [
// F.Signature -> bufferAttribute( array, type, stride, offset )
Expand Down
70 changes: 70 additions & 0 deletions examples/jsm/nodes/accessors/MorphNode.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import Node, { addNodeClass } from '../core/Node.js';
import { NodeUpdateType } from '../core/constants.js';
import { nodeProxy } from '../shadernode/ShaderNode.js';
import { uniform } from '../core/UniformNode.js';
import { reference } from './ReferenceNode.js';
import { bufferAttribute } from './BufferAttributeNode.js';
import { positionLocal } from './PositionNode.js';

class MorphNode extends Node {

constructor( mesh ) {

super( 'void' );

this.mesh = mesh;
this.morphBaseInfluence = uniform( 1 );

this.updateType = NodeUpdateType.OBJECT;

}

constructAttribute( builder, name, assignNode = positionLocal ) {

const mesh = this.mesh;
const attributes = mesh.geometry.morphAttributes[ name ];

builder.stack.assign( assignNode, assignNode.mul( this.morphBaseInfluence ) );

for ( let i = 0; i < attributes.length; i ++ ) {

const attribute = attributes[ i ];

const bufferAttrib = bufferAttribute( attribute.array, 'vec3' );
const influence = reference( i, 'float', mesh.morphTargetInfluences );

builder.stack.assign( assignNode, assignNode.add( bufferAttrib.mul( influence ) ) );

}

}

construct( builder ) {

this.constructAttribute( builder, 'position' );

}

update() {

const morphBaseInfluence = this.morphBaseInfluence;

if ( this.mesh.geometry.morphTargetsRelative ) {

morphBaseInfluence.value = 1;

} else {

morphBaseInfluence.value = 1 - this.mesh.morphTargetInfluences.reduce( ( a, b ) => a + b, 0 );

}

}

}

export default MorphNode;

export const morph = nodeProxy( MorphNode );

addNodeClass( MorphNode );
66 changes: 66 additions & 0 deletions examples/jsm/nodes/core/IndexNode.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import Node, { addNodeClass } from './Node.js';
import { varying } from './VaryingNode.js';
import { nodeImmutable } from '../shadernode/ShaderNode.js';

class IndexNode extends Node {

constructor( scope ) {

super( 'uint' );

this.scope = scope;

this.isInstanceIndexNode = true;

}

generate( builder ) {

const nodeType = this.getNodeType( builder );
const scope = this.scope;

let propertyName;

if ( scope === IndexNode.VERTEX ) {

propertyName = builder.getVertexIndex();

} else if ( scope === IndexNode.INSTANCE ) {

propertyName = builder.getInstanceIndex();

} else {

throw new Error( 'THREE.IndexNode: Unknown scope: ' + scope );

}

let output;

if ( builder.shaderStage === 'vertex' || builder.shaderStage === 'compute' ) {

output = propertyName;

} else {

const nodeVarying = varying( this );

output = nodeVarying.build( builder, nodeType );

}

return output;

}

}

IndexNode.VERTEX = 'vertex';
IndexNode.INSTANCE = 'instance';

export default IndexNode;

export const vertexIndex = nodeImmutable( IndexNode, IndexNode.VERTEX );
export const instanceIndex = nodeImmutable( IndexNode, IndexNode.INSTANCE );

addNodeClass( IndexNode );
45 changes: 0 additions & 45 deletions examples/jsm/nodes/core/InstanceIndexNode.js

This file was deleted.

6 changes: 6 additions & 0 deletions examples/jsm/nodes/core/NodeBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,12 @@ class NodeBuilder {

}

getVertexIndex() {

console.warn( 'Abstract function.' );

}

getInstanceIndex() {

console.warn( 'Abstract function.' );
Expand Down
2 changes: 1 addition & 1 deletion examples/jsm/nodes/geometry/RangeNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Node, { addNodeClass } from '../core/Node.js';
import { getValueType } from '../core/NodeUtils.js';
import { buffer } from '../accessors/BufferNode.js';
//import { bufferAttribute } from '../accessors/BufferAttributeNode.js';
import { instanceIndex } from '../core/InstanceIndexNode.js';
import { instanceIndex } from '../core/IndexNode.js';
import { nodeProxy, float } from '../shadernode/ShaderNode.js';

import { Vector4, MathUtils } from 'three';
Expand Down
28 changes: 23 additions & 5 deletions examples/jsm/nodes/materials/NodeMaterial.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ import { materialAlphaTest, materialColor, materialOpacity, materialEmissive } f
import { modelViewProjection } from '../accessors/ModelViewProjectionNode.js';
import { transformedNormalView } from '../accessors/NormalNode.js';
import { instance } from '../accessors/InstanceNode.js';
import { positionLocal } from '../accessors/PositionNode.js';
import { positionLocal, positionView } from '../accessors/PositionNode.js';
import { skinning } from '../accessors/SkinningNode.js';
import { morph } from '../accessors/MorphNode.js';
import { texture } from '../accessors/TextureNode.js';
import { cubeTexture } from '../accessors/CubeTextureNode.js';
import { lightsWithoutWrap } from '../lighting/LightsNode.js';
import { mix } from '../math/MathNode.js';
import { mix, dFdx, dFdy } from '../math/MathNode.js';
import { float, vec3, vec4 } from '../shadernode/ShaderNode.js';
import AONode from '../lighting/AONode.js';
import EnvironmentNode from '../lighting/EnvironmentNode.js';
Expand Down Expand Up @@ -99,9 +100,16 @@ class NodeMaterial extends ShaderMaterial {
constructPosition( builder ) {

const object = builder.object;
const geometry = object.geometry;

builder.addStack();

if ( geometry.morphAttributes.position || geometry.morphAttributes.normal || geometry.morphAttributes.color ) {

builder.stack.add( morph( object ) );

}

if ( object.isSkinnedMesh === true ) {

builder.stack.add( skinning( object ) );
Expand Down Expand Up @@ -169,11 +177,21 @@ class NodeMaterial extends ShaderMaterial {

// NORMAL VIEW

const normalNode = this.normalNode ? vec3( this.normalNode ) : materialNormal;
if ( this.flatShading === true ) {

const fdx = dFdx( positionView );
const fdy = dFdy( positionView.negate() ); // use -positionView ?
const normalNode = fdx.cross( fdy ).normalize();

stack.assign( transformedNormalView, normalNode );
stack.assign( transformedNormalView, normalNode );

return normalNode;
} else {

const normalNode = this.normalNode ? vec3( this.normalNode ) : materialNormal;

stack.assign( transformedNormalView, normalNode );

}

}

Expand Down
4 changes: 2 additions & 2 deletions examples/jsm/renderers/common/Background.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,14 @@ class Background extends DataMap {

// no background settings, use clear color configuration from the renderer

_clearColor.copy( renderer._clearColor );
_clearColor.copyLinearToSRGB( renderer._clearColor );
_clearAlpha = renderer._clearAlpha;

} else if ( background.isColor === true ) {

// background is an opaque color

_clearColor.copy( background );
_clearColor.copyLinearToSRGB( background );
_clearAlpha = 1;
forceClear = true;

Expand Down
6 changes: 6 additions & 0 deletions examples/jsm/renderers/webgl/nodes/GLSLNodeBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,12 @@ class GLSLNodeBuilder extends NodeBuilder {

}

getVertexIndex() {

return 'gl_VertexID';

}

getFrontFacing() {

return 'gl_FrontFacing';
Expand Down
Loading

0 comments on commit c944e5a

Please sign in to comment.