Explorar o código

update based in https://github.com/mrdoob/three.js/pull/17358 and https://github.com/mrdoob/three.js/pull/17359

sunag %!s(int64=6) %!d(string=hai) anos
pai
achega
3e4cee3663

+ 1 - 6
examples/jsm/nodes/Nodes.d.ts

@@ -57,12 +57,6 @@ export * from './math/CondNode';
 export * from './procedural/NoiseNode';
 export * from './procedural/CheckerNode';
 
-// bsdfs
-
-export * from './bsdfs/BlinnShininessExponentNode';
-export * from './bsdfs/BlinnExponentToRoughnessNode';
-export * from './bsdfs/RoughnessToBlinnExponentNode';
-
 // misc
 
 export * from './misc/TextureCubeUVNode';
@@ -79,6 +73,7 @@ export * from './utils/TimerNode';
 export * from './utils/VelocityNode';
 export * from './utils/UVTransformNode';
 export * from './utils/MaxMIPLevelNode';
+export * from './utils/SpecularMIPLevelNode';
 export * from './utils/ColorSpaceNode';
 
 // effects

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

@@ -59,12 +59,6 @@ export { CondNode } from './math/CondNode.js';
 export { NoiseNode } from './procedural/NoiseNode.js';
 export { CheckerNode } from './procedural/CheckerNode.js';
 
-// bsdfs
-
-export { BlinnShininessExponentNode } from './bsdfs/BlinnShininessExponentNode.js';
-export { BlinnExponentToRoughnessNode } from './bsdfs/BlinnExponentToRoughnessNode.js';
-export { RoughnessToBlinnExponentNode } from './bsdfs/RoughnessToBlinnExponentNode.js';
-
 // misc
 
 export { TextureCubeUVNode } from './misc/TextureCubeUVNode.js';
@@ -81,6 +75,7 @@ export { TimerNode } from './utils/TimerNode.js';
 export { VelocityNode } from './utils/VelocityNode.js';
 export { UVTransformNode } from './utils/UVTransformNode.js';
 export { MaxMIPLevelNode } from './utils/MaxMIPLevelNode.js';
+export { SpecularMIPLevelNode } from './utils/SpecularMIPLevelNode.js';
 export { ColorSpaceNode } from './utils/ColorSpaceNode.js';
 export { SubSlotNode } from './utils/SubSlotNode.js';
 

+ 12 - 1
examples/jsm/nodes/accessors/ReflectNode.js

@@ -55,11 +55,22 @@ ReflectNode.prototype.generate = function ( builder, output ) {
 			case ReflectNode.VECTOR:
 
 				var viewNormalNode = builder.context.viewNormal || new NormalNode();
+				var roughnessNode = builder.context.roughness;
 
 				var viewNormal = viewNormalNode.build( builder, 'v3' );
 				var viewPosition = new PositionNode( PositionNode.VIEW ).build( builder, 'v3' );
+				var roughness = roughnessNode ? roughnessNode.build( builder, 'f' ) : undefined;
 
-				var code = `inverseTransformDirection( reflect( -normalize( ${viewPosition} ), ${viewNormal} ), viewMatrix )`;
+				var method = `reflect( -normalize( ${viewPosition} ), ${viewNormal} )`;
+
+				if ( viewNormalNode && roughness ) {
+
+					// Mixing the reflection with the normal is more accurate and keeps rough objects from gathering light from behind their tangent plane.
+					method = `normalize( mix( ${method}, ${viewNormal}, ${roughness} * ${roughness} ) )`;
+
+				}
+
+				var code = `inverseTransformDirection( ${method}, viewMatrix )`;
 
 				if ( isUnique ) {
 

+ 0 - 13
examples/jsm/nodes/bsdfs/BlinnExponentToRoughnessNode.d.ts

@@ -1,13 +0,0 @@
-import { TempNode } from '../core/TempNode';
-import { NodeBuilder } from '../core/NodeBuilder';
-import { BlinnShininessExponentNode } from './BlinnShininessExponentNode';
-
-export class BlinnExponentToRoughnessNode extends TempNode {
-  constructor(blinnExponent?: BlinnShininessExponentNode);
-
-  blinnExponent: BlinnShininessExponentNode;
-  nodeType: string;
-
-  generate(builder: NodeBuilder, output: string): string;
-  copy(source: BlinnExponentToRoughnessNode): this;
-}

+ 0 - 52
examples/jsm/nodes/bsdfs/BlinnExponentToRoughnessNode.js

@@ -1,52 +0,0 @@
-/**
- * @author sunag / http://www.sunag.com.br/
- */
-
-import { TempNode } from '../core/TempNode.js';
-import { BlinnShininessExponentNode } from './BlinnShininessExponentNode.js';
-
-function BlinnExponentToRoughnessNode( blinnExponent ) {
-
-	TempNode.call( this, 'f' );
-
-	this.blinnExponent = blinnExponent || new BlinnShininessExponentNode();
-
-}
-
-BlinnExponentToRoughnessNode.prototype = Object.create( TempNode.prototype );
-BlinnExponentToRoughnessNode.prototype.constructor = BlinnExponentToRoughnessNode;
-BlinnExponentToRoughnessNode.prototype.nodeType = "BlinnExponentToRoughness";
-
-BlinnExponentToRoughnessNode.prototype.generate = function ( builder, output ) {
-
-	return builder.format( 'BlinnExponentToGGXRoughness( ' + this.blinnExponent.build( builder, 'f' ) + ' )', this.type, output );
-
-};
-
-BlinnExponentToRoughnessNode.prototype.copy = function ( source ) {
-
-	TempNode.prototype.copy.call( this, source );
-
-	this.blinnExponent = source.blinnExponent;
-
-	return this;
-
-};
-
-BlinnExponentToRoughnessNode.prototype.toJSON = function ( meta ) {
-
-	var data = this.getJSONNode( meta );
-
-	if ( ! data ) {
-
-		data = this.createJSONNode( meta );
-
-		data.blinnExponent = this.blinnExponent;
-
-	}
-
-	return data;
-
-};
-
-export { BlinnExponentToRoughnessNode };

+ 0 - 10
examples/jsm/nodes/bsdfs/BlinnShininessExponentNode.d.ts

@@ -1,10 +0,0 @@
-import { TempNode } from '../core/TempNode';
-import { NodeBuilder } from '../core/NodeBuilder';
-
-export class BlinnShininessExponentNode extends TempNode {
-  constructor();
-
-  nodeType: string;
-
-  generate(builder: NodeBuilder, output: string): string;
-}

+ 0 - 31
examples/jsm/nodes/bsdfs/BlinnShininessExponentNode.js

@@ -1,31 +0,0 @@
-/**
- * @author sunag / http://www.sunag.com.br/
- */
-
-import { TempNode } from '../core/TempNode.js';
-
-function BlinnShininessExponentNode() {
-
-	TempNode.call( this, 'f' );
-
-}
-
-BlinnShininessExponentNode.prototype = Object.create( TempNode.prototype );
-BlinnShininessExponentNode.prototype.constructor = BlinnShininessExponentNode;
-BlinnShininessExponentNode.prototype.nodeType = "BlinnShininessExponent";
-
-BlinnShininessExponentNode.prototype.generate = function ( builder, output ) {
-
-	if ( builder.isCache( 'clearcoat' ) ) {
-
-		return builder.format( 'Material_Clearcoat_BlinnShininessExponent( material )', this.type, output );
-
-	} else {
-
-		return builder.format( 'Material_BlinnShininessExponent( material )', this.type, output );
-
-	}
-
-};
-
-export { BlinnShininessExponentNode };

+ 0 - 94
examples/jsm/nodes/bsdfs/RoughnessToBlinnExponentNode.js

@@ -1,94 +0,0 @@
-/**
- * @author sunag / http://www.sunag.com.br/
- */
-
-import { TempNode } from '../core/TempNode.js';
-import { FunctionNode } from '../core/FunctionNode.js';
-import { MaxMIPLevelNode } from '../utils/MaxMIPLevelNode.js';
-import { BlinnShininessExponentNode } from './BlinnShininessExponentNode.js';
-
-function RoughnessToBlinnExponentNode( texture ) {
-
-	TempNode.call( this, 'f' );
-
-	this.texture = texture;
-
-	this.maxMIPLevel = new MaxMIPLevelNode( texture );
-	this.blinnShininessExponent = new BlinnShininessExponentNode();
-
-}
-
-RoughnessToBlinnExponentNode.Nodes = ( function () {
-
-	var getSpecularMIPLevel = new FunctionNode( [
-		// taken from here: http://casual-effects.blogspot.ca/2011/08/plausible-environment-lighting-in-two.html
-		"float getSpecularMIPLevel( const in float blinnShininessExponent, const in float maxMIPLevelScalar ) {",
-
-		//	float envMapWidth = pow( 2.0, maxMIPLevelScalar );
-		//	float desiredMIPLevel = log2( envMapWidth * sqrt( 3.0 ) ) - 0.5 * log2( pow2( blinnShininessExponent ) + 1.0 );
-
-		"	float desiredMIPLevel = maxMIPLevelScalar + 0.79248 - 0.5 * log2( pow2( blinnShininessExponent ) + 1.0 );",
-
-		// clamp to allowable LOD ranges.
-		"	return clamp( desiredMIPLevel, 0.0, maxMIPLevelScalar );",
-
-		"}"
-	].join( "\n" ) );
-
-	return {
-		getSpecularMIPLevel: getSpecularMIPLevel
-	};
-
-} )();
-
-RoughnessToBlinnExponentNode.prototype = Object.create( TempNode.prototype );
-RoughnessToBlinnExponentNode.prototype.constructor = RoughnessToBlinnExponentNode;
-RoughnessToBlinnExponentNode.prototype.nodeType = "RoughnessToBlinnExponent";
-
-RoughnessToBlinnExponentNode.prototype.generate = function ( builder, output ) {
-
-	if ( builder.isShader( 'fragment' ) ) {
-
-		this.maxMIPLevel.texture = this.texture;
-
-		var getSpecularMIPLevel = builder.include( RoughnessToBlinnExponentNode.Nodes.getSpecularMIPLevel );
-
-		return builder.format( getSpecularMIPLevel + '( ' + this.blinnShininessExponent.build( builder, 'f' ) + ', ' + this.maxMIPLevel.build( builder, 'f' ) + ' )', this.type, output );
-
-	} else {
-
-		console.warn( "THREE.RoughnessToBlinnExponentNode is not compatible with " + builder.shader + " shader." );
-
-		return builder.format( '0.0', this.type, output );
-
-	}
-
-};
-
-RoughnessToBlinnExponentNode.prototype.copy = function ( source ) {
-
-	TempNode.prototype.copy.call( this, source );
-
-	this.texture = source.texture;
-
-	return this;
-
-};
-
-RoughnessToBlinnExponentNode.prototype.toJSON = function ( meta ) {
-
-	var data = this.getJSONNode( meta );
-
-	if ( ! data ) {
-
-		data = this.createJSONNode( meta );
-
-		data.texture = this.texture;
-
-	}
-
-	return data;
-
-};
-
-export { RoughnessToBlinnExponentNode };

+ 1 - 1
examples/jsm/nodes/inputs/CubeTextureNode.js

@@ -41,7 +41,7 @@ CubeTextureNode.prototype.generate = function ( builder, output ) {
 
 	if ( bias === undefined && builder.context.bias ) {
 
-		bias = new builder.context.bias( this ).build( builder, 'f' );
+		bias = builder.context.bias.setTexture( this ).build( builder, 'f' );
 
 	}
 

+ 1 - 1
examples/jsm/nodes/inputs/TextureNode.js

@@ -42,7 +42,7 @@ TextureNode.prototype.generate = function ( builder, output ) {
 
 	if ( bias === undefined && builder.context.bias ) {
 
-		bias = new builder.context.bias( this ).build( builder, 'f' );
+		bias = builder.context.bias.setTexture( this ).build( builder, 'f' );
 
 	}
 

+ 9 - 5
examples/jsm/nodes/materials/nodes/StandardNode.js

@@ -11,7 +11,7 @@ import { Node } from '../../core/Node.js';
 import { ExpressionNode } from '../../core/ExpressionNode.js';
 import { ColorNode } from '../../inputs/ColorNode.js';
 import { FloatNode } from '../../inputs/FloatNode.js';
-import { RoughnessToBlinnExponentNode } from '../../bsdfs/RoughnessToBlinnExponentNode.js';
+import { SpecularMIPLevelNode } from '../../utils/SpecularMIPLevelNode.js';
 
 function StandardNode() {
 
@@ -129,8 +129,12 @@ StandardNode.prototype.build = function ( builder ) {
 
 	} else {
 
+		var specularRoughness = new ExpressionNode('material.specularRoughness', 'f' );
+		var clearcoatRoughness = new ExpressionNode('material.clearcoatRoughness', 'f' );
+
 		var contextEnvironment = {
-			bias: RoughnessToBlinnExponentNode,
+			roughness: specularRoughness,
+			bias: new SpecularMIPLevelNode( specularRoughness ),
 			viewNormal: new ExpressionNode('normal', 'v3'),
 			gamma: true
 		};
@@ -140,7 +144,8 @@ StandardNode.prototype.build = function ( builder ) {
 		};
 
 		var contextClearcoatEnvironment = {
-			bias: RoughnessToBlinnExponentNode,
+			roughness: clearcoatRoughness,
+			bias: new SpecularMIPLevelNode( clearcoatRoughness ),
 			viewNormal: new ExpressionNode('clearcoatNormal', 'v3'),
 			gamma: true
 		};
@@ -235,7 +240,6 @@ StandardNode.prototype.build = function ( builder ) {
 		builder.requires.transparent = alpha !== undefined;
 
 		builder.addParsCode( [
-
 			"varying vec3 vViewPosition;",
 
 			"#ifndef FLAT_SHADED",
@@ -457,7 +461,7 @@ StandardNode.prototype.build = function ( builder ) {
 
 			if ( builder.requires.irradiance ) {
 
-				output.push( "irradiance += PI * " + environment.irradiance.result + ";" );
+				output.push( "iblIrradiance += PI * " + environment.irradiance.result + ";" );
 
 			}
 

+ 35 - 20
examples/jsm/nodes/misc/NormalMapNode.js

@@ -20,37 +20,52 @@ function NormalMapNode( value, scale ) {
 
 NormalMapNode.Nodes = ( function () {
 
-	var perturbNormal2Arb = new FunctionNode( [
+	var perturbNormal2Arb = new FunctionNode( 
 
-		// Per-Pixel Tangent Space Normal Mapping
-		// http://hacksoflife.blogspot.ch/2009/11/per-pixel-tangent-space-normal-mapping.html
+// Per-Pixel Tangent Space Normal Mapping
+// http://hacksoflife.blogspot.ch/2009/11/per-pixel-tangent-space-normal-mapping.html
+	
+`vec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm, vec3 map, vec2 vUv, vec2 normalScale ) {
 
-		"vec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm, vec3 map, vec2 mUv, vec2 normalScale ) {",
+	// Workaround for Adreno 3XX dFd*( vec3 ) bug. See #9988
 
-		// Workaround for Adreno 3XX dFd*( vec3 ) bug. See #9988
+	vec3 q0 = vec3( dFdx( eye_pos.x ), dFdx( eye_pos.y ), dFdx( eye_pos.z ) );
+	vec3 q1 = vec3( dFdy( eye_pos.x ), dFdy( eye_pos.y ), dFdy( eye_pos.z ) );
+	vec2 st0 = dFdx( vUv.st );
+	vec2 st1 = dFdy( vUv.st );
 
-		"	vec3 q0 = vec3( dFdx( eye_pos.x ), dFdx( eye_pos.y ), dFdx( eye_pos.z ) );",
-		"	vec3 q1 = vec3( dFdy( eye_pos.x ), dFdy( eye_pos.y ), dFdy( eye_pos.z ) );",
-		"	vec2 st0 = dFdx( mUv.st );",
-		"	vec2 st1 = dFdy( mUv.st );",
+	float scale = sign( st1.t * st0.s - st0.t * st1.s ); // we do not care about the magnitude
 
-		"	float scale = sign( st1.t * st0.s - st0.t * st1.s );", // we do not care about the magnitude
+	vec3 S = normalize( ( q0 * st1.t - q1 * st0.t ) * scale );
+	vec3 T = normalize( ( - q0 * st1.s + q1 * st0.s ) * scale );
+	vec3 N = normalize( surf_norm );
 
-		"	vec3 S = normalize( ( q0 * st1.t - q1 * st0.t ) * scale );",
-		"	vec3 T = normalize( ( - q0 * st1.s + q1 * st0.s ) * scale );",
-		"	vec3 N = normalize( surf_norm );",
-		"	mat3 tsn = mat3( S, T, N );",
+	vec3 mapN = map * 2.0 - 1.0;
 
-		"	vec3 mapN = map * 2.0 - 1.0;",
+	mapN.xy *= normalScale;
 
-		"	mapN.xy *= normalScale;",
-		"	mapN.xy *= ( float( gl_FrontFacing ) * 2.0 - 1.0 );",
+	#ifdef DOUBLE_SIDED
 
-		"	return normalize( tsn * mapN );",
+		// Workaround for Adreno GPUs gl_FrontFacing bug. See #15850 and #10331
+		// http://hacksoflife.blogspot.com/2009/11/per-pixel-tangent-space-normal-mapping.html?showComment=1522254677437#c5087545147696715943
+		vec3 NfromST = cross( S, T );
+		if( dot( NfromST, N ) > 0.0 ) {
 
-		"}"
+			S *= -1.0;
+			T *= -1.0;
 
-	].join( "\n" ), null, { derivatives: true } );
+		}
+
+	#else
+
+		mapN.xy *= ( float( gl_FrontFacing ) * 2.0 - 1.0 );
+
+	#endif
+
+	mat3 tsn = mat3( S, T, N );
+	return normalize( tsn * mapN );
+
+}`, null, { derivatives: true } );
 
 	return {
 		perturbNormal2Arb: perturbNormal2Arb

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

@@ -9,26 +9,26 @@ import { TextureCubeUVNode } from './TextureCubeUVNode.js';
 import { ReflectNode } from '../accessors/ReflectNode.js';
 import { NormalNode } from '../accessors/NormalNode.js';
 import { ColorSpaceNode } from '../utils/ColorSpaceNode.js';
-import { BlinnExponentToRoughnessNode } from '../bsdfs/BlinnExponentToRoughnessNode.js';
+import { SpecularMIPLevelNode } from '../utils/SpecularMIPLevelNode.js';
 
 function TextureCubeNode( value, textureSize, uv, bias ) {
 
 	TempNode.call( this, 'v4' );
 
 	this.value = value;
-	this.textureSize = textureSize || new FloatNode( 1024 );
-	this.uv = uv || new ReflectNode( ReflectNode.VECTOR );
-	this.bias = bias || new BlinnExponentToRoughnessNode();
+
+	textureSize = textureSize || new FloatNode( 1024 );
 
 	this.radianceCache = { uv: new TextureCubeUVNode(
-		this.uv,
-		this.textureSize,
-		this.bias
+		uv || new ReflectNode( ReflectNode.VECTOR ),
+		textureSize,
+		// bias should be replaced in builder.context in build process
+		bias
 	) };
 
 	this.irradianceCache = { uv: new TextureCubeUVNode(
 		new NormalNode( NormalNode.WORLD ),
-		this.textureSize,
+		textureSize,
 		new FloatNode( 1 ).setReadonly( true )
 	) };
 
@@ -83,6 +83,12 @@ TextureCubeNode.prototype.generate = function ( builder, output ) {
 
 		builder.require( 'irradiance' );
 
+		if ( builder.context.bias ) {
+
+			builder.context.bias.setTexture( this );
+
+		}
+
 		var cache = builder.slot === 'irradiance' ? this.irradianceCache : this.radianceCache;
 		var result = this.generateTextureCubeUV( builder, cache );
 

+ 5 - 3
examples/jsm/nodes/misc/TextureCubeUVNode.js

@@ -7,13 +7,13 @@ import { ConstNode } from '../core/ConstNode.js';
 import { StructNode } from '../core/StructNode.js';
 import { FunctionNode } from '../core/FunctionNode.js';
 
-function TextureCubeUVNode( uv, textureSize, blinnExponentToRoughness ) {
+function TextureCubeUVNode( uv, textureSize, bias ) {
 
 	TempNode.call( this, 'TextureCubeUVData' ); // TextureCubeUVData is type as StructNode
 
 	this.uv = uv;
 	this.textureSize = textureSize;
-	this.blinnExponentToRoughness = blinnExponentToRoughness;
+	this.bias = bias;
 
 }
 
@@ -171,8 +171,10 @@ TextureCubeUVNode.prototype.generate = function ( builder, output ) {
 
 		var textureCubeUV = builder.include( TextureCubeUVNode.Nodes.textureCubeUV );
 
+		var biasNode = this.bias || builder.context.bias;
+
 		return builder.format( textureCubeUV + '( ' + this.uv.build( builder, 'v3' ) + ', ' +
-			this.blinnExponentToRoughness.build( builder, 'f' ) + ', ' +
+			biasNode.build( builder, 'f' ) + ', ' +
 			this.textureSize.build( builder, 'f' ) + ' )', this.getType( builder ), output );
 
 	} else {

+ 2 - 4
examples/jsm/nodes/bsdfs/RoughnessToBlinnExponentNode.d.ts → examples/jsm/nodes/utils/SpecularMIPLevelNode.d..ts

@@ -1,19 +1,17 @@
 import { TempNode } from '../core/TempNode';
 import { NodeBuilder } from '../core/NodeBuilder';
 import { MaxMIPLevelNode } from '../utils/MaxMIPLevelNode';
-import { BlinnShininessExponentNode } from './BlinnShininessExponentNode';
 import { FunctionNode } from '../core/FunctionNode';
 
-export class RoughnessToBlinnExponentNode extends TempNode {
+export class SpecularMIPLevelNode extends TempNode {
   constructor(texture: Node);
 
   texture: Node;
   maxMIPLevel: MaxMIPLevelNode;
-  blinnShininessExponent: BlinnShininessExponentNode;
   nodeType: string;
 
   generate(builder: NodeBuilder, output: string): string;
-  copy(source: RoughnessToBlinnExponentNode): this;
+  copy(source: SpecularMIPLevelNode): this;
 
   static Nodes: {
     getSpecularMIPLevel: FunctionNode;

+ 102 - 0
examples/jsm/nodes/utils/SpecularMIPLevelNode.js

@@ -0,0 +1,102 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+import { TempNode } from '../core/TempNode.js';
+import { FunctionNode } from '../core/FunctionNode.js';
+import { MaxMIPLevelNode } from './MaxMIPLevelNode.js';
+
+function SpecularMIPLevelNode( roughness, texture ) {
+
+	TempNode.call( this, 'f' );
+
+	this.roughness = roughness;
+	this.texture = texture;
+
+	this.maxMIPLevel = undefined;
+
+}
+
+SpecularMIPLevelNode.Nodes = ( function () {
+
+	var getSpecularMIPLevel = new FunctionNode( [
+		// taken from here: http://casual-effects.blogspot.ca/2011/08/plausible-environment-lighting-in-two.html
+		"float getSpecularMIPLevel( const in float roughness, const in float maxMIPLevelScalar ) {",
+
+		"	float sigma = PI * roughness * roughness / ( 1.0 + roughness );",
+		"	float desiredMIPLevel = maxMIPLevelScalar + log2( sigma );",
+
+		// clamp to allowable LOD ranges.
+		"	return clamp( desiredMIPLevel, 0.0, maxMIPLevelScalar );",
+
+		"}"
+	].join( "\n" ) );
+
+	return {
+		getSpecularMIPLevel: getSpecularMIPLevel
+	};
+
+} )();
+
+SpecularMIPLevelNode.prototype = Object.create( TempNode.prototype );
+SpecularMIPLevelNode.prototype.constructor = SpecularMIPLevelNode;
+SpecularMIPLevelNode.prototype.nodeType = "SpecularMIPLevel";
+
+SpecularMIPLevelNode.prototype.setTexture = function( texture ) {
+
+	this.texture = texture;
+
+	return this;
+
+};
+
+SpecularMIPLevelNode.prototype.generate = function ( builder, output ) {
+
+	if ( builder.isShader( 'fragment' ) ) {
+
+		this.maxMIPLevel = this.maxMIPLevel || new MaxMIPLevelNode();
+		this.maxMIPLevel.texture = this.texture;
+
+		var getSpecularMIPLevel = builder.include( SpecularMIPLevelNode.Nodes.getSpecularMIPLevel );
+
+		return builder.format( getSpecularMIPLevel + '( ' + this.roughness.build( builder, 'f' ) + ', ' + this.maxMIPLevel.build( builder, 'f' ) + ' )', this.type, output );
+
+	} else {
+
+		console.warn( "THREE.SpecularMIPLevelNode is not compatible with " + builder.shader + " shader." );
+
+		return builder.format( '0.0', this.type, output );
+
+	}
+
+};
+
+SpecularMIPLevelNode.prototype.copy = function ( source ) {
+
+	TempNode.prototype.copy.call( this, source );
+
+	this.texture = source.texture;
+	this.roughness = source.roughness;
+
+	return this;
+
+};
+
+SpecularMIPLevelNode.prototype.toJSON = function ( meta ) {
+
+	var data = this.getJSONNode( meta );
+
+	if ( ! data ) {
+
+		data = this.createJSONNode( meta );
+
+		data.texture = this.texture;
+		data.roughness = this.roughness;
+
+	}
+
+	return data;
+
+};
+
+export { SpecularMIPLevelNode };

+ 2 - 2
examples/webgl_materials_nodes.html

@@ -582,7 +582,7 @@
 						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.environment = subSlotNode;
 
 						// GUI
 
@@ -2488,7 +2488,7 @@
 						// MATERIAL
 
 						var bias = new Nodes.FloatNode( .5 );
-						var maxMIPLevel = new Nodes.MaxMIPLevelNode( new Nodes.TextureCubeNode( cubemap ) );
+						var maxMIPLevel = new Nodes.MaxMIPLevelNode( new Nodes.TextureCubeNode( cubemap, undefined, undefined, bias ) );
 						var mipsBias = new Nodes.OperatorNode( bias, maxMIPLevel, Nodes.OperatorNode.MUL );
 
 						mtl = new Nodes.PhongNodeMaterial();