瀏覽代碼

Merge pull request #17167 from sunag/dev-fix-nodes

NodeMaterial: Fix irradiance
Mr.doob 6 年之前
父節點
當前提交
5902347bc2

+ 1 - 0
examples/jsm/nodes/Nodes.js

@@ -82,6 +82,7 @@ export { VelocityNode } from './utils/VelocityNode.js';
 export { UVTransformNode } from './utils/UVTransformNode.js';
 export { MaxMIPLevelNode } from './utils/MaxMIPLevelNode.js';
 export { ColorSpaceNode } from './utils/ColorSpaceNode.js';
+export { SubSlotNode } from './utils/SubSlotNode.js';
 
 // effects
 

+ 6 - 0
examples/jsm/nodes/core/NodeBuilder.js

@@ -449,6 +449,12 @@ NodeBuilder.prototype = {
 
 	},
 
+	require: function ( name ) {
+
+		this.requires[ name ] = true;
+
+	},
+
 	isDefined: function ( name ) {
 
 		return this.defines[ name ] !== undefined;

+ 39 - 6
examples/jsm/nodes/materials/nodes/StandardNode.js

@@ -153,7 +153,20 @@ StandardNode.prototype.build = function ( builder ) {
 		if ( this.shadow ) this.shadow.analyze( builder );
 		if ( this.emissive ) this.emissive.analyze( builder, { slot: 'emissive' } );
 
-		if ( this.environment ) this.environment.analyze( builder, { cache: 'env', context: contextEnvironment, slot: 'environment' } ); // isolate environment from others inputs ( see TextureNode, CubeTextureNode )
+		if ( this.environment ) {
+
+			// isolate environment from others inputs ( see TextureNode, CubeTextureNode )
+			// environment.analyze will detect if there is a need of calculate irradiance
+
+			this.environment.analyze( builder, { cache: 'radiance', context: contextEnvironment, slot: 'radiance' } ); 
+
+			if ( builder.requires.irradiance ) {
+
+				this.environment.analyze( builder, { cache: 'irradiance', context: contextEnvironment, slot: 'irradiance' } ); 
+
+			}
+
+		}
 
 		// build code
 
@@ -179,7 +192,21 @@ StandardNode.prototype.build = function ( builder ) {
 		var shadow = this.shadow ? this.shadow.flow( builder, 'c' ) : undefined;
 		var emissive = this.emissive ? this.emissive.flow( builder, 'c', { slot: 'emissive' } ) : undefined;
 
-		var environment = this.environment ? this.environment.flow( builder, 'c', { cache: 'env', context: contextEnvironment, slot: 'environment' } ) : undefined;
+		var environment;
+
+		if ( this.environment ) {
+
+			environment = {
+				radiance: this.environment.flow( builder, 'c', { cache: 'radiance', context: contextEnvironment, slot: 'radiance' } )
+			};
+
+			if ( builder.requires.irradiance ) {
+
+				environment.irradiance = this.environment.flow( builder, 'c', { cache: 'irradiance', context: contextEnvironment, slot: 'irradiance' } );
+
+			}
+
+		}
 
 		var clearCoatEnv = useClearCoat && environment ? this.environment.flow( builder, 'c', { cache: 'clearCoat', context: contextEnvironment, slot: 'environment' } ) : undefined;
 
@@ -371,7 +398,13 @@ StandardNode.prototype.build = function ( builder ) {
 
 		if ( environment ) {
 
-			output.push( environment.code );
+			output.push( environment.radiance.code );
+
+			if ( builder.requires.irradiance ) {
+
+				output.push( environment.irradiance.code );
+
+			}
 
 			if ( clearCoatEnv ) {
 
@@ -382,11 +415,11 @@ StandardNode.prototype.build = function ( builder ) {
 
 			}
 
-			output.push( "radiance += " + environment.result + ";" );
+			output.push( "radiance += " + environment.radiance.result + ";" );
 
-			if ( environment.extra.irradiance ) {
+			if ( builder.requires.irradiance ) {
 
-				output.push( "irradiance += PI * " + environment.extra.irradiance + ";" );
+				output.push( "irradiance += PI * " + environment.irradiance.result + ";" );
 
 			}
 

+ 14 - 4
examples/jsm/nodes/misc/TextureCubeNode.js

@@ -79,12 +79,12 @@ TextureCubeNode.prototype.generate = function ( builder, output ) {
 
 	if ( builder.isShader( 'fragment' ) ) {
 
-		var radiance = this.generateTextureCubeUV( builder, this.radianceCache );
-		var irradiance = this.generateTextureCubeUV( builder, this.irradianceCache );
+		builder.require( 'irradiance' );
 
-		builder.context.extra.irradiance = irradiance;
+		var cache = builder.slot === 'irradiance' ? this.irradianceCache : this.radianceCache;
+		var result = this.generateTextureCubeUV( builder, cache );
 
-		return builder.format( 'vec4( ' + radiance + ', 1.0 )', this.getType( builder ), output );
+		return builder.format( 'vec4( ' + result + ', 1.0 )', this.getType( builder ), output );
 
 	} else {
 
@@ -96,6 +96,16 @@ TextureCubeNode.prototype.generate = function ( builder, output ) {
 
 };
 
+TextureCubeNode.prototype.copy = function ( source ) {
+
+	TempNode.prototype.copy.call( this, source );
+
+	this.value = source.value;
+
+	return this;
+
+};
+
 TextureCubeNode.prototype.toJSON = function ( meta ) {
 
 	var data = this.getJSONNode( meta );

+ 11 - 0
examples/jsm/nodes/utils/SubSlot.d.ts

@@ -0,0 +1,11 @@
+import { TempNode } from '../core/TempNode';
+import { NodeBuilder } from '../core/NodeBuilder';
+
+export class SubSlots extends TempNode {
+  constructor(slots?: object);
+
+  slots: Node[];
+
+  generate(builder: NodeBuilder, output: string): string;
+  copy(source: SubSlots): this;
+}

+ 79 - 0
examples/jsm/nodes/utils/SubSlotNode.js

@@ -0,0 +1,79 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+import { TempNode } from '../core/TempNode.js';
+
+function SubSlotNode( slots ) {
+
+	TempNode.call( this );
+
+	this.slots = slots || {};
+
+}
+
+SubSlotNode.prototype = Object.create( TempNode.prototype );
+SubSlotNode.prototype.constructor = SubSlotNode;
+SubSlotNode.prototype.nodeType = "SubSlot";
+
+SubSlotNode.prototype.getType = function ( builder, output ) {
+
+	return output;
+
+};
+
+SubSlotNode.prototype.generate = function ( builder, output ) {
+
+	if ( this.slots[ builder.slot ] ) {
+
+		return this.slots[ builder.slot ].build( builder, output )
+
+	}
+
+	return builder.format( '0.0', 'f', output );
+
+};
+
+SubSlotNode.prototype.copy = function ( source ) {
+
+	TempNode.prototype.copy.call( this, source );
+
+	for ( var prop in source.slots ) {
+
+		this.slots[ prop ] = source.slots[ prop ];
+
+	}
+
+	return this;
+
+};
+
+SubSlotNode.prototype.toJSON = function ( meta ) {
+
+	var data = this.getJSONNode( meta );
+
+	if ( ! data ) {
+
+		data = this.createJSONNode( meta );
+
+		data.slots = {};
+
+		for ( var prop in this.slots ) {
+
+			var slot = this.slots[ prop ];
+
+			if ( slot ) {
+
+				data.slots[ prop ] = slot.toJSON( meta ).uuid;
+
+			}
+
+		}
+
+	}
+
+	return data;
+
+};
+
+export { SubSlotNode };

+ 8 - 2
examples/webgl_materials_envmaps_pmrem_nodes.html

@@ -46,6 +46,7 @@
 			import {
 				StandardNodeMaterial,
 				FloatNode,
+				OperatorNode,
 				TextureNode,
 				TextureCubeNode
 			} from './jsm/nodes/Nodes.js';
@@ -55,13 +56,14 @@
 				roughness: 0.0,
 				metalness: 0.0,
 				exposure: 1.0,
+				intensity: 1.0,
 				animate: true,
 				debug: false
 			};
 
 			var container, stats;
 			var camera, scene, renderer, controls;
-			var nodeMaterial, nodeTexture, nodeTextureSize, torusMesh, planeMesh;
+			var nodeMaterial, nodeTexture, nodeTextureSize, nodeTextureIntensity, torusMesh, planeMesh;
 			var hdrCubeRenderTarget;
 			var hdrCubeMap;
 
@@ -118,8 +120,9 @@
 
 				nodeTexture = new TextureNode();
 				nodeTextureSize = new FloatNode( 1024 );
+				nodeTextureIntensity = new FloatNode( 1 );
 
-				nodeMaterial.environment = new TextureCubeNode( nodeTexture, nodeTextureSize );
+				nodeMaterial.environment = new OperatorNode( new TextureCubeNode( nodeTexture, nodeTextureSize ), nodeTextureIntensity, OperatorNode.MUL );
 
 				torusMesh = new THREE.Mesh( geometry, nodeMaterial );
 				scene.add( torusMesh );
@@ -164,6 +167,7 @@
 				gui.add( params, 'roughness', 0, 1, 0.01 );
 				gui.add( params, 'metalness', 0, 1, 0.01 );
 				gui.add( params, 'exposure', 0, 2, 0.01 );
+				gui.add( params, 'intensity', 0, 2, 0.01 );
 				gui.add( params, 'animate', true );
 				gui.add( params, 'debug', false );
 				gui.open();
@@ -197,6 +201,8 @@
 				torusMesh.material.roughness.value = params.roughness;
 				torusMesh.material.metalness.value = params.metalness;
 
+				nodeTextureIntensity.value = params.intensity;
+
 				if ( nodeTextureSize.value !== params.textureSize ) {
 
 					generate( params.textureSize );

+ 248 - 7
examples/webgl_materials_nodes.html

@@ -20,14 +20,20 @@
 
 			import { GUI } from './jsm/libs/dat.gui.module.js';
 			import { OrbitControls } from './jsm/controls/OrbitControls.js';
-			import { NodeMaterialLoader, NodeMaterialLoaderUtils } from './jsm/loaders/NodeMaterialLoader.js';
+
 			import { TeapotBufferGeometry } from './jsm/geometries/TeapotBufferGeometry.js';
 
+			import { HDRCubeTextureLoader } from './jsm/loaders/HDRCubeTextureLoader.js';
+			import { PMREMGenerator } from './jsm/pmrem/PMREMGenerator.js';
+			import { PMREMCubeUVPacker } from './jsm/pmrem/PMREMCubeUVPacker.js';
+
+			import { NodeMaterialLoader, NodeMaterialLoaderUtils } from './jsm/loaders/NodeMaterialLoader.js';
+
 			import * as Nodes from './jsm/nodes/Nodes.js';
 
 			var container = document.getElementById( 'container' );
 
-			var renderer, scene, camera, clock = new THREE.Clock(), fov = 50;
+			var renderer, scene, lightGroup, camera, clock = new THREE.Clock(), fov = 50;
 			var frame = new Nodes.NodeFrame();
 			var teapot, mesh;
 			var controls;
@@ -84,6 +90,45 @@
 
 			}();
 
+			function generatePREM( cubeMap, textureSize ) {
+
+				textureSize = textureSize || 1024;
+
+				var pmremGenerator = new PMREMGenerator( cubeMap, undefined, textureSize / 4 );
+				pmremGenerator.update( renderer );
+
+				var pmremCubeUVPacker = new PMREMCubeUVPacker( pmremGenerator.cubeLods );
+				pmremCubeUVPacker.update( renderer );
+
+				pmremGenerator.dispose();
+				pmremCubeUVPacker.dispose();
+
+				return pmremCubeUVPacker.CubeUVRenderTarget.texture;
+
+			}
+
+			var premTexture;
+
+			function getPREM( callback, textureSize ) {
+
+				if ( premTexture ) return callback( premTexture );
+
+				var hdrUrls = [ 'px.hdr', 'nx.hdr', 'py.hdr', 'ny.hdr', 'pz.hdr', 'nz.hdr' ];
+				var hdrCubeMap = new HDRCubeTextureLoader()
+					.setPath( './textures/cube/pisaHDR/' )
+					.setDataType( THREE.UnsignedByteType )
+					.load( hdrUrls, function () {
+
+						premTexture = generatePREM( hdrCubeMap, textureSize );
+
+						library[ premTexture.uuid ] = premTexture;
+
+						callback( premTexture );
+
+					} );
+
+			}
+
 			window.addEventListener( 'load', init );
 
 			function init() {
@@ -107,15 +152,20 @@
 				controls.minDistance = 50;
 				controls.maxDistance = 200;
 
-				scene.add( new THREE.AmbientLight( 0x464646 ) );
+				lightGroup = new THREE.Group();
+				scene.add( lightGroup );
+
+				var light;
+
+				lightGroup.add( new THREE.AmbientLight( 0x464646 ) );
 
-				var light = new THREE.DirectionalLight( 0xffddcc, 1 );
+				light = new THREE.DirectionalLight( 0xffddcc, 1 );
 				light.position.set( 1, 0.75, 0.5 );
-				scene.add( light );
+				lightGroup.add( light );
 
-				var light = new THREE.DirectionalLight( 0xccccff, 1 );
+				light = new THREE.DirectionalLight( 0xccccff, 1 );
 				light.position.set( - 1, 0.75, - 0.5 );
-				scene.add( light );
+				lightGroup.add( light );
 
 				teapot = new TeapotBufferGeometry( 15, 18 );
 
@@ -145,6 +195,7 @@
 					'basic / mesh-standard': 'mesh-standard',
 					'basic / standard': 'standard',
 					'basic / physical': 'physical',
+					'basic / prem': 'prem',
 					'basic / phong': 'phong',
 					'basic / layers': 'layers',
 					'basic / rim': 'rim',
@@ -178,6 +229,7 @@
 					'node / position': 'node-position',
 					'node / normal': 'node-normal',
 					'node / reflect': 'node-reflect',
+					'misc / sub-slot': 'sub-slot',
 					'misc / smoke': 'smoke',
 					'misc / firefly': 'firefly',
 					'misc / reserved-keywords': 'reserved-keywords',
@@ -238,6 +290,8 @@
 
 				move = false;
 
+				lightGroup.visible = true;
+
 				if ( mesh.material ) mesh.material.dispose();
 
 				if ( rtTexture ) {
@@ -393,6 +447,193 @@
 
 						break;
 
+					case 'prem':
+
+						// MATERIAL
+
+						mtl = new Nodes.StandardNodeMaterial();
+
+						//mtl.color = // albedo (vec3)
+						//mtl.alpha = // opacity (float)
+						//mtl.roughness = // roughness (float)
+						//mtl.metalness = // metalness (float)
+						//mtl.normal = // normal (vec3)
+						//mtl.emissive = // emissive color (vec3)
+						//mtl.ambient = // ambient color (vec3)
+						//mtl.shadow = // shadowmap (vec3)
+						//mtl.light = // custom-light (vec3)
+						//mtl.ao = // ambient occlusion (float)
+						//mtl.environment = // reflection/refraction (vec3)
+						//mtl.position = // vertex local position (vec3)
+
+						var mask = new Nodes.SwitchNode( new Nodes.TextureNode( getTexture( "decalDiffuse" ) ), 'w' );
+
+						var intensity = new Nodes.FloatNode( 1 );
+
+						var normalScale = new Nodes.FloatNode( .3 );
+
+						var roughnessA = new Nodes.FloatNode( .5 );
+						var metalnessA = new Nodes.FloatNode( .5 );
+
+						var roughnessB = new Nodes.FloatNode( 0 );
+						var metalnessB = new Nodes.FloatNode( 1 );
+
+						var roughness = new Nodes.MathNode(
+							roughnessA,
+							roughnessB,
+							mask,
+							Nodes.MathNode.MIX
+						);
+
+						var metalness = new Nodes.MathNode(
+							metalnessA,
+							metalnessB,
+							mask,
+							Nodes.MathNode.MIX
+						);
+
+						var normalMask = new Nodes.OperatorNode(
+							new Nodes.MathNode( mask, Nodes.MathNode.INVERT ),
+							normalScale,
+							Nodes.OperatorNode.MUL
+						);
+
+						mtl.color = new Nodes.ColorNode( 0xEEEEEE );
+						mtl.roughness = roughness;
+						mtl.metalness = metalness;
+						mtl.normal = new Nodes.NormalMapNode( new Nodes.TextureNode( getTexture( "grassNormal" ) ) );
+						mtl.normal.scale = normalMask;
+
+						getPREM(function(texture) {
+
+							var envNode = new Nodes.TextureCubeNode( new Nodes.TextureNode( texture ) );
+
+							mtl.environment = new Nodes.OperatorNode( envNode, intensity, Nodes.OperatorNode.MUL );
+							mtl.needsUpdate = true;
+
+						});
+
+						// GUI
+
+						addGui( 'color', mtl.color.value.getHex(), function ( val ) {
+
+							mtl.color.value.setHex( val );
+
+						}, true );
+
+						addGui( 'intensity', intensity.value, function ( val ) {
+
+							intensity.value = val;
+
+						}, false, 0, 2 );
+
+						addGui( 'roughnessA', roughnessA.value, function ( val ) {
+
+							roughnessA.value = val;
+
+						}, false, 0, 1 );
+
+						addGui( 'metalnessA', metalnessA.value, function ( val ) {
+
+							metalnessA.value = val;
+
+						}, false, 0, 1 );
+
+						addGui( 'roughnessB', roughnessB.value, function ( val ) {
+
+							roughnessB.value = val;
+
+						}, false, 0, 1 );
+
+						addGui( 'metalnessB', metalnessB.value, function ( val ) {
+
+							metalnessB.value = val;
+
+						}, false, 0, 1 );
+
+						addGui( 'normalScale', normalScale.value, function ( val ) {
+
+							normalScale.value = val;
+
+						}, false, 0, 1 );
+
+						break;
+
+					case 'sub-slot':
+
+						// disable dynamic light
+
+						lightGroup.visible = false;
+
+						// MATERIAL
+
+						mtl = new Nodes.StandardNodeMaterial();
+
+						// NODES
+
+						var mask = new Nodes.SwitchNode( new Nodes.TextureNode( getTexture( "decalDiffuse" ) ), 'w' );
+
+						var normalScale = new Nodes.FloatNode( .3 );
+
+						var radiance = new Nodes.FloatNode( 1 );
+						var irradiance = new Nodes.FloatNode( 1 );
+
+						var roughness = new Nodes.FloatNode( .5 );
+						var metalness = new Nodes.FloatNode( .5 );
+
+						mtl.color = new Nodes.ColorNode( 0xEEEEEE );
+						mtl.roughness = roughness;
+						mtl.metalness = metalness;
+						mtl.normal = new Nodes.NormalMapNode( new Nodes.TextureNode( getTexture( "grassNormal" ) ) );
+						mtl.normal.scale = normalScale;
+
+						getPREM(function(texture) {
+
+							var envNode = new Nodes.TextureCubeNode( new Nodes.TextureNode( texture ) );
+
+							var subSlotNode = new Nodes.SubSlotNode();
+							subSlotNode.slots['radiance'] = new Nodes.OperatorNode( radiance, envNode, Nodes.OperatorNode.MUL );
+							subSlotNode.slots['irradiance'] = new Nodes.OperatorNode( irradiance, envNode, Nodes.OperatorNode.MUL );
+
+							mtl.environment = subSlotNode;
+							mtl.needsUpdate = true;
+
+						});
+
+						// GUI
+
+						addGui( 'radiance', radiance.value, function ( val ) {
+
+							radiance.value = val;
+
+						}, false, 0, 2 );
+
+						addGui( 'irradiance', irradiance.value, function ( val ) {
+
+							irradiance.value = val;
+
+						}, false, 0, 2 );
+
+						addGui( 'roughness', roughness.value, function ( val ) {
+
+							roughness.value = val;
+
+						}, false, 0, 1 );
+
+						addGui( 'metalness', metalness.value, function ( val ) {
+
+							metalness.value = val;
+
+						}, false, 0, 1 );
+
+						addGui( 'normalScale', normalScale.value, function ( val ) {
+
+							normalScale.value = val;
+
+						}, false, 0, 1 );
+
+						break;
+
 					case 'mesh-standard':
 
 						// MATERIAL