Skip to content
Merged
1 change: 1 addition & 0 deletions examples/files.json
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@
"webgl_materials_envmaps_hdr_nodes",
"webgl_materials_envmaps_pmrem_nodes",
"webgl_materials_nodes",
"webgl_materials_standard_nodes",
"webgl_mirror_nodes",
"webgl_performance_nodes",
"webgl_postprocessing_nodes",
Expand Down
2 changes: 2 additions & 0 deletions examples/jsm/renderers/nodes/accessors/UVNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ class UVNode extends AttributeNode {

this.index = index;

Object.defineProperty( this, 'isUVNode', { value: true } );

}

getAttributeName( /*builder*/ ) {
Expand Down
115 changes: 115 additions & 0 deletions examples/jsm/renderers/webgl/nodes/WebGLNodeBuilder.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import NodeBuilder from '../../nodes/core/NodeBuilder.js';
import NodeSlot from '../../nodes/core/NodeSlot.js';

class WebGLNodeBuilder extends NodeBuilder {

constructor( material, renderer, properties ) {

super( material, renderer );

this.properties = properties;

this._parseMaterial();

}

_parseMaterial() {

const material = this.material;

// parse inputs

if ( material.colorNode !== undefined ) {

this.addSlot( 'fragment', new NodeSlot( material.colorNode, 'COLOR', 'vec4' ) );

}

}

getVaryFromNode( node, type ) {

const vary = super.getVaryFromNode( node, type );

if ( node.isUVNode ) {

vary.name = 'vUv';

}

return vary;

}

getTexture( textureProperty, uvSnippet ) {

return `sRGBToLinear( texture2D( ${textureProperty}, ${uvSnippet} ) )`;

}

getUniformsHeaderSnippet( shaderStage ) {

const uniforms = this.uniforms[ shaderStage ];

let snippet = '';

for ( let uniform of uniforms ) {

if ( uniform.type === 'texture' ) {

snippet += `uniform sampler2D ${uniform.name};`;

} else {

let vectorType = this.getVectorType( uniform.type );

snippet += `uniform ${vectorType} ${uniform.name};`;

}

}

return snippet;

}

getAttributesHeaderSnippet( /*shaderStage*/ ) {

}

getVarysHeaderSnippet( /*shaderStage*/ ) {

}

getVarysBodySnippet( /*shaderStage*/ ) {

}

composeUniforms() {

const uniforms = this.uniforms[ 'fragment' ];

for ( let uniform of uniforms ) {

this.properties.uniforms[ uniform.name ] = uniform;

}

}

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();

return this;

}

}

export { WebGLNodeBuilder };
44 changes: 44 additions & 0 deletions examples/jsm/renderers/webgl/nodes/WebGLNodes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { WebGLNodeBuilder } from './WebGLNodeBuilder.js';

import { Material } from '../../../../../build/three.module.js';

function addCodeAfterSnippet( source, snippet, code ) {

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.onNodeBuild = function ( parameters, renderer ) {

const nodeBuilder = new WebGLNodeBuilder( this, renderer, parameters ).build();

let fragmentShader = parameters.fragmentShader;

fragmentShader = addCodeAfterSnippet( fragmentShader, '#include <color_pars_fragment>',
`#ifdef NODE_HEADER_UNIFORMS

NODE_HEADER_UNIFORMS

#endif` );

fragmentShader = addCodeAfterSnippet( fragmentShader, '#include <color_fragment>',
`#ifdef NODE_COLOR

diffuseColor *= NODE_COLOR;

#endif` );

parameters.fragmentShader = fragmentShader;

};
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
206 changes: 206 additions & 0 deletions examples/webgl_materials_standard_nodes.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js webgl - materials - standard (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> - webgl physically based material<br/>
<a href="http://www.polycount.com/forum/showthread.php?t=130641" target="_blank" rel="noopener">Cerberus(FFVII Gun) model</a> by Andrew Maximov.
</div>

<script type="module">

import * as THREE from '../build/three.module.js';

import Stats from './jsm/libs/stats.module.js';

import { GUI } from './jsm/libs/dat.gui.module.js';
import { TrackballControls } from './jsm/controls/TrackballControls.js';
import { OBJLoader } from './jsm/loaders/OBJLoader.js';
import { RGBELoader } from './jsm/loaders/RGBELoader.js';

import './jsm/renderers/webgl/nodes/WebGLNodes.js';

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';

const statsEnabled = true;

let container, stats;

let camera, scene, renderer, controls;

init();
animate();

function init() {

container = document.createElement( 'div' );
document.body.appendChild( container );

renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );

renderer.outputEncoding = THREE.sRGBEncoding;
renderer.toneMapping = THREE.ReinhardToneMapping;
renderer.toneMappingExposure = 3;

//

scene = new THREE.Scene();

camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 0.01, 1000 );
camera.position.z = 2;

controls = new TrackballControls( camera, renderer.domElement );

//

scene.add( new THREE.HemisphereLight( 0x443333, 0x222233, 4 ) );

//

const material = new THREE.MeshStandardMaterial();

new OBJLoader()
.setPath( 'models/obj/cerberus/' )
.load( 'Cerberus.obj', function ( group ) {

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;
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.roughnessMap.wrapS = THREE.RepeatWrapping;
material.metalnessMap.wrapS = THREE.RepeatWrapping;
material.normalMap.wrapS = THREE.RepeatWrapping;

group.traverse( function ( child ) {

if ( child.isMesh ) {

child.material = material;

}

} );

group.position.x = - 0.45;
group.rotation.y = - Math.PI / 2;
scene.add( group );

} );

const environments = {

'Venice Sunset': { filename: 'venice_sunset_1k.hdr' },
'Overpass': { filename: 'pedestrian_overpass_1k.hdr' }

};

function loadEnvironment(name) {

if ( environments[ name ].texture !== undefined ) {

scene.background = environments[ name ].texture;
scene.environment = environments[ name ].texture;
return;

}

const filename = environments[ name ].filename;
new RGBELoader()
.setDataType( THREE.UnsignedByteType )
.setPath( 'textures/equirectangular/' )
.load( filename, function ( hdrEquirect ) {

const hdrCubeRenderTarget = pmremGenerator.fromEquirectangular( hdrEquirect );
hdrEquirect.dispose();

scene.background = hdrCubeRenderTarget.texture;
scene.environment = hdrCubeRenderTarget.texture;
environments[ name ].texture = hdrCubeRenderTarget.texture;

} );

}

const params = {

environment: Object.keys( environments )[ 0 ]

};
loadEnvironment( params.environment );

const gui = new GUI();
gui.add( params, 'environment', Object.keys( environments ) ).onChange( function( value ) {

loadEnvironment(value);

} );
gui.open();

const pmremGenerator = new THREE.PMREMGenerator( renderer );
pmremGenerator.compileEquirectangularShader();

//

if ( statsEnabled ) {

stats = new Stats();
container.appendChild( stats.dom );

}

window.addEventListener( 'resize', onWindowResize );

}

//

function onWindowResize() {

renderer.setSize( window.innerWidth, window.innerHeight );

camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();

}

//

function animate() {

requestAnimationFrame( animate );

controls.update();
renderer.render( scene, camera );

if ( statsEnabled ) stats.update();

}

</script>

</body>
</html>
2 changes: 2 additions & 0 deletions src/materials/Material.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ Material.prototype = Object.assign( Object.create( EventDispatcher.prototype ),

isMaterial: true,

onNodeBuild: function ( /* shaderobject, renderer */ ) {},

onBeforeCompile: function ( /* shaderobject, renderer */ ) {},

customProgramCacheKey: function () {
Expand Down
Loading