Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WebGPU: Instancing #23835

Merged
merged 6 commits into from
Apr 5, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions examples/files.json
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,7 @@
"webgpu": [
"webgpu_compute",
"webgpu_depth_texture",
"webgpu_instance_mesh",
"webgpu_instance_uniform",
"webgpu_lights_custom",
"webgpu_lights_selective",
Expand All @@ -317,6 +318,7 @@
"webgpu_rtt",
"webgpu_sandbox",
"webgpu_skinning",
"webgpu_skinning_instancing",
"webgpu_skinning_points"
],
"webaudio": [
Expand Down
6 changes: 3 additions & 3 deletions examples/jsm/nodes/Nodes.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import ContextNode from './core/ContextNode.js';
import ExpressionNode from './core/ExpressionNode.js';
import FunctionCallNode from './core/FunctionCallNode.js';
import FunctionNode from './core/FunctionNode.js';
import InstanceIndexNode from './core/InstanceIndexNode.js';
import Node from './core/Node.js';
import NodeAttribute from './core/NodeAttribute.js';
import NodeBuilder from './core/NodeBuilder.js';
Expand Down Expand Up @@ -81,9 +82,6 @@ import FogRangeNode from './fog/FogRangeNode.js';
// core
export * from './core/constants.js';

// functions
export * from './functions/BSDFs.js';

// materials
export * from './materials/Materials.js';

Expand All @@ -101,6 +99,7 @@ const nodeLib = {
ExpressionNode,
FunctionCallNode,
FunctionNode,
InstanceIndexNode,
Node,
NodeAttribute,
NodeBuilder,
Expand Down Expand Up @@ -190,6 +189,7 @@ export {
ExpressionNode,
FunctionCallNode,
FunctionNode,
InstanceIndexNode,
Node,
NodeAttribute,
NodeBuilder,
Expand Down
58 changes: 58 additions & 0 deletions examples/jsm/nodes/accessors/InstanceNode.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import Node from '../core/Node.js';
import {
vec3,
mat3,
mul,
assign,
buffer,
element,
dot,
div,
temp,
instanceIndex,
positionLocal,
normalLocal
} from '../shadernode/ShaderNodeElements.js';

class InstanceNode extends Node {

constructor( instanceMesh ) {

super( 'void' );

this.instanceMesh = instanceMesh;

//

const instanceBufferNode = buffer( instanceMesh.instanceMatrix.array, 'mat4', instanceMesh.count );

this.instanceMatrixNode = temp( element( instanceBufferNode, instanceIndex ) );

}

generate( builder ) {

const { instanceMatrixNode } = this;

// POSITION

const instancePosition = mul( instanceMatrixNode, positionLocal ).xyz;

// NORMAL

const m = mat3( instanceMatrixNode[ 0 ].xyz, instanceMatrixNode[ 1 ].xyz, instanceMatrixNode[ 2 ].xyz );

const transformedNormal = div( normalLocal, vec3( dot( m[ 0 ], m[ 0 ] ), dot( m[ 1 ], m[ 1 ] ), dot( m[ 2 ], m[ 2 ] ) ) );

const instanceNormal = mul( m, transformedNormal ).xyz;

// ASSIGNS

assign( positionLocal, instancePosition ).build( builder );
assign( normalLocal, instanceNormal ).build( builder );

}

}

export default InstanceNode;
21 changes: 21 additions & 0 deletions examples/jsm/nodes/core/InstanceIndexNode.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import Node from './Node.js';

class InstanceIndexNode extends Node {

constructor() {

super( 'uint' );

}

generate( builder ) {

return builder.getInstanceIndex();

}

}

InstanceIndexNode.prototype.isInstanceIndexNode = true;

export default InstanceIndexNode;
35 changes: 27 additions & 8 deletions examples/jsm/nodes/core/NodeBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export const shaderStages = [ 'fragment', 'vertex' ];
export const vector = [ 'x', 'y', 'z', 'w' ];

const toFloat = ( value ) => {

value = Number( value );

return value + ( value % 1 ? '' : '.0' );
Expand Down Expand Up @@ -142,6 +142,18 @@ class NodeBuilder {

}

isAvailable( /*name*/ ) {

return false;

}

getInstanceIndex( /*shaderStage*/ ) {

console.warn( 'Abstract function.' );

}

getTexture( /* textureProperty, uvSnippet */ ) {

console.warn( 'Abstract function.' );
Expand Down Expand Up @@ -193,6 +205,10 @@ class NodeBuilder {

return `${ this.getType( type ) }( ${ getConst( value.x ) }, ${ getConst( value.y ) }, ${ getConst( value.z ) }, ${ getConst( value.w ) } )`;

} else if ( typeLength > 4 ) {

return `${ this.getType( type ) }()`;

}

throw new Error( `NodeBuilder: Type '${type}' not found in generate constant attempt.` );
Expand Down Expand Up @@ -257,7 +273,7 @@ class NodeBuilder {

isReference( type ) {

return type === 'void' || type === 'property' || type === 'sampler';
return type === 'void' || type === 'property' || type === 'sampler' || type === 'texture' || type === 'cubeTexture';

}

Expand Down Expand Up @@ -320,6 +336,8 @@ class NodeBuilder {
if ( type === 2 ) return 'vec2';
if ( type === 3 ) return 'vec3';
if ( type === 4 ) return 'vec4';
if ( type === 9 ) return 'mat3';
if ( type === 16 ) return 'mat4';

return 0;

Expand All @@ -332,6 +350,8 @@ class NodeBuilder {

if ( vecNum !== null ) return Number( vecNum[ 1 ] );
if ( vecType === 'float' || vecType === 'bool' || vecType === 'int' || vecType === 'uint' ) return 1;
if ( /mat3/.exec( type ) ) return 9;
sunag marked this conversation as resolved.
Show resolved Hide resolved
if ( /mat4/.exec( type ) ) return 16;

return 0;

Expand Down Expand Up @@ -660,18 +680,17 @@ class NodeBuilder {
const fromTypeLength = this.getTypeLength( fromType );
const toTypeLength = this.getTypeLength( toType );

if ( fromTypeLength === 0 ) { // fromType is matrix-like
if ( fromTypeLength > 4 ) { // fromType is matrix-like

const vectorType = this.getVectorFromMatrix( fromType );
// @TODO: ignore for now

return this.format( `( ${ snippet } * ${ this.getType( vectorType ) }( 1.0 ) )`, vectorType, toType );
return snippet;

}

if ( toTypeLength === 0 ) { // toType is matrix-like
if ( toTypeLength > 4 ) { // toType is matrix-like

// ignore for now
//return `${ this.getType( toType ) }( ${ snippet } )`;
// @TODO: ignore for now

return snippet;

Expand Down
34 changes: 25 additions & 9 deletions examples/jsm/nodes/materials/NodeMaterial.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import ExpressionNode from '../core/ExpressionNode.js';
import {
float, vec3, vec4,
assign, label, mul, add, mix, bypass,
positionLocal, skinning, modelViewProjection, lightContext, colorSpace,
positionLocal, skinning, instance, modelViewProjection, lightContext, colorSpace,
materialAlphaTest, materialColor, materialOpacity
} from '../shadernode/ShaderNodeElements.js';

Expand All @@ -30,18 +30,35 @@ class NodeMaterial extends ShaderMaterial {

generateMain( builder ) {

// VERTEX STAGE
const rendererObject = builder.object;
sunag marked this conversation as resolved.
Show resolved Hide resolved

// < VERTEX STAGE >

let vertex = positionLocal;

if ( this.positionNode ) vertex = bypass( vertex, assign( positionLocal, this.positionNode ) );
if ( builder.object.isSkinnedMesh ) vertex = bypass( vertex, skinning( builder.object ) );
if ( this.positionNode !== null ) {

vertex = bypass( vertex, assign( vertex, this.positionNode ) );

}

if ( rendererObject.isInstancedMesh === true && builder.isAvailable( 'instance' ) === true ) {

vertex = bypass( vertex, instance( rendererObject ) );

}

if ( rendererObject.isSkinnedMesh === true ) {

vertex = bypass( vertex, skinning( rendererObject ) );

}

builder.context.vertex = vertex;

builder.addFlow( 'vertex', modelViewProjection() );

// FRAGMENT STAGE
// < FRAGMENT STAGE >

let colorNode = vec4( this.colorNode || materialColor );
let opacityNode = this.opacityNode ? float( this.opacityNode ) : materialOpacity;
Expand All @@ -63,8 +80,9 @@ class NodeMaterial extends ShaderMaterial {
const alphaTestNode = this.alphaTestNode ? float( this.alphaTestNode ) : materialAlphaTest;

builder.addFlow( 'fragment', label( alphaTestNode, 'AlphaTest' ) );

// @TODO: remove ExpressionNode here and then possibly remove it completely
builder.addFlow( 'fragment', new ExpressionNode( 'if ( DiffuseColor.a <= AlphaTest ) { discard; }' ) );
// TODO: remove ExpressionNode here and then possibly remove it completely

}

Expand Down Expand Up @@ -106,11 +124,9 @@ class NodeMaterial extends ShaderMaterial {
// This approach is to reuse the native refreshUniforms*
// and turn available the use of features like transmission and environment in core

let value;

for ( const property in values ) {

value = values[ property ];
const value = values[ property ];

if ( this[ property ] === undefined ) {

Expand Down
6 changes: 3 additions & 3 deletions examples/jsm/nodes/math/MathNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,9 @@ class MathNode extends TempNode {
const bType = this.bNode ? this.bNode.getNodeType( builder ) : null;
const cType = this.cNode ? this.cNode.getNodeType( builder ) : null;

const aLen = builder.getTypeLength( aType );
const bLen = builder.getTypeLength( bType );
const cLen = builder.getTypeLength( cType );
const aLen = builder.isVector( aType ) ? builder.getTypeLength( aType ) : 0;
sunag marked this conversation as resolved.
Show resolved Hide resolved
const bLen = builder.isVector( bType ) ? builder.getTypeLength( bType ) : 0;
const cLen = builder.isVector( cType ) ? builder.getTypeLength( cType ) : 0;

if ( aLen > bLen && aLen > cLen ) {

Expand Down
4 changes: 4 additions & 0 deletions examples/jsm/nodes/shadernode/ShaderNodeElements.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import VarNode from '../core/VarNode.js';
import AttributeNode from '../core/AttributeNode.js';
import UniformNode from '../core/UniformNode.js';
import BypassNode from '../core/BypassNode.js';
import InstanceIndexNode from '../core/InstanceIndexNode.js';

// accessor nodes
import BufferNode from '../accessors/BufferNode.js';
Expand All @@ -16,6 +17,7 @@ import PositionNode from '../accessors/PositionNode.js';
import SkinningNode from '../accessors/SkinningNode.js';
import TextureNode from '../accessors/TextureNode.js';
import UVNode from '../accessors/UVNode.js';
import InstanceNode from '../accessors/InstanceNode.js';

// math nodes
import OperatorNode from '../math/OperatorNode.js';
Expand Down Expand Up @@ -137,6 +139,7 @@ export const shiftLeft = nodeProxy( OperatorNode, '<<' );
export const shiftRight = nodeProxy( OperatorNode, '>>' );

export const element = nodeProxy( ArrayElementNode );
export const instanceIndex = nodeObject( new InstanceIndexNode() );

export const modelViewProjection = nodeProxy( ModelViewProjectionNode );

Expand Down Expand Up @@ -170,6 +173,7 @@ export const materialRoughness = nodeObject( new MaterialNode( MaterialNode.ROUG
export const materialMetalness = nodeObject( new MaterialNode( MaterialNode.METALNESS ) );

export const skinning = nodeProxy( SkinningNode );
export const instance = nodeProxy( InstanceNode );

export const lightContext = nodeProxy( LightContextNode );

Expand Down
6 changes: 3 additions & 3 deletions examples/jsm/nodes/utils/ArrayElementNode.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Node from '../core/Node.js';
import TempNode from '../core/Node.js';

class ArrayElementNode extends Node {
class ArrayElementNode extends TempNode {

constructor( node, indexNode ) {

Expand All @@ -20,7 +20,7 @@ class ArrayElementNode extends Node {
generate( builder ) {

const nodeSnippet = this.node.build( builder );
const indexSnippet = this.indexNode.build( builder, 'int' );
const indexSnippet = this.indexNode.build( builder, 'uint' );

return `${nodeSnippet}[ ${indexSnippet} ]`;

Expand Down
3 changes: 1 addition & 2 deletions examples/jsm/nodes/utils/ConvertNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,9 @@ class ConvertNode extends Node {

if ( builder.isReference( convertTo ) === false ) {

const convertToSnippet = builder.getType( convertTo );
const nodeSnippet = node.build( builder, convertTo );

return `${ builder.getVectorType( convertToSnippet ) }( ${ nodeSnippet } )`;
return builder.format( nodeSnippet, this.getNodeType( builder ), convertTo );

} else {

Expand Down
4 changes: 3 additions & 1 deletion examples/jsm/renderers/webgpu/WebGPURenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -847,7 +847,9 @@ class WebGPURenderer {

const drawRange = geometry.drawRange;
const firstVertex = drawRange.start;
const instanceCount = ( geometry.isInstancedBufferGeometry ) ? geometry.instanceCount : 1;

let instanceCount = object.isInstancedMesh ? object.count : 1;
instanceCount = geometry.isInstancedBufferGeometry ? geometry.instanceCount : instanceCount;
sunag marked this conversation as resolved.
Show resolved Hide resolved

if ( hasIndex === true ) {

Expand Down
Loading