Explorar o código

Merge pull request #8237 from bhouston/pmrem4

HDR EnvMap example with high quality, flexible PMREM Generator.
Mr.doob %!s(int64=9) %!d(string=hai) anos
pai
achega
c2b8ba5b41

+ 1 - 0
examples/files.js

@@ -119,6 +119,7 @@ var files = {
 		"webgl_materials_cubemap_refraction",
 		"webgl_materials_displacementmap",
 		"webgl_materials_envmaps",
+		"webgl_materials_envmaps_hdr",
 		"webgl_materials_grass",
 		"webgl_materials_lightmap",
 		"webgl_materials_nodes",

+ 26 - 0
examples/js/Encodings.js

@@ -0,0 +1,26 @@
+/**
+ * @author Ben Houston / http://clara.io / bhouston
+ * @author Prashant Sharma / spidersharma03
+ */
+
+THREE.Encodings = function() {
+  if( THREE.toHalf === undefined ) throw new Error("THREE.Encodings is required for HDRCubeMapLoader when loading half data.");
+}
+
+THREE.Encodings.RGBEByteToRGBFloat = function( sourceArray, sourceOffset, destArray, destOffset ) {
+  var e = sourceArray[sourceOffset+3];
+  var scale = Math.pow(2.0, e - 128.0) / 255.0;
+
+  destArray[destOffset+0] = sourceArray[sourceOffset+0] * scale;
+  destArray[destOffset+1] = sourceArray[sourceOffset+1] * scale;
+  destArray[destOffset+2] = sourceArray[sourceOffset+2] * scale;
+}
+
+THREE.Encodings.RGBEByteToRGBHalf = function( sourceArray, sourceOffset, destArray, destOffset ) {
+  var e = sourceArray[sourceOffset+3];
+  var scale = Math.pow(2.0, e - 128.0) / 255.0;
+
+  destArray[destOffset+0] = THREE.toHalf( sourceArray[sourceOffset+0] * scale );
+  destArray[destOffset+1] = THREE.toHalf( sourceArray[sourceOffset+1] * scale );
+  destArray[destOffset+2] = THREE.toHalf( sourceArray[sourceOffset+2] * scale );
+}

+ 51 - 0
examples/js/Half.js

@@ -0,0 +1,51 @@
+/**
+ * Source: http://gamedev.stackexchange.com/questions/17326/conversion-of-a-number-from-single-precision-floating-point-representation-to-a/17410#17410
+ */
+
+THREE.toHalf = (function() {
+  var floatView = new Float32Array(1);
+  var int32View = new Int32Array(floatView.buffer);
+
+  /* This method is faster than the OpenEXR implementation (very often
+   * used, eg. in Ogre), with the additional benefit of rounding, inspired
+   * by James Tursa?s half-precision code. */
+  return function toHalf(val) {
+
+    floatView[0] = val;
+    var x = int32View[0];
+
+    var bits = (x >> 16) & 0x8000; /* Get the sign */
+    var m = (x >> 12) & 0x07ff; /* Keep one extra bit for rounding */
+    var e = (x >> 23) & 0xff; /* Using int is faster here */
+
+    /* If zero, or denormal, or exponent underflows too much for a denormal
+     * half, return signed zero. */
+    if (e < 103) {
+      return bits;
+    }
+
+    /* If NaN, return NaN. If Inf or exponent overflow, return Inf. */
+    if (e > 142) {
+      bits |= 0x7c00;
+      /* If exponent was 0xff and one mantissa bit was set, it means NaN,
+           * not Inf, so make sure we set one mantissa bit too. */
+      bits |= ((e == 255) ? 0 : 1) && (x & 0x007fffff);
+      return bits;
+    }
+
+    /* If exponent underflows but not too much, return a denormal */
+    if (e < 113) {
+      m |= 0x0800;
+      /* Extra rounding may overflow and set mantissa to 0 and exponent
+       * to 1, which is OK. */
+      bits |= (m >> (114 - e)) + ((m >> (113 - e)) & 1);
+      return bits;
+    }
+
+    bits |= ((e - 112) << 10) | (m >> 1);
+    /* Extra rounding. An overflow will set mantissa to 0 and increment
+     * the exponent, which is OK. */
+    bits += m & 1;
+    return bits;
+  };
+}());

+ 83 - 0
examples/js/loaders/HDRCubeMapLoader.js

@@ -0,0 +1,83 @@
+/**
+* @author Prashant Sharma / spidersharma03
+* @author Ben Houston / http://clara.io / bhouston
+*/
+
+THREE.HDRCubeMapLoader = function (manager) {
+  this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
+  // override in sub classes
+  this.hdrLoader = new THREE.RGBELoader();
+
+  if( THREE.Encodings === undefined ) throw new Error( "HDRCubeMapLoader requires THREE.Encodings" );
+}
+
+THREE.HDRCubeMapLoader.prototype.load = function(type, urls, onLoad, onProgress, onError) {
+  var texture = new THREE.CubeTexture( [] );
+
+  texture.type = type;
+  texture.encoding = (type === THREE.UnsignedByteType) ? THREE.RGBEEncoding : THREE.LinearEncoding;
+  texture.format = (type === THREE.UnsignedByteType ) ? THREE.RGBAFormat : THREE.RGBFormat;
+  texture.minFilter = (texture.encoding === THREE.RGBEEncoding ) ? THREE.NearestFilter : THREE.LinearFilter;
+  texture.magFilter = (texture.encoding === THREE.RGBEEncoding ) ? THREE.NearestFilter : THREE.LinearFilter;
+  texture.generateMipmaps = (texture.encoding !== THREE.RGBEEncoding );
+  texture.anisotropy = 0;
+
+  var scope = this.hdrLoader;
+
+  var loaded = 0;
+
+  function loadHDRData(i, onLoad, onProgress, onError) {
+    var loader = new THREE.XHRLoader( this.manager );
+    loader.setResponseType( 'arraybuffer' );
+
+    loader.load( urls[i], function ( buffer ) {
+      loaded++;
+
+      var texData = scope._parser( buffer );
+
+      if ( ! texData ) return;
+
+      if(type === THREE.FloatType) {
+        var numElements = ( texData.data.length / 4 )*3;
+        var floatdata = new Float32Array( numElements );
+        for( var j=0; j<numElements; j++) {
+          THREE.Encodings.RGBEByteToRGBFloat( texData.data, j*4, floatdata, j*3 );
+        }
+        texData.data = floatdata;
+      }
+      else if(type === THREE.HalfFloatType) {
+        var numElements = ( texData.data.length / 4 )*3;
+        var halfdata = new Uint16Array( numElements );
+        for( var j=0; j<numElements; j++) {
+          THREE.Encodings.RGBEByteToRGBHalf( texData.data, j*4, halfdata, j*3 );
+        }
+        texData.data = halfdata;
+      }
+
+      if ( undefined !== texData.image ) {
+        texture[i].images = texData.image;
+      }
+      else if ( undefined !== texData.data ) {
+        var dataTexture = new THREE.DataTexture(texData.data, texData.width, texData.height);
+        dataTexture.format = texture.format;
+        dataTexture.type = texture.type;
+        dataTexture.encoding = texture.encoding;
+        dataTexture.minFilter = texture.minFilter;
+        dataTexture.magFilter = texture.magFilter;
+        dataTexture.generateMipmaps = texture.generateMipmaps;
+
+        texture.images[i] = dataTexture;
+      }
+
+      if(loaded === 6) {
+        texture.needsUpdate = true;
+        if ( onLoad ) onLoad( texture );
+      }
+    }, onProgress, onError );
+  }
+
+  for(var i=0; i<urls.length; i++) {
+    loadHDRData(i, onLoad, onProgress, onError);
+  }
+  return texture;
+};

+ 167 - 0
examples/js/pmrem/PMREMCubeUVPacker.js

@@ -0,0 +1,167 @@
+/**
+ * @author Prashant Sharma / spidersharma03
+ * @author Ben Houston / bhouston, https://clara.io
+ */
+
+
+THREE.PMREMCubeUVPacker = function( cubeTextureLods, numLods ) {
+
+	this.cubeLods = cubeTextureLods;
+	this.numLods = numLods;
+	var size = cubeTextureLods[ 0 ].width * 4;
+
+	this.CubeUVRenderTarget = new THREE.WebGLRenderTarget( size, size,
+	{ format: THREE.RGBAFormat, magFilter: THREE.LinearFilter, minFilter: THREE.LinearFilter, type: cubeTextureLods[ 0 ].texture.type } );
+	this.CubeUVRenderTarget.texture.generateMipmaps = false;
+  this.CubeUVRenderTarget.mapping = THREE.CubeUVReflectionMapping;
+	this.camera = new THREE.OrthographicCamera( - size * 0.5, size * 0.5, - size * 0.5, size * 0.5, 0.0, 1000 );
+
+	this.scene = new THREE.Scene();
+	this.scene.add( this.camera );
+
+	this.objects = [];
+	var xOffset = 0;
+	var faceOffsets = [];
+	faceOffsets.push( new THREE.Vector2( 0, 0 ) );
+	faceOffsets.push( new THREE.Vector2( 1, 0 ) );
+	faceOffsets.push( new THREE.Vector2( 2, 0 ) );
+	faceOffsets.push( new THREE.Vector2( 0, 1 ) );
+	faceOffsets.push( new THREE.Vector2( 1, 1 ) );
+	faceOffsets.push( new THREE.Vector2( 2, 1 ) );
+	var yOffset = 0;
+	var textureResolution = size;
+	size = cubeTextureLods[ 0 ].width;
+
+	var offset2 = 0;
+	var c = 4.0;
+	this.numLods = Math.log2( cubeTextureLods[ 0 ].width ) - 2;
+	for ( var i = 0; i < this.numLods; i ++ ) {
+
+		var offset1 = ( textureResolution - textureResolution / c ) * 0.5;
+		if ( size > 16 )
+		c *= 2;
+		var nMips = size > 16 ? 6 : 1;
+		var mipOffsetX = 0;
+		var mipOffsetY = 0;
+		var mipSize = size;
+
+		for ( var j = 0; j < nMips; j ++ ) {
+
+			// Mip Maps
+			for ( var k = 0; k < 6; k ++ ) {
+
+				// 6 Cube Faces
+				var material = this.getShader();
+				material.uniforms[ "envMap" ].value = this.cubeLods[ i ];
+				material.envMap = this.cubeLods[ i ]
+				material.uniforms[ "faceIndex" ].value = k;
+				material.uniforms[ "mapSize" ].value = mipSize;
+				var color = material.uniforms[ "testColor" ].value;
+				//color.copy(testColor[j]);
+				var planeMesh = new THREE.Mesh(
+				new THREE.PlaneGeometry( mipSize, mipSize, 0 ),
+				material );
+				planeMesh.position.x = faceOffsets[ k ].x * mipSize - offset1 + mipOffsetX;
+				planeMesh.position.y = faceOffsets[ k ].y * mipSize - offset1 + offset2 + mipOffsetY;
+				planeMesh.material.side = THREE.DoubleSide;
+				this.scene.add( planeMesh );
+				this.objects.push( planeMesh );
+
+			}
+			mipOffsetY += 1.75 * mipSize;
+			mipOffsetX += 1.25 * mipSize;
+			mipSize /= 2;
+
+		}
+		offset2 += 2 * size;
+		if ( size > 16 )
+		size /= 2;
+
+	}
+
+};
+
+THREE.PMREMCubeUVPacker.prototype = {
+
+	constructor : THREE.PMREMCubeUVPacker,
+
+	update: function( renderer ) {
+
+		var gammaInput = renderer.gammaInput;
+    var gammaOutput = renderer.gammaOutput;
+    renderer.gammaInput = false;
+    renderer.gammaOutput = false;
+
+		renderer.render( this.scene, this.camera, this.CubeUVRenderTarget, true );
+
+    renderer.gammaInput = renderer.gammaInput;
+    renderer.gammaOutput = renderer.gammaOutput;
+	},
+
+  getShader: function() {
+
+    var shaderMaterial = new THREE.ShaderMaterial( {
+
+      uniforms: {
+       	"faceIndex": { type: 'i', value: 0 },
+       	"mapSize": { type: 'f', value: 0 },
+       	"envMap": { type: 't', value: null },
+       	"testColor": { type: 'v3', value: new THREE.Vector3( 1, 1, 1 ) }
+      },
+
+      vertexShader:
+        "precision highp float;\
+         varying vec2 vUv;\
+         void main() {\
+            vUv = uv;\
+            gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\
+         }",
+
+      fragmentShader:
+       "precision highp float;\
+        varying vec2 vUv;\
+        uniform samplerCube envMap;\
+        uniform float mapSize;\
+        uniform vec3 testColor;\
+        uniform int faceIndex;\
+        \
+        void main() {\
+          vec3 sampleDirection;\
+          vec2 uv = vUv;\
+          uv = uv * 2.0 - 1.0;\
+          uv.y *= -1.0;\
+          if(faceIndex == 0) {\
+              sampleDirection = normalize(vec3(1.0, uv.y, -uv.x));\
+          }\
+          else if(faceIndex == 1) {\
+              sampleDirection = normalize(vec3(uv.x, 1.0, uv.y));\
+          }\
+          else if(faceIndex == 2) {\
+              sampleDirection = normalize(vec3(uv.x, uv.y, 1.0));\
+          }\
+          else if(faceIndex == 3) {\
+              sampleDirection = normalize(vec3(-1.0, uv.y, uv.x));\
+          }\
+          else if(faceIndex == 4) {\
+              sampleDirection = normalize(vec3(uv.x, -1.0, -uv.y));\
+          }\
+          else {\
+              sampleDirection = normalize(vec3(-uv.x, uv.y, -1.0));\
+          }\
+          vec4 color = envMapTexelToLinear( textureCube( envMap, sampleDirection ) );\
+          gl_FragColor = linearToOutputTexel( color * vec4(testColor, 1.0) );\
+        }",
+
+			blending: THREE.CustomBlending,
+			blendSrc: THREE.OneFactor,
+			blendDst: THREE.ZeroFactor,
+			blendSrcAlpha: THREE.OneFactor,
+			blendDstAlpha: THREE.ZeroFactor,
+			blendEquation: THREE.AddEquation
+    });
+
+		return shaderMaterial;
+
+  }
+
+};

+ 232 - 0
examples/js/pmrem/PMREMGenerator.js

@@ -0,0 +1,232 @@
+/**
+ * @author Prashant Sharma / spidersharma03
+ * @author Ben Houston / bhouston, https://clara.io
+ */
+
+ THREE.PMREMGenerator = function( cubeTexture ) {
+	if ( cubeTexture instanceof THREE.CubeTexture ) {
+
+		if ( cubeTexture.images[ 0 ] === undefined )
+		  console.error( "CubeTexture Not Initialized" );
+      if(cubeTexture.images[ 0 ] instanceof THREE.DataTexture) {
+          this.resolution = cubeTexture.images[ 0 ].image.width;
+      }
+      else {
+          this.resolution = cubeTexture.images[ 0 ].width;
+      }
+
+	}
+	else if ( cubeTexture instanceof THREE.WebGLRenderTargetCube ) {
+		if ( cubeTexture === undefined ) console.error( "Render Target Not Initialized" );
+		this.resolution = cubeTexture.width;
+	}
+	else {
+		console.error( "Wrong Input to PMREMGenerator" );
+	}
+	this.sourceTexture = cubeTexture;
+
+	this.cubeLods = [];
+
+	var size = this.resolution;
+  var params = { format: this.sourceTexture.format, magFilter: this.sourceTexture.magFilter, minFilter: this.sourceTexture.minFilter, type: this.sourceTexture.type };
+
+	this.numLods = Math.log2( size ) - 2;
+  for ( var i = 0; i < this.numLods; i ++ ) {
+		var renderTarget = new THREE.WebGLRenderTargetCube( size, size, params );
+		renderTarget.texture.generateMipmaps = this.sourceTexture.generateMipmaps;
+    renderTarget.texture.anisotropy = this.sourceTexture.anisotropy;
+    renderTarget.texture.encoding = this.sourceTexture.encoding;
+    renderTarget.texture.minFilter = this.sourceTexture.minFilter;
+    renderTarget.texture.magFilter = this.sourceTexture.magFilter;
+		this.cubeLods.push( renderTarget );
+		size = Math.max( 16, size / 2 );
+	}
+
+	this.camera = new THREE.OrthographicCamera( - 1, 1, 1, - 1, 0.0, 1000 );
+
+  this.shader = this.getShader();
+	this.planeMesh = new THREE.Mesh( new THREE.PlaneGeometry( 2, 2, 0 ), this.shader );
+	this.planeMesh.material.side = THREE.DoubleSide;
+	this.scene = new THREE.Scene();
+	this.scene.add( this.planeMesh );
+	this.scene.add( this.camera );
+
+	this.shader.uniforms[ "envMap" ].value = this.sourceTexture;
+  this.shader.envMap = this.sourceTexture;
+};
+
+THREE.PMREMGenerator.prototype = {
+
+	constructor : THREE.PMREMGenerator,
+
+	update: function( renderer ) {
+
+		this.shader.uniforms[ "envMap" ].value = this.sourceTexture;
+    this.shader.envMap = this.sourceTexture;
+
+    var gammaInput = renderer.gammaInput;
+    var gammaOutput = renderer.gammaOutput;
+    renderer.gammaInput = false;
+    renderer.gammaOutput = false;
+		for ( var i = 0; i < this.numLods; i ++ ) {
+
+			var r = i / ( this.numLods - 1 );
+			this.shader.uniforms[ "roughness" ].value = r * 0.9;
+			var size = this.cubeLods[ i ].width;
+			this.shader.uniforms[ "mapSize" ].value = size;
+			this.renderToCubeMapTarget( renderer, this.cubeLods[ i ] );
+			if ( i < 5 )
+			this.shader.uniforms[ "envMap" ].value = this.cubeLods[ i ];
+
+		}
+
+    renderer.gammaInput = renderer.gammaInput;
+    renderer.gammaOutput = renderer.gammaOutput;
+
+	},
+
+	renderToCubeMapTarget: function( renderer, renderTarget ) {
+
+		for ( var i = 0; i < 6; i ++ ) {
+		  this.renderToCubeMapTargetFace( renderer, renderTarget, i )
+    }
+
+	},
+
+	renderToCubeMapTargetFace: function( renderer, renderTarget, faceIndex ) {
+		renderTarget.activeCubeFace = faceIndex;
+		this.shader.uniforms[ "faceIndex" ].value = faceIndex;
+		renderer.render( this.scene, this.camera, renderTarget, true );
+
+	},
+
+  getShader: function() {
+
+    return new THREE.ShaderMaterial( {
+
+      uniforms: {
+        "faceIndex": { type: 'i', value: 0 },
+        "roughness": { type: 'f', value: 0.5 },
+        "mapSize": { type: 'f', value: 0.5 },
+        "envMap": { type: 't', value: null },
+        "testColor": { type: 'v3', value: new THREE.Vector3( 1, 1, 1 ) }
+      },
+
+      vertexShader:
+        "varying vec2 vUv;\n\
+        void main() {\n\
+           vUv = uv;\n\
+           gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n\
+        }",
+
+      fragmentShader:
+        "varying vec2 vUv;\n\
+        uniform int faceIndex;\n\
+        uniform float roughness;\n\
+        uniform samplerCube envMap;\n\
+        uniform float mapSize;\n\
+        uniform vec3 testColor;\n\
+        \n\
+        float rnd(vec2 uv) {\n\
+           return fract(sin(dot(uv, vec2(12.9898, 78.233) * 2.0)) * 43758.5453);\n\
+        }\n\
+        float GGXRoughnessToBlinnExponent( const in float ggxRoughness ) {\n\
+           float a = ggxRoughness + 0.0001;\n\
+           a *= a;\n\
+           return ( 2.0 / a - 2.0 );\n\
+        }\n\
+        const float PI = 3.14159265358979;\n\
+        vec3 ImportanceSamplePhong(vec2 uv, mat3 vecSpace, float specPow) {\n\
+           float phi = uv.y * 2.0 * PI;\n\
+           float cosTheta = pow(1.0 - uv.x, 1.0 / (specPow + 1.0));\n\
+           float sinTheta = sqrt(1.0 - cosTheta * cosTheta);\n\
+           vec3 sampleDir = vec3(cos(phi) * sinTheta, sin(phi) * sinTheta, cosTheta);\n\
+           return vecSpace * sampleDir;\n\
+        }\n\
+        vec3 ImportanceSampleGGX( vec2 uv, mat3 vecSpace, float Roughness )\n\
+        {\n\
+               float a = Roughness * Roughness;\n\
+               float Phi = 2.0 * PI * uv.x;\n\
+               float CosTheta = sqrt( (1.0 - uv.y) / ( 1.0 + (a*a - 1.0) * uv.y ) );\n\
+               float SinTheta = sqrt( 1.0 - CosTheta * CosTheta );\n\
+               return vecSpace * vec3(SinTheta * cos( Phi ), SinTheta * sin( Phi ), CosTheta);\n\
+        }\n\
+        mat3 matrixFromVector(vec3 n) {\n\
+           float a = 1.0 / (1.0 + n.z);\n\
+           float b = -n.x * n.y * a;\n\
+           vec3 b1 = vec3(1.0 - n.x * n.x * a, b, -n.x);\n\
+           vec3 b2 = vec3(b, 1.0 - n.y * n.y * a, -n.y);\n\
+           return mat3(b1, b2, n);\n\
+        }\n\
+        \n\
+        vec4 testColorMap(float Roughness) {\n\
+           vec4 color;\n\
+           if(faceIndex == 0)\n\
+               color = vec4(1.0,0.0,0.0,1.0);\n\
+           else if(faceIndex == 1)\n\
+               color = vec4(0.0,1.0,0.0,1.0);\n\
+           else if(faceIndex == 2)\n\
+               color = vec4(0.0,0.0,1.0,1.0);\n\
+           else if(faceIndex == 3)\n\
+               color = vec4(1.0,1.0,0.0,1.0);\n\
+           else if(faceIndex == 4)\n\
+               color = vec4(0.0,1.0,1.0,1.0);\n\
+           else\n\
+               color = vec4(1.0,0.0,1.0,1.0);\n\
+           color *= ( 1.0 - Roughness );\n\
+           return color;\n\
+        }\n\
+        void main() {\n\
+           vec3 sampleDirection;\n\
+           vec2 uv = vUv*2.0 - 1.0;\n\
+           float offset = -1.0/mapSize;\n\
+           const float a = -1.0;\n\
+           const float b = 1.0;\n\
+           float c = -1.0 + offset;\n\
+           float d = 1.0 - offset;\n\
+           float bminusa = b - a;\n\
+           uv.x = (uv.x - a)/bminusa * d - (uv.x - b)/bminusa * c;\n\
+           uv.y = (uv.y - a)/bminusa * d - (uv.y - b)/bminusa * c;\n\
+           if (faceIndex==0) {\n\
+               sampleDirection = vec3(1.0, -uv.y, -uv.x);\n\
+           }\n\
+           else if (faceIndex==1) {\n\
+               sampleDirection = vec3(-1.0, -uv.y, uv.x);\n\
+           } else if (faceIndex==2) {\n\
+               sampleDirection = vec3(uv.x, 1.0, uv.y);\n\
+           } else if (faceIndex==3) {\n\
+               sampleDirection = vec3(uv.x, -1.0, -uv.y);\n\
+           } else if (faceIndex==4) {\n\
+               sampleDirection = vec3(uv.x, -uv.y, 1.0);\n\
+           } else {\n\
+               sampleDirection = vec3(-uv.x, -uv.y, -1.0);\n\
+           }\n\
+           mat3 vecSpace = matrixFromVector(normalize(sampleDirection));\n\
+           vec3 rgbColor = vec3(0.0);\n\
+           const int NumSamples = 1024;\n\
+           vec3 vect;\n\
+           float weight = 0.0;\n\
+           for(int i=0; i<NumSamples; i++) {\n\
+               float sini = sin(float(i));\n\
+               float cosi = cos(float(i));\n\
+               float rand = rnd(vec2(sini, cosi));\n\
+               vect = ImportanceSampleGGX(vec2(float(i) / float(NumSamples), rand), vecSpace, roughness);\n\
+               float dotProd = dot(vect, normalize(sampleDirection));\n\
+               weight += dotProd;\n\
+               vec3 color = envMapTexelToLinear(textureCube(envMap,vect)).rgb;\n\
+               rgbColor.rgb += color;\n\
+           }\n\
+           rgbColor /= float(NumSamples);\n\
+           //rgbColor = testColorMap( roughness ).rgb;\n\
+           gl_FragColor = linearToOutputTexel( vec4( rgbColor, 1.0 ) );\n\
+        }",
+        blending: THREE.CustomBlending,
+        blendSrc: THREE.OneFactor,
+        blendDst: THREE.ZeroFactor,
+        blendSrcAlpha: THREE.OneFactor,
+        blendDstAlpha: THREE.ZeroFactor,
+        blendEquation: THREE.AddEquation
+      }
+    );
+  }
+};

BIN=BIN
examples/textures/cube/hdrPisa/nx.hdr


BIN=BIN
examples/textures/cube/hdrPisa/ny.hdr


BIN=BIN
examples/textures/cube/hdrPisa/nz.hdr


BIN=BIN
examples/textures/cube/hdrPisa/px.hdr


BIN=BIN
examples/textures/cube/hdrPisa/py.hdr


BIN=BIN
examples/textures/cube/hdrPisa/pz.hdr


BIN=BIN
examples/textures/roughness_map.jpg


+ 255 - 0
examples/webgl_materials_envmaps_hdr.html

@@ -0,0 +1,255 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>threejs webgl - materials</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;
+			}
+
+			#info {
+				position: absolute;
+				top: 0px; width: 100%;
+				padding: 5px;
+			}
+		</style>
+	</head>
+	<body>
+
+		<div id="container"></div>
+		<div id="info"><a href="http://threejs.org" target="_blank">threejs</a> - High dynamic range (RGBE) Image-based Lighting (IBL) using run-time generated pre-filtered roughness mipmaps (PMREM)<br/>
+			Created by Prashant Sharma and <a href="http://clara.io/" target="_blank">Ben Houston</a>.</div>
+
+		<script src="../build/three.min.js"></script>
+		<script src="../examples/js/controls/OrbitControls.js"></script>
+		<script src="../src/loaders/BinaryTextureLoader.js"></script>
+		<script src="../examples/js/loaders/RGBELoader.js"></script>
+		<script src="../examples/js/loaders/HDRCubeMapLoader.js"></script>
+
+		<script src="../examples/js/Detector.js"></script>
+		<script src="../examples/js/libs/stats.min.js"></script>
+
+		<script src="../examples/js/Half.js"></script>
+		<script src="../examples/js/Encodings.js"></script>
+    <script src="../examples/js/pmrem/PMREMGenerator.js"></script>
+    <script src="../examples/js/pmrem/PMREMCubeUVPacker.js"></script>
+    <script src="../examples/js/libs/dat.gui.min.js"></script>
+
+		<script src="../examples/js/postprocessing/EffectComposer.js"></script>
+		<script src="../examples/js/postprocessing/RenderPass.js"></script>
+		<script src="../examples/js/postprocessing/MaskPass.js"></script>
+		<script src="../examples/js/postprocessing/ShaderPass.js"></script>
+		<script src="../examples/js/shaders/CopyShader.js"></script>
+		<script src="../examples/js/shaders/FXAAShader.js"></script>
+		<script src="../examples/js/postprocessing/BloomPass.js"></script>
+		<script src="../examples/js/shaders/ConvolutionShader.js"></script>
+
+		<script>
+
+			if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
+
+			var container, stats;
+			var params = {
+				projection: 'normal',
+				roughness: 1.0,
+				bumpScale: 0.3,
+				background: false,
+			};
+			var camera, scene, renderer, controls, objects = [];
+			var hdrCubeMap;
+			var composer;
+			var standardMaterial;
+
+			var loader = new THREE.FontLoader();
+			loader.load( '../examples/fonts/gentilis_regular.typeface.js', function ( font ) {
+
+				init( font );
+				animate();
+
+			} );
+
+			function init( font ) {
+
+				container = document.createElement( 'div' );
+				document.body.appendChild( container );
+
+				camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 2000 );
+				camera.position.set( 0.0, 40, 40 * 3.5 );
+
+				scene = new THREE.Scene();
+
+				// Materials
+				var hdrpath = "../examples/textures/cube/hdrPisa/";
+				var hdrformat = '.hdr';
+				var hdrurls = [
+					hdrpath + 'px' + hdrformat, hdrpath + 'nx' + hdrformat,
+					hdrpath + 'py' + hdrformat, hdrpath + 'ny' + hdrformat,
+					hdrpath + 'pz' + hdrformat, hdrpath + 'nz' + hdrformat
+				];
+
+				renderer = new THREE.WebGLRenderer( { alpha: false, antialias: false } );
+
+			  var roughnessTexture = THREE.ImageUtils.loadTexture( "../examples/textures/roughness_map.jpg" );
+			  roughnessTexture.wrapS = THREE.RepeatWrapping;
+			  roughnessTexture.wrapT = THREE.RepeatWrapping;
+			  roughnessTexture.repeat.set( 9, 2 );
+
+				var hdrType = THREE.UnsignedByteType;
+		  /*  if ( renderer.extensions.get( 'OES_texture_half_float' ) && renderer.extensions.get( 'OES_texture_half_float_linear' ) ) {
+		      hdrType = THREE.HalfFloatType;
+		    }
+				else if ( renderer.extensions.get( 'OES_texture_float' ) && renderer.extensions.get( 'OES_texture_float_linear' ) ) {
+		      hdrType = THREE.FloatType;
+		    }*/
+
+				var hdrCubeMap = new THREE.HDRCubeMapLoader().load( hdrType, hdrurls, function ( hdrCubeMap ) {
+
+				  var pmremGenerator = new THREE.PMREMGenerator( hdrCubeMap );
+					pmremGenerator.update(renderer);
+
+
+				  var pmremCubeUVPacker = new THREE.PMREMCubeUVPacker(pmremGenerator.cubeLods);
+				  pmremCubeUVPacker.update(renderer);
+
+				  standardMaterial = new THREE.MeshStandardMaterial( {
+						map: null,
+						roughnessMap: roughnessTexture,
+						bumpScale: -0.05,
+						bumpMap: roughnessTexture,
+						color: 0xffffff,
+						metalness: 0.9,
+						roughness: 1.0,
+						envMap: pmremCubeUVPacker.CubeUVRenderTarget,
+						shading: THREE.SmoothShading
+					});
+					var geometry = new THREE.TorusKnotGeometry( 18, 8, 150, 20 );;
+				  var torusMesh1 = new THREE.Mesh( geometry, standardMaterial );
+				  torusMesh1.position.x = 0.0;
+				  scene.add( torusMesh1 );
+					objects.push( torusMesh1 );
+
+					var floorMaterial = new THREE.MeshStandardMaterial( {
+						map: null,
+						roughnessMap: null,
+						color: 0xffffff,
+						metalness: 0.0,
+						roughness: 0.0,
+						envMap: pmremCubeUVPacker.CubeUVRenderTarget,
+						shading: THREE.SmoothShading
+					});
+
+					var planeGeometry = new THREE.PlaneBufferGeometry( 200, 200 );
+				  var planeMesh1 = new THREE.Mesh( planeGeometry, floorMaterial );
+					planeMesh1.position.y = -50;
+					planeMesh1.rotation.x = -Math.PI * 0.5;
+					scene.add( planeMesh1 );
+				} );
+
+				// Lights
+
+				scene.add( new THREE.AmbientLight( 0x222222 ) );
+
+				var spotLight = new THREE.SpotLight( 0xffffff );
+				spotLight.position.set( 10, 100, 10 );
+				spotLight.angle = Math.PI/10;
+				spotLight.penumbra = 0.2
+				scene.add( spotLight );
+
+				renderer.setClearColor( 0x0a0a0a, 0 );
+				renderer.setPixelRatio( window.devicePixelRatio );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+
+				container.appendChild( renderer.domElement );
+
+				renderer.gammaInput = true;
+				renderer.gammaOutput = true;
+
+				composer = new THREE.EffectComposer(renderer);
+				composer.setSize(window.innerWidth, window.innerHeight);
+
+				var renderScene = new THREE.RenderPass(scene, camera);
+				composer.addPass(renderScene);
+
+				var copyPass = new THREE.ShaderPass(THREE.CopyShader);
+				copyPass.renderToScreen = true;
+				composer.addPass(copyPass);
+
+				stats = new Stats();
+				stats.domElement.style.position = 'absolute';
+				stats.domElement.style.top = '0px';
+
+				container.appendChild( stats.domElement );
+
+				controls = new THREE.OrbitControls( camera );
+				controls.target.set( 0, 0, 0 );
+				controls.update();
+
+				window.addEventListener( 'resize', onWindowResize, false );
+
+				var gui = new dat.GUI();
+
+				gui.add( params, 'roughness', 0, 1 );
+				gui.add( params, 'bumpScale', -1, 1 );
+				gui.open();
+			}
+
+			function onWindowResize() {
+
+				var width = window.innerWidth;
+				var height = window.innerHeight;
+
+				camera.aspect = width / height;
+				camera.updateProjectionMatrix();
+
+				renderer.setSize( width, height );
+				composer.setSize( width, height );
+
+			}
+
+			//
+
+			function animate() {
+
+				requestAnimationFrame( animate );
+
+				render();
+				stats.update();
+
+			}
+
+			function render() {
+				if(standardMaterial !== undefined) {
+					standardMaterial.roughness = params.roughness;
+					standardMaterial.bumpScale = -0.05 * params.bumpScale;
+				}
+
+				var timer = Date.now() * 0.00025;
+
+				camera.lookAt( scene.position );
+
+				for ( var i = 0, l = objects.length; i < l; i ++ ) {
+
+					var object = objects[ i ];
+
+					object.rotation.y += 0.005;
+
+				}
+
+				composer.render();
+
+			}
+
+		</script>
+
+	</body>
+</html>

+ 2 - 0
src/Three.js

@@ -219,6 +219,8 @@ THREE.EquirectangularReflectionMapping = 303;
 THREE.EquirectangularRefractionMapping = 304;
 
 THREE.SphericalReflectionMapping = 305;
+THREE.CubeUVReflectionMapping = 306;
+THREE.CubeUVRefractionMapping = 307;
 
 // Wrapping modes
 

+ 4 - 0
src/renderers/shaders/ShaderChunk/bsdfs.glsl

@@ -148,3 +148,7 @@ vec3 BRDF_Specular_BlinnPhong( const in IncidentLight incidentLight, const in Ge
 float GGXRoughnessToBlinnExponent( const in float ggxRoughness ) {
 	return ( 2.0 / square( ggxRoughness + 0.0001 ) - 2.0 );
 }
+
+float BlinnExponentToGGXRoughness( const in float blinnExponent ) {
+	return sqrt( 2.0 / ( blinnExponent + 2.0 ) );
+}

+ 138 - 0
src/renderers/shaders/ShaderChunk/cube_uv_reflection_fragment.glsl

@@ -0,0 +1,138 @@
+#ifdef ENVMAP_TYPE_CUBE_UV
+
+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;
+}
+
+vec2 MipLevelInfo( vec3 vec, float textureSize, float roughnessLevel, float roughness ) {
+    float s = log2(textureSize*0.25) - 1.0;
+    float scale = pow(2.0, s - 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
+    float rangeClamp = pow(2.0, (6.0 - 1.0) * 2.0);
+    d = clamp(d, 1.0, rangeClamp);
+    float mipLevel = 0.5 * log2(d);
+    return vec2(floor(mipLevel), fract(mipLevel));
+}
+
+vec2 getCubeUV(vec3 direction, float roughnessLevel, float mipLevel, float textureSize) {
+    float maxLods =  log2(textureSize*0.25) - 2.0;
+    mipLevel = roughnessLevel > maxLods - 3.0 ? 0.0 : mipLevel;
+    float a = 16.0/textureSize;
+    float powScale = pow(2.0,roughnessLevel + mipLevel);
+    float scale = 1.0/pow(2.0,roughnessLevel + 2.0 + mipLevel);
+    float mipOffset = 0.75*(1.0 - 1.0/pow(2.0, mipLevel))/pow(2.0,roughnessLevel);
+    bool bRes = mipLevel == 0.0;
+    scale =  bRes && (scale < a) ? a : scale;
+
+    vec3 r;
+    vec2 offset;
+    int face = getFaceFromDirection(direction);
+
+    if( face == 0) {
+        r = vec3(direction.x, -direction.z, direction.y);
+        offset = vec2(0.0+mipOffset,0.75/powScale);
+        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/powScale);
+        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/powScale);
+        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/powScale);
+        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/powScale);
+        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/powScale);
+        offset.y = bRes && (offset.y < 2.0*a) ?  0.0 : offset.y;
+    }
+    r = normalize(r);
+    float texelOffset = 0.5/textureSize;
+    float s1 = (r.y/abs(r.x) + 1.0)*0.5;
+    float s2 = (r.z/abs(r.x) + 1.0)*0.5;
+    vec2 uv = offset + vec2(s1*scale, s2*scale);
+    float min_x = offset.x + texelOffset; float max_x = offset.x + scale - texelOffset;
+    float min_y = offset.y + texelOffset;
+    float max_y = offset.y + scale - texelOffset;
+    float delx = max_x - min_x;
+    float dely = max_y - min_y;
+    uv.x = min_x + s1*delx;
+    uv.y = min_y + s2*dely;
+    return uv;
+}
+
+vec3 convertRGBEToRGB(vec4 rgbe) {
+    float d = pow(2.0, rgbe.w*256.0 - 128.0);
+    return vec3(rgbe) * d;
+}
+
+vec3 tonemap(vec3 RGB) {
+    float LogAvgLum = 0.08;//0.08
+    float key = 1.0;
+    float Ywhite = 1e3;
+    Ywhite *= Ywhite;
+    float sat = 1.0;
+    float Ylum = dot(RGB ,vec3(0.2126, 0.7152, 0.0722));
+    float Y = key/LogAvgLum * Ylum ;
+    float Yd = Y * ( 1.0 + Y/Ywhite)/( 1.0 + Y) ;
+    return Yd * pow(RGB/Ylum ,vec3(sat, sat, sat));
+ }
+
+vec4 textureCubeUV(vec3 reflectedDirection, float roughness, float textureSize) {
+    float maxLods =  log2(textureSize*0.25) - 3.0;
+    float roughnessVal = roughness*maxLods;
+    float r1 = floor(roughnessVal);
+    float r2 = r1 + 1.0;
+    float t = fract(roughnessVal);
+    vec2 mipInfo = MipLevelInfo(reflectedDirection, textureSize, r1, roughness);
+    float s = mipInfo.y;
+    float level0 = mipInfo.x;
+    float level1 = level0 + 1.0;
+    level1 = level1 > 5.0 ? 5.0 : level1;
+    // Tri linear interpolation.
+    vec2 uv_10 = getCubeUV(reflectedDirection, r1, level0, textureSize);
+    vec2 uv_11 = getCubeUV(reflectedDirection, r1, level1, textureSize);
+    vec2 uv_20 = getCubeUV(reflectedDirection, r2, level0, textureSize);
+    vec2 uv_21 = getCubeUV(reflectedDirection, r2, level1, textureSize);
+    vec4 color10 = envMapTexelToLinear(texture2D(envMap, uv_10));
+    vec4 color11 = envMapTexelToLinear(texture2D(envMap, uv_11));
+    vec4 color20 = envMapTexelToLinear(texture2D(envMap, uv_20));
+    vec4 color21 = envMapTexelToLinear(texture2D(envMap, uv_21));
+    vec4 c1 = mix(color10 , color11,  s);
+    vec4 c2 = mix(color20 , color21,  s);
+    vec4 c3 = mix(c1 , c2,  t);
+    return vec4(c3.rgb, 1.0);
+}
+
+#endif

+ 6 - 5
src/renderers/shaders/ShaderChunk/encodings.glsl

@@ -19,12 +19,13 @@ vec4 LinearTosRGB( in vec4 value ) {
 }
 
 vec4 RGBEToLinear( in vec4 value ) {
-  return vec4( value.xyz * exp2( value.w*256.0 - 128.0 ), 1.0 );
+  return vec4( value.rgb * exp2( value.a * 255.0 - 128.0 ), 1.0 );
 }
 vec4 LinearToRGBE( in vec4 value ) {
-  float maxComponent = max(max(value.r, value.g), value.b );
-  float fExp = ceil( log2(maxComponent) );
-  return vec4( value.rgb / exp2(fExp), (fExp + 128.0) / 255.0 );
+  float maxComponent = max( max( value.r, value.g ), value.b );
+  float fExp = clamp( ceil( log2( maxComponent ) ), -128.0, 127.0 );
+  return vec4( value.rgb / exp2( fExp ), ( fExp + 128.0 ) / 255.0 );
+//  return vec4( value.brg, ( 3.0 + 128.0 ) / 256.0 );
 }
 
 // reference: http://iwasbeingirony.blogspot.ca/2010/06/difference-between-rgbm-and-rgbd.html
@@ -33,7 +34,7 @@ vec4 RGBMToLinear( in vec4 value, in float maxRange ) {
 }
 vec4 LinearToRGBM( in vec4 value, in float maxRange ) {
   float maxRGB = max( value.x, max( value.g, value.b ) );
-  float M      = maxRGB / maxRange;
+  float M      = clamp( maxRGB / maxRange, 0.0, 1.0 );
   M            = ceil( M * 255.0 ) / 255.0;
   return vec4( value.rgb / ( M * maxRange ), M );
 }

+ 10 - 0
src/renderers/shaders/ShaderChunk/lights_pars.glsl

@@ -179,6 +179,11 @@
 
 			#endif
 
+		#elif defined( ENVMAP_TYPE_CUBE_UV )
+
+				vec3 queryVec = flipNormal * vec3( flipEnvMap * worldNormal.x, worldNormal.yz );
+				vec4 envMapColor = textureCubeUV(queryVec, 1.0, 1024.0);
+
 		#else
 
 			vec3 envMapColor = vec3( 0.0 );
@@ -245,6 +250,11 @@
 
 			#endif
 
+		#elif defined( ENVMAP_TYPE_CUBE_UV )
+
+			vec3 queryReflectVec = flipNormal * vec3( flipEnvMap * reflectVec.x, reflectVec.yz );
+			vec4 envMapColor = textureCubeUV(queryReflectVec, BlinnExponentToGGXRoughness(blinnShininessExponent), 1024.0);
+
 		#elif defined( ENVMAP_TYPE_EQUIREC )
 
 			vec2 sampleUV;

+ 3 - 3
src/renderers/shaders/ShaderChunk/lights_template.glsl

@@ -101,12 +101,12 @@ IncidentLight directLight;
 
 	#endif
 
-	// #if defined( USE_ENVMAP ) && defined( STANDARD )
+	#if defined( USE_ENVMAP ) && defined( STANDARD ) && defined( ENVMAP_TYPE_CUBE_UV )
 
 		// TODO, replace 8 with the real maxMIPLevel
-		// irradiance += getLightProbeIndirectIrradiance( /*lightProbe,*/ geometry, 8 ); // comment out until seams are fixed
+	 	irradiance += getLightProbeIndirectIrradiance( /*lightProbe,*/ geometry, 8 );
 
-	// #endif
+	#endif
 
 	RE_IndirectDiffuse( irradiance, geometry, material, reflectedLight );
 

+ 1 - 0
src/renderers/shaders/ShaderLib/meshstandard_frag.glsl

@@ -29,6 +29,7 @@ varying vec3 vViewPosition;
 #include <fog_pars_fragment>
 #include <bsdfs>
 #include <ambient_pars>
+#include <cube_uv_reflection_fragment>
 #include <lights_pars>
 #include <lights_standard_pars_fragment>
 #include <shadowmap_pars_fragment>

+ 6 - 1
src/renderers/webgl/WebGLProgram.js

@@ -51,7 +51,7 @@ THREE.WebGLProgram = ( function () {
 		extensions = extensions || {};
 
 		var chunks = [
-			( extensions.derivatives || parameters.bumpMap || parameters.normalMap || parameters.flatShading ) ? '#extension GL_OES_standard_derivatives : enable' : '',
+			( extensions.derivatives  || parameters.envMapCubeUV || parameters.bumpMap || parameters.normalMap || parameters.flatShading ) ? '#extension GL_OES_standard_derivatives : enable' : '',
 			( extensions.fragDepth || parameters.logarithmicDepthBuffer ) && rendererExtensions.get( 'EXT_frag_depth' ) ? '#extension GL_EXT_frag_depth : enable' : '',
 			( extensions.drawBuffers ) && rendererExtensions.get( 'WEBGL_draw_buffers' ) ? '#extension GL_EXT_draw_buffers : require' : '',
 			( extensions.shaderTextureLOD || parameters.envMap ) && rendererExtensions.get( 'EXT_shader_texture_lod' ) ? '#extension GL_EXT_shader_texture_lod : enable' : '',
@@ -279,6 +279,11 @@ THREE.WebGLProgram = ( function () {
 					envMapTypeDefine = 'ENVMAP_TYPE_CUBE';
 					break;
 
+				case THREE.CubeUVReflectionMapping:
+				case THREE.CubeUVRefractionMapping:
+					envMapTypeDefine = 'ENVMAP_TYPE_CUBE_UV';
+					break;
+
 				case THREE.EquirectangularReflectionMapping:
 				case THREE.EquirectangularRefractionMapping:
 					envMapTypeDefine = 'ENVMAP_TYPE_EQUIREC';

+ 4 - 3
src/renderers/webgl/WebGLPrograms.js

@@ -70,18 +70,17 @@ THREE.WebGLPrograms = function ( renderer, capabilities ) {
 	function getTextureEncodingFromMap( map, gammaOverrideLinear ) {
 
 		var encoding;
-
 		if( ! map ) {
 
 			encoding = THREE.LinearEncoding;
 
 		}
-		else if( map instanceof THREE.Texture ) {
+		else if( map instanceof THREE.Texture || map instanceof THREE.CubeTexture ) {
 
 			encoding = map.encoding;
 
 		}
-		else if( map instanceof THREE.WebGLRenderTarget ) {
+		else if( map instanceof THREE.WebGLRenderTarget || map instanceof THREE.THREE.WebGLRenderTargetCube ) {
 
 			encoding = map.texture.encoding;
 
@@ -129,6 +128,8 @@ THREE.WebGLPrograms = function ( renderer, capabilities ) {
 			envMap: !! material.envMap,
 			envMapMode: material.envMap && material.envMap.mapping,
 			envMapEncoding: getTextureEncodingFromMap( material.envMap, renderer.gammaInput ),
+			envMapCubeUV: (!!material.envMap) && ((material.envMap.mapping === THREE.CubeUVReflectionMapping) ||
+							(material.envMap.mapping === THREE.CubeUVRefractionMapping)),
 			lightMap: !! material.lightMap,
 			aoMap: !! material.aoMap,
 			emissiveMap: !! material.emissiveMap,

+ 1 - 0
utils/build/includes/common.json

@@ -132,6 +132,7 @@
 	"src/renderers/shaders/ShaderChunk/color_pars_vertex.glsl",
 	"src/renderers/shaders/ShaderChunk/color_vertex.glsl",
 	"src/renderers/shaders/ShaderChunk/common.glsl",
+	"src/renderers/shaders/ShaderChunk/cube_uv_reflection_fragment.glsl",	
 	"src/renderers/shaders/ShaderChunk/defaultnormal_vertex.glsl",
 	"src/renderers/shaders/ShaderChunk/displacementmap_vertex.glsl",
 	"src/renderers/shaders/ShaderChunk/displacementmap_pars_vertex.glsl",