diff --git a/examples/files.json b/examples/files.json index 45199a3d76aabd..a48c92382d111d 100644 --- a/examples/files.json +++ b/examples/files.json @@ -229,6 +229,7 @@ ], "webgl / nodes": [ "webgl_materials_instance_uniform_nodes", + "webgl_materials_physical_clearcoat_nodes", "webgl_materials_standard_nodes", "webgl_nodes_playground", "webgl_points_nodes" diff --git a/examples/jsm/nodes/materials/Materials.js b/examples/jsm/nodes/materials/Materials.js index aadfb633f29240..853123a6da3167 100644 --- a/examples/jsm/nodes/materials/Materials.js +++ b/examples/jsm/nodes/materials/Materials.js @@ -2,6 +2,7 @@ import NodeMaterial from './NodeMaterial.js'; import LineBasicNodeMaterial from './LineBasicNodeMaterial.js'; import MeshBasicNodeMaterial from './MeshBasicNodeMaterial.js'; import MeshStandardNodeMaterial from './MeshStandardNodeMaterial.js'; +import MeshPhysicalNodeMaterial from './MeshPhysicalNodeMaterial.js'; import PointsNodeMaterial from './PointsNodeMaterial.js'; import SpriteNodeMaterial from './SpriteNodeMaterial.js'; @@ -10,6 +11,7 @@ export { LineBasicNodeMaterial, MeshBasicNodeMaterial, MeshStandardNodeMaterial, + MeshPhysicalNodeMaterial, PointsNodeMaterial, SpriteNodeMaterial }; @@ -21,8 +23,9 @@ NodeMaterial.fromMaterial = function ( material ) { LineBasicNodeMaterial, MeshBasicNodeMaterial, MeshStandardNodeMaterial, + MeshPhysicalNodeMaterial, PointsNodeMaterial, - SpriteNodeMaterial, + SpriteNodeMaterial }; const type = material.type.replace( 'Material', 'NodeMaterial' ); diff --git a/examples/jsm/nodes/materials/MeshPhysicalNodeMaterial.js b/examples/jsm/nodes/materials/MeshPhysicalNodeMaterial.js new file mode 100644 index 00000000000000..0b07a3f7dae2a7 --- /dev/null +++ b/examples/jsm/nodes/materials/MeshPhysicalNodeMaterial.js @@ -0,0 +1,40 @@ +import MeshStandardNodeMaterial from './MeshStandardNodeMaterial.js'; + +import { MeshPhysicalMaterial } from 'three'; + +const defaultValues = new MeshPhysicalMaterial(); + +export default class MeshPhysicalNodeMaterial extends MeshStandardNodeMaterial { + + constructor( parameters ) { + + super(); + + this.isMeshPhysicalNodeMaterial = true; + + this.clearcoatNode = null; + this.clearcoatRoughnessNode = null; + this.clearcoatNormalNode = null; + + this.sheen = 0; + this.clearcoat = 0; + this.iridescence = 0; + this.transmission = 0; + + this.setDefaultValues( defaultValues ); + + this.setValues( parameters ); + + } + + copy( source ) { + + this.clearcoatNode = source.clearcoatNode; + this.clearcoatRoughnessNode = source.clearcoatRoughnessNode; + this.clearcoatNormalNode = source.clearcoatNormalNode; + + return super.copy( source ); + + } + +} diff --git a/examples/jsm/nodes/materials/MeshStandardNodeMaterial.js b/examples/jsm/nodes/materials/MeshStandardNodeMaterial.js index 23287e1f3d13a8..a4bd5ecead0f60 100644 --- a/examples/jsm/nodes/materials/MeshStandardNodeMaterial.js +++ b/examples/jsm/nodes/materials/MeshStandardNodeMaterial.js @@ -35,9 +35,6 @@ export default class MeshStandardNodeMaterial extends NodeMaterial { this.metalnessNode = null; this.roughnessNode = null; - this.clearcoatNode = null; - this.clearcoatRoughnessNode = null; - this.envNode = null; this.lightsNode = null; @@ -158,9 +155,6 @@ export default class MeshStandardNodeMaterial extends NodeMaterial { this.metalnessNode = source.metalnessNode; this.roughnessNode = source.roughnessNode; - this.clearcoatNode = source.clearcoatNode; - this.clearcoatRoughnessNode = source.clearcoatRoughnessNode; - this.envNode = source.envNode; this.lightsNode = source.lightsNode; diff --git a/examples/jsm/renderers/webgl/nodes/WebGLNodeBuilder.js b/examples/jsm/renderers/webgl/nodes/WebGLNodeBuilder.js index 7d4bcf9ef37a6c..a0b10eab7dddc3 100644 --- a/examples/jsm/renderers/webgl/nodes/WebGLNodeBuilder.js +++ b/examples/jsm/renderers/webgl/nodes/WebGLNodeBuilder.js @@ -14,7 +14,8 @@ const nodeShaderLib = { LineBasicNodeMaterial: ShaderLib.basic, MeshBasicNodeMaterial: ShaderLib.basic, PointsNodeMaterial: ShaderLib.points, - MeshStandardNodeMaterial: ShaderLib.standard + MeshStandardNodeMaterial: ShaderLib.standard, + MeshPhysicalMaterial: ShaderLib.physical }; function getIncludeSnippet( name ) { @@ -70,7 +71,8 @@ class WebGLNodeBuilder extends NodeBuilder { // shader lib - if ( material.isMeshStandardNodeMaterial ) type = 'MeshStandardNodeMaterial'; + 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'; @@ -130,15 +132,31 @@ class WebGLNodeBuilder extends NodeBuilder { } - if ( material.clearcoatNode && material.clearcoatNode.isNode ) { + if ( material.isMeshPhysicalNodeMaterial ) { - this.addSlot( 'fragment', new SlotNode( material.clearcoatNode, 'CLEARCOAT', 'float' ) ); + if ( material.clearcoatNode && material.clearcoatNode.isNode ) { - } + this.addSlot( 'fragment', new SlotNode( material.clearcoatNode, 'CLEARCOAT', 'float' ) ); + + if ( material.clearcoatRoughnessNode && material.clearcoatRoughnessNode.isNode ) { + + this.addSlot( 'fragment', new SlotNode( material.clearcoatRoughnessNode, 'CLEARCOAT_ROUGHNESS', 'float' ) ); + + } + + if ( material.clearcoatNormalNode && material.clearcoatNormalNode.isNode ) { + + this.addSlot( 'fragment', new SlotNode( material.clearcoatNormalNode, 'CLEARCOAT_NORMAL', 'vec3' ) ); + + } - if ( material.clearcoatRoughnessNode && material.clearcoatRoughnessNode.isNode ) { + material.defines.USE_CLEARCOAT = ''; - this.addSlot( 'fragment', new SlotNode( material.clearcoatRoughnessNode, 'CLEARCOAT_ROUGHNESS', 'float' ) ); + } else { + + delete material.defines.USE_CLEARCOAT; + + } } @@ -250,9 +268,7 @@ class WebGLNodeBuilder extends NodeBuilder { const attributes = this.attributes; - for ( let index = 0; index < attributes.length; index ++ ) { - - const attribute = attributes[ index ]; + for ( const attribute of attributes ) { // ignore common attributes to prevent redefinitions if ( attribute.name === 'uv' || attribute.name === 'position' || attribute.name === 'normal' ) @@ -274,9 +290,7 @@ class WebGLNodeBuilder extends NodeBuilder { const varys = this.varys; - for ( let index = 0; index < varys.length; index ++ ) { - - const vary = varys[ index ]; + for ( const vary of varys ) { snippet += `varying ${vary.type} ${vary.name}; `; @@ -319,7 +333,7 @@ class WebGLNodeBuilder extends NodeBuilder { const shaderProperty = getShaderStageProperty( shaderStage ); - this.shader[ shaderProperty ] = this.shader[ shaderProperty ].replaceAll( source, target ); + this[ shaderProperty ] = this[ shaderProperty ].replaceAll( source, target ); } @@ -433,6 +447,7 @@ ${this.shader[ getShaderStageProperty( shaderStage ) ]} _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' ); @@ -442,6 +457,7 @@ ${this.shader[ getShaderStageProperty( shaderStage ) ]} 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 iridescenceNode = this.getSlot( 'fragment', 'IRIDESCENCE' ); const iridescenceIORNode = this.getSlot( 'fragment', 'IRIDESCENCE_IOR' ); const iridescenceThicknessNode = this.getSlot( 'fragment', 'IRIDESCENCE_THICKNESS' ); @@ -513,7 +529,7 @@ ${this.shader[ getShaderStageProperty( shaderStage ) ]} this.addCodeAfterSnippet( 'fragment', - 'material.clearcoatRoughness = clearcoatRoughness;', + 'material.clearcoat = clearcoat;', `${clearcoatNode.code}\n\tmaterial.clearcoat = ${clearcoatNode.result};` ); @@ -529,9 +545,19 @@ ${this.shader[ getShaderStageProperty( shaderStage ) ]} } - if ( iridescenceNode !== undefined ) { + if ( clearcoatNormalNode !== undefined ) { this.addCodeAfterSnippet( + 'fragment', + 'vec3 clearcoatNormal = geometryNormal;', + `${clearcoatNormalNode.code}\n\tclearcoatNormal = ${clearcoatNormalNode.result};` + ); + + } + + if ( iridescenceNode !== undefined ) { + + this.addCodeAfterInclude( 'fragment', 'iridescence_fragment', `${iridescenceNode.code}\n\tmaterial.iridescence = ${iridescenceNode.result};` @@ -541,7 +567,7 @@ ${this.shader[ getShaderStageProperty( shaderStage ) ]} if ( iridescenceIORNode !== undefined ) { - this.addCodeAfterSnippet( + this.addCodeAfterInclude( 'fragment', 'iridescence_fragment', `${iridescenceIORNode.code}\n\tmaterial.iridescenceIOR = ${iridescenceIORNode.result};` @@ -551,7 +577,7 @@ ${this.shader[ getShaderStageProperty( shaderStage ) ]} if ( iridescenceThicknessNode !== undefined ) { - this.addCodeAfterSnippet( + this.addCodeAfterInclude( 'fragment', 'iridescence_fragment', `${iridescenceThicknessNode.code}\n\tmaterial.iridescenceThickness = ${iridescenceThicknessNode.result};` diff --git a/examples/screenshots/webgl_materials_physical_clearcoat_nodes.jpg b/examples/screenshots/webgl_materials_physical_clearcoat_nodes.jpg new file mode 100644 index 00000000000000..9827e23d95ce1c Binary files /dev/null and b/examples/screenshots/webgl_materials_physical_clearcoat_nodes.jpg differ diff --git a/examples/webgl_materials_physical_clearcoat_nodes.html b/examples/webgl_materials_physical_clearcoat_nodes.html new file mode 100644 index 00000000000000..5ccdfcf22299f9 --- /dev/null +++ b/examples/webgl_materials_physical_clearcoat_nodes.html @@ -0,0 +1,256 @@ + + + + three.js webgl - materials - clearcoat nodes + + + + + +
+ three.js webgl - materials - clearcoat nodes +
+ + + + + + + + + +