Browse Source

New WebGLNodeBuilder + Nodes: transmission (#24453)

* Cache CheckerNode if necessary

* Nodes: Refactor, ior, specular, transmission nodes

* update example

* add transmission example

* update transmission

* update transmission (2)

* update screenshot
sunag 3 years ago
parent
commit
2f584ebbcc

+ 1 - 0
examples/files.json

@@ -230,6 +230,7 @@
 	],
 	"webgl / nodes": [
 		"webgl_nodes_loader_gltf_iridescence",
+		"webgl_nodes_loader_gltf_transmission",
 		"webgl_nodes_loader_gltf_sheen",
 		"webgl_nodes_materials_instance_uniform",
 		"webgl_nodes_materials_physical_clearcoat",

+ 16 - 0
examples/jsm/nodes/materials/MeshPhysicalNodeMaterial.js

@@ -23,6 +23,14 @@ export default class MeshPhysicalNodeMaterial extends MeshStandardNodeMaterial {
 		this.iridescenceIORNode = null;
 		this.iridescenceThicknessNode = null;
 
+		this.specularIntensityNode = null;
+		this.specularColorNode = null;
+
+		this.transmissionNode = null;
+		this.thicknessNode = null;
+		this.attenuationDistanceNode = null;
+		this.attenuationColorNode = null;
+
 		this.sheen = 0;
 		this.clearcoat = 0;
 		this.iridescence = 0;
@@ -47,6 +55,14 @@ export default class MeshPhysicalNodeMaterial extends MeshStandardNodeMaterial {
 		this.iridescenceIORNode = source.iridescenceIORNode;
 		this.iridescenceThicknessNode = source.iridescenceThicknessNode;
 
+		this.specularIntensityNode = source.specularIntensityNode;
+		this.specularColorNode = source.specularColorNode;
+
+		this.transmissionNode = source.transmissionNode;
+		this.thicknessNode = source.thicknessNode;
+		this.attenuationDistanceNode = source.attenuationDistanceNode;
+		this.attenuationColorNode = source.attenuationColorNode;
+
 		return super.copy( source );
 
 	}

+ 2 - 2
examples/jsm/nodes/procedural/CheckerNode.js

@@ -1,4 +1,4 @@
-import Node from '../core/Node.js';
+import TempNode from '../core/TempNode.js';
 import { ShaderNode, uv, add, mul, floor, mod, sign } from '../shadernode/ShaderNodeBaseElements.js';
 
 const checkerShaderNode = new ShaderNode( ( inputs ) => {
@@ -13,7 +13,7 @@ const checkerShaderNode = new ShaderNode( ( inputs ) => {
 
 } );
 
-class CheckerNode extends Node {
+class CheckerNode extends TempNode {
 
 	constructor( uvNode = uv() ) {
 

+ 8 - 4
examples/jsm/renderers/webgl/nodes/SlotNode.js

@@ -2,12 +2,16 @@ import Node from 'three-nodes/core/Node.js';
 
 class SlotNode extends Node {
 
-	constructor( node, name, nodeType ) {
+	constructor( params ) {
 
-		super( nodeType );
+		super( params.nodeType );
 
-		this.node = node;
-		this.name = name;
+		this.node = null;
+		this.source = null;
+		this.target = null;
+		this.inclusionType = 'replace';
+
+		Object.assign( this, params );
 
 	}
 

+ 282 - 251
examples/jsm/renderers/webgl/nodes/WebGLNodeBuilder.js

@@ -2,7 +2,6 @@ import NodeBuilder, { defaultShaderStages } from 'three-nodes/core/NodeBuilder.j
 import NodeFrame from 'three-nodes/core/NodeFrame.js';
 import SlotNode from './SlotNode.js';
 import GLSLNodeParser from 'three-nodes/parsers/GLSLNodeParser.js';
-import WebGLPhysicalContextNode from './WebGLPhysicalContextNode.js';
 
 import { PerspectiveCamera, ShaderChunk, ShaderLib, UniformsUtils, UniformsLib,
 	LinearEncoding, RGBAFormat, UnsignedByteType, sRGBEncoding } from 'three';
@@ -16,7 +15,7 @@ const nodeShaderLib = {
 	MeshBasicNodeMaterial: ShaderLib.basic,
 	PointsNodeMaterial: ShaderLib.points,
 	MeshStandardNodeMaterial: ShaderLib.standard,
-	MeshPhysicalMaterial: ShaderLib.physical
+	MeshPhysicalNodeMaterial: ShaderLib.physical
 };
 
 const glslMethods = {
@@ -44,8 +43,12 @@ class WebGLNodeBuilder extends NodeBuilder {
 		this.shader = shader;
 		this.slots = { vertex: [], fragment: [] };
 
+		this._parseShaderLib();
+		this._parseInclude( 'fragment', 'lights_physical_fragment', 'clearcoat_normal_fragment_begin', 'transmission_fragment' );
 		this._parseObject();
 
+		this._sortSlotsToFlow();
+
 	}
 
 	getMethod( method ) {
@@ -58,8 +61,6 @@ class WebGLNodeBuilder extends NodeBuilder {
 
 		this.slots[ shaderStage ].push( slotNode );
 
-		return this.addFlow( shaderStage, slotNode );
-
 	}
 
 	addFlowCode( code ) {
@@ -74,20 +75,12 @@ class WebGLNodeBuilder extends NodeBuilder {
 
 	}
 
-	_parseObject() {
-
-		const { material, renderer } = this;
+	_parseShaderLib() {
 
-		let type = material.type;
+		const type = this.material.type;
 
 		// shader lib
 
-		if ( material.isMeshPhysicalNodeMaterial ) type = 'MeshPhysicalMaterial';
-		else if ( material.isMeshStandardNodeMaterial ) type = 'MeshStandardNodeMaterial';
-		else if ( material.isMeshBasicNodeMaterial ) type = 'MeshBasicNodeMaterial';
-		else if ( material.isPointsNodeMaterial ) type = 'PointsNodeMaterial';
-		else if ( material.isLineBasicNodeMaterial ) type = 'LineBasicNodeMaterial';
-
 		if ( nodeShaderLib[ type ] !== undefined ) {
 
 			const shaderLib = nodeShaderLib[ type ];
@@ -99,9 +92,20 @@ class WebGLNodeBuilder extends NodeBuilder {
 
 		}
 
+	}
+
+	_parseObject() {
+
+		const { material, renderer } = this;
+
 		if ( renderer.toneMappingNode?.isNode === true ) {
 
-			this.replaceCode( 'fragment', getIncludeSnippet( 'tonemapping_fragment' ), '' );
+			this.addSlot( 'fragment', new SlotNode( {
+				node: material.colorNode,
+				nodeType: 'vec4',
+				source: getIncludeSnippet( 'tonemapping_fragment' ),
+				target: ''
+			} ) );
 
 		}
 
@@ -109,129 +113,308 @@ class WebGLNodeBuilder extends NodeBuilder {
 
 		if ( material.colorNode && material.colorNode.isNode ) {
 
-			this.addSlot( 'fragment', new SlotNode( material.colorNode, 'COLOR', 'vec4' ) );
+			this.addSlot( 'fragment', new SlotNode( {
+				node: material.colorNode,
+				nodeType: 'vec4',
+				source: getIncludeSnippet( 'color_fragment' ),
+				target: 'diffuseColor = %RESULT%;',
+				inclusionType: 'append'
+			} ) );
 
 		}
 
 		if ( material.opacityNode && material.opacityNode.isNode ) {
 
-			this.addSlot( 'fragment', new SlotNode( material.opacityNode, 'OPACITY', 'float' ) );
+			this.addSlot( 'fragment', new SlotNode( {
+				node: material.opacityNode,
+				nodeType: 'float',
+				source: getIncludeSnippet( 'alphatest_fragment' ),
+				target: 'diffuseColor.a = %RESULT%;',
+				inclusionType: 'append'
+			} ) );
 
 		}
 
 		if ( material.normalNode && material.normalNode.isNode ) {
 
-			this.addSlot( 'fragment', new SlotNode( material.normalNode, 'NORMAL', 'vec3' ) );
+			this.addSlot( 'fragment', new SlotNode( {
+				node: material.normalNode,
+				nodeType: 'vec3',
+				source: getIncludeSnippet( 'normal_fragment_begin' ),
+				target: 'normal = %RESULT%;',
+				inclusionType: 'append'
+			} ) );
 
 		}
 
 		if ( material.emissiveNode && material.emissiveNode.isNode ) {
 
-			this.addSlot( 'fragment', new SlotNode( material.emissiveNode, 'EMISSIVE', 'vec3' ) );
+			this.addSlot( 'fragment', new SlotNode( {
+				node: material.emissiveNode,
+				nodeType: 'vec3',
+				source: getIncludeSnippet( 'emissivemap_fragment' ),
+				target: 'totalEmissiveRadiance = %RESULT%;',
+				inclusionType: 'append'
+			} ) );
 
 		}
 
-		if ( material.metalnessNode && material.metalnessNode.isNode ) {
+		if ( material.isMeshStandardNodeMaterial ) {
 
-			this.addSlot( 'fragment', new SlotNode( material.metalnessNode, 'METALNESS', 'float' ) );
+			if ( material.metalnessNode && material.metalnessNode.isNode ) {
 
-		}
+				this.addSlot( 'fragment', new SlotNode( {
+					node: material.metalnessNode,
+					nodeType: 'float',
+					source: getIncludeSnippet( 'metalnessmap_fragment' ),
+					target: 'metalnessFactor = %RESULT%;',
+					inclusionType: 'append'
+				} ) );
 
-		if ( material.roughnessNode && material.roughnessNode.isNode ) {
+			}
 
-			this.addSlot( 'fragment', new SlotNode( material.roughnessNode, 'ROUGHNESS', 'float' ) );
+			if ( material.roughnessNode && material.roughnessNode.isNode ) {
 
-		}
+				this.addSlot( 'fragment', new SlotNode( {
+					node: material.roughnessNode,
+					nodeType: 'float',
+					source: getIncludeSnippet( 'roughnessmap_fragment' ),
+					target: 'roughnessFactor = %RESULT%;',
+					inclusionType: 'append'
+				} ) );
 
-		if ( material.isMeshPhysicalNodeMaterial ) {
+			}
 
-			if ( material.clearcoatNode && material.clearcoatNode.isNode ) {
+			if ( material.isMeshPhysicalNodeMaterial ) {
 
-				this.addSlot( 'fragment', new SlotNode( material.clearcoatNode, 'CLEARCOAT', 'float' ) );
+				if ( material.clearcoatNode && material.clearcoatNode.isNode ) {
 
-				if ( material.clearcoatRoughnessNode && material.clearcoatRoughnessNode.isNode ) {
+					this.addSlot( 'fragment', new SlotNode( {
+						node: material.clearcoatNode,
+						nodeType: 'float',
+						source: 'material.clearcoat = clearcoat;',
+						target: 'material.clearcoat = %RESULT%;'
+					} ) );
 
-					this.addSlot( 'fragment', new SlotNode( material.clearcoatRoughnessNode, 'CLEARCOAT_ROUGHNESS', 'float' ) );
+					if ( material.clearcoatRoughnessNode && material.clearcoatRoughnessNode.isNode ) {
 
-				}
+						this.addSlot( 'fragment', new SlotNode( {
+							node: material.clearcoatRoughnessNode,
+							nodeType: 'float',
+							source: 'material.clearcoatRoughness = clearcoatRoughness;',
+							target: 'material.clearcoatRoughness = %RESULT%;'
+						} ) );
 
-				if ( material.clearcoatNormalNode && material.clearcoatNormalNode.isNode ) {
+					}
 
-					this.addSlot( 'fragment', new SlotNode( material.clearcoatNormalNode, 'CLEARCOAT_NORMAL', 'vec3' ) );
+					if ( material.clearcoatNormalNode && material.clearcoatNormalNode.isNode ) {
+
+						this.addSlot( 'fragment', new SlotNode( {
+							node: material.clearcoatNormalNode,
+							nodeType: 'vec3',
+							source: 'vec3 clearcoatNormal = geometryNormal;',
+							target: 'vec3 clearcoatNormal = %RESULT%;'
+						} ) );
+
+					}
+
+					material.defines.USE_CLEARCOAT = '';
+
+				} else {
+
+					delete material.defines.USE_CLEARCOAT;
 
 				}
 
-				material.defines.USE_CLEARCOAT = '';
+				if ( material.sheenNode && material.sheenNode.isNode ) {
 
-			} else {
+					this.addSlot( 'fragment', new SlotNode( {
+						node: material.sheenNode,
+						nodeType: 'vec3',
+						source: 'material.sheenColor = sheenColor;',
+						target: 'material.sheenColor = %RESULT%;'
+					} ) );
 
-				delete material.defines.USE_CLEARCOAT;
+					if ( material.sheenRoughnessNode && material.sheenRoughnessNode.isNode ) {
 
-			}
+						this.addSlot( 'fragment', new SlotNode( {
+							node: material.sheenRoughnessNode,
+							nodeType: 'float',
+							source: 'material.sheenRoughness = clamp( sheenRoughness, 0.07, 1.0 );',
+							target: 'material.sheenRoughness = clamp( %RESULT%, 0.07, 1.0 );'
+						} ) );
 
-			if ( material.sheenNode && material.sheenNode.isNode ) {
+					}
 
-				this.addSlot( 'fragment', new SlotNode( material.sheenNode, 'SHEEN', 'vec3' ) );
+					material.defines.USE_SHEEN = '';
 
-				if ( material.sheenRoughnessNode && material.sheenRoughnessNode.isNode ) {
+				} else {
 
-					this.addSlot( 'fragment', new SlotNode( material.sheenRoughnessNode, 'SHEEN_ROUGHNESS', 'float' ) );
+					delete material.defines.USE_SHEEN;
 
 				}
 
-				material.defines.USE_SHEEN = '';
+				if ( material.iridescenceNode && material.iridescenceNode.isNode ) {
 
-			} else {
+					this.addSlot( 'fragment', new SlotNode( {
+						node: material.iridescenceNode,
+						nodeType: 'float',
+						source: 'material.iridescence = iridescence;',
+						target: 'material.iridescence = %RESULT%;'
+					} ) );
 
-				delete material.defines.USE_SHEEN;
+					if ( material.iridescenceIORNode && material.iridescenceIORNode.isNode ) {
 
-			}
+						this.addSlot( 'fragment', new SlotNode( {
+							node: material.iridescenceIORNode,
+							nodeType: 'float',
+							source: 'material.iridescenceIOR = iridescenceIOR;',
+							target: 'material.iridescenceIOR = %RESULT%;'
+						} ) );
 
-			if ( material.iridescenceNode && material.iridescenceNode.isNode ) {
+					}
 
-				this.addSlot( 'fragment', new SlotNode( material.iridescenceNode, 'IRIDESCENCE', 'float' ) );
+					if ( material.iridescenceThicknessNode && material.iridescenceThicknessNode.isNode ) {
 
-				if ( material.iridescenceIORNode && material.iridescenceIORNode.isNode ) {
+						this.addSlot( 'fragment', new SlotNode( {
+							node: material.iridescenceThicknessNode,
+							nodeType: 'float',
+							source: 'material.iridescenceThickness = iridescenceThicknessMaximum;',
+							target: 'material.iridescenceThickness = %RESULT%;'
+						} ) );
 
-					this.addSlot( 'fragment', new SlotNode( material.iridescenceIORNode, 'IRIDESCENCE_IOR', 'float' ) );
+					}
+
+					material.defines.USE_IRIDESCENCE = '';
+
+				} else {
+
+					delete material.defines.USE_IRIDESCENCE;
 
 				}
 
-				if ( material.iridescenceThicknessNode && material.iridescenceThicknessNode.isNode ) {
+				if ( material.iorNode && material.iorNode.isNode ) {
 
-					this.addSlot( 'fragment', new SlotNode( material.iridescenceThicknessNode, 'IRIDESCENCE_THICKNESS', 'float' ) );
+					this.addSlot( 'fragment', new SlotNode( {
+						node: material.iorNode,
+						nodeType: 'float',
+						source: 'material.ior = ior;',
+						target: 'material.ior = %RESULT%;'
+					} ) );
 
 				}
 
-				material.defines.USE_IRIDESCENCE = '';
+				if ( material.specularColorNode && material.specularColorNode.isNode ) {
 
-			} else {
+					this.addSlot( 'fragment', new SlotNode( {
+						node: material.specularColorNode,
+						nodeType: 'vec3',
+						source: 'vec3 specularColorFactor = specularColor;',
+						target: 'vec3 specularColorFactor = %RESULT%;'
+					} ) );
 
-				delete material.defines.USE_IRIDESCENCE;
+				}
 
-			}
+				if ( material.specularIntensityNode && material.specularIntensityNode.isNode ) {
 
-		}
+					this.addSlot( 'fragment', new SlotNode( {
+						node: material.specularIntensityNode,
+						nodeType: 'float',
+						source: 'float specularIntensityFactor = specularIntensity;',
+						target: 'float specularIntensityFactor = %RESULT%;'
+					} ) );
+
+				}
+
+				if ( material.transmissionNode && material.transmissionNode.isNode ) {
+
+					this.addSlot( 'fragment', new SlotNode( {
+						node: material.transmissionNode,
+						nodeType: 'float',
+						source: 'material.transmission = transmission;',
+						target: 'material.transmission = %RESULT%;'
+					} ) );
+
+					if ( material.thicknessNode && material.thicknessNode.isNode ) {
+
+						this.addSlot( 'fragment', new SlotNode( {
+							node: material.thicknessNode,
+							nodeType: 'float',
+							source: 'material.thickness = thickness;',
+							target: 'material.thickness = %RESULT%;'
+						} ) );
+
+					}
+
+					if ( material.thicknessNode && material.thicknessNode.isNode ) {
+
+						this.addSlot( 'fragment', new SlotNode( {
+							node: material.thicknessNode,
+							nodeType: 'float',
+							source: 'material.thickness = thickness;',
+							target: 'material.thickness = %RESULT%;'
+						} ) );
+
+					}
+
+					if ( material.attenuationDistanceNode && material.attenuationDistanceNode.isNode ) {
+
+						this.addSlot( 'fragment', new SlotNode( {
+							node: material.attenuationDistanceNode,
+							nodeType: 'float',
+							source: 'material.attenuationDistance = attenuationDistance;',
+							target: 'material.attenuationDistance = %RESULT%;'
+						} ) );
+
+					}
+
+					if ( material.attenuationColorNode && material.attenuationColorNode.isNode ) {
+
+						this.addSlot( 'fragment', new SlotNode( {
+							node: material.attenuationColorNode,
+							nodeType: 'vec3',
+							source: 'material.attenuationColor = attenuationColor;',
+							target: 'material.attenuationColor = %RESULT%;'
+						} ) );
+
+					}
+
+					material.transmission = 1;
+					material.defines.USE_TRANSMISSION = '';
+
+				} else {
 
-		if ( material.envNode && material.envNode.isNode ) {
+					material.transmission = 0;
+					delete material.defines.USE_TRANSMISSION;
 
-			const envRadianceNode = new WebGLPhysicalContextNode( WebGLPhysicalContextNode.RADIANCE, material.envNode );
-			const envIrradianceNode = new WebGLPhysicalContextNode( WebGLPhysicalContextNode.IRRADIANCE, material.envNode );
+				}
 
-			this.addSlot( 'fragment', new SlotNode( envRadianceNode, 'RADIANCE', 'vec3' ) );
-			this.addSlot( 'fragment', new SlotNode( envIrradianceNode, 'IRRADIANCE', 'vec3' ) );
+			}
 
 		}
 
+		//
+
 		if ( material.positionNode && material.positionNode.isNode ) {
 
-			this.addSlot( 'vertex', new SlotNode( material.positionNode, 'POSITION', 'vec3' ) );
+			this.addSlot( 'vertex', new SlotNode( {
+				node: material.positionNode,
+				nodeType: 'vec3',
+				source: getIncludeSnippet( 'begin_vertex' ),
+				target: 'transformed = %RESULT%;',
+				inclusionType: 'append'
+			} ) );
 
 		}
 
 		if ( material.sizeNode && material.sizeNode.isNode ) {
 
-			this.addSlot( 'vertex', new SlotNode( material.sizeNode, 'SIZE', 'float' ) );
+			this.addSlot( 'vertex', new SlotNode( {
+				node: material.sizeNode,
+				nodeType: 'float',
+				source: 'gl_PointSize = size;',
+				target: 'gl_PointSize = %RESULT%;'
+			} ) );
 
 		}
 
@@ -335,7 +518,7 @@ class WebGLNodeBuilder extends NodeBuilder {
 
 	}
 
-	addCodeAfterSnippet( shaderStage, snippet, code ) {
+	addCodeAfterCode( shaderStage, snippet, code ) {
 
 		const shaderProperty = getShaderStageProperty( shaderStage );
 
@@ -356,14 +539,6 @@ class WebGLNodeBuilder extends NodeBuilder {
 
 	}
 
-	addCodeAfterInclude( shaderStage, includeName, code ) {
-
-		const includeSnippet = getIncludeSnippet( includeName );
-
-		this.addCodeAfterSnippet( shaderStage, includeSnippet, code );
-
-	}
-
 	replaceCode( shaderStage, source, target ) {
 
 		const shaderProperty = getShaderStageProperty( shaderStage );
@@ -372,19 +547,6 @@ class WebGLNodeBuilder extends NodeBuilder {
 
 	}
 
-	parseInclude( shaderStage, ...includes ) {
-
-		for ( const name of includes ) {
-
-			const includeSnippet = getIncludeSnippet( name );
-			const code = ShaderChunk[ name ];
-
-			this.replaceCode( shaderStage, includeSnippet, code );
-
-		}
-
-	}
-
 	getTextureEncodingFromMap( map ) {
 
 		const isWebGL2 = this.renderer.capabilities.isWebGL2;
@@ -463,208 +625,77 @@ ${this.shader[ getShaderStageProperty( shaderStage ) ]}
 
 	}
 
-	getSlot( shaderStage, name ) {
-
-		const slots = this.slots[ shaderStage ];
+	_parseInclude( shaderStage, ...includes ) {
 
-		for ( const node of slots ) {
+		for ( const name of includes ) {
 
-			if ( node.name === name ) {
+			const includeSnippet = getIncludeSnippet( name );
+			const code = ShaderChunk[ name ];
 
-				return this.getFlowData( node/*, shaderStage*/ );
+			const shaderProperty = getShaderStageProperty( shaderStage );
 
-			}
+			this.shader[ shaderProperty ] = this.shader[ shaderProperty ].replaceAll( includeSnippet, code );
 
 		}
 
 	}
 
-	_addSnippets() {
-
-		this.parseInclude( 'fragment', 'lights_physical_fragment' );
-		this.parseInclude( 'fragment', 'clearcoat_normal_fragment_begin' );
-
-		const colorSlot = this.getSlot( 'fragment', 'COLOR' );
-		const opacityNode = this.getSlot( 'fragment', 'OPACITY' );
-		const normalSlot = this.getSlot( 'fragment', 'NORMAL' );
-		const emissiveNode = this.getSlot( 'fragment', 'EMISSIVE' );
-		const roughnessNode = this.getSlot( 'fragment', 'ROUGHNESS' );
-		const metalnessNode = this.getSlot( 'fragment', 'METALNESS' );
-		const clearcoatNode = this.getSlot( 'fragment', 'CLEARCOAT' );
-		const clearcoatRoughnessNode = this.getSlot( 'fragment', 'CLEARCOAT_ROUGHNESS' );
-		const clearcoatNormalNode = this.getSlot( 'fragment', 'CLEARCOAT_NORMAL' );
-		const sheenNode = this.getSlot( 'fragment', 'SHEEN' );
-		const sheenRoughnessNode = this.getSlot( 'fragment', 'SHEEN_ROUGHNESS' );
-		const iridescenceNode = this.getSlot( 'fragment', 'IRIDESCENCE' );
-		const iridescenceIORNode = this.getSlot( 'fragment', 'IRIDESCENCE_IOR' );
-		const iridescenceThicknessNode = this.getSlot( 'fragment', 'IRIDESCENCE_THICKNESS' );
-
-		const positionNode = this.getSlot( 'vertex', 'POSITION' );
-		const sizeNode = this.getSlot( 'vertex', 'SIZE' );
-
-		if ( colorSlot !== undefined ) {
-
-			this.addCodeAfterInclude(
-				'fragment',
-				'color_fragment',
-				`${colorSlot.code}\n\tdiffuseColor = ${colorSlot.result};`
-			);
-
-		}
-
-		if ( opacityNode !== undefined ) {
-
-			this.addCodeAfterInclude(
-				'fragment',
-				'alphatest_fragment',
-				`${opacityNode.code}\n\tdiffuseColor.a = ${opacityNode.result};`
-			);
-
-		}
-
-		if ( normalSlot !== undefined ) {
-
-			this.addCodeAfterInclude(
-				'fragment',
-				'normal_fragment_begin',
-				`${normalSlot.code}\n\tnormal = ${normalSlot.result};`
-			);
-
-		}
+	_sortSlotsToFlow() {
 
-		if ( emissiveNode !== undefined ) {
-
-			this.addCodeAfterInclude(
-				'fragment',
-				'emissivemap_fragment',
-				`${emissiveNode.code}\n\ttotalEmissiveRadiance = ${emissiveNode.result};`
-			);
-
-		}
-
-		if ( roughnessNode !== undefined ) {
-
-			this.addCodeAfterInclude(
-				'fragment',
-				'roughnessmap_fragment',
-				`${roughnessNode.code}\n\troughnessFactor = ${roughnessNode.result};`
-			);
-
-		}
-
-		if ( metalnessNode !== undefined ) {
-
-			this.addCodeAfterInclude(
-				'fragment',
-				'metalnessmap_fragment',
-				`${metalnessNode.code}\n\tmetalnessFactor = ${metalnessNode.result};`
-			);
-
-		}
-
-		if ( clearcoatNode !== undefined ) {
-
-			this.addCodeAfterSnippet(
-				'fragment',
-				'material.clearcoat = clearcoat;',
-				`${clearcoatNode.code}\n\tmaterial.clearcoat = ${clearcoatNode.result};`
-			);
-
-			if ( clearcoatRoughnessNode !== undefined ) {
-
-				this.addCodeAfterSnippet(
-					'fragment',
-					'material.clearcoatRoughness = clearcoatRoughness;',
-					`${clearcoatRoughnessNode.code}\n\tmaterial.clearcoatRoughness = ${clearcoatRoughnessNode.result};`
-				);
-
-			}
-
-			if ( clearcoatNormalNode !== undefined ) {
+		for ( const shaderStage of defaultShaderStages ) {
 
-				this.addCodeAfterSnippet(
-					'fragment',
-					'vec3 clearcoatNormal = geometryNormal;',
-					`${clearcoatNormalNode.code}\n\tclearcoatNormal = ${clearcoatNormalNode.result};`
-				);
+			const sourceCode = this.shader[ getShaderStageProperty( shaderStage ) ];
 
-			}
+			const slots = this.slots[ shaderStage ].sort( ( slotA, slotB ) => {
 
-		}
+				if ( sourceCode.indexOf( slotA.source ) == - 1 ) {
+					//console.log( slotA, sourceCode.indexOf( slotA.source ), sourceCode.indexOf( slotB.source ) );
+					//console.log(sourceCode);
+				}
 
-		if ( sheenNode !== undefined ) {
+				return sourceCode.indexOf( slotA.source ) > sourceCode.indexOf( slotB.source ) ? 1 : - 1;
 
-			this.addCodeAfterSnippet(
-				'fragment',
-				'material.sheenColor = sheenColor;',
-				`${sheenNode.code}\n\tmaterial.sheenColor = ${sheenNode.result};`
-			);
+			} );
 
-			if ( sheenRoughnessNode !== undefined ) {
+			for ( const slotNode of slots ) {
 
-				this.replaceCode(
-					'fragment',
-					'material.sheenRoughness = clamp( sheenRoughness, 0.07, 1.0 );',
-					`${sheenRoughnessNode.code}\n\tmaterial.sheenRoughness = clamp( ${sheenRoughnessNode.result}, 0.07, 1.0 );`
-				);
+				this.addFlow( shaderStage, slotNode );
 
 			}
 
 		}
 
-		if ( iridescenceNode !== undefined ) {
-
-			this.addCodeAfterSnippet(
-				'fragment',
-				'material.iridescence = iridescence;',
-				`${iridescenceNode.code}\n\tmaterial.iridescence = ${iridescenceNode.result};`
-			);
-
-		}
+	}
 
-		if ( iridescenceIORNode !== undefined ) {
+	_addSnippets() {
 
-			this.addCodeAfterSnippet(
-				'fragment',
-				'material.iridescenceIOR = iridescenceIOR;',
-				`${iridescenceIORNode.code}\n\tmaterial.iridescenceIOR = ${iridescenceIORNode.result};`
-			);
+		for ( const shaderStage of defaultShaderStages ) {
 
-		}
+			for ( const slotNode of this.slots[ shaderStage ] ) {
 
-		if ( iridescenceThicknessNode !== undefined ) {
+				const flowData = this.getFlowData( slotNode/*, shaderStage*/ );
 
-			this.addCodeAfterSnippet(
-				'fragment',
-				'material.iridescenceThickness = iridescenceThicknessMaximum;',
-				`${iridescenceThicknessNode.code}\n\tmaterial.iridescenceThickness = ${iridescenceThicknessNode.result};`
-			);
+				const inclusionType = slotNode.inclusionType;
+				const source = slotNode.source;
+				const target = flowData.code + '\n\t' + slotNode.target.replace( '%RESULT%', flowData.result );
 
-		}
+				if ( inclusionType === 'append' ) {
 
-		if ( positionNode !== undefined ) {
+					this.addCodeAfterCode( shaderStage, source, target );
 
-			this.addCodeAfterInclude(
-				'vertex',
-				'begin_vertex',
-				`${positionNode.code}\n\ttransformed = ${positionNode.result};`
-			);
+				} else if ( inclusionType === 'replace' ) {
 
-		}
+					this.replaceCode( shaderStage, source, target );
 
-		if ( sizeNode !== undefined ) {
+				} else {
 
-			this.addCodeAfterSnippet(
-				'vertex',
-				'gl_PointSize = size;',
-				`${sizeNode.code}\n\tgl_PointSize = ${sizeNode.result};`
-			);
+					console.warn( `Inclusion type "${ inclusionType }" not compatible.` );
 
-		}
+				}
 
-		for ( const shaderStage of defaultShaderStages ) {
+			}
 
-			this.addCodeAfterSnippet(
+			this.addCodeAfterCode(
 				shaderStage,
 				'main() {',
 				this.flowCode[ shaderStage ]

+ 5 - 1
examples/jsm/renderers/webgl/nodes/WebGLNodes.js

@@ -8,7 +8,11 @@ export const nodeFrame = new NodeFrame();
 
 Material.prototype.onBuild = function ( object, parameters, renderer ) {
 
-	builders.set( this, new WebGLNodeBuilder( object, renderer, parameters ).build() );
+	if ( object.material.isNodeMaterial === true ) {
+
+		builders.set( this, new WebGLNodeBuilder( object, renderer, parameters ).build() );
+
+	}
 
 };
 

+ 0 - 45
examples/jsm/renderers/webgl/nodes/WebGLPhysicalContextNode.js

@@ -1,45 +0,0 @@
-import ContextNode from 'three-nodes/core/ContextNode.js';
-import NormalNode from 'three-nodes/accessors/NormalNode.js';
-import ExpressionNode from 'three-nodes/core/ExpressionNode.js';
-import ConstNode from 'three-nodes/core/ConstNode.js';
-
-class WebGLPhysicalContextNode extends ContextNode {
-
-	static RADIANCE = 'radiance';
-	static IRRADIANCE = 'irradiance';
-
-	constructor( scope, node ) {
-
-		super( node, 'vec3' );
-
-		this.scope = scope;
-
-	}
-
-	generate( builder, output ) {
-
-		const scope = this.scope;
-
-		let roughness = null;
-
-		if ( scope === WebGLPhysicalContextNode.RADIANCE ) {
-
-			roughness = new ExpressionNode( 'roughnessFactor', 'float' );
-
-		} else if ( scope === WebGLPhysicalContextNode.IRRADIANCE ) {
-
-			roughness = new ConstNode( 1 );
-
-			this.context.uv = new NormalNode( NormalNode.WORLD );
-
-		}
-
-		this.context.roughness = roughness;
-
-		return super.generate( builder, output );
-
-	}
-
-}
-
-export default WebGLPhysicalContextNode;

BIN
examples/screenshots/webgl_nodes_loader_gltf_transmission.jpg


+ 161 - 0
examples/webgl_nodes_loader_gltf_transmission.html

@@ -0,0 +1,161 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js webgl - GLTFloader + transmission + nodes</title>
+		<meta charset="utf-8">
+		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+		<link type="text/css" rel="stylesheet" href="main.css">
+	</head>
+
+	<body>
+		<div id="info">
+			<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> - GLTFLoader + <a href="https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_transmission" target="_blank" rel="noopener">KHR_materials_transmission</a> + Nodes<br />
+			Iridescent Dish With Olives by <a href="https://github.com/echadwick-wayfair" target="_blank" rel="noopener">Eric Chadwick</a><br />
+			<a href="https://hdrihaven.com/hdri/?h=royal_esplanade" target="_blank" rel="noopener">Royal Esplanade</a> by <a href="https://hdrihaven.com/" target="_blank" rel="noopener">HDRI Haven</a>
+		</div>
+
+		<!-- Import maps polyfill -->
+		<!-- Remove this when import maps will be widely supported -->
+		<script async src="https://unpkg.com/[email protected]/dist/es-module-shims.js"></script>
+
+		<script type="importmap">
+			{
+				"imports": {
+					"three": "../build/three.module.js",
+					"three-nodes/": "./jsm/nodes/"
+				}
+			}
+		</script>
+
+		<script type="module">
+
+			import * as THREE from 'three';
+
+			import { NodeMaterial, float, texture, mul } from 'three-nodes/Nodes.js';
+
+			import { nodeFrame } from './jsm/renderers/webgl/nodes/WebGLNodes.js';
+
+			import { OrbitControls } from './jsm/controls/OrbitControls.js';
+			import { GLTFLoader } from './jsm/loaders/GLTFLoader.js';
+			import { RGBELoader } from './jsm/loaders/RGBELoader.js';
+
+			import { DRACOLoader } from './jsm/loaders/DRACOLoader.js';
+
+			let camera, scene, renderer, controls, clock, mixer;
+
+			init();
+			animate();
+
+			function init() {
+
+				clock = new THREE.Clock();
+
+				const container = document.createElement( 'div' );
+				document.body.appendChild( container );
+
+				camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.25, 20 );
+				camera.position.set( 0, 0.4, 0.7 );
+
+				scene = new THREE.Scene();
+
+				new RGBELoader()
+					.setPath( 'textures/equirectangular/' )
+					.load( 'royal_esplanade_1k.hdr', function ( envMap ) {
+
+						envMap.mapping = THREE.EquirectangularReflectionMapping;
+
+						scene.background = envMap;
+						scene.environment = envMap;
+
+						// model
+
+						new GLTFLoader()
+							.setPath( 'models/gltf/' )
+							.setDRACOLoader( new DRACOLoader().setDecoderPath( 'js/libs/draco/gltf/' ) )
+							.load( 'IridescentDishWithOlives.glb', function ( gltf ) {
+
+								// nodes
+
+								const glassMesh = gltf.scene.getObjectByName( 'glassCover' );
+
+								const material = glassMesh.material;
+
+								if ( material && material.transmission > 0 ) {
+
+									const nodeMaterial = NodeMaterial.fromMaterial( material );
+									nodeMaterial.transmissionNode = float( 1 );
+									nodeMaterial.iorNode = float( 1.5 );
+									nodeMaterial.thicknessNode = mul( texture( material.thicknessMap ).g, 0.1 );
+									//nodeMaterial.attenuationDistanceNode;
+									//nodeMaterial.attenuationColorNode;
+
+									// ignore traditional maps
+									nodeMaterial.transmissionMap = null;
+									nodeMaterial.thicknessMap = null;
+
+									glassMesh.material = nodeMaterial;
+
+								}
+
+								mixer = new THREE.AnimationMixer( gltf.scene );
+								mixer.clipAction( gltf.animations[ 0 ] ).play();
+								scene.add( gltf.scene );
+
+							} );
+
+					} );
+
+				renderer = new THREE.WebGLRenderer( { antialias: true } );
+				renderer.setPixelRatio( window.devicePixelRatio );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				renderer.toneMapping = THREE.ACESFilmicToneMapping;
+				renderer.toneMappingExposure = 1;
+				renderer.outputEncoding = THREE.sRGBEncoding;
+				container.appendChild( renderer.domElement );
+
+				controls = new OrbitControls( camera, renderer.domElement );
+				controls.enableDamping = true;
+				controls.minDistance = 0.5;
+				controls.maxDistance = 1;
+				controls.target.set( 0, 0.1, 0 );
+				controls.update();
+
+				window.addEventListener( 'resize', onWindowResize );
+
+			}
+
+			function onWindowResize() {
+
+				camera.aspect = window.innerWidth / window.innerHeight;
+				camera.updateProjectionMatrix();
+
+				renderer.setSize( window.innerWidth, window.innerHeight );
+
+			}
+
+			//
+
+			function animate() {
+
+				requestAnimationFrame( animate );
+
+				if ( mixer ) mixer.update( clock.getDelta() );
+
+				controls.update(); // required if damping enabled
+
+				render();
+
+			}
+
+			function render() {
+
+				nodeFrame.update();
+
+				renderer.render( scene, camera );
+
+			}
+
+		</script>
+
+	</body>
+</html>

+ 8 - 8
examples/webgl_nodes_materials_instance_uniform.html

@@ -27,8 +27,8 @@
 		<script type="module">
 
 			import * as THREE from 'three';
-			import * as Nodes from 'three-nodes/Nodes.js';
-			import { add, mul } from 'three-nodes/Nodes.js';
+
+			import { MeshStandardNodeMaterial, Node, NodeUpdateType, uniform, cubeTexture, add, mul } from 'three-nodes/Nodes.js';
 
 			import Stats from './jsm/libs/stats.module.js';
 
@@ -36,15 +36,15 @@
 
 			import { nodeFrame } from './jsm/renderers/webgl/nodes/WebGLNodes.js';
 
-			class InstanceUniformNode extends Nodes.Node {
+			class InstanceUniformNode extends Node {
 
 				constructor() {
 
 					super( 'vec3' );
 
-					this.updateType = Nodes.NodeUpdateType.Object;
+					this.updateType = NodeUpdateType.Object;
 
-					this.uniformNode = new Nodes.UniformNode( new THREE.Color() );
+					this.uniformNode = uniform( new THREE.Color() );
 
 				}
 
@@ -99,14 +99,14 @@
 					path + 'pz' + format, path + 'nz' + format
 				];
 
-				const cubeTexture = new THREE.CubeTextureLoader().load( urls );
+				const cubeMap = new THREE.CubeTextureLoader().load( urls );
 
 				// Material
 
 				const instanceUniform = new InstanceUniformNode();
-				const cubeTextureNode = new Nodes.CubeTextureNode( cubeTexture );
+				const cubeTextureNode = cubeTexture( cubeMap );
 
-				const material = new THREE.MeshStandardMaterial();
+				const material = new MeshStandardNodeMaterial();
 				material.colorNode = add( instanceUniform, cubeTextureNode );
 				material.emissiveNode = mul( instanceUniform, cubeTextureNode );