Browse Source

Merge pull request #18161 from sunag/dev-nodematerial-prem

NodeMaterial: PREM Update
Michael Herzog 5 years ago
parent
commit
3492536c22

+ 2 - 2
examples/jsm/nodes/accessors/ReflectNode.js

@@ -54,7 +54,7 @@ ReflectNode.prototype.generate = function ( builder, output ) {
 
 			case ReflectNode.VECTOR:
 
-				var viewNormalNode = builder.context.viewNormal || new NormalNode();
+				var viewNormalNode = builder.context.viewNormal || new NormalNode( NormalNode.VIEW );
 				var roughnessNode = builder.context.roughness;
 
 				var viewNormal = viewNormalNode.build( builder, 'v3' );
@@ -63,7 +63,7 @@ ReflectNode.prototype.generate = function ( builder, output ) {
 
 				var method = `reflect( -normalize( ${viewPosition} ), ${viewNormal} )`;
 
-				if ( viewNormalNode && roughness ) {
+				if ( 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} ) )`;

+ 9 - 51
examples/jsm/nodes/misc/TextureCubeNode.js

@@ -10,26 +10,24 @@ import { ReflectNode } from '../accessors/ReflectNode.js';
 import { NormalNode } from '../accessors/NormalNode.js';
 import { ColorSpaceNode } from '../utils/ColorSpaceNode.js';
 
-function TextureCubeNode( value, textureSize, uv, bias ) {
+function TextureCubeNode( value, uv, bias ) {
 
 	TempNode.call( this, 'v4' );
 
 	this.value = value;
 
-	textureSize = textureSize || new FloatNode( 1024 );
-
-	this.radianceCache = { uv: new TextureCubeUVNode(
+	this.radianceNode = new TextureCubeUVNode(
+		this.value,
 		uv || new ReflectNode( ReflectNode.VECTOR ),
-		textureSize,
 		// bias should be replaced in builder.context in build process
 		bias
-	) };
+	);
 
-	this.irradianceCache = { uv: new TextureCubeUVNode(
+	this.irradianceNode = new TextureCubeUVNode(
+		this.value,
 		new NormalNode( NormalNode.WORLD ),
-		textureSize,
 		new FloatNode( 1 ).setReadonly( true )
-	) };
+	);
 
 }
 
@@ -37,45 +35,6 @@ TextureCubeNode.prototype = Object.create( TempNode.prototype );
 TextureCubeNode.prototype.constructor = TextureCubeNode;
 TextureCubeNode.prototype.nodeType = "TextureCube";
 
-TextureCubeNode.prototype.generateTextureCubeUV = function ( builder, cache ) {
-
-	var uv_10 = cache.uv.build( builder ) + '.uv_10',
-		uv_20 = cache.uv.build( builder ) + '.uv_20',
-		t = cache.uv.build( builder ) + '.t';
-
-	var color10 = 'texture2D( ' + this.value.build( builder, 'sampler2D' ) + ', ' + uv_10 + ' )',
-		color20 = 'texture2D( ' + this.value.build( builder, 'sampler2D' ) + ', ' + uv_20 + ' )';
-
-	// add a custom context for fix incompatibility with the core
-	// include ColorSpace function only for vertex shader (in fragment shader color space functions is added automatically by core)
-	// this should be removed in the future
-	// context.include =: is used to include or not functions if used FunctionNode
-	// context.ignoreCache =: not create temp variables nodeT0..9 to optimize the code
-	var context = { include: builder.isShader( 'vertex' ), ignoreCache: true };
-	var outputType = this.getType( builder );
-
-	builder.addContext( context );
-
-	cache.colorSpace10 = cache.colorSpace10 || new ColorSpaceNode( new ExpressionNode( '', outputType ) );
-	cache.colorSpace10.fromDecoding( builder.getTextureEncodingFromMap( this.value.value ) );
-	cache.colorSpace10.input.parse( color10 );
-
-	color10 = cache.colorSpace10.build( builder, outputType );
-
-	cache.colorSpace20 = cache.colorSpace20 || new ColorSpaceNode( new ExpressionNode( '', outputType ) );
-	cache.colorSpace20.fromDecoding( builder.getTextureEncodingFromMap( this.value.value ) );
-	cache.colorSpace20.input.parse( color20 );
-
-	color20 = cache.colorSpace20.build( builder, outputType );
-
-	// end custom context
-
-	builder.removeContext();
-
-	return 'mix( ' + color10 + ', ' + color20 + ', ' + t + ' ).rgb';
-
-};
-
 TextureCubeNode.prototype.generate = function ( builder, output ) {
 
 	if ( builder.isShader( 'fragment' ) ) {
@@ -88,10 +47,9 @@ TextureCubeNode.prototype.generate = function ( builder, output ) {
 
 		}
 
-		var cache = builder.slot === 'irradiance' ? this.irradianceCache : this.radianceCache;
-		var result = this.generateTextureCubeUV( builder, cache );
+		var scopeNode = builder.slot === 'irradiance' ? this.irradianceNode : this.radianceNode;
 
-		return builder.format( 'vec4( ' + result + ', 1.0 )', this.getType( builder ), output );
+		return scopeNode.build( builder, output );
 
 	} else {
 

+ 223 - 146
examples/jsm/nodes/misc/TextureCubeUVNode.js

@@ -6,157 +6,159 @@ import { TempNode } from '../core/TempNode.js';
 import { ConstNode } from '../core/ConstNode.js';
 import { StructNode } from '../core/StructNode.js';
 import { FunctionNode } from '../core/FunctionNode.js';
+import { FunctionCallNode } from '../core/FunctionCallNode.js';
+import { ExpressionNode } from '../core/ExpressionNode.js';
+import { ReflectNode } from '../accessors/ReflectNode.js';
+import { FloatNode } from '../inputs/FloatNode.js';
+import { OperatorNode } from '../math/OperatorNode.js';
+import { MathNode } from '../math/MathNode.js';
+import { CondNode } from '../math/CondNode.js';
+import { ColorSpaceNode } from '../utils/ColorSpaceNode.js';
 
-function TextureCubeUVNode( uv, textureSize, bias ) {
+function TextureCubeUVNode( value, uv, bias ) {
 
-	TempNode.call( this, 'TextureCubeUVData' ); // TextureCubeUVData is type as StructNode
+	TempNode.call( this, 'v4' );
 
+	this.value = value,
 	this.uv = uv;
-	this.textureSize = textureSize;
 	this.bias = bias;
 
 }
 
 TextureCubeUVNode.Nodes = ( function () {
 
-	var TextureCubeUVData = new StructNode( [
-		"struct TextureCubeUVData {",
-		"	vec2 uv_10;",
-		"	vec2 uv_20;",
-		"	float t;",
-		"}"
-	].join( "\n" ) );
-
-	var getFaceFromDirection = new FunctionNode( [
-		"int getFaceFromDirection(vec3 direction) {",
-		"	vec3 absDirection = abs(direction);",
-		"	int face = -1;",
-		"	if( absDirection.x > absDirection.z ) {",
-		"		if(absDirection.x > absDirection.y )",
-		"			face = direction.x > 0.0 ? 0 : 3;",
-		"		else",
-		"			face = direction.y > 0.0 ? 1 : 4;",
-		"	}",
-		"	else {",
-		"		if(absDirection.z > absDirection.y )",
-		"			face = direction.z > 0.0 ? 2 : 5;",
-		"		else",
-		"			face = direction.y > 0.0 ? 1 : 4;",
-		"	}",
-		"	return face;",
-		"}"
-	].join( "\n" ) );
-
-	var cubeUV_maxLods1 = new ConstNode( "#define cubeUV_maxLods1 ( log2( cubeUV_textureSize * 0.25 ) - 1.0 )" );
-	var cubeUV_rangeClamp = new ConstNode( "#define cubeUV_rangeClamp ( exp2( ( 6.0 - 1.0 ) * 2.0 ) )" );
-
-	var MipLevelInfo = new FunctionNode( [
-		"vec2 MipLevelInfo( vec3 vec, float roughnessLevel, float roughness, in float cubeUV_textureSize ) {",
-		"	float scale = exp2(cubeUV_maxLods1 - roughnessLevel);",
-		"	float dxRoughness = dFdx(roughness);",
-		"	float dyRoughness = dFdy(roughness);",
-		"	vec3 dx = dFdx( vec * scale * dxRoughness );",
-		"	vec3 dy = dFdy( vec * scale * dyRoughness );",
-		"	float d = max( dot( dx, dx ), dot( dy, dy ) );",
-		// Clamp the value to the max mip level counts. hard coded to 6 mips"
-		"	d = clamp(d, 1.0, cubeUV_rangeClamp);",
-		"	float mipLevel = 0.5 * log2(d);",
-		"	return vec2(floor(mipLevel), fract(mipLevel));",
-		"}"
-	].join( "\n" ), [ cubeUV_maxLods1, cubeUV_rangeClamp ], { derivatives: true } );
-
-	var cubeUV_maxLods2 = new ConstNode( "#define cubeUV_maxLods2 ( log2( cubeUV_textureSize * 0.25 ) - 2.0 )" );
-	var cubeUV_rcpTextureSize = new ConstNode( "#define cubeUV_rcpTextureSize ( 1.0 / cubeUV_textureSize )" );
-
-	var getCubeUV = new FunctionNode( [
-		"vec2 getCubeUV( vec3 direction, float roughnessLevel, float mipLevel, in float cubeUV_textureSize ) {",
-		"	mipLevel = roughnessLevel > cubeUV_maxLods2 - 3.0 ? 0.0 : mipLevel;",
-		"	float a = 16.0 * cubeUV_rcpTextureSize;",
-		"",
-		"	vec2 exp2_packed = exp2( vec2( roughnessLevel, mipLevel ) );",
-		"	vec2 rcp_exp2_packed = vec2( 1.0 ) / exp2_packed;",
-		// float powScale = exp2(roughnessLevel + mipLevel);"
-		"	float powScale = exp2_packed.x * exp2_packed.y;",
-		// float scale =  1.0 / exp2(roughnessLevel + 2.0 + mipLevel);"
-		"	float scale = rcp_exp2_packed.x * rcp_exp2_packed.y * 0.25;",
-		// float mipOffset = 0.75*(1.0 - 1.0/exp2(mipLevel))/exp2(roughnessLevel);"
-		"	float mipOffset = 0.75*(1.0 - rcp_exp2_packed.y) * rcp_exp2_packed.x;",
-		"",
-		"	bool bRes = mipLevel == 0.0;",
-		"	scale =  bRes && (scale < a) ? a : scale;",
-		"",
-		"	vec3 r;",
-		"	vec2 offset;",
-		"	int face = getFaceFromDirection(direction);",
-		"",
-		"	float rcpPowScale = 1.0 / powScale;",
-		"",
-		"	if( face == 0) {",
-		"		r = vec3(direction.x, -direction.z, direction.y);",
-		"		offset = vec2(0.0+mipOffset,0.75 * rcpPowScale);",
-		"		offset.y = bRes && (offset.y < 2.0*a) ? a : offset.y;",
-		"	}",
-		"	else if( face == 1) {",
-		"		r = vec3(direction.y, direction.x, direction.z);",
-		"		offset = vec2(scale+mipOffset, 0.75 * rcpPowScale);",
-		"		offset.y = bRes && (offset.y < 2.0*a) ? a : offset.y;",
-		"	}",
-		"	else if( face == 2) {",
-		"		r = vec3(direction.z, direction.x, direction.y);",
-		"		offset = vec2(2.0*scale+mipOffset, 0.75 * rcpPowScale);",
-		"		offset.y = bRes && (offset.y < 2.0*a) ? a : offset.y;",
-		"	}",
-		"	else if( face == 3) {",
-		"		r = vec3(direction.x, direction.z, direction.y);",
-		"		offset = vec2(0.0+mipOffset,0.5 * rcpPowScale);",
-		"		offset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y;",
-		"	}",
-		"	else if( face == 4) {",
-		"		r = vec3(direction.y, direction.x, -direction.z);",
-		"		offset = vec2(scale+mipOffset, 0.5 * rcpPowScale);",
-		"		offset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y;",
-		"	}",
-		"	else {",
-		"		r = vec3(direction.z, -direction.x, direction.y);",
-		"		offset = vec2(2.0*scale+mipOffset, 0.5 * rcpPowScale);",
-		"		offset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y;",
-		"	}",
-		"	r = normalize(r);",
-		"	float texelOffset = 0.5 * cubeUV_rcpTextureSize;",
-		"	vec2 s = ( r.yz / abs( r.x ) + vec2( 1.0 ) ) * 0.5;",
-		"	vec2 base = offset + vec2( texelOffset );",
-		"	return base + s * ( scale - 2.0 * texelOffset );",
-		"}"
-	].join( "\n" ), [ cubeUV_maxLods2, cubeUV_rcpTextureSize, getFaceFromDirection ] );
-
-	var cubeUV_maxLods3 = new ConstNode( "#define cubeUV_maxLods3 ( log2( cubeUV_textureSize * 0.25 ) - 3.0 )" );
-
-	var textureCubeUV = new FunctionNode( [
-		"TextureCubeUVData textureCubeUV( vec3 reflectedDirection, float roughness, in float cubeUV_textureSize ) {",
-		"	float roughnessVal = roughness * cubeUV_maxLods3;",
-		"	float r1 = floor(roughnessVal);",
-		"	float r2 = r1 + 1.0;",
-		"	float t = fract(roughnessVal);",
-		"	vec2 mipInfo = MipLevelInfo(reflectedDirection, r1, roughness, cubeUV_textureSize);",
-		"	float s = mipInfo.y;",
-		"	float level0 = mipInfo.x;",
-		"	float level1 = level0 + 1.0;",
-		"	level1 = level1 > 5.0 ? 5.0 : level1;",
-		"",
-		// round to nearest mipmap if we are not interpolating."
-		"	level0 += min( floor( s + 0.5 ), 5.0 );",
-		"",
-		// Tri linear interpolation."
-		"	vec2 uv_10 = getCubeUV(reflectedDirection, r1, level0, cubeUV_textureSize);",
-		"	vec2 uv_20 = getCubeUV(reflectedDirection, r2, level0, cubeUV_textureSize);",
-		"",
-		"	return TextureCubeUVData(uv_10, uv_20, t);",
-		"}"
-	].join( "\n" ), [ TextureCubeUVData, cubeUV_maxLods3, MipLevelInfo, getCubeUV ] );
+	var TextureCubeUVData = new StructNode(
+`struct TextureCubeUVData {
+	vec4 tl;
+	vec4 tr;
+	vec4 br;
+	vec4 bl;
+	vec2 f;
+}` );
+
+	var cubeUV_maxMipLevel = new ConstNode( `float cubeUV_maxMipLevel 8.0`, true );
+	var cubeUV_minMipLevel = new ConstNode( `float cubeUV_minMipLevel 4.0`, true );
+	var cubeUV_maxTileSize = new ConstNode( `float cubeUV_maxTileSize 256.0`, true );
+	var cubeUV_minTileSize = new ConstNode( `float cubeUV_minTileSize 16.0`, true );
+
+	// These shader functions convert between the UV coordinates of a single face of
+	// a cubemap, the 0-5 integer index of a cube face, and the direction vector for
+	// sampling a textureCube (not generally normalized).
+
+	var getFace = new FunctionNode(
+`float getFace(vec3 direction) {
+    vec3 absDirection = abs(direction);
+    float face = -1.0;
+    if (absDirection.x > absDirection.z) {
+      if (absDirection.x > absDirection.y)
+        face = direction.x > 0.0 ? 0.0 : 3.0;
+      else
+        face = direction.y > 0.0 ? 1.0 : 4.0;
+    } else {
+      if (absDirection.z > absDirection.y)
+        face = direction.z > 0.0 ? 2.0 : 5.0;
+      else
+        face = direction.y > 0.0 ? 1.0 : 4.0;
+    }
+    return face;
+}` );
+	getFace.useKeywords = false;
+
+	var getUV = new FunctionNode(
+`vec2 getUV(vec3 direction, float face) {
+    vec2 uv;
+    if (face == 0.0) {
+      uv = vec2(-direction.z, direction.y) / abs(direction.x);
+    } else if (face == 1.0) {
+      uv = vec2(direction.x, -direction.z) / abs(direction.y);
+    } else if (face == 2.0) {
+      uv = direction.xy / abs(direction.z);
+    } else if (face == 3.0) {
+      uv = vec2(direction.z, direction.y) / abs(direction.x);
+    } else if (face == 4.0) {
+      uv = direction.xz / abs(direction.y);
+    } else {
+      uv = vec2(-direction.x, direction.y) / abs(direction.z);
+    }
+    return 0.5 * (uv + 1.0);
+}` );
+	getUV.useKeywords = false;
+
+	var bilinearCubeUV = new FunctionNode(
+`TextureCubeUVData bilinearCubeUV(sampler2D envMap, vec3 direction, float mipInt) {
+  float face = getFace(direction);
+  float filterInt = max(cubeUV_minMipLevel - mipInt, 0.0);
+  mipInt = max(mipInt, cubeUV_minMipLevel);
+  float faceSize = exp2(mipInt);
+  float texelSize = 1.0 / (3.0 * cubeUV_maxTileSize);
+  vec2 uv = getUV(direction, face) * (faceSize - 1.0);
+  vec2 f = fract(uv);
+  uv += 0.5 - f;
+  if (face > 2.0) {
+    uv.y += faceSize;
+    face -= 3.0;
+  }
+  uv.x += face * faceSize;
+  if(mipInt < cubeUV_maxMipLevel){
+    uv.y += 2.0 * cubeUV_maxTileSize;
+  }
+  uv.y += filterInt * 2.0 * cubeUV_minTileSize;
+  uv.x += 3.0 * max(0.0, cubeUV_maxTileSize - 2.0 * faceSize);
+  uv *= texelSize;
+  vec4 tl = texture2D(envMap, uv);
+  uv.x += texelSize;
+  vec4 tr = texture2D(envMap, uv);
+  uv.y += texelSize;
+  vec4 br = texture2D(envMap, uv);
+  uv.x -= texelSize;
+  vec4 bl = texture2D(envMap, uv);
+
+  return TextureCubeUVData( tl, tr, br, bl, f );
+}`, [ TextureCubeUVData, getFace, getUV, cubeUV_maxMipLevel, cubeUV_minMipLevel, cubeUV_maxTileSize, cubeUV_minTileSize ] );
+	bilinearCubeUV.useKeywords = false;
+
+	// These defines must match with PMREMGenerator
+
+	var r0 = new ConstNode( `float r0 1.0`, true );
+	var v0 = new ConstNode( `float v0 0.339`, true );
+	var m0 = new ConstNode( `float m0 -2.0`, true );
+	var r1 = new ConstNode( `float r1 0.8`, true );
+	var v1 = new ConstNode( `float v1 0.276`, true );
+	var m1 = new ConstNode( `float m1 -1.0`, true );
+	var r4 = new ConstNode( `float r4 0.4`, true );
+	var v4 = new ConstNode( `float v4 0.046`, true );
+	var m4 = new ConstNode( `float m4 2.0`, true );
+	var r5 = new ConstNode( `float r5 0.305`, true );
+	var v5 = new ConstNode( `float v5 0.016`, true );
+	var m5 = new ConstNode( `float m5 3.0`, true );
+	var r6 = new ConstNode( `float r6 0.21`, true );
+	var v6 = new ConstNode( `float v6 0.0038`, true );
+	var m6 = new ConstNode( `float m6 4.0`, true );
+
+	var defines = [ r0, v0, m0, r1, v1, m1, r4, v4, m4, r5, v5, m5, r6, v6, m6 ];
+
+	var roughnessToMip = new FunctionNode(
+`float roughnessToMip(float roughness) {
+  float mip = 0.0;
+  if (roughness >= r1) {
+    mip = (r0 - roughness) * (m1 - m0) / (r0 - r1) + m0;
+  } else if (roughness >= r4) {
+    mip = (r1 - roughness) * (m4 - m1) / (r1 - r4) + m1;
+  } else if (roughness >= r5) {
+    mip = (r4 - roughness) * (m5 - m4) / (r4 - r5) + m4;
+  } else if (roughness >= r6) {
+    mip = (r5 - roughness) * (m6 - m5) / (r5 - r6) + m5;
+  } else {
+    mip = -2.0 * log2(1.16 * roughness);// 1.16 = 1.79^0.25
+  }
+  return mip;
+}`, defines );
 
 	return {
-		TextureCubeUVData: TextureCubeUVData,
-		textureCubeUV: textureCubeUV
+		bilinearCubeUV: bilinearCubeUV,
+		roughnessToMip: roughnessToMip,
+		m0: m0,
+		cubeUV_maxMipLevel: cubeUV_maxMipLevel
 	};
 
 } )();
@@ -165,17 +167,92 @@ TextureCubeUVNode.prototype = Object.create( TempNode.prototype );
 TextureCubeUVNode.prototype.constructor = TextureCubeUVNode;
 TextureCubeUVNode.prototype.nodeType = "TextureCubeUV";
 
+TextureCubeUVNode.prototype.bilinearCubeUV = function ( builder, texture, uv, mipInt ) {
+	
+	var bilinearCubeUV = new FunctionCallNode( TextureCubeUVNode.Nodes.bilinearCubeUV, [ texture, uv, mipInt ] );
+	
+	this.colorSpaceTL = this.colorSpaceTL || new ColorSpaceNode( new ExpressionNode( '', 'v4' ) );
+	this.colorSpaceTL.fromDecoding( builder.getTextureEncodingFromMap( this.value.value ) );
+	this.colorSpaceTL.input.parse( bilinearCubeUV.build( builder ) + '.tl' );
+	
+	this.colorSpaceTR = this.colorSpaceTR || new ColorSpaceNode( new ExpressionNode( '', 'v4' ) );
+	this.colorSpaceTR.fromDecoding( builder.getTextureEncodingFromMap( this.value.value ) );
+	this.colorSpaceTR.input.parse( bilinearCubeUV.build( builder ) + '.tr' );
+	
+	this.colorSpaceBL = this.colorSpaceBL || new ColorSpaceNode( new ExpressionNode( '', 'v4' ) );
+	this.colorSpaceBL.fromDecoding( builder.getTextureEncodingFromMap( this.value.value ) );
+	this.colorSpaceBL.input.parse( bilinearCubeUV.build( builder ) + '.bl' );
+	
+	this.colorSpaceBR = this.colorSpaceBR || new ColorSpaceNode( new ExpressionNode( '', 'v4' ) );
+	this.colorSpaceBR.fromDecoding( builder.getTextureEncodingFromMap( this.value.value ) );
+	this.colorSpaceBR.input.parse( bilinearCubeUV.build( builder ) + '.br' );
+	
+	var f = bilinearCubeUV.build( builder ) + '.f';
+	
+	// add a custom context for fix incompatibility with the core
+	// include ColorSpace function only for vertex shader (in fragment shader color space functions is added automatically by core)
+	// this should be removed in the future
+	// context.include =: is used to include or not functions if used FunctionNode
+	// context.ignoreCache =: not create temp variables nodeT0..9 to optimize the code
+	var context = { include: builder.isShader( 'vertex' ), ignoreCache: true };
+
+	builder.addContext( context );
+	
+	this.colorSpaceTLExp = new ExpressionNode( this.colorSpaceTL.build( builder, 'v4' ), 'v4' );
+	this.colorSpaceTRExp = new ExpressionNode( this.colorSpaceTR.build( builder, 'v4' ), 'v4' );
+	this.colorSpaceBLExp = new ExpressionNode( this.colorSpaceBL.build( builder, 'v4' ), 'v4' );
+	this.colorSpaceBRExp = new ExpressionNode( this.colorSpaceBR.build( builder, 'v4' ), 'v4' );
+
+	// end custom context
+
+	builder.removeContext();
+
+	// --
+
+	var output = new ExpressionNode(`mix( mix( cubeUV_TL, cubeUV_TR, cubeUV.f.x ), mix( cubeUV_BL, cubeUV_BR, cubeUV.f.x ), cubeUV.f.y )`, 'v4' );
+	output.keywords['cubeUV_TL'] = this.colorSpaceTLExp;
+	output.keywords['cubeUV_TR'] = this.colorSpaceTRExp;
+	output.keywords['cubeUV_BL'] = this.colorSpaceBLExp;
+	output.keywords['cubeUV_BR'] = this.colorSpaceBRExp;
+	output.keywords['cubeUV'] = bilinearCubeUV;
+
+	return output;
+	
+};
+
 TextureCubeUVNode.prototype.generate = function ( builder, output ) {
 
 	if ( builder.isShader( 'fragment' ) ) {
 
-		var textureCubeUV = builder.include( TextureCubeUVNode.Nodes.textureCubeUV );
+		var uv = this.uv;
+		var bias = this.bias || builder.context.roughness;
+
+		var mipV = new FunctionCallNode( TextureCubeUVNode.Nodes.roughnessToMip, [ bias ] );
+		var mip = new MathNode( mipV, TextureCubeUVNode.Nodes.m0, TextureCubeUVNode.Nodes.cubeUV_maxMipLevel, MathNode.CLAMP );
+		var mipInt  = new MathNode( mip, MathNode.FLOOR );
+		var mipF  = new MathNode( mip, MathNode.FRACT );
+
+		var color0 = this.bilinearCubeUV( builder, this.value, uv, mipInt );
+		var color1 = this.bilinearCubeUV( builder, this.value, uv, new OperatorNode(
+			mipInt,
+			new FloatNode( 1 ).setReadonly( true ),
+			OperatorNode.ADD
+		) );
+
+		var color1Mix = new MathNode( color0, color1, mipF, MathNode.MIX );
 
-		var biasNode = this.bias || builder.context.roughness;
+/*
+		// TODO: Optimize this in the future
+		var cond = new CondNode( 
+			mipF, 
+			new FloatNode( 0 ).setReadonly( true ), 
+			CondNode.EQUAL, 
+			color0, // if
+			color1Mix  // else
+		);		
+*/
 
-		return builder.format( textureCubeUV + '( ' + this.uv.build( builder, 'v3' ) + ', ' +
-			biasNode.build( builder, 'f' ) + ', ' +
-			this.textureSize.build( builder, 'f' ) + ' )', this.getType( builder ), output );
+		return builder.format( color1Mix.build( builder ), 'v4', output );
 
 	} else {
 
@@ -195,9 +272,9 @@ TextureCubeUVNode.prototype.toJSON = function ( meta ) {
 
 		data = this.createJSONNode( meta );
 
+		data.value = this.value.toJSON( meta ).uuid;
 		data.uv = this.uv.toJSON( meta ).uuid;
-		data.textureSize = this.textureSize.toJSON( meta ).uuid;
-		data.blinnExponentToRoughness = this.blinnExponentToRoughness.toJSON( meta ).uuid;
+		data.bias = this.bias.toJSON( meta ).uuid;
 
 	}
 

+ 61 - 31
examples/webgl_materials_envmaps_hdr_nodes.html

@@ -1,35 +1,18 @@
 <!DOCTYPE html>
 <html lang="en">
 	<head>
-		<title>threejs webgl - node material - hdr environment mapping</title>
+		<title>threejs webgl - materials - hdr environment mapping</title>
 		<meta charset="utf-8">
 		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
-		<style>
-			body {
-				color: #fff;
-				font-family:Monospace;
-				font-size:13px;
-				text-align:center;
-
-				background-color: #000;
-
-				margin: 0px;
-				overflow: hidden;
-			}
-			a { color: #00f }
-
-			#info {
-				position: absolute;
-				top: 0px; width: 100%;
-				padding: 5px;
-			}
-		</style>
+		<link type="text/css" rel="stylesheet" href="main.css">
 	</head>
 	<body>
 
 		<div id="container"></div>
-		<div id="info"><a href="http://threejs.org" target="_blank" rel="noopener">threejs</a> - High dynamic range (RGBE) Image-based Lighting (IBL)<br />using run-time generated pre-filtered roughness mipmaps (PMREM)<br/>
-			Created by Prashant Sharma and <a href="http://clara.io/" target="_blank" rel="noopener">Ben Houston</a>.</div>
+		<div id="info">
+			<a href="http://threejs.org" target="_blank" rel="noopener">threejs</a> - High dynamic range (RGBE) Image-based Lighting (IBL)<br />using run-time generated pre-filtered roughness mipmaps (PMREM)<br/>
+			Created by Prashant Sharma and <a href="http://clara.io/" target="_blank" rel="noopener">Ben Houston</a>.
+		</div>
 
 		<script type="module">
 
@@ -41,6 +24,7 @@
 			import { OrbitControls } from './jsm/controls/OrbitControls.js';
 			import { HDRCubeTextureLoader } from './jsm/loaders/HDRCubeTextureLoader.js';
 			import { PMREMGenerator } from './jsm/pmrem/PMREMGenerator.js';
+
 			import { MeshStandardNodeMaterial } from './jsm/nodes/Nodes.js';
 
 			var params = {
@@ -50,18 +34,53 @@
 				exposure: 1.0,
 				nodes: true,
 				animate: true,
-				debug: false,
+				debug: false
 			};
 
 			var container, stats;
 			var camera, scene, renderer, controls;
 			var torusMesh, torusMeshNode, planeMesh;
-			var ldrCubeRenderTarget, hdrCubeRenderTarget, rgbmCubeRenderTarget;
+			var generatedCubeRenderTarget, ldrCubeRenderTarget, hdrCubeRenderTarget, rgbmCubeRenderTarget;
 			var ldrCubeMap, hdrCubeMap, rgbmCubeMap;
 
 			init();
 			animate();
 
+			function getEnvScene() {
+
+				var envScene = new THREE.Scene();
+
+				var geometry = new THREE.BoxBufferGeometry();
+    			geometry.deleteAttribute('uv');
+    			var roomMaterial = new THREE.MeshStandardMaterial({metalness: 0, side: THREE.BackSide});
+				var room = new THREE.Mesh(geometry, roomMaterial);
+				room.scale.setScalar(10);
+				envScene.add(room);
+
+				var mainLight = new THREE.PointLight(0xffffff, 50, 0, 2);
+				envScene.add(mainLight);
+
+				var lightMaterial = new THREE.MeshLambertMaterial( { color: 0x000000, emissive: 0xffffff, emissiveIntensity: 10 } );
+
+				var light1 = new THREE.Mesh(geometry, lightMaterial);
+				light1.position.set(-5, 2, 0);
+				light1.scale.set(0.1, 1, 1);
+				envScene.add(light1);
+
+				var light2 = new THREE.Mesh(geometry, lightMaterial);
+				light2.position.set(0, 5, 0);
+				light2.scale.set(1, 0.1, 1);
+				envScene.add(light2);
+
+				var light2 = new THREE.Mesh(geometry, lightMaterial);
+				light2.position.set(2, 1, 5);
+				light2.scale.set(1.5, 2, 0.1);
+				envScene.add(light2);
+
+				return envScene;
+
+			}
+
 			function init() {
 
 				container = document.createElement( 'div' );
@@ -74,6 +93,7 @@
 				scene.background = new THREE.Color( 0x000000 );
 
 				renderer = new THREE.WebGLRenderer();
+				renderer.physicallyCorrectLights = true;
 				renderer.toneMapping = THREE.LinearToneMapping;
 
 				//
@@ -97,7 +117,10 @@
 				torusMeshNode = new THREE.Mesh( geometry, material );
 				scene.add( torusMeshNode );
 
-				planeMesh = new THREE.Mesh( new THREE.PlaneBufferGeometry( 200, 200 ), new THREE.MeshBasicMaterial() );
+				var geometry = new THREE.PlaneBufferGeometry( 200, 200 );
+				var material = new THREE.MeshBasicMaterial();
+
+				planeMesh = new THREE.Mesh( geometry, material );
 				planeMesh.position.y = - 50;
 				planeMesh.rotation.x = - Math.PI * 0.5;
 				scene.add( planeMesh );
@@ -109,6 +132,9 @@
 
 				}
 
+				var envScene = getEnvScene();
+				generatedCubeRenderTarget = pmremGenerator.fromScene( envScene, 0.04 );
+
 				var hdrUrls = [ 'px.hdr', 'nx.hdr', 'py.hdr', 'ny.hdr', 'pz.hdr', 'nz.hdr' ];
 				hdrCubeMap = new HDRCubeTextureLoader()
 					.setPath( './textures/cube/pisaHDR/' )
@@ -153,7 +179,7 @@
 				renderer.setSize( window.innerWidth, window.innerHeight );
 				container.appendChild( renderer.domElement );
 
-				//renderer.toneMapping = THREE.ReinhardToneMapping;
+				//renderer.toneMapping = ReinhardToneMapping;
 				renderer.outputEncoding = THREE.sRGBEncoding;
 
 				stats = new Stats();
@@ -167,7 +193,7 @@
 
 				var gui = new GUI();
 
-				gui.add( params, 'envMap', [ 'LDR', 'HDR', 'RGBM16' ] );
+				gui.add( params, 'envMap', [ 'Generated', 'LDR', 'HDR', 'RGBM16' ] );
 				gui.add( params, 'roughness', 0, 1, 0.01 );
 				gui.add( params, 'metalness', 0, 1, 0.01 );
 				gui.add( params, 'exposure', 0, 2, 0.01 );
@@ -202,19 +228,23 @@
 
 			function render() {
 
+				torusMesh.visible = ! params.nodes;
+				torusMeshNode.visible = params.nodes;
+
 				torusMesh.material.roughness = params.roughness;
 				torusMesh.material.metalness = params.metalness;
 
 				torusMeshNode.material.roughness = params.roughness;
 				torusMeshNode.material.metalness = params.metalness;
 
-				torusMesh.visible = ! params.nodes;
-				torusMeshNode.visible = params.nodes;
-
 				var renderTarget, cubeMap;
 
 				switch ( params.envMap ) {
 
+					case 'Generated':
+						renderTarget = generatedCubeRenderTarget;
+						cubeMap = generatedCubeRenderTarget.texture;
+						break;
 					case 'LDR':
 						renderTarget = ldrCubeRenderTarget;
 						cubeMap = ldrCubeMap;

+ 4 - 15
examples/webgl_materials_envmaps_pmrem_nodes.html

@@ -51,7 +51,6 @@
 			} from './jsm/nodes/Nodes.js';
 
 			var params = {
-				textureSize: 1024,
 				roughness: 0.0,
 				metalness: 0.0,
 				exposure: 1.0,
@@ -62,16 +61,14 @@
 
 			var container, stats;
 			var camera, scene, renderer, controls;
-			var nodeMaterial, nodeTexture, nodeTextureSize, nodeTextureIntensity, torusMesh, planeMesh;
+			var nodeMaterial, nodeTexture, nodeTextureIntensity, torusMesh, planeMesh;
 			var hdrCubeRenderTarget;
 			var hdrCubeMap;
 
 			init();
 			animate();
 
-			function generate( textureSize ) {
-
-				nodeTextureSize.value = textureSize;
+			function generate() {
 
 				var pmremGenerator = new PMREMGenerator( renderer );
 				hdrCubeRenderTarget = pmremGenerator.fromCubemap( hdrCubeMap );
@@ -112,10 +109,9 @@
 				nodeMaterial.visible = false;
 
 				nodeTexture = new TextureNode();
-				nodeTextureSize = new FloatNode( 1024 );
 				nodeTextureIntensity = new FloatNode( 1 );
 
-				nodeMaterial.environment = new OperatorNode( new TextureCubeNode( nodeTexture, nodeTextureSize ), nodeTextureIntensity, OperatorNode.MUL );
+				nodeMaterial.environment = new OperatorNode( new TextureCubeNode( nodeTexture ), nodeTextureIntensity, OperatorNode.MUL );
 
 				torusMesh = new THREE.Mesh( geometry, nodeMaterial );
 				scene.add( torusMesh );
@@ -131,7 +127,7 @@
 					.setDataType( THREE.UnsignedByteType )
 					.load( hdrUrls, function () {
 
-						generate( params.textureSize );
+						generate();
 
 						nodeMaterial.visible = true;
 
@@ -155,7 +151,6 @@
 
 				var gui = new GUI();
 
-				gui.add( params, 'textureSize', [ 128, 256, 512, 1024, 2048, 4096 ] );
 				gui.add( params, 'roughness', 0, 1, 0.01 );
 				gui.add( params, 'metalness', 0, 1, 0.01 );
 				gui.add( params, 'exposure', 0, 2, 0.01 );
@@ -195,12 +190,6 @@
 
 				nodeTextureIntensity.value = params.intensity;
 
-				if ( nodeTextureSize.value !== params.textureSize ) {
-
-					generate( params.textureSize );
-
-				}
-
 				if ( params.animate ) {
 
 					torusMesh.rotation.y += 0.005;

+ 0 - 29
src/renderers/shaders/ShaderChunk/cube_uv_reflection_fragment.glsl.js

@@ -98,35 +98,6 @@ vec3 bilinearCubeUV(sampler2D envMap, vec3 direction, float mipInt) {
 #define v6 0.0038
 #define m6 4.0
 
-float roughnessToVariance(float roughness) {
-  float variance = 0.0;
-  if (roughness >= r1) {
-    variance = (r0 - roughness) * (v1 - v0) / (r0 - r1) + v0;
-  } else if (roughness >= r4) {
-    variance = (r1 - roughness) * (v4 - v1) / (r1 - r4) + v1;
-  } else if (roughness >= r5) {
-    variance = (r4 - roughness) * (v5 - v4) / (r4 - r5) + v4;
-  } else {
-    float roughness2 = roughness * roughness;
-    variance = 1.79 * roughness2 * roughness2;
-  }
-  return variance;
-}
-
-float varianceToRoughness(float variance) {
-  float roughness = 0.0;
-  if (variance >= v1) {
-    roughness = (v0 - variance) * (r1 - r0) / (v0 - v1) + r0;
-  } else if (variance >= v4) {
-    roughness = (v1 - variance) * (r4 - r1) / (v1 - v4) + r1;
-  } else if (variance >= v5) {
-    roughness = (v4 - variance) * (r5 - r4) / (v4 - v5) + r4;
-  } else {
-    roughness = pow(0.559 * variance, 0.25);// 0.559 = 1.0 / 1.79
-  }
-  return roughness;
-}
-
 float roughnessToMip(float roughness) {
   float mip = 0.0;
   if (roughness >= r1) {