Bläddra i källkod

Merge pull request #7859 from sunag/dev

NodeMaterial rev5 + LightNode
Mr.doob 9 år sedan
förälder
incheckning
fbb8f54692

+ 20 - 0
examples/js/nodes/BuilderNode.js

@@ -35,6 +35,20 @@ THREE.BuilderNode.prototype = {
 
 	},
 
+	isCache : function( name ) {
+
+		var i = this.caches.length;
+
+		while ( i -- ) {
+
+			if ( this.caches[ i ].name == name ) return true;
+
+		}
+
+		return false;
+
+	},
+
 	updateCache : function() {
 
 		var cache = this.caches[ this.caches.length - 1 ];
@@ -62,6 +76,12 @@ THREE.BuilderNode.prototype = {
 
 	},
 
+	colorToVector : function( color ) {
+
+		return color.replace( 'r', 'x' ).replace( 'g', 'y' ).replace( 'b', 'z' ).replace( 'a', 'w' );
+
+	},
+
 	getFormatConstructor : function( len ) {
 
 		return THREE.BuilderNode.constructors[ len - 1 ];

+ 40 - 1
examples/js/nodes/NodeLib.js

@@ -71,6 +71,31 @@ THREE.NodeLib.add( new THREE.FunctionNode( [
 "}"
 ].join( "\n" ), null, { derivatives: true } ) );
 
+//
+//	Noise
+//
+
+THREE.NodeLib.add( new THREE.FunctionNode( [
+"float snoise(vec2 co) {",
+	"return fract( sin( dot(co.xy, vec2(12.9898,78.233) ) ) * 43758.5453 );",
+"}"
+].join( "\n" ) ) );
+
+//
+//	Hue
+//
+
+THREE.NodeLib.add( new THREE.FunctionNode( [
+"vec3 hue_rgb(vec3 rgb, float adjustment) {",
+	"const mat3 RGBtoYIQ = mat3(0.299, 0.587, 0.114, 0.595716, -0.274453, -0.321263, 0.211456, -0.522591, 0.311135);",
+	"const mat3 YIQtoRGB = mat3(1.0, 0.9563, 0.6210, 1.0, -0.2721, -0.6474, 1.0, -1.107, 1.7046);",
+	"vec3 yiq = RGBtoYIQ * rgb;",
+	"float hue = atan(yiq.z, yiq.y) + adjustment;",
+	"float chroma = sqrt(yiq.z * yiq.z + yiq.y * yiq.y);",
+	"return YIQtoRGB * vec3(yiq.x, chroma * cos(hue), chroma * sin(hue));",
+"}"
+].join( "\n" ) ) );
+
 //
 //	Saturation
 //
@@ -88,8 +113,22 @@ THREE.NodeLib.add( new THREE.FunctionNode( [
 //
 
 THREE.NodeLib.add( new THREE.FunctionNode( [
-// Algorithm from Chapter 10 of Graphics Shaders.
+// Algorithm from Chapter 10 of Graphics Shaders
 "float luminance_rgb(vec3 rgb) {",
 	"return dot(rgb, LUMA);",
 "}"
 ].join( "\n" ) ) );
+
+//
+//	Vibrance
+//
+
+THREE.NodeLib.add( new THREE.FunctionNode( [
+// Shader by Evan Wallace adapted by @lo-th
+"vec3 vibrance_rgb(vec3 rgb, float adjustment) {",
+	"float average = (rgb.r + rgb.g + rgb.b) / 3.0;",
+	"float mx = max(rgb.r, max(rgb.g, rgb.b));",
+	"float amt = (mx - average) * (-3.0 * adjustment);",
+	"return mix(rgb.rgb, vec3(mx), amt);",
+"}"
+].join( "\n" ) ) );

+ 29 - 0
examples/js/nodes/accessors/LightNode.js

@@ -0,0 +1,29 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.LightNode = function() {
+
+	THREE.TempNode.call( this, 'v3', { shared: false } );
+
+};
+
+THREE.LightNode.prototype = Object.create( THREE.TempNode.prototype );
+THREE.LightNode.prototype.constructor = THREE.LightNode;
+
+THREE.LightNode.prototype.generate = function( builder, output ) {
+
+	if ( builder.isCache( 'light' ) ) {
+
+		return builder.format( 'reflectedLight.directDiffuse', this.getType( builder ), output )
+
+	}
+	else {
+
+		console.warn( "THREE.LightNode is compatible only in \"light\" channel." );
+
+		return builder.format( 'vec3( 0.0 )', this.getType( builder ), output );
+
+	}
+
+};

+ 60 - 19
examples/js/nodes/materials/PhongNode.js

@@ -74,8 +74,10 @@ THREE.PhongNode.prototype.build = function( builder ) {
 
 		if ( transform ) {
 
-			output.push( transform.code );
-			output.push( "transformed = " + transform.result + ";" );
+			output.push(
+				transform.code,
+				"transformed = " + transform.result + ";"
+			);
 
 		}
 
@@ -105,6 +107,8 @@ THREE.PhongNode.prototype.build = function( builder ) {
 
 		if ( this.alpha ) this.alpha.verify( builder );
 
+		if ( this.light ) this.light.verify( builder, 'light' );
+
 		if ( this.ao ) this.ao.verify( builder );
 		if ( this.ambient ) this.ambient.verify( builder );
 		if ( this.shadow ) this.shadow.verify( builder );
@@ -118,13 +122,15 @@ THREE.PhongNode.prototype.build = function( builder ) {
 
 		// build code
 
-		var color = this.color.buildCode( builder, 'v4' );
+		var color = this.color.buildCode( builder, 'c' );
 		var specular = this.specular.buildCode( builder, 'c' );
 		var shininess = this.shininess.buildCode( builder, 'fv1' );
 
 		var alpha = this.alpha ? this.alpha.buildCode( builder, 'fv1' ) : undefined;
 
-		var ao = this.ao ? this.ao.buildCode( builder, 'c' ) : undefined;
+		var light = this.light ? this.light.buildCode( builder, 'v3', 'light' ) : undefined;
+
+		var ao = this.ao ? this.ao.buildCode( builder, 'fv1' ) : undefined;
 		var ambient = this.ambient ? this.ambient.buildCode( builder, 'c' ) : undefined;
 		var shadow = this.shadow ? this.shadow.buildCode( builder, 'c' ) : undefined;
 		var emissive = this.emissive ? this.emissive.buildCode( builder, 'c' ) : undefined;
@@ -152,8 +158,11 @@ THREE.PhongNode.prototype.build = function( builder ) {
 				// prevent undeclared normal
 				THREE.ShaderChunk[ "normal_fragment" ],
 
+				// prevent undeclared material
+			"	BlinnPhongMaterial material;",
+
 				color.code,
-			"	vec4 diffuseColor = " + color.result + ";",
+			"	vec3 diffuseColor = " + color.result + ";",
 			"	ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );",
 
 				THREE.ShaderChunk[ "logdepthbuf_fragment" ],
@@ -193,44 +202,75 @@ THREE.PhongNode.prototype.build = function( builder ) {
 
 		}
 
+		// optimization for now
+
+		output.push( 'material.diffuseColor = ' + ( light ? 'vec3( 1.0 )' : 'diffuseColor' ) + ';' );
+
 		output.push(
 			THREE.ShaderChunk[ "shadowmap_fragment" ],
 
 			// accumulation
-			THREE.ShaderChunk[ "lights_phong_fragment" ],
+			'material.specularColor = specular;',
+			'material.specularShininess = shininess;',
+			'material.specularStrength = specularStrength;',
+
 			THREE.ShaderChunk[ "lights_template" ]
 		);
 
+		if ( light ) {
+
+			output.push(
+				light.code,
+				"reflectedLight.directDiffuse = " + light.result + ";"
+			);
+
+			// apply color
+
+			output.push(
+				"reflectedLight.directDiffuse *= diffuseColor;",
+				"reflectedLight.indirectDiffuse *= diffuseColor;"
+			);
+
+		}
+
 		if ( ao ) {
 
-			output.push( ao.code );
-			output.push( "reflectedLight.indirectDiffuse *= " + ao.result + ";" );
+			output.push(
+				ao.code,
+				"reflectedLight.indirectDiffuse *= " + ao.result + ";"
+			);
 
 		}
 
 		if ( ambient ) {
 
-			output.push( ambient.code );
-			output.push( "reflectedLight.indirectDiffuse += " + ambient.result + ";" );
+			output.push(
+				ambient.code,
+				"reflectedLight.indirectDiffuse += " + ambient.result + ";"
+			);
 
 		}
 
 		if ( shadow ) {
 
-			output.push( shadow.code );
-			output.push( "reflectedLight.directDiffuse *= " + shadow.result + ";" );
-			output.push( "reflectedLight.directSpecular *= " + shadow.result + ";" );
+			output.push(
+				shadow.code,
+				"reflectedLight.directDiffuse *= " + shadow.result + ";",
+				"reflectedLight.directSpecular *= " + shadow.result + ";"
+			);
 
 		}
 
 		if ( emissive ) {
 
-			output.push( emissive.code );
-			output.push( "reflectedLight.directDiffuse += " + emissive.result + ";" );
+			output.push(
+				emissive.code,
+				"reflectedLight.directDiffuse += " + emissive.result + ";"
+			);
 
 		}
 
-		output.push( "vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular;" );
+		output.push( "vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular;" );
 
 		if ( environment ) {
 
@@ -238,9 +278,10 @@ THREE.PhongNode.prototype.build = function( builder ) {
 
 			if ( environmentAlpha ) {
 
-				output.push( environmentAlpha.code );
-
-				output.push( "outgoingLight = mix(" + 'outgoingLight' + "," + environment.result + "," + environmentAlpha.result + ");" );
+				output.push(
+					environmentAlpha.code,
+					"outgoingLight = mix(" + 'outgoingLight' + "," + environment.result + "," + environmentAlpha.result + ");"
+				);
 
 			}
 			else {

+ 1 - 1
examples/js/nodes/materials/PhongNodeMaterial.js

@@ -14,4 +14,4 @@ THREE.PhongNodeMaterial.prototype = Object.create( THREE.NodeMaterial.prototype
 THREE.PhongNodeMaterial.prototype.constructor = THREE.PhongNodeMaterial;
 
 THREE.NodeMaterial.addShortcuts( THREE.PhongNodeMaterial.prototype, 'node',
-[ 'color', 'alpha', 'specular', 'shininess', 'normal', 'normalScale', 'emissive', 'ambient', 'shadow', 'ao', 'environment', 'environmentAlpha', 'transform' ] );
+[ 'color', 'alpha', 'specular', 'shininess', 'normal', 'normalScale', 'emissive', 'ambient', 'light', 'shadow', 'ao', 'environment', 'environmentAlpha', 'transform' ] );

+ 56 - 18
examples/js/nodes/materials/StandardNode.js

@@ -74,8 +74,10 @@ THREE.StandardNode.prototype.build = function( builder ) {
 
 		if ( transform ) {
 
-			output.push( transform.code );
-			output.push( "transformed = " + transform.result + ";" );
+			output.push(
+				transform.code,
+				"transformed = " + transform.result + ";"
+			);
 
 		}
 
@@ -111,6 +113,8 @@ THREE.StandardNode.prototype.build = function( builder ) {
 
 		if ( this.alpha ) this.alpha.verify( builder );
 
+		if ( this.light ) this.light.verify( builder, 'light' );
+
 		if ( this.ao ) this.ao.verify( builder );
 		if ( this.ambient ) this.ambient.verify( builder );
 		if ( this.shadow ) this.shadow.verify( builder );
@@ -123,13 +127,15 @@ THREE.StandardNode.prototype.build = function( builder ) {
 
 		// build code
 
-		var color = this.color.buildCode( builder, 'v4' );
+		var color = this.color.buildCode( builder, 'c' );
 		var roughness = this.roughness.buildCode( builder, 'fv1' );
 		var metalness = this.metalness.buildCode( builder, 'fv1' );
 
 		var alpha = this.alpha ? this.alpha.buildCode( builder, 'fv1' ) : undefined;
 
-		var ao = this.ao ? this.ao.buildCode( builder, 'c' ) : undefined;
+		var light = this.light ? this.light.buildCode( builder, 'v3', 'light' ) : undefined;
+
+		var ao = this.ao ? this.ao.buildCode( builder, 'fv1' ) : undefined;
 		var ambient = this.ambient ? this.ambient.buildCode( builder, 'c' ) : undefined;
 		var shadow = this.shadow ? this.shadow.buildCode( builder, 'c' ) : undefined;
 		var emissive = this.emissive ? this.emissive.buildCode( builder, 'c' ) : undefined;
@@ -167,9 +173,10 @@ THREE.StandardNode.prototype.build = function( builder ) {
 
 				// prevent undeclared material
 			"	StandardMaterial material;",
+			"	material.diffuseColor = vec3( 1.0 );",
 
 				color.code,
-			"	vec4 diffuseColor = " + color.result + ";",
+			"	vec3 diffuseColor = " + color.result + ";",
 			"	ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );",
 
 				THREE.ShaderChunk[ "logdepthbuf_fragment" ],
@@ -207,50 +214,81 @@ THREE.StandardNode.prototype.build = function( builder ) {
 
 		}
 
+		// optimization for now
+
+		output.push( 'material.diffuseColor = ' + ( light ? 'vec3( 1.0 )' : 'diffuseColor * (1.0 - metalnessFactor)' ) + ';' );
+
 		output.push(
 			THREE.ShaderChunk[ "shadowmap_fragment" ],
 
 			// accumulation
-			'material.diffuseColor = diffuseColor.rgb * ( 1.0 - metalnessFactor );',
 			'material.specularRoughness = clamp( roughnessFactor, 0.001, 1.0 );', // disney's remapping of [ 0, 1 ] roughness to [ 0.001, 1 ]
-			'material.specularColor = mix( vec3( 0.04 ), diffuseColor.rgb, metalnessFactor );',
+			'material.specularColor = mix( vec3( 0.04 ), diffuseColor, metalnessFactor );',
 
 			THREE.ShaderChunk[ "lights_template" ]
 		);
 
+		if ( light ) {
+
+			output.push(
+				light.code,
+				"reflectedLight.directDiffuse = " + light.result + ";"
+			);
+
+			// apply color
+
+			output.push(
+				"diffuseColor *= 1.0 - metalnessFactor;",
+
+				"reflectedLight.directDiffuse *= diffuseColor;",
+				"reflectedLight.indirectDiffuse *= diffuseColor;"
+			);
+
+		}
+
 		if ( ao ) {
 
-			output.push( ao.code );
-			output.push( "reflectedLight.indirectDiffuse *= " + ao.result + ";" );
+			output.push(
+				ao.code,
+				"reflectedLight.indirectDiffuse *= " + ao.result + ";"
+			);
 
 		}
 
 		if ( ambient ) {
 
-			output.push( ambient.code );
-			output.push( "reflectedLight.indirectDiffuse += " + ambient.result + ";" );
+			output.push(
+				ambient.code,
+				"reflectedLight.indirectDiffuse += " + ambient.result + ";"
+			);
 
 		}
 
 		if ( shadow ) {
 
-			output.push( shadow.code );
-			output.push( "reflectedLight.directDiffuse *= " + shadow.result + ";" );
-			output.push( "reflectedLight.directSpecular *= " + shadow.result + ";" );
+			output.push(
+				shadow.code,
+				"reflectedLight.directDiffuse *= " + shadow.result + ";",
+				"reflectedLight.directSpecular *= " + shadow.result + ";"
+			);
 
 		}
 
 		if ( emissive ) {
 
-			output.push( emissive.code );
-			output.push( "reflectedLight.directDiffuse += " + emissive.result + ";" );
+			output.push(
+				emissive.code,
+				"reflectedLight.directDiffuse += " + emissive.result + ";"
+			);
 
 		}
 
 		if ( environment ) {
 
-			output.push( environment.code );
-			output.push( "RE_IndirectSpecular(" + environment.result + ", geometry, material, reflectedLight );" );
+			output.push(
+				environment.code,
+				"RE_IndirectSpecular(" + environment.result + ", geometry, material, reflectedLight );"
+			);
 
 		}
 

+ 1 - 1
examples/js/nodes/materials/StandardNodeMaterial.js

@@ -14,4 +14,4 @@ THREE.StandardNodeMaterial.prototype = Object.create( THREE.NodeMaterial.prototy
 THREE.StandardNodeMaterial.prototype.constructor = THREE.StandardNodeMaterial;
 
 THREE.NodeMaterial.addShortcuts( THREE.StandardNodeMaterial.prototype, 'node',
-[ 'color', 'alpha', 'roughness', 'metalness', 'normal', 'normalScale', 'emissive', 'ambient', 'shadow', 'ao', 'environment', 'transform' ] );
+[ 'color', 'alpha', 'roughness', 'metalness', 'normal', 'normalScale', 'emissive', 'ambient', 'light', 'shadow', 'ao', 'environment', 'transform' ] );

+ 1 - 1
examples/js/nodes/math/Math1Node.js

@@ -30,7 +30,7 @@ THREE.Math1Node.TAN = 'tan';
 THREE.Math1Node.ASIN = 'asin';
 THREE.Math1Node.ACOS = 'acos';
 THREE.Math1Node.ARCTAN = 'atan';
-THREE.Math1Node.ABS = 'abc';
+THREE.Math1Node.ABS = 'abs';
 THREE.Math1Node.SIGN = 'sign';
 THREE.Math1Node.LENGTH = 'length';
 THREE.Math1Node.NEGATE = 'negate';

+ 1 - 1
examples/js/nodes/math/Math2Node.js

@@ -79,7 +79,7 @@ THREE.Math2Node.prototype.generate = function( builder, output ) {
 
 		case THREE.Math2Node.MIN:
 		case THREE.Math2Node.MAX:
-		case THREE.Math2Node.MODULO:
+		case THREE.Math2Node.MOD:
 			a = this.a.build( builder, type );
 			b = this.b.build( builder, bl == 1 ? 'fv1' : type );
 			break;

+ 3 - 4
examples/js/nodes/math/Math3Node.js

@@ -62,20 +62,19 @@ THREE.Math3Node.prototype.generate = function( builder, output ) {
 			a = this.a.build( builder, type );
 			b = this.b.build( builder, type );
 			c = this.c.build( builder, 'fv1' );
-		break;
+			break;
 
 		case THREE.Math3Node.MIX:
-		case THREE.Math3Node.SMOOTHSTEP:
 			a = this.a.build( builder, type );
 			b = this.b.build( builder, type );
 			c = this.c.build( builder, cl == 1 ? 'fv1' : type );
-		break;
+			break;
 
 		default:
 			a = this.a.build( builder, type );
 			b = this.b.build( builder, type );
 			c = this.c.build( builder, type );
-		break;
+			break;
 
 	}
 

+ 70 - 0
examples/js/nodes/utils/ColorAdjustmentNode.js

@@ -0,0 +1,70 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.ColorAdjustmentNode = function( rgb, adjustment, method ) {
+
+	THREE.TempNode.call( this, 'v3' );
+
+	this.rgb = rgb;
+	this.adjustment = adjustment;
+
+	this.method = method || THREE.ColorAdjustmentNode.SATURATION;
+
+};
+
+THREE.ColorAdjustmentNode.SATURATION = 'saturation';
+THREE.ColorAdjustmentNode.HUE = 'hue';
+THREE.ColorAdjustmentNode.VIBRANCE = 'vibrance';
+THREE.ColorAdjustmentNode.BRIGHTNESS = 'brightness';
+THREE.ColorAdjustmentNode.CONTRAST = 'contrast';
+
+THREE.ColorAdjustmentNode.prototype = Object.create( THREE.TempNode.prototype );
+THREE.ColorAdjustmentNode.prototype.constructor = THREE.ColorAdjustmentNode;
+
+THREE.ColorAdjustmentNode.prototype.generate = function( builder, output ) {
+
+	var rgb = this.rgb.build( builder, 'v3' );
+	var adjustment = this.adjustment.build( builder, 'fv1' );
+
+	var name;
+
+	switch ( this.method ) {
+
+		case THREE.ColorAdjustmentNode.SATURATION:
+
+			name = 'saturation_rgb';
+
+			break;
+
+		case THREE.ColorAdjustmentNode.HUE:
+
+			name = 'hue_rgb';
+
+			break;
+
+		case THREE.ColorAdjustmentNode.VIBRANCE:
+
+			name = 'vibrance_rgb';
+
+			break;
+
+		case THREE.ColorAdjustmentNode.BRIGHTNESS:
+
+			return builder.format( '(' + rgb + '+' + adjustment + ')', this.getType( builder ), output );
+
+			break;
+
+		case THREE.ColorAdjustmentNode.CONTRAST:
+
+			return builder.format( '(' + rgb + '*' + adjustment + ')', this.getType( builder ), output );
+
+			break;
+
+	}
+
+	builder.include( name );
+
+	return builder.format( name + '(' + rgb + ',' + adjustment + ')', this.getType( builder ), output );
+
+};

+ 22 - 0
examples/js/nodes/utils/LuminanceNode.js

@@ -0,0 +1,22 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.LuminanceNode = function( rgb ) {
+
+	THREE.TempNode.call( this, 'fv1' );
+
+	this.rgb = rgb;
+
+};
+
+THREE.LuminanceNode.prototype = Object.create( THREE.TempNode.prototype );
+THREE.LuminanceNode.prototype.constructor = THREE.LuminanceNode;
+
+THREE.LuminanceNode.prototype.generate = function( builder, output ) {
+
+	builder.include( 'luminance_rgb' );
+
+	return builder.format( 'luminance_rgb(' + this.rgb.build( builder, 'v3' ) + ')', this.getType( builder ), output );
+
+};

+ 22 - 0
examples/js/nodes/utils/NoiseNode.js

@@ -0,0 +1,22 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.NoiseNode = function( coord ) {
+
+	THREE.TempNode.call( this, 'fv1' );
+
+	this.coord = coord;
+
+};
+
+THREE.NoiseNode.prototype = Object.create( THREE.TempNode.prototype );
+THREE.NoiseNode.prototype.constructor = THREE.NoiseNode;
+
+THREE.NoiseNode.prototype.generate = function( builder, output ) {
+
+	builder.include( 'snoise' );
+
+	return builder.format( 'snoise(' + this.coord.build( builder, 'v2' ) + ')', this.getType( builder ), output );
+
+};

+ 2 - 2
examples/js/nodes/utils/NormalMapNode.js

@@ -29,14 +29,14 @@ THREE.NormalMapNode.prototype.generate = function( builder, output ) {
 			this.normal.build( builder, 'v3' ) + ',' +
 			this.value.build( builder, 'v3' ) + ',' +
 			this.value.coord.build( builder, 'v2' ) + ',' +
-			this.scale.build( builder, 'fv1' ) + ')', this.type, output );
+			this.scale.build( builder, 'v2' ) + ')', this.getType( builder ), output );
 
 	}
 	else {
 
 		console.warn( "THREE.NormalMapNode is not compatible with " + builder.shader + " shader." );
 
-		return builder.format( 'vec3( 0.0 )', this.type, output );
+		return builder.format( 'vec3( 0.0 )', this.getType( builder ), output );
 
 	}
 

+ 5 - 4
examples/js/nodes/utils/SwitchNode.js

@@ -24,18 +24,19 @@ THREE.SwitchNode.prototype.generate = function( builder, output ) {
 
 	var type = this.node.getType( builder );
 	var inputLength = builder.getFormatLength( type ) - 1;
+	var components = builder.colorToVector( this.components );
 
 	var node = this.node.build( builder, type );
 
 	var outputLength = 0;
 
-	var i, len = this.components.length;
+	var i, len = components.length;
 
 	// get max length
 
 	for ( i = 0; i < len; i ++ ) {
 
-		outputLength = Math.max( outputLength, builder.getIndexByElement( this.components.charAt( i ) ) );
+		outputLength = Math.max( outputLength, builder.getIndexByElement( components.charAt( i ) ) );
 
 	}
 
@@ -47,8 +48,8 @@ THREE.SwitchNode.prototype.generate = function( builder, output ) {
 
 	for ( i = 0; i < len; i ++ ) {
 
-		var elm = this.components.charAt( i );
-		var idx = builder.getIndexByElement( this.components.charAt( i ) );
+		var elm = components.charAt( i );
+		var idx = builder.getIndexByElement( components.charAt( i ) );
 
 		if ( idx > outputLength ) idx = outputLength;
 

+ 363 - 24
examples/webgl_materials_nodes.html

@@ -58,6 +58,7 @@
 		<script src="js/nodes/accessors/ColorsNode.js"></script>
 		<script src="js/nodes/accessors/CameraNode.js"></script>
 		<script src="js/nodes/accessors/ReflectNode.js"></script>
+		<script src="js/nodes/accessors/LightNode.js"></script>
 
 		<!-- Inputs -->
 		<script src="js/nodes/inputs/IntNode.js"></script>
@@ -81,6 +82,9 @@
 		<script src="js/nodes/utils/TimerNode.js"></script>
 		<script src="js/nodes/utils/RoughnessToBlinnExponentNode.js"></script>
 		<script src="js/nodes/utils/VelocityNode.js"></script>
+		<script src="js/nodes/utils/LuminanceNode.js"></script>
+		<script src="js/nodes/utils/ColorAdjustmentNode.js"></script>
+		<script src="js/nodes/utils/NoiseNode.js"></script>
 
 		<!-- Phong Material -->
 		<script src="js/nodes/materials/PhongNode.js"></script>
@@ -185,11 +189,16 @@
 				'basic / phong': 'phong',
 				'basic / layers': 'layers',
 				'basic / rim': 'rim',
+				'basic / color-adjustment': 'color-adjustment',
 				'adv / fresnel': 'fresnel',
 				'adv / saturation': 'saturation',
 				'adv / top-bottom': 'top-bottom',
+				'adv / skin': 'skin',
+				'adv / skin-phong': 'skin-phong',
 				'adv / caustic': 'caustic',
 				'adv / displace': 'displace',
+				'adv / plush': 'plush',
+				'adv / toon': 'toon',
 				'adv / camera-depth': 'camera-depth',
 				'adv / soft-body': 'soft-body',
 				'adv / wave': 'wave',
@@ -220,7 +229,7 @@
 				} );
 
 			}
-			else if (typeof value == 'object') {
+			else if ( typeof value == 'object' ) {
 
 				node = gui.add( param, name, value ).onChange( function() {
 
@@ -262,19 +271,21 @@
 
 					mtl = new THREE.PhongNodeMaterial();
 
-					//mtl.color = // albedo color
-					//mtl.alpha = // opacity (0 at 1)
-					//mtl.specular = // specular color
+					//mtl.color = // albedo (vec3)
+					//mtl.alpha = // opacity (float)
+					//mtl.specular = // specular color (vec3)
 					//mtl.shininess = // shininess (float)
-					//mtl.normal = // normalmap
-					//mtl.normalScale = // normalmap scale
-					//mtl.emissive = // emissive color
-					//mtl.ambient = // ambient color
-					//mtl.shadow = // shadowmap
-					//mtl.ao = // ambient occlusion
-					//mtl.environment = // reflection map (CubeMap recommended)
-					//mtl.environmentAlpha = // environment alpha
-					//mtl.transform = // vertex transformation
+					//mtl.normal = // normalmap (vec3)
+					//mtl.normalScale = // normalmap scale (vec2)
+					//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.light = // input/output light (vec3)
+					//mtl.environment = // reflection/refraction (vec3)
+					//mtl.environmentAlpha = // environment alpha (float)
+					//mtl.transform = // vertex transformation (vec3)
 
 					var mask = new THREE.SwitchNode( new THREE.TextureNode( decalDiffuse ), 'w' );
 
@@ -294,19 +305,19 @@
 
 					mtl = new THREE.StandardNodeMaterial();
 
-					//mtl.color = // albedo color
-					//mtl.alpha = // opacity (0 at 1)
+					//mtl.color = // albedo (vec3)
+					//mtl.alpha = // opacity (float)
 					//mtl.roughness = // roughness (float)
 					//mtl.metalness = // metalness (float)
-					//mtl.normal = // normalmap
-					//mtl.normalScale = // normalmap scale
-					//mtl.emissive = // emissive color
-					//mtl.ambient = // ambient color
-					//mtl.shadow = // shadowmap
-					//mtl.ao = // ambient occlusion
-					//mtl.environment = // reflection map (CubeMap recommended)
-					//mtl.environmentAlpha = // environment alpha
-					//mtl.transform = // vertex transformation
+					//mtl.normal = // normalmap (vec3)
+					//mtl.normalScale = // normalmap scale (vec2)
+					//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.transform = // vertex transformation (vec3)
 
 					var mask = new THREE.SwitchNode( new THREE.TextureNode( decalDiffuse ), 'w' );
 
@@ -583,6 +594,62 @@
 
 					break;
 
+				case 'color-adjustment':
+
+					// MATERIAL
+
+					mtl = new THREE.PhongNodeMaterial();
+
+					var texture = new THREE.TextureNode( brick );
+
+					var hue = new THREE.FloatNode();
+					var sataturation = new THREE.FloatNode( 1 );
+					var vibrance = new THREE.FloatNode();
+					var brightness = new THREE.FloatNode( 0 );
+					var contrast = new THREE.FloatNode( 1 );
+
+					var hueNode = new THREE.ColorAdjustmentNode( texture, hue, THREE.ColorAdjustmentNode.HUE );
+					var satNode = new THREE.ColorAdjustmentNode( hueNode, sataturation, THREE.ColorAdjustmentNode.SATURATION );
+					var vibranceNode = new THREE.ColorAdjustmentNode( satNode, vibrance, THREE.ColorAdjustmentNode.VIBRANCE );
+					var brightnessNode = new THREE.ColorAdjustmentNode( vibranceNode, brightness, THREE.ColorAdjustmentNode.BRIGHTNESS );
+					var contrastNode = new THREE.ColorAdjustmentNode( brightnessNode, contrast, THREE.ColorAdjustmentNode.CONTRAST );
+
+					mtl.color = contrastNode;
+
+					// GUI
+
+					addGui( 'hue', hue.number, function( val ) {
+
+						hue.number = val;
+
+					}, false, 0, Math.PI * 2 );
+
+					addGui( 'saturation', sataturation.number, function( val ) {
+
+						sataturation.number = val;
+
+					}, false, 0, 2 );
+
+					addGui( 'vibrance', vibrance.number, function( val ) {
+
+						vibrance.number = val;
+
+					}, false, - 1, 1 );
+
+					addGui( 'brightness', brightness.number, function( val ) {
+
+						brightness.number = val;
+
+					}, false, 0, .5 );
+
+					addGui( 'contrast', contrast.number, function( val ) {
+
+						contrast.number = val;
+
+					}, false, 0, 2 );
+
+					break;
+
 				case 'fresnel':
 
 					// MATERIAL
@@ -1221,6 +1288,278 @@
 
 					break;
 
+				case 'plush':
+
+					// MATERIAL
+
+					mtl = new THREE.PhongNodeMaterial();
+
+					var color = new THREE.ColorNode( 0x8D8677 );
+					var mildness = new THREE.FloatNode( 1.6 );
+					var fur = new THREE.FloatNode( .5 );
+
+					var posDirection = new THREE.Math1Node( new THREE.PositionNode( THREE.PositionNode.VIEW ), THREE.Math1Node.NORMALIZE );
+					var norDirection = new THREE.Math1Node( new THREE.NormalNode( THREE.NormalNode.VIEW ), THREE.Math1Node.NORMALIZE );
+
+					var viewZ = new THREE.Math2Node(
+						posDirection,
+						norDirection,
+						THREE.Math2Node.DOT
+					);
+
+					// without luma correction for now
+					var mildnessColor = new THREE.OperatorNode(
+						color,
+						mildness,
+						THREE.OperatorNode.MUL
+					);
+
+					var furScale = new THREE.OperatorNode(
+						viewZ,
+						fur,
+						THREE.OperatorNode.MUL
+					);
+
+					mtl.color = color;
+					mtl.normal = new THREE.TextureNode( grassNormal );
+					mtl.normalScale = furScale;
+					mtl.environment = mildnessColor;
+					mtl.environmentAlpha = new THREE.Math1Node( viewZ, THREE.Math1Node.INVERT );
+					mtl.shininess = new THREE.FloatNode( 0 );
+
+					// GUI
+
+					addGui( 'color', color.value.getHex(), function( val ) {
+
+						color.value.setHex( val );
+
+					}, true );
+
+					addGui( 'mildness', mildness.number, function( val ) {
+
+						mildness.number = val;
+
+					}, false, 1, 2 );
+
+					addGui( 'fur', fur.number, function( val ) {
+
+						fur.number = val;
+
+					}, false, 0, 2 );
+
+					break;
+
+				case 'skin':
+				case 'skin-phong':
+
+					// MATERIAL
+
+					mtl = name == 'skin' ? new THREE.StandardNodeMaterial() : new THREE.PhongNodeMaterial();
+
+					var skinColor = new THREE.ColorNode( 0xFFC495 );
+					var bloodColor = new THREE.ColorNode( 0x6b0602 );
+					var wrapLight = new THREE.FloatNode( 1.5 );
+					var wrapShadow = new THREE.FloatNode( 0 );
+
+					var directLight = new THREE.LightNode();
+
+					var lightLuminance = new THREE.LuminanceNode( directLight );
+
+					var lightWrap = new THREE.Math3Node(
+						wrapShadow,
+						wrapLight,
+						lightLuminance,
+						THREE.Math3Node.SMOOTHSTEP
+					);
+
+					var lightTransition = new THREE.OperatorNode(
+						lightWrap,
+						new THREE.ConstNode( THREE.ConstNode.PI2 ),
+						THREE.OperatorNode.MUL
+					);
+
+					var wrappedLight = new THREE.Math1Node( lightTransition, THREE.Math1Node.SIN );
+
+					var wrappedLightColor = new THREE.OperatorNode(
+						wrappedLight,
+						bloodColor,
+						THREE.OperatorNode.MUL
+					);
+
+					var bloodArea = new THREE.Math1Node( wrappedLightColor, THREE.Math1Node.SAT );
+
+					var totalLight = new THREE.OperatorNode(
+						directLight,
+						bloodArea,
+						THREE.OperatorNode.ADD
+					);
+
+					mtl.color = skinColor;
+					mtl.light = totalLight;
+
+					if ( name == 'skin' ) {
+
+						// StandardNodeMaterial
+
+						mtl.metalness = new THREE.FloatNode( .05 );
+						mtl.roughness = new THREE.FloatNode( .5 );
+						mtl.environment = new THREE.CubeTextureNode( cubemap );
+
+					}
+					else {
+
+						// PhongNodeMaterial
+
+						mtl.specular = new THREE.ColorNode( 0x2f2e2d );
+						mtl.shininess = new THREE.FloatNode( 10 );
+
+					}
+
+					// GUI
+
+					addGui( 'skinColor', skinColor.value.getHex(), function( val ) {
+
+						skinColor.value.setHex( val );
+
+					}, true );
+
+					addGui( 'bloodColor', bloodColor.value.getHex(), function( val ) {
+
+						bloodColor.value.setHex( val );
+
+					}, true );
+
+					addGui( 'wrapLight', wrapLight.number, function( val ) {
+
+						wrapLight.number = val;
+
+					}, false, 0, 3 );
+
+					addGui( 'wrapShadow', wrapShadow.number, function( val ) {
+
+						wrapShadow.number = val;
+
+					}, false, -1, 0 );
+
+					break;
+
+				case 'toon':
+
+					// MATERIAL
+
+					mtl = new THREE.PhongNodeMaterial();
+
+					var count = new THREE.FloatNode( 3.43 );
+					var sceneDirectLight = new THREE.LightNode();
+					var color = new THREE.ColorNode( 0xAABBFF );
+
+					var lineColor = new THREE.ColorNode( 0xFF0000 );
+					var lineSize = new THREE.FloatNode( 0.23 );
+					var lineInner = new THREE.FloatNode( 0 );
+
+					// CEL
+
+					var lightLuminance = new THREE.LuminanceNode( sceneDirectLight );
+
+					var preCelLight = new THREE.OperatorNode(
+						lightLuminance,
+						count,
+						THREE.OperatorNode.MUL
+					);
+
+					var celLight = new THREE.Math1Node(
+						preCelLight,
+						THREE.Math1Node.CEIL
+					);
+
+					var posCelLight = new THREE.OperatorNode(
+						celLight,
+						count,
+						THREE.OperatorNode.DIV
+					);
+
+					// LINE
+
+					var posDirection = new THREE.Math1Node( new THREE.PositionNode( THREE.PositionNode.VIEW ), THREE.Math1Node.NORMALIZE );
+					var norDirection = new THREE.Math1Node( new THREE.NormalNode( THREE.NormalNode.VIEW ), THREE.Math1Node.NORMALIZE );
+
+					var viewZ = new THREE.Math2Node(
+						posDirection,
+						norDirection,
+						THREE.Math2Node.DOT
+					);
+
+					var lineOutside = new THREE.Math1Node(
+						viewZ,
+						THREE.Math1Node.ABS
+					);
+
+					var line = new THREE.OperatorNode(
+						lineOutside,
+						new THREE.FloatNode( 1 ),
+						THREE.OperatorNode.DIV
+					);
+
+					var lineScaled = new THREE.Math3Node(
+						line,
+						lineSize,
+						lineInner,
+						THREE.Math3Node.SMOOTHSTEP
+					);
+
+					var innerContour = new THREE.Math1Node( new THREE.Math1Node( lineScaled, THREE.Math1Node.SAT ), THREE.Math1Node.INVERT );
+
+					// APPLY
+
+					mtl.color = color;
+					mtl.light = posCelLight;
+					mtl.shininess = new THREE.FloatNode( 0 );
+
+					mtl.environment = lineColor;
+					mtl.environmentAlpha = innerContour;
+
+					// GUI
+
+					addGui( 'color', color.value.getHex(), function( val ) {
+
+						color.value.setHex( val );
+
+					}, true );
+
+					addGui( 'lineColor', lineColor.value.getHex(), function( val ) {
+
+						lineColor.value.setHex( val );
+
+					}, true );
+
+					addGui( 'count', count.number, function( val ) {
+
+						count.number = val;
+
+					}, false, 1, 8 );
+
+					addGui( 'lineSize', lineSize.number, function( val ) {
+
+						lineSize.number = val;
+
+					}, false, 0, 1 );
+
+					addGui( 'lineInner', lineInner.number, function( val ) {
+
+						lineInner.number = val;
+
+					}, false, 0, 1 );
+
+					addGui( 'ignoreIndirectLight', false, function( val ) {
+
+						mtl.ao = val ? new THREE.FloatNode() : undefined;
+
+						mtl.build();
+
+					} );
+
+					break;
+
 				case 'firefly':
 
 					// MATERIAL

+ 42 - 27
examples/webgl_postprocessing_nodes.html

@@ -79,7 +79,7 @@
 		<script src="js/nodes/utils/SwitchNode.js"></script>
 		<script src="js/nodes/utils/JoinNode.js"></script>
 		<script src="js/nodes/utils/TimerNode.js"></script>
-		<script src="js/nodes/utils/NormalMapNode.js"></script>
+		<script src="js/nodes/utils/ColorAdjustmentNode.js"></script>
 
 		<!-- Post-Processing -->
 		<script src="js/nodes/postprocessing/NodePass.js"></script>
@@ -90,7 +90,7 @@
 			var object, light, nodepass;
 			var gui, guiElements = [];
 
-			var param = { example: 'brightness' };
+			var param = { example: 'color-adjustment' };
 
 			var lensflare2 = new THREE.TextureLoader().load( 'textures/lensflare2.jpg' );
 			lensflare2.wrapS = lensflare2.wrapT = THREE.RepeatWrapping;
@@ -171,42 +171,57 @@
 
 				switch ( name ) {
 
-					case 'brightness':
+					case 'color-adjustment':
 
 						var screen = new THREE.ScreenNode();
 
-						var brightness = new THREE.FloatNode( - 1 );
-						var contrast = new THREE.FloatNode( 3 );
+						var hue = new THREE.FloatNode();
+						var sataturation = new THREE.FloatNode( 1 );
+						var vibrance = new THREE.FloatNode();
+						var brightness = new THREE.FloatNode( 0 );
+						var contrast = new THREE.FloatNode( 1 );
 
-						var contrastResult = new THREE.OperatorNode(
-							screen,
-							contrast,
-							THREE.OperatorNode.MUL
-						);
+						var hueNode = new THREE.ColorAdjustmentNode( screen, hue, THREE.ColorAdjustmentNode.HUE );
+						var satNode = new THREE.ColorAdjustmentNode( hueNode, sataturation, THREE.ColorAdjustmentNode.SATURATION );
+						var vibranceNode = new THREE.ColorAdjustmentNode( satNode, vibrance, THREE.ColorAdjustmentNode.VIBRANCE );
+						var brightnessNode = new THREE.ColorAdjustmentNode( vibranceNode, brightness, THREE.ColorAdjustmentNode.BRIGHTNESS );
+						var contrastNode = new THREE.ColorAdjustmentNode( brightnessNode, contrast, THREE.ColorAdjustmentNode.CONTRAST );
 
-						var brightnessResult = new THREE.OperatorNode(
-							contrastResult,
-							brightness,
-							THREE.OperatorNode.ADD
-						);
-
-						nodepass.value = brightnessResult;
+						nodepass.value = contrastNode;
 
 						// GUI
 
+						addGui( 'hue', hue.number, function( val ) {
+
+							hue.number = val;
+
+						}, false, 0, Math.PI * 2 );
+
+						addGui( 'saturation', sataturation.number, function( val ) {
+
+							sataturation.number = val;
+
+						}, false, 0, 2 );
+
+						addGui( 'vibrance', vibrance.number, function( val ) {
+
+							vibrance.number = val;
+
+						}, false, - 1, 1 );
+
 						addGui( 'brightness', brightness.number, function( val ) {
 
 							brightness.number = val;
 
-						}, false, - 1, 2 );
+						}, false, 0, .5 );
 
 						addGui( 'contrast', contrast.number, function( val ) {
 
 							contrast.number = val;
 
-						}, false, 0, 4 );
+						}, false, 0, 2 );
 
-					break;
+						break;
 
 					case 'fade':
 
@@ -238,7 +253,7 @@
 
 						}, false, 0, 1 );
 
-					break;
+						break;
 
 					case 'invert':
 
@@ -266,7 +281,7 @@
 
 						}, false, 0, 1 );
 
-					break;
+						break;
 
 					case 'blends':
 
@@ -295,7 +310,7 @@
 
 						} );
 
-					break;
+						break;
 
 					case 'saturation':
 
@@ -326,7 +341,7 @@
 
 						}, false, 0, 2 );
 
-					break;
+						break;
 
 					case 'refraction':
 
@@ -335,7 +350,7 @@
 						var normal = new THREE.TextureNode( decalNormal );
 						var normalXY = new THREE.SwitchNode( normal, 'xy' );
 						var scale = new THREE.FloatNode( .5 );
-						var flip = new THREE.Vector2Node( -1, 1 );
+						var flip = new THREE.Vector2Node( - 1, 1 );
 
 						var normalXYFlip = new THREE.Math1Node(
 							normalXY,
@@ -387,7 +402,7 @@
 
 						} );
 
-					break;
+						break;
 
 					case 'mosaic':
 
@@ -456,7 +471,7 @@
 
 						} );
 
-					break;
+						break;
 
 				}