瀏覽代碼

NodeMaterial rev5 + LightNode

SUNAG 9 年之前
父節點
當前提交
e81b960d49

+ 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 ];

+ 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 );
+
+	}
+
+};

+ 55 - 18
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,12 +122,14 @@ 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 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;
@@ -152,8 +158,12 @@ THREE.PhongNode.prototype.build = function( builder ) {
 				// prevent undeclared normal
 				THREE.ShaderChunk[ "normal_fragment" ],
 
+				// prevent undeclared material
+			"	BlinnPhongMaterial 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" ],
@@ -197,40 +207,66 @@ THREE.PhongNode.prototype.build = function( builder ) {
 			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 +274,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' ] );

+ 49 - 16
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,12 +127,14 @@ 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 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;
@@ -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" ],
@@ -211,46 +218,72 @@ THREE.StandardNode.prototype.build = function( builder ) {
 			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 );',
 
 			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' ] );

+ 335 - 2
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() {
 
@@ -271,8 +280,10 @@
 					//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.light = // input/output light (vec3)
+					//mtl.environment = // reflection/refraction (vec3)
 					//mtl.environmentAlpha = // environment alpha (float)
 					//mtl.transform = // vertex transformation (vec3)
 
@@ -303,6 +314,7 @@
 					//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)
@@ -582,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
@@ -1220,6 +1288,271 @@
 
 					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( 2.5 );
+
+					var directLight = new THREE.LightNode();
+
+					var lightLuminance = new THREE.LuminanceNode( directLight );
+
+					var lightWrap = new THREE.Math3Node(
+						new THREE.FloatNode( - 1 ),
+						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 );
+
+					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;
 
 				}