未验证 提交 f17e42db 编写于 作者: S sunag 提交者: GitHub

WebGLNodeBuilder: New features (#22474)

* move to webgl/nodes

* WebGLNodes: normal, opacity, emissive, roughness, metalness, clearcoat, clearcoatRoughness

* ignore normalNode for now
上级 f4515f69
import NodeBuilder from '../../nodes/core/NodeBuilder.js';
import NodeSlot from '../../nodes/core/NodeSlot.js';
import WebGLPhysicalContextNode from './WebGLPhysicalContextNode.js';
import { ShaderChunk } from 'three';
const shaderStages = [ 'vertex', 'fragment' ];
function getIncludeSnippet( name ) {
return `#include <${name}>`;
}
function getShaderStageProperty( shaderStage ) {
return `${shaderStage}Shader`;
}
class WebGLNodeBuilder extends NodeBuilder {
constructor( material, renderer, properties ) {
constructor( material, renderer, shader ) {
super( material, renderer );
this.properties = properties;
this.shader = shader;
this._parseMaterial();
......@@ -19,35 +36,97 @@ class WebGLNodeBuilder extends NodeBuilder {
// parse inputs
if ( material.colorNode !== undefined ) {
if ( material.colorNode && material.colorNode.isNode ) {
this.addSlot( 'fragment', new NodeSlot( material.colorNode, 'COLOR', 'vec4' ) );
}
}
if ( material.opacityNode && material.opacityNode.isNode ) {
this.addSlot( 'fragment', new NodeSlot( material.opacityNode, 'OPACITY', 'float' ) );
}
if ( material.normalNode && material.normalNode.isNode ) {
this.addSlot( 'fragment', new NodeSlot( material.normalNode, 'NORMAL', 'vec3' ) );
}
if ( material.emissiveNode && material.emissiveNode.isNode ) {
getVaryFromNode( node, type ) {
this.addSlot( 'fragment', new NodeSlot( material.emissiveNode, 'EMISSIVE', 'vec3' ) );
}
if ( material.metalnessNode && material.metalnessNode.isNode ) {
this.addSlot( 'fragment', new NodeSlot( material.metalnessNode, 'METALNESS', 'float' ) );
}
if ( material.roughnessNode && material.roughnessNode.isNode ) {
this.addSlot( 'fragment', new NodeSlot( material.roughnessNode, 'ROUGHNESS', 'float' ) );
}
if ( material.clearcoatNode && material.clearcoatNode.isNode ) {
this.addSlot( 'fragment', new NodeSlot( material.clearcoatNode, 'CLEARCOAT', 'float' ) );
}
if ( material.clearcoatRoughnessNode && material.clearcoatRoughnessNode.isNode ) {
this.addSlot( 'fragment', new NodeSlot( material.clearcoatRoughnessNode, 'CLEARCOAT_ROUGHNESS', 'float' ) );
}
const vary = super.getVaryFromNode( node, type );
if ( material.envNode && material.envNode.isNode ) {
if ( node.isUVNode ) {
const envRadianceNode = new WebGLPhysicalContextNode( WebGLPhysicalContextNode.RADIANCE, material.envNode );
const envIrradianceNode = new WebGLPhysicalContextNode( WebGLPhysicalContextNode.IRRADIANCE, material.envNode );
vary.name = 'vUv';
this.addSlot( 'fragment', new NodeSlot( envRadianceNode, 'RADIANCE', 'vec3' ) );
this.addSlot( 'fragment', new NodeSlot( envIrradianceNode, 'IRRADIANCE', 'vec3' ) );
}
return vary;
}
getTexture( textureProperty, uvSnippet, biasSnippet = null ) {
if ( biasSnippet !== null ) {
return `texture2D( ${textureProperty}, ${uvSnippet}, ${biasSnippet} )`;
} else {
return `texture2D( ${textureProperty}, ${uvSnippet} )`;
}
}
getTexture( textureProperty, uvSnippet ) {
getCubeTexture( textureProperty, uvSnippet, biasSnippet = null ) {
const textureCube = 'textureCubeLodEXT'; // textureCubeLodEXT textureLod
if ( biasSnippet !== null ) {
return `sRGBToLinear( texture2D( ${textureProperty}, ${uvSnippet} ) )`;
return `${textureCube}( ${textureProperty}, ${uvSnippet}, ${biasSnippet} )`;
} else {
return `${textureCube}( ${textureProperty}, ${uvSnippet} )`;
}
}
getUniformsHeaderSnippet( shaderStage ) {
getUniforms( shaderStage ) {
const uniforms = this.uniforms[ shaderStage ];
......@@ -57,13 +136,17 @@ class WebGLNodeBuilder extends NodeBuilder {
if ( uniform.type === 'texture' ) {
snippet += `uniform sampler2D ${uniform.name};`;
snippet += `uniform sampler2D ${uniform.name}; `;
} else if ( uniform.type === 'cubeTexture' ) {
snippet += `uniform samplerCube ${uniform.name}; `;
} else {
const vectorType = this.getVectorType( uniform.type );
snippet += `uniform ${vectorType} ${uniform.name};`;
snippet += `uniform ${vectorType} ${uniform.name}; `;
}
......@@ -73,51 +156,233 @@ class WebGLNodeBuilder extends NodeBuilder {
}
getAttributesHeaderSnippet( /*shaderStage*/ ) {
getAttributes( shaderStage ) {
let snippet = '';
if ( shaderStage === 'vertex' ) {
const attributes = this.attributes;
for ( let index = 0; index < attributes.length; index ++ ) {
const attribute = attributes[ index ];
// ignore common attributes to prevent redefinitions
if ( attribute.name === 'uv' || attribute.name === 'position' || attribute.name === 'normal' )
continue;
snippet += `attribute ${attribute.type} ${attribute.name}; `;
}
}
return snippet;
}
getVarsHeaderSnippet( /*shaderStage*/ ) {
getVarys( shaderStage ) {
let snippet = '';
const varys = this.varys;
for ( let index = 0; index < varys.length; index ++ ) {
const vary = varys[ index ];
snippet += `varying ${vary.type} ${vary.name}; `;
}
return snippet;
}
getVarsBodySnippet( /*shaderStage*/ ) {
addCodeAfterSnippet( shaderStage, snippet, code ) {
const shaderProperty = getShaderStageProperty( shaderStage );
let source = this.shader[ shaderProperty ];
const index = source.indexOf( snippet );
if ( index !== - 1 ) {
const start = source.substring( 0, index + snippet.length );
const end = source.substring( index + snippet.length );
source = `${start}\n${code}\n${end}`;
}
this.shader[ shaderProperty ] = source;
}
getVarysHeaderSnippet( /*shaderStage*/ ) {
addCodeAfterInclude( shaderStage, includeName, code ) {
const includeSnippet = getIncludeSnippet( includeName );
this.addCodeAfterSnippet( shaderStage, includeSnippet, code );
}
getVarysBodySnippet( /*shaderStage*/ ) {
replaceCode( shaderStage, source, target ) {
const shaderProperty = getShaderStageProperty( shaderStage );
this.shader[ shaderProperty ] = this.shader[ shaderProperty ].replaceAll( source, target );
}
composeUniforms() {
parseInclude( shaderStage, ...includes ) {
const uniforms = this.uniforms[ 'fragment' ];
for ( const name of includes ) {
for ( const uniform of uniforms ) {
const includeSnippet = getIncludeSnippet( name );
const code = ShaderChunk[ name ];
this.properties.uniforms[ uniform.name ] = uniform;
this.replaceCode( shaderStage, includeSnippet, code );
}
}
/*prependCode( code ) {
this.shader.vertexShader = code + this.shader.vertexShader;
this.shader.fragmentShader = code + this.shader.fragmentShader;
}*/
build() {
super.build();
this.properties.defines[ 'NODE_HEADER_UNIFORMS' ] = this.defines[ 'fragment' ][ 'NODE_HEADER_UNIFORMS' ];
this.properties.defines[ 'NODE_COLOR' ] = this.defines[ 'fragment' ][ 'NODE_COLOR' ];
this.composeUniforms();
this._addSnippets();
this._buildShader();
return this;
}
_addSnippets() {
this.parseInclude( 'fragment', 'lights_physical_fragment' );
this.addCodeAfterInclude( 'fragment', 'normal_fragment_begin',
`#ifdef NODE_NORMAL
NODE_CODE_NORMAL
normal = NODE_NORMAL;
#endif` );
this.addCodeAfterInclude( 'fragment', 'color_fragment',
`#ifdef NODE_COLOR
NODE_CODE_COLOR
diffuseColor = NODE_COLOR;
#endif` );
this.addCodeAfterInclude( 'fragment', 'alphamap_fragment',
`#ifdef NODE_OPACITY
NODE_CODE_OPACITY
diffuseColor.a *= NODE_OPACITY;
#endif` );
this.addCodeAfterInclude( 'fragment', 'emissivemap_fragment',
`#ifdef NODE_EMISSIVE
NODE_CODE_EMISSIVE
totalEmissiveRadiance = NODE_EMISSIVE;
#endif` );
this.addCodeAfterInclude( 'fragment', 'roughnessmap_fragment',
`#ifdef NODE_ROUGHNESS
NODE_CODE_ROUGHNESS
roughnessFactor = NODE_ROUGHNESS;
#endif` );
this.addCodeAfterInclude( 'fragment', 'metalnessmap_fragment',
`#ifdef NODE_METALNESS
NODE_CODE_METALNESS
metalnessFactor = NODE_METALNESS;
#endif` );
this.addCodeAfterSnippet( 'fragment', 'material.clearcoatRoughness = clearcoatRoughness;',
`#ifdef NODE_CLEARCOAT
NODE_CODE_CLEARCOAT
material.clearcoat = NODE_CLEARCOAT;
#endif
#ifdef NODE_CLEARCOAT_ROUGHNESS
NODE_CODE_CLEARCOAT_ROUGHNESS
material.clearcoatRoughness = NODE_CLEARCOAT_ROUGHNESS;
#endif` );
this.addCodeAfterInclude( 'fragment', 'lights_fragment_begin',
`#ifdef NODE_RADIANCE
NODE_CODE_RADIANCE
radiance += NODE_RADIANCE;
NODE_CODE_IRRADIANCE
iblIrradiance += PI * NODE_IRRADIANCE;
#endif` );
for ( const shaderStage of shaderStages ) {
this.addCodeAfterSnippet( shaderStage, 'main() {',
`#ifdef NODE_CODE
NODE_CODE
#endif` );
}
}
_buildShader() {
for ( const shaderStage of shaderStages ) {
// uniforms
for ( const uniform of this.uniforms[ shaderStage ] ) {
this.shader.uniforms[ uniform.name ] = uniform;
}
// code
const shaderProperty = getShaderStageProperty( shaderStage );
const nodeCode = this[ shaderProperty ];
this.shader[ shaderProperty ] = nodeCode + this.shader[ shaderProperty ];
}
}
}
export { WebGLNodeBuilder };
import { WebGLNodeBuilder } from './WebGLNodeBuilder.js';
import NodeFrame from '../../nodes/core/NodeFrame.js';
import { Material } from '../../../../../build/three.module.js';
import { Material } from 'three';
function addCodeAfterSnippet( source, snippet, code ) {
const builders = new WeakMap();
export const nodeFrame = new NodeFrame();
const index = source.indexOf( snippet );
if ( index !== - 1 ) {
const start = source.substring( 0, index + snippet.length );
const end = source.substring( index + snippet.length );
return `${start}\n${code}\n${end}`;
}
return source;
Material.prototype.onBuild = function ( parameters, renderer ) {
}
builders.set( this, new WebGLNodeBuilder( this, renderer, parameters ).build() );
Material.prototype.onBuild = function ( parameters, renderer ) {
};
new WebGLNodeBuilder( this, renderer, parameters ).build();
Material.prototype.onUpdate = function ( renderer, scene, camera, geometry, object ) {
let fragmentShader = parameters.fragmentShader;
const nodeBuilder = builders.get( this );
fragmentShader = addCodeAfterSnippet( fragmentShader, '#include <color_pars_fragment>',
`#ifdef NODE_HEADER_UNIFORMS
if ( nodeBuilder !== undefined ) {
NODE_HEADER_UNIFORMS
nodeFrame.update();
#endif` );
nodeFrame.material = this;
nodeFrame.camera = camera;
nodeFrame.object = object;
fragmentShader = addCodeAfterSnippet( fragmentShader, '#include <color_fragment>',
`#ifdef NODE_COLOR
for ( const node of nodeBuilder.updateNodes ) {
diffuseColor = NODE_COLOR;
nodeFrame.updateNode( node );
#endif` );
}
parameters.fragmentShader = fragmentShader;
}
};
import ContextNode from '../core/ContextNode.js';
import NormalNode from '../accessors/NormalNode.js';
import ExpressionNode from '../core/ExpressionNode.js';
import FloatNode from '../inputs/FloatNode.js';
import ContextNode from '../../nodes/core/ContextNode.js';
import NormalNode from '../../nodes/accessors/NormalNode.js';
import ExpressionNode from '../../nodes/core/ExpressionNode.js';
import FloatNode from '../../nodes/inputs/FloatNode.js';
class PhysicalMaterialContextNode extends ContextNode {
class WebGLPhysicalContextNode extends ContextNode {
static RADIANCE = 'radiance';
static IRRADIANCE = 'irradiance';
......@@ -22,11 +22,11 @@ class PhysicalMaterialContextNode extends ContextNode {
let roughness = null;
if ( scope === PhysicalMaterialContextNode.RADIANCE ) {
if ( scope === WebGLPhysicalContextNode.RADIANCE ) {
roughness = new ExpressionNode( 'roughnessFactor', 'float' );
} else if ( scope === PhysicalMaterialContextNode.IRRADIANCE ) {
} else if ( scope === WebGLPhysicalContextNode.IRRADIANCE ) {
roughness = new FloatNode( 1.0 ).setConst( true );
......@@ -42,4 +42,4 @@ class PhysicalMaterialContextNode extends ContextNode {
}
export default PhysicalMaterialContextNode;
export default WebGLPhysicalContextNode;
......@@ -36,6 +36,7 @@
import TextureNode from './jsm/renderers/nodes/inputs/TextureNode.js';
import Vector3Node from './jsm/renderers/nodes/inputs/Vector3Node.js';
import OperatorNode from './jsm/renderers/nodes/math/OperatorNode.js';
import SwitchNode from './jsm/renderers/nodes/utils/SwitchNode.js';
let container, stats;
......@@ -82,23 +83,25 @@
const loader = new THREE.TextureLoader()
.setPath( 'models/obj/cerberus/' );
material.roughness = 1; // attenuates roughnessMap
material.metalness = 1; // attenuates metalnessMap
const diffuseMap = loader.load( 'Cerberus_A.jpg' );
diffuseMap.wrapS = THREE.RepeatWrapping;
diffuseMap.encoding = THREE.sRGBEncoding;
//material.map = diffuseMap;
const rmMap = loader.load( 'Cerberus_RM.jpg' );
rmMap.wrapS = THREE.RepeatWrapping;
const normalMap = loader.load( 'Cerberus_N.jpg' );
normalMap.wrapS = THREE.RepeatWrapping;
const mpMapNode = new TextureNode( rmMap );
material.colorNode = new OperatorNode( '*', new TextureNode( diffuseMap ), new Vector3Node( material.color ) );
// roughness is in G channel, metalness is in B channel
material.metalnessMap = material.roughnessMap = loader.load( 'Cerberus_RM.jpg' );
material.normalMap = loader.load( 'Cerberus_N.jpg' );
material.roughnessNode = new SwitchNode( mpMapNode, 'g' );
material.metalnessNode = new SwitchNode( mpMapNode, 'b' );
material.roughnessMap.wrapS = THREE.RepeatWrapping;
material.metalnessMap.wrapS = THREE.RepeatWrapping;
material.normalMap.wrapS = THREE.RepeatWrapping;
material.normalMap = normalMap;
group.traverse( function ( child ) {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册