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 & NodeMaterial: ComputeNode #23905

Merged
merged 12 commits into from
Apr 21, 2022
10 changes: 9 additions & 1 deletion examples/jsm/nodes/Nodes.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ import SkinningNode from './accessors/SkinningNode.js';
import TextureNode from './accessors/TextureNode.js';
import UVNode from './accessors/UVNode.js';

// gpgpu
import ComputeNode from './gpgpu/ComputeNode.js';

// display
import ColorSpaceNode from './display/ColorSpaceNode.js';
import NormalMapNode from './display/NormalMapNode.js';
Expand Down Expand Up @@ -118,6 +121,9 @@ const nodeLib = {
VarNode,
VaryNode,

// compute
ComputeNode,

// accessors
BufferNode,
CameraNode,
Expand Down Expand Up @@ -210,6 +216,9 @@ export {
VarNode,
VaryNode,

// compute
ComputeNode,

// accessors
BufferNode,
CameraNode,
Expand Down Expand Up @@ -265,5 +274,4 @@ export {
NodeLoader,
NodeObjectLoader,
NodeMaterialLoader

};
21 changes: 21 additions & 0 deletions examples/jsm/nodes/accessors/StorageBufferNode.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import BufferNode from './BufferNode.js';

class StorageBufferNode extends BufferNode {

constructor( value, bufferType, bufferCount = 0 ) {

super( value, bufferType, bufferCount );

}

getInputType( /*builder*/ ) {

return 'storageBuffer';

}

}

StorageBufferNode.prototype.isStorageBufferNode = true;

export default StorageBufferNode;
39 changes: 21 additions & 18 deletions examples/jsm/nodes/core/NodeBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,18 @@ import { NodeUpdateType } from './constants.js';

import { REVISION, LinearEncoding } from 'three';

export const shaderStages = [ 'fragment', 'vertex' ];
export const defaultShaderStages = [ 'fragment', 'vertex' ];
export const shaderStages = [ ...defaultShaderStages, 'compute' ];
export const vector = [ 'x', 'y', 'z', 'w' ];

const typeFromLength = new Map();
typeFromLength.set( 1, 'float' );
typeFromLength.set( 2, 'vec2' );
typeFromLength.set( 3, 'vec3' );
typeFromLength.set( 4, 'vec4' );
typeFromLength.set( 9, 'mat3' );
typeFromLength.set( 16, 'mat4' );

const toFloat = ( value ) => {

value = Number( value );
Expand All @@ -24,7 +33,7 @@ class NodeBuilder {
constructor( object, renderer, parser ) {

this.object = object;
this.material = object.material;
this.material = object.material || null;
this.renderer = renderer;
this.parser = parser;

Expand All @@ -34,14 +43,15 @@ class NodeBuilder {

this.vertexShader = null;
this.fragmentShader = null;
this.computeShader = null;

this.flowNodes = { vertex: [], fragment: [] };
this.flowCode = { vertex: '', fragment: '' };
this.uniforms = { vertex: [], fragment: [], index: 0 };
this.codes = { vertex: [], fragment: [] };
this.flowNodes = { vertex: [], fragment: [], compute: [] };
this.flowCode = { vertex: '', fragment: '', compute: [] };
this.uniforms = { vertex: [], fragment: [], compute: [], index: 0 };
this.codes = { vertex: [], fragment: [], compute: [] };
this.attributes = [];
this.varys = [];
this.vars = { vertex: [], fragment: [] };
this.vars = { vertex: [], fragment: [], compute: [] };
this.flow = { code: '' };
this.stack = [];

Expand Down Expand Up @@ -330,16 +340,9 @@ class NodeBuilder {

}

getTypeFromLength( type ) {

if ( type === 1 ) return 'float';
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';
getTypeFromLength( length ) {

return 0;
return typeFromLength.get( length );

}

Expand Down Expand Up @@ -369,7 +372,7 @@ class NodeBuilder {

if ( nodeData === undefined ) {

nodeData = { vertex: {}, fragment: {} };
nodeData = { vertex: {}, fragment: {}, compute: {} };

this.nodesData.set( node, nodeData );

Expand Down Expand Up @@ -592,7 +595,7 @@ class NodeBuilder {

getHash() {

return this.vertexShader + this.fragmentShader;
return this.vertexShader + this.fragmentShader + this.computeShader;

}

Expand Down
47 changes: 47 additions & 0 deletions examples/jsm/nodes/gpgpu/ComputeNode.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import Node from '../core/Node.js';
import { NodeUpdateType } from '../core/constants.js';

class ComputeNode extends Node {

constructor( dispatchCount, workgroupSize = [ 64 ] ) {

super( 'void' );

this.updateType = NodeUpdateType.Object;

this.dispatchCount = dispatchCount;
this.workgroupSize = workgroupSize;

this.computeNode = null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this can be an argument in the constructor?


}

update( { renderer } ) {

renderer.compute( this );

}

generate( builder ) {

const { shaderStage } = builder;

if ( shaderStage === 'compute' ) {

const snippet = this.computeNode.build( builder, 'void' );

if ( snippet !== '' ) {

builder.addFlowCode( snippet );

}

}

}

}

ComputeNode.prototype.isComputeNode = true;

export default ComputeNode;
10 changes: 7 additions & 3 deletions examples/jsm/nodes/materials/MeshStandardNodeMaterial.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import NodeMaterial from './NodeMaterial.js';
import {
float, vec3, vec4,
assign, label, mul, invert, mix,
context, assign, label, mul, invert, mix,
normalView,
materialRoughness, materialMetalness
} from '../shadernode/ShaderNodeElements.js';
Expand Down Expand Up @@ -61,9 +61,13 @@ export default class MeshStandardNodeMaterial extends NodeMaterial {

generateLight( builder, { diffuseColorNode, lightNode } ) {

const outgoingLightNode = super.generateLight( builder, { diffuseColorNode, lightNode, lightingModelNode: PhysicalLightingModel } );
let outgoingLightNode = super.generateLight( builder, { diffuseColorNode, lightNode, lightingModelNode: PhysicalLightingModel } );

// @TODO: add IBL code here
// TONE MAPPING

const renderer = builder.renderer;

if ( renderer.toneMappingNode ) outgoingLightNode = context( renderer.toneMappingNode, { color: outgoingLightNode } );

return outgoingLightNode;

Expand Down
8 changes: 1 addition & 7 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, bypass,
positionLocal, skinning, instance, modelViewProjection, context, lightContext, colorSpace,
positionLocal, skinning, instance, modelViewProjection, lightContext, colorSpace,
materialAlphaTest, materialColor, materialOpacity
} from '../shadernode/ShaderNodeElements.js';

Expand Down Expand Up @@ -112,16 +112,10 @@ class NodeMaterial extends ShaderMaterial {

generateOutput( builder, { diffuseColorNode, outgoingLightNode } ) {

const { renderer } = builder;

// OUTPUT

let outputNode = vec4( outgoingLightNode, diffuseColorNode.a );

// TONE MAPPING

if ( renderer.toneMappingNode ) outputNode = context( renderer.toneMappingNode, { color: outputNode } );

// ENCODING

outputNode = colorSpace( outputNode, builder.renderer.outputEncoding );
Expand Down
16 changes: 12 additions & 4 deletions examples/jsm/nodes/parsers/WGSLNodeFunction.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import NodeFunction from '../core/NodeFunction.js';
import NodeFunctionInput from '../core/NodeFunctionInput.js';

const declarationRegexp = /^fn\s*([a-z_0-9]+)?\s*\(([\s\S]*?)\)\s*\-\>\s*([a-z_0-9]+)?/i;
const declarationRegexp = /^[fn]*\s*([a-z_0-9]+)?\s*\(([\s\S]*?)\)\s*[\-\>]*\s*([a-z_0-9]+)?/i;
const propertiesRegexp = /[a-z_0-9]+/ig;

const wgslTypeLib = {
f32: 'float'
};

const parse = ( source ) => {

source = source.trim();
Expand Down Expand Up @@ -36,7 +40,9 @@ const parse = ( source ) => {
// default

const name = propsMatches[ i ++ ][ 0 ];
const type = propsMatches[ i ++ ][ 0 ];
let type = propsMatches[ i ++ ][ 0 ];

type = wgslTypeLib[ type ] || type;

// precision

Expand All @@ -54,7 +60,7 @@ const parse = ( source ) => {
const blockCode = source.substring( declaration[ 0 ].length );

const name = declaration[ 1 ] !== undefined ? declaration[ 1 ] : '';
const type = declaration[ 3 ];
const type = declaration[ 3 ] || 'void';

return {
type,
Expand Down Expand Up @@ -87,7 +93,9 @@ class WGSLNodeFunction extends NodeFunction {

getCode( name = this.name ) {

return `fn ${ name } ( ${ this.inputsCode.trim() } ) -> ${ this.type }` + this.blockCode;
const type = this.type !== 'void' ? '-> ' + this.type : '';

return `fn ${ name } ( ${ this.inputsCode.trim() } ) ${ type }` + this.blockCode;

}

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

// accessor nodes
import BufferNode from '../accessors/BufferNode.js';
import StorageBufferNode from '../accessors/StorageBufferNode.js';
import CameraNode from '../accessors/CameraNode.js';
import MaterialNode from '../accessors/MaterialNode.js';
import ModelNode from '../accessors/ModelNode.js';
Expand All @@ -20,6 +22,9 @@ import TextureNode from '../accessors/TextureNode.js';
import UVNode from '../accessors/UVNode.js';
import InstanceNode from '../accessors/InstanceNode.js';

// gpgpu
import ComputeNode from '../gpgpu/ComputeNode.js';

// math nodes
import OperatorNode from '../math/OperatorNode.js';
import CondNode from '../math/CondNode.js';
Expand All @@ -29,6 +34,7 @@ import MathNode from '../math/MathNode.js';
import ArrayElementNode from '../utils/ArrayElementNode.js';
import ConvertNode from '../utils/ConvertNode.js';
import JoinNode from '../utils/JoinNode.js';
import TimerNode from '../utils/TimerNode.js';

// other nodes
import ColorSpaceNode from '../display/ColorSpaceNode.js';
Expand Down Expand Up @@ -109,9 +115,15 @@ export const join = ( ...params ) => nodeObject( new JoinNode( nodeArray( params
export const uv = ( ...params ) => nodeObject( new UVNode( ...params ) );
export const attribute = ( ...params ) => nodeObject( new AttributeNode( ...params ) );
export const buffer = ( ...params ) => nodeObject( new BufferNode( ...params ) );
export const storage = ( ...params ) => nodeObject( new StorageBufferNode( ...params ) );
export const texture = ( ...params ) => nodeObject( new TextureNode( ...params ) );
export const sampler = ( texture ) => nodeObject( new ConvertNode( texture.isNode === true ? texture : new TextureNode( texture ), 'sampler' ) );

export const timer = ( ...params ) => nodeObject( new TimerNode( ...params ) );

export const compute = ( ...params ) => nodeObject( new ComputeNode( ...params ) );
export const func = ( ...params ) => nodeObject( new FunctionNode( ...params ) );

export const cond = nodeProxy( CondNode );

export const add = nodeProxy( OperatorNode, '+' );
Expand Down
23 changes: 19 additions & 4 deletions examples/jsm/nodes/shadernode/ShaderNodeUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,13 +141,28 @@ const ShaderNodeProxy = function ( NodeClass, scope = null, factor = null ) {

export const ShaderNodeScript = function ( jsFunc ) {

return { call: ( inputs, builder ) => {
//@TODO: Move this to Node extended class

inputs = new ShaderNodeObjects( inputs );
const self =
{
build: ( builder ) => {

return new ShaderNodeObject( jsFunc( inputs, builder ) );
self.call( {}, builder );

} };
return '';

},

call: ( inputs, builder ) => {

inputs = new ShaderNodeObjects( inputs );

return new ShaderNodeObject( jsFunc( inputs, builder ) );

}
};

return self;

};

Expand Down
Loading