2
0
Эх сурвалжийг харах

Merge pull request #18004 from elalish/pmrem

PMREM Upgrade
Mr.doob 5 жил өмнө
parent
commit
c7d8895804
37 өөрчлөгдсөн 1683 нэмэгдсэн , 1353 устгасан
  1. 0 233
      examples/js/pmrem/PMREMCubeUVPacker.js
  2. 698 224
      examples/js/pmrem/PMREMGenerator.js
  3. 0 11
      examples/jsm/pmrem/PMREMCubeUVPacker.d.ts
  4. 0 253
      examples/jsm/pmrem/PMREMCubeUVPacker.js
  5. 8 9
      examples/jsm/pmrem/PMREMGenerator.d.ts
  6. 712 229
      examples/jsm/pmrem/PMREMGenerator.js
  7. BIN
      examples/textures/equirectangular/pedestrian_overpass_1k.hdr
  8. BIN
      examples/textures/equirectangular/pedestrian_overpass_2k.hdr
  9. BIN
      examples/textures/equirectangular/venice_sunset_1k.hdr
  10. BIN
      examples/textures/equirectangular/venice_sunset_2k.hdr
  11. 13 31
      examples/webgl_furnace_test.html
  12. 5 18
      examples/webgl_loader_gltf.html
  13. 5 18
      examples/webgl_loader_gltf_extensions.html
  14. 4 14
      examples/webgl_materials_cars.html
  15. 11 21
      examples/webgl_materials_envmaps_exr.html
  16. 11 33
      examples/webgl_materials_envmaps_hdr.html
  17. 10 31
      examples/webgl_materials_envmaps_hdr_nodes.html
  18. 3 10
      examples/webgl_materials_envmaps_pmrem_nodes.html
  19. 4 12
      examples/webgl_materials_nodes.html
  20. 3 11
      examples/webgl_materials_physical_clearcoat.html
  21. 3 11
      examples/webgl_materials_physical_transparency.html
  22. 3 10
      examples/webgl_materials_reflectivity.html
  23. 3 11
      examples/webgl_materials_standard.html
  24. 3 11
      examples/webgl_materials_variations_physical.html
  25. 3 11
      examples/webgl_materials_variations_standard.html
  26. 2 9
      examples/webgl_tonemapping.html
  27. 4 0
      src/renderers/WebGLRenderer.js
  28. 145 115
      src/renderers/shaders/ShaderChunk/cube_uv_reflection_fragment.glsl.js
  29. 9 1
      src/renderers/shaders/ShaderChunk/envmap_fragment.glsl.js
  30. 6 5
      src/renderers/shaders/ShaderLib.js
  31. 7 4
      src/renderers/shaders/ShaderLib/cube_frag.glsl.js
  32. 1 0
      src/renderers/shaders/ShaderLib/meshbasic_frag.glsl.js
  33. 1 0
      src/renderers/shaders/ShaderLib/meshlambert_frag.glsl.js
  34. 1 0
      src/renderers/shaders/ShaderLib/meshphong_frag.glsl.js
  35. 4 5
      src/renderers/webgl/WebGLBackground.js
  36. 1 1
      src/renderers/webgl/WebGLProgram.js
  37. 0 1
      utils/modularize.js

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

@@ -1,233 +0,0 @@
-/**
- * @author Prashant Sharma / spidersharma03
- * @author Ben Houston / bhouston, https://clara.io
- *
- * This class takes the cube lods(corresponding to different roughness values), and creates a single cubeUV
- * Texture. The format for a given roughness set of faces is simply::
- * +X+Y+Z
- * -X-Y-Z
- * For every roughness a mip map chain is also saved, which is essential to remove the texture artifacts due to
- * minification.
- * Right now for every face a PlaneMesh is drawn, which leads to a lot of geometry draw calls, but can be replaced
- * later by drawing a single buffer and by sending the appropriate faceIndex via vertex attributes.
- * The arrangement of the faces is fixed, as assuming this arrangement, the sampling function has been written.
- */
-
-THREE.PMREMCubeUVPacker = ( function () {
-
-	var camera = new THREE.OrthographicCamera();
-	var scene = new THREE.Scene();
-	var shader = getShader();
-
-	var PMREMCubeUVPacker = function ( cubeTextureLods ) {
-
-		this.cubeLods = cubeTextureLods;
-		var size = cubeTextureLods[ 0 ].width * 4;
-
-		var sourceTexture = cubeTextureLods[ 0 ].texture;
-		var params = {
-			format: sourceTexture.format,
-			magFilter: sourceTexture.magFilter,
-			minFilter: sourceTexture.minFilter,
-			type: sourceTexture.type,
-			generateMipmaps: sourceTexture.generateMipmaps,
-			anisotropy: sourceTexture.anisotropy,
-			encoding: ( sourceTexture.encoding === THREE.RGBEEncoding ) ? THREE.RGBM16Encoding : sourceTexture.encoding
-		};
-
-		if ( params.encoding === THREE.RGBM16Encoding ) {
-
-			params.magFilter = THREE.LinearFilter;
-			params.minFilter = THREE.LinearFilter;
-
-		}
-
-		this.CubeUVRenderTarget = new THREE.WebGLRenderTarget( size, size, params );
-		this.CubeUVRenderTarget.texture.name = "PMREMCubeUVPacker.cubeUv";
-		this.CubeUVRenderTarget.texture.mapping = THREE.CubeUVReflectionMapping;
-
-		this.objects = [];
-
-		var geometry = new THREE.PlaneBufferGeometry( 1, 1 );
-
-		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 textureResolution = size;
-		size = cubeTextureLods[ 0 ].width;
-
-		var offset2 = 0;
-		var c = 4.0;
-		this.numLods = Math.log( cubeTextureLods[ 0 ].width ) / Math.log( 2 ) - 2; // IE11 doesn't support Math.log2
-		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 = shader.clone();
-					material.uniforms[ 'envMap' ].value = this.cubeLods[ i ].texture;
-					material.envMap = this.cubeLods[ i ].texture;
-					material.uniforms[ 'faceIndex' ].value = k;
-					material.uniforms[ 'mapSize' ].value = mipSize;
-
-					var planeMesh = new THREE.Mesh( geometry, material );
-					planeMesh.position.x = faceOffsets[ k ].x * mipSize - offset1 + mipOffsetX;
-					planeMesh.position.y = faceOffsets[ k ].y * mipSize - offset1 + offset2 + mipOffsetY;
-					planeMesh.material.side = THREE.BackSide;
-					planeMesh.scale.setScalar( mipSize );
-					this.objects.push( planeMesh );
-
-				}
-				mipOffsetY += 1.75 * mipSize;
-				mipOffsetX += 1.25 * mipSize;
-				mipSize /= 2;
-
-			}
-			offset2 += 2 * size;
-			if ( size > 16 ) size /= 2;
-
-		}
-
-	};
-
-	PMREMCubeUVPacker.prototype = {
-
-		constructor: PMREMCubeUVPacker,
-
-		update: function ( renderer ) {
-
-			var size = this.cubeLods[ 0 ].width * 4;
-			// top and bottom are swapped for some reason?
-			camera.left = - size * 0.5;
-			camera.right = size * 0.5;
-			camera.top = - size * 0.5;
-			camera.bottom = size * 0.5;
-			camera.near = 0;
-			camera.far = 1;
-			camera.updateProjectionMatrix();
-
-			for ( var i = 0; i < this.objects.length; i ++ ) {
-
-				scene.add( this.objects[ i ] );
-
-			}
-
-			var gammaInput = renderer.gammaInput;
-			var gammaOutput = renderer.gammaOutput;
-			var toneMapping = renderer.toneMapping;
-			var toneMappingExposure = renderer.toneMappingExposure;
-			var currentRenderTarget = renderer.getRenderTarget();
-
-			renderer.gammaInput = false;
-			renderer.gammaOutput = false;
-			renderer.toneMapping = THREE.LinearToneMapping;
-			renderer.toneMappingExposure = 1.0;
-			renderer.setRenderTarget( this.CubeUVRenderTarget );
-			renderer.render( scene, camera );
-
-			renderer.setRenderTarget( currentRenderTarget );
-			renderer.toneMapping = toneMapping;
-			renderer.toneMappingExposure = toneMappingExposure;
-			renderer.gammaInput = gammaInput;
-			renderer.gammaOutput = gammaOutput;
-
-			for ( var i = 0; i < this.objects.length; i ++ ) {
-
-				scene.remove( this.objects[ i ] );
-
-			}
-
-		},
-
-		dispose: function () {
-
-			for ( var i = 0, l = this.objects.length; i < l; i ++ ) {
-
-				this.objects[ i ].material.dispose();
-
-			}
-
-			this.objects[ 0 ].geometry.dispose();
-
-		}
-
-	};
-
-	function getShader() {
-
-		var shaderMaterial = new THREE.ShaderMaterial( {
-
-			uniforms: {
-				"faceIndex": { value: 0 },
-				"mapSize": { value: 0 },
-				"envMap": { value: null },
-				"testColor": { 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 );\
-        }",
-
-			blending: THREE.NoBlending
-
-		} );
-
-		shaderMaterial.type = 'PMREMCubeUVPacker';
-
-		return shaderMaterial;
-
-	}
-
-
-	return PMREMCubeUVPacker;
-
-} )();

+ 698 - 224
examples/js/pmrem/PMREMGenerator.js

@@ -1,293 +1,767 @@
 /**
- * @author Prashant Sharma / spidersharma03
- * @author Ben Houston / bhouston, https://clara.io
+ * @author Emmett Lalish / elalish
  *
- * To avoid cube map seams, I create an extra pixel around each face. This way when the cube map is
- * sampled by an application later(with a little care by sampling the centre of the texel), the extra 1 border
- *	of pixels makes sure that there is no seams artifacts present. This works perfectly for cubeUV format as
- *	well where the 6 faces can be arranged in any manner whatsoever.
- * Code in the beginning of fragment shader's main function does this job for a given resolution.
- *	Run Scene_PMREM_Test.html in the examples directory to see the sampling from the cube lods generated
- *	by this class.
+ * This class generates a Prefiltered, Mipmapped Radiance Environment Map
+ * (PMREM) from a cubeMap environment texture. This allows different levels of
+ * blur to be quickly accessed based on material roughness. It is packed into a
+ * special CubeUV format that allows us to perform custom interpolation so that
+ * we can support nonlinear formats such as RGBE. Unlike a traditional mipmap
+ * chain, it only goes down to the LOD_MIN level (above), and then creates extra
+ * even more filtered 'mips' at the same LOD_MIN resolution, associated with
+ * higher roughness levels. In this way we maintain resolution to smoothly
+ * interpolate diffuse lighting while limiting sampling computation.
  */
 
 THREE.PMREMGenerator = ( function () {
 
-	var shader = getShader();
-	var camera = new THREE.OrthographicCamera( - 1, 1, 1, - 1, 0.0, 1000 );
-	var scene = new THREE.Scene();
-	var planeMesh = new THREE.Mesh( new THREE.PlaneBufferGeometry( 2, 2, 0 ), shader );
-	planeMesh.material.side = THREE.DoubleSide;
-	scene.add( planeMesh );
-	scene.add( camera );
+	var LOD_MIN = 4;
+	var LOD_MAX = 8;
+	var SIZE_MAX = Math.pow( 2, LOD_MAX );
+	// The standard deviations (radians) associated with the extra mips. These are
+	// chosen to approximate a Trowbridge-Reitz distribution function times the
+	// geometric shadowing function. These sigma values squared must match the
+	// variance #defines in cube_uv_reflection_fragment.glsl.js.
+	var EXTRA_LOD_SIGMA = [ 0.125, 0.215, 0.35, 0.446, 0.526, 0.582 ];
+	var TOTAL_LODS = LOD_MAX - LOD_MIN + 1 + EXTRA_LOD_SIGMA.length;
+	// The maximum length of the blur for loop. Smaller sigmas will use fewer
+	// samples and exit early, but not recompile the shader.
+	var MAX_SAMPLES = 20;
+	var ENCODINGS = {
+		[ THREE.LinearEncoding ]: 0,
+		[ THREE.sRGBEncoding ]: 1,
+		[ THREE.RGBEEncoding ]: 2,
+		[ THREE.RGBM7Encoding ]: 3,
+		[ THREE.RGBM16Encoding ]: 4,
+		[ THREE.RGBDEncoding ]: 5,
+		[ THREE.GammaEncoding ]: 6
+	  };
+
+	var _flatCamera = new THREE.OrthographicCamera();
+	var _blurMaterial = _getBlurShader( MAX_SAMPLES );
+	var _equirectShader = null;
+	var _cubemapShader = null;
+
+	var { _lodPlanes, _sizeLods, _sigmas } = _createPlanes();
+	var _pingPongRenderTarget = null;
+	var _renderer = null;
+
+	// Golden Ratio
+	var PHI = ( 1 + Math.sqrt( 5 ) ) / 2;
+	var INV_PHI = 1 / PHI;
+	// Vertices of a dodecahedron (except the opposites, which represent the
+	// same axis), used as axis directions evenly spread on a sphere.
+	var _axisDirections = [
+		new THREE.Vector3( 1, 1, 1 ),
+		new THREE.Vector3( - 1, 1, 1 ),
+		new THREE.Vector3( 1, 1, - 1 ),
+		new THREE.Vector3( - 1, 1, - 1 ),
+		new THREE.Vector3( 0, PHI, INV_PHI ),
+		new THREE.Vector3( 0, PHI, - INV_PHI ),
+		new THREE.Vector3( INV_PHI, 0, PHI ),
+		new THREE.Vector3( - INV_PHI, 0, PHI ),
+		new THREE.Vector3( PHI, INV_PHI, 0 ),
+		new THREE.Vector3( - PHI, INV_PHI, 0 ) ];
+
+	var PMREMGenerator = function ( renderer ) {
+
+		_renderer = renderer;
 
-	var PMREMGenerator = function ( sourceTexture, samplesPerLevel, resolution ) {
+	};
+
+	PMREMGenerator.prototype = {
+
+		constructor: PMREMGenerator,
+
+		/**
+		 * Generates a PMREM from a supplied Scene, which can be faster than using an
+		 * image if networking bandwidth is low. Optional sigma specifies a blur radius
+		 * in radians to be applied to the scene before PMREM generation. Optional near
+		 * and far planes ensure the scene is rendered in its entirety (the cubeCamera
+		 * is placed at the origin).
+		 */
+		fromScene: function ( scene, sigma = 0, near = 0.1, far = 100 ) {
+
+			var cubeUVRenderTarget = _allocateTargets();
+			_sceneToCubeUV( scene, near, far, cubeUVRenderTarget );
+			if ( sigma > 0 ) {
+
+				_blur( cubeUVRenderTarget, 0, 0, sigma );
+
+			}
+			_applyPMREM( cubeUVRenderTarget );
+			_cleanup();
+
+			return cubeUVRenderTarget;
+
+		},
+
+		/**
+		 * Generates a PMREM from an equirectangular texture, which can be either LDR
+		 * (RGBFormat) or HDR (RGBEFormat). The ideal input image size is 1k (1024 x 512),
+		 * as this matches best with the 256 x 256 cubemap output.
+		 */
+		fromEquirectangular: function ( equirectangular ) {
+
+			equirectangular.magFilter = THREE.NearestFilter;
+			equirectangular.minFilter = THREE.NearestFilter;
+			equirectangular.generateMipmaps = false;
+
+			return this.fromCubemap( equirectangular );
+
+		},
+
+		/**
+		 * Generates a PMREM from an cubemap texture, which can be either LDR
+		 * (RGBFormat) or HDR (RGBEFormat). The ideal input cube size is 256 x 256,
+		 * as this matches best with the 256 x 256 cubemap output.
+		 */
+		fromCubemap: function ( cubemap ) {
+
+			var cubeUVRenderTarget = _allocateTargets( cubemap );
+			_textureToCubeUV( cubemap, cubeUVRenderTarget );
+			_applyPMREM( cubeUVRenderTarget );
+			_cleanup();
+
+			return cubeUVRenderTarget;
+
+		},
+
+		/**
+		 * Disposes of the PMREMGenerator's internal memory. Note that PMREMGenerator is a static class,
+		 * so you should not need more than one PMREMGenerator object. If you do, calling dispose() on
+		 * one of them will cause any others to also become unusable.
+		 */
+		dispose: function () {
+
+			_blurMaterial.dispose();
+			if ( _cubemapShader != null ) _cubemapShader.dispose();
+			if ( _equirectShader != null ) _equirectShader.dispose();
+			var plane;
+			for ( plane of _lodPlanes ) {
+
+				plane.dispose();
+
+			}
+
+		},
+
+	};
+
+	function _createPlanes() {
+
+		var _lodPlanes = [];
+		var _sizeLods = [];
+		var _sigmas = [];
 
-		this.sourceTexture = sourceTexture;
-		this.resolution = ( resolution !== undefined ) ? resolution : 256; // NODE: 256 is currently hard coded in the glsl code for performance reasons
-		this.samplesPerLevel = ( samplesPerLevel !== undefined ) ? samplesPerLevel : 32;
+		var lod = LOD_MAX;
+		for ( var i = 0; i < TOTAL_LODS; i ++ ) {
 
-		var monotonicEncoding = ( this.sourceTexture.encoding === THREE.LinearEncoding ) ||
-			( this.sourceTexture.encoding === THREE.GammaEncoding ) || ( this.sourceTexture.encoding === THREE.sRGBEncoding );
+			var sizeLod = Math.pow( 2, lod );
+			_sizeLods.push( sizeLod );
+			var sigma = 1.0 / sizeLod;
+			if ( i > LOD_MAX - LOD_MIN ) {
 
-		this.sourceTexture.minFilter = ( monotonicEncoding ) ? THREE.LinearFilter : THREE.NearestFilter;
-		this.sourceTexture.magFilter = ( monotonicEncoding ) ? THREE.LinearFilter : THREE.NearestFilter;
-		this.sourceTexture.generateMipmaps = this.sourceTexture.generateMipmaps && monotonicEncoding;
+				sigma = EXTRA_LOD_SIGMA[ i - LOD_MAX + LOD_MIN - 1 ];
 
-		this.cubeLods = [];
+			} else if ( i == 0 ) {
+
+				sigma = 0;
+
+			}
+			_sigmas.push( sigma );
+
+			var texelSize = 1.0 / ( sizeLod - 1 );
+			var min = - texelSize / 2;
+			var max = 1 + texelSize / 2;
+			var uv1 = [ min, min, max, min, max, max, min, min, max, max, min, max ];
+
+			var cubeFaces = 6;
+			var vertices = 6;
+			var positionSize = 3;
+			var uvSize = 2;
+			var faceIndexSize = 1;
+
+			var position = new Float32Array( positionSize * vertices * cubeFaces );
+			var uv = new Float32Array( uvSize * vertices * cubeFaces );
+			var faceIndex = new Float32Array( faceIndexSize * vertices * cubeFaces );
+
+			for ( var face = 0; face < cubeFaces; face ++ ) {
+
+				var x = ( face % 3 ) * 2 / 3 - 1;
+				var y = face > 2 ? 0 : - 1;
+				var coordinates = [
+					[ x, y, 0 ],
+					[ x + 2 / 3, y, 0 ],
+					[ x + 2 / 3, y + 1, 0 ],
+					[ x, y, 0 ],
+					[ x + 2 / 3, y + 1, 0 ],
+					[ x, y + 1, 0 ]
+				];
+				position.set( [].concat( ...coordinates ),
+					positionSize * vertices * face );
+				uv.set( uv1, uvSize * vertices * face );
+				var fill = [ face, face, face, face, face, face ];
+				faceIndex.set( fill, faceIndexSize * vertices * face );
+
+			}
+			var planes = new THREE.BufferGeometry();
+			planes.setAttribute(
+				'position', new THREE.BufferAttribute( position, positionSize ) );
+			planes.setAttribute( 'uv', new THREE.BufferAttribute( uv, uvSize ) );
+			planes.setAttribute(
+				'faceIndex', new THREE.BufferAttribute( faceIndex, faceIndexSize ) );
+			_lodPlanes.push( planes );
+
+			if ( lod > LOD_MIN ) {
+
+				lod --;
+
+			}
+
+		}
+		return { _lodPlanes, _sizeLods, _sigmas };
+
+	}
+
+	function _allocateTargets( equirectangular ) {
 
-		var size = this.resolution;
 		var params = {
-			format: this.sourceTexture.format,
-			magFilter: this.sourceTexture.magFilter,
-			minFilter: this.sourceTexture.minFilter,
-			type: this.sourceTexture.type,
-			generateMipmaps: this.sourceTexture.generateMipmaps,
-			anisotropy: this.sourceTexture.anisotropy,
-			encoding: this.sourceTexture.encoding
+		  magFilter: THREE.NearestFilter,
+		  minFilter: THREE.NearestFilter,
+		  generateMipmaps: false,
+		  type: equirectangular ? equirectangular.type : THREE.UnsignedByteType,
+		  format: equirectangular ? equirectangular.format : THREE.RGBEFormat,
+		  encoding: equirectangular ? equirectangular.encoding : THREE.RGBEEncoding,
+		  depthBuffer: false,
+		  stencilBuffer: false
 		};
+		var cubeUVRenderTarget = _createRenderTarget(
+			{ ...params, depthBuffer: ( equirectangular ? false : true ) } );
+		_pingPongRenderTarget = _createRenderTarget( params );
+		return cubeUVRenderTarget;
+
+	}
+
+	function _cleanup() {
 
-		// how many LODs fit in the given CubeUV Texture.
-		this.numLods = Math.log( size ) / Math.log( 2 ) - 2; // IE11 doesn't support Math.log2
+		_pingPongRenderTarget.dispose();
+		_renderer.setRenderTarget( null );
+		var size = _renderer.getSize( new THREE.Vector2() );
+		_renderer.setViewport( 0, 0, size.x, size.y );
 
-		for ( var i = 0; i < this.numLods; i ++ ) {
+	}
 
-			var renderTarget = new THREE.WebGLRenderTargetCube( size, size, params );
-			renderTarget.texture.name = "PMREMGenerator.cube" + i;
-			this.cubeLods.push( renderTarget );
-			size = Math.max( 16, size / 2 );
+	function _sceneToCubeUV( scene, near, far, cubeUVRenderTarget ) {
+
+		var fov = 90;
+		var aspect = 1;
+		var cubeCamera = new THREE.PerspectiveCamera( fov, aspect, near, far );
+		var upSign = [ 1, 1, 1, 1, - 1, 1 ];
+		var forwardSign = [ 1, 1, - 1, - 1, - 1, 1 ];
+
+		var gammaOutput = _renderer.gammaOutput;
+		var toneMapping = _renderer.toneMapping;
+		var toneMappingExposure = _renderer.toneMappingExposure;
+		var clearColor = _renderer.getClearColor();
+		var clearAlpha = _renderer.getClearAlpha();
+
+		_renderer.toneMapping = THREE.LinearToneMapping;
+		_renderer.toneMappingExposure = 1.0;
+		_renderer.gammaOutput = false;
+		scene.scale.z *= - 1;
+
+		var background = scene.background;
+		if ( background && background.isColor ) {
+
+			background.convertSRGBToLinear();
+			// Convert linear to RGBE
+			var maxComponent = Math.max( background.r, background.g, background.b );
+			var fExp = Math.min( Math.max( Math.ceil( Math.log2( maxComponent ) ), - 128.0 ), 127.0 );
+			background = background.multiplyScalar( Math.pow( 2.0, - fExp ) );
+			var alpha = ( fExp + 128.0 ) / 255.0;
+			_renderer.setClearColor( background, alpha );
+			scene.background = null;
 
 		}
 
-	};
+		_renderer.setRenderTarget( cubeUVRenderTarget );
+		for ( var i = 0; i < 6; i ++ ) {
 
-	PMREMGenerator.prototype = {
+			var col = i % 3;
+			if ( col == 0 ) {
 
-		constructor: PMREMGenerator,
+				cubeCamera.up.set( 0, upSign[ i ], 0 );
+				cubeCamera.lookAt( forwardSign[ i ], 0, 0 );
 
-		/*
-		 * Prashant Sharma / spidersharma03: More thought and work is needed here.
-		 * Right now it's a kind of a hack to use the previously convolved map to convolve the current one.
-		 * I tried to use the original map to convolve all the lods, but for many textures(specially the high frequency)
-		 * even a high number of samples(1024) dosen't lead to satisfactory results.
-		 * By using the previous convolved maps, a lower number of samples are generally sufficient(right now 32, which
-		 * gives okay results unless we see the reflection very carefully, or zoom in too much), however the math
-		 * goes wrong as the distribution function tries to sample a larger area than what it should be. So I simply scaled
-		 * the roughness by 0.9(totally empirical) to try to visually match the original result.
-		 * The condition "if(i <5)" is also an attemt to make the result match the original result.
-		 * This method requires the most amount of thinking I guess. Here is a paper which we could try to implement in future::
-		 * https://developer.nvidia.com/gpugems/GPUGems3/gpugems3_ch20.html
-		 */
-		update: function ( renderer ) {
+			} else if ( col == 1 ) {
+
+				cubeCamera.up.set( 0, 0, upSign[ i ] );
+				cubeCamera.lookAt( 0, forwardSign[ i ], 0 );
 
-			// Texture should only be flipped for CubeTexture, not for
-			// a Texture created via THREE.WebGLRenderTargetCube.
-			var tFlip = ( this.sourceTexture.isCubeTexture ) ? - 1 : 1;
+			} else {
 
-			shader.defines[ 'SAMPLES_PER_LEVEL' ] = this.samplesPerLevel;
-			shader.uniforms[ 'faceIndex' ].value = 0;
-			shader.uniforms[ 'envMap' ].value = this.sourceTexture;
-			shader.envMap = this.sourceTexture;
-			shader.needsUpdate = true;
+				cubeCamera.up.set( 0, upSign[ i ], 0 );
+				cubeCamera.lookAt( 0, 0, forwardSign[ i ] );
+
+			}
+			_setViewport(
+				col * SIZE_MAX, i > 2 ? SIZE_MAX : 0, SIZE_MAX, SIZE_MAX );
+			_renderer.render( scene, cubeCamera );
 
-			var gammaInput = renderer.gammaInput;
-			var gammaOutput = renderer.gammaOutput;
-			var toneMapping = renderer.toneMapping;
-			var toneMappingExposure = renderer.toneMappingExposure;
-			var currentRenderTarget = renderer.getRenderTarget();
+		}
 
-			renderer.toneMapping = THREE.LinearToneMapping;
-			renderer.toneMappingExposure = 1.0;
-			renderer.gammaInput = false;
-			renderer.gammaOutput = false;
+		_renderer.toneMapping = toneMapping;
+		_renderer.toneMappingExposure = toneMappingExposure;
+		_renderer.gammaOutput = gammaOutput;
+		_renderer.setClearColor( clearColor, clearAlpha );
+		scene.scale.z *= - 1;
 
-			for ( var i = 0; i < this.numLods; i ++ ) {
+	}
 
-				var r = i / ( this.numLods - 1 );
-				shader.uniforms[ 'roughness' ].value = r * 0.9; // see comment above, pragmatic choice
-				// Only apply the tFlip for the first LOD
-				shader.uniforms[ 'tFlip' ].value = ( i == 0 ) ? tFlip : 1;
-				var size = this.cubeLods[ i ].width;
-				shader.uniforms[ 'mapSize' ].value = size;
-				this.renderToCubeMapTarget( renderer, this.cubeLods[ i ] );
+	function _textureToCubeUV( texture, cubeUVRenderTarget ) {
 
-				if ( i < 5 ) shader.uniforms[ 'envMap' ].value = this.cubeLods[ i ].texture;
+		var scene = new THREE.Scene();
+		if ( texture.isCubeTexture ) {
 
-			}
+			if ( _cubemapShader == null ) {
 
-			renderer.setRenderTarget( currentRenderTarget );
-			renderer.toneMapping = toneMapping;
-			renderer.toneMappingExposure = toneMappingExposure;
-			renderer.gammaInput = gammaInput;
-			renderer.gammaOutput = gammaOutput;
+				_cubemapShader = _getCubemapShader();
 
-		},
+			}
 
-		renderToCubeMapTarget: function ( renderer, renderTarget ) {
+		} else {
 
-			for ( var i = 0; i < 6; i ++ ) {
+			if ( _equirectShader == null ) {
 
-				this.renderToCubeMapTargetFace( renderer, renderTarget, i );
+				_equirectShader = _getEquirectShader();
 
 			}
 
-		},
+		}
+		var material = texture.isCubeTexture ? _cubemapShader : _equirectShader;
+		scene.add( new THREE.Mesh( _lodPlanes[ 0 ], material ) );
+		var uniforms = material.uniforms;
 
-		renderToCubeMapTargetFace: function ( renderer, renderTarget, faceIndex ) {
+		uniforms[ 'envMap' ].value = texture;
+		if ( ! texture.isCubeTexture ) {
 
-			shader.uniforms[ 'faceIndex' ].value = faceIndex;
-			renderer.setRenderTarget( renderTarget, faceIndex );
-			renderer.clear();
-			renderer.render( scene, camera );
+			uniforms[ 'texelSize' ].value.set( 1.0 / texture.image.width, 1.0 / texture.image.height );
 
-		},
+		}
+		uniforms[ 'inputEncoding' ].value = ENCODINGS[ texture.encoding ];
+		uniforms[ 'outputEncoding' ].value = ENCODINGS[ texture.encoding ];
 
-		dispose: function () {
+		_renderer.setRenderTarget( cubeUVRenderTarget );
+		_setViewport( 0, 0, 3 * SIZE_MAX, 2 * SIZE_MAX );
+		_renderer.render( scene, _flatCamera );
+
+	}
+
+	function _createRenderTarget( params ) {
+
+		var cubeUVRenderTarget =
+		new THREE.WebGLRenderTarget( 3 * SIZE_MAX, 3 * SIZE_MAX, params );
+		cubeUVRenderTarget.texture.mapping = THREE.CubeUVReflectionMapping;
+		cubeUVRenderTarget.texture.name = 'PMREM.cubeUv';
+		return cubeUVRenderTarget;
 
-			for ( var i = 0, l = this.cubeLods.length; i < l; i ++ ) {
+	}
+
+	function _setViewport( x, y, width, height ) {
+
+		var dpr = _renderer.getPixelRatio();
+		_renderer.setViewport( x / dpr, y / dpr, width / dpr, height / dpr );
+
+	}
+
+	function _applyPMREM( cubeUVRenderTarget ) {
+
+		var autoClear = _renderer.autoClear;
+		_renderer.autoClear = false;
+
+	  	for ( var i = 1; i < TOTAL_LODS; i ++ ) {
+
+			var sigma = Math.sqrt(
+				_sigmas[ i ] * _sigmas[ i ] -
+			_sigmas[ i - 1 ] * _sigmas[ i - 1 ] );
+			var poleAxis =
+			_axisDirections[ ( i - 1 ) % _axisDirections.length ];
+			_blur( cubeUVRenderTarget, i - 1, i, sigma, poleAxis );
+
+		}
+
+		_renderer.autoClear = autoClear;
+
+	}
+
+	/**
+   * This is a two-pass Gaussian blur for a cubemap. Normally this is done
+   * vertically and horizontally, but this breaks down on a cube. Here we apply
+   * the blur latitudinally (around the poles), and then longitudinally (towards
+   * the poles) to approximate the orthogonally-separable blur. It is least
+   * accurate at the poles, but still does a decent job.
+   */
+	function _blur( cubeUVRenderTarget, lodIn, lodOut, sigma, poleAxis ) {
+
+		_halfBlur(
+			cubeUVRenderTarget,
+			_pingPongRenderTarget,
+			lodIn,
+			lodOut,
+			sigma,
+			'latitudinal',
+			poleAxis );
+
+		_halfBlur(
+			_pingPongRenderTarget,
+			cubeUVRenderTarget,
+			lodOut,
+			lodOut,
+			sigma,
+			'longitudinal',
+			poleAxis );
 
-				this.cubeLods[ i ].dispose();
+	}
+
+	function _halfBlur( targetIn, targetOut, lodIn, lodOut, sigmaRadians, direction, poleAxis ) {
+
+		if ( direction !== 'latitudinal' && direction !== 'longitudinal' ) {
+
+			console.error(
+				'blur direction must be either latitudinal or longitudinal!' );
+
+		}
+
+		// Number of standard deviations at which to cut off the discrete approximation.
+		var STANDARD_DEVIATIONS = 3;
+
+		var blurScene = new THREE.Scene();
+		blurScene.add( new THREE.Mesh( _lodPlanes[ lodOut ], _blurMaterial ) );
+		var blurUniforms = _blurMaterial.uniforms;
+
+		var pixels = _sizeLods[ lodIn ] - 1;
+		var radiansPerPixel = isFinite( sigmaRadians ) ? Math.PI / ( 2 * pixels ) : 2 * Math.PI / ( 2 * MAX_SAMPLES - 1 );
+		var sigmaPixels = sigmaRadians / radiansPerPixel;
+		var samples = isFinite( sigmaRadians ) ? 1 + Math.floor( STANDARD_DEVIATIONS * sigmaPixels ) : MAX_SAMPLES;
+
+		if ( samples > MAX_SAMPLES ) {
+
+			console.warn( `sigmaRadians, ${
+				sigmaRadians}, is too large and will clip, as it requested ${
+				samples} samples when the maximum is set to ${MAX_SAMPLES}` );
+
+		}
+
+		var weights = [];
+		var sum = 0;
+		for ( var i = 0; i < MAX_SAMPLES; ++ i ) {
+
+			var x = i / sigmaPixels;
+			var weight = Math.exp( - x * x / 2 );
+			weights.push( weight );
+			if ( i == 0 ) {
+
+	  			 sum += weight;
+
+			} else if ( i < samples ) {
+
+	  			sum += 2 * weight;
 
 			}
 
-			shader.dispose();
+		}
+		weights = weights.map( w => w / sum );
 
-		},
+		blurUniforms[ 'envMap' ].value = targetIn.texture;
+		blurUniforms[ 'samples' ].value = samples;
+		blurUniforms[ 'weights' ].value = weights;
+		blurUniforms[ 'latitudinal' ].value = direction === 'latitudinal';
+		if ( poleAxis ) {
 
-	};
+			blurUniforms[ 'poleAxis' ].value = poleAxis;
+
+		}
+		blurUniforms[ 'dTheta' ].value = radiansPerPixel;
+		blurUniforms[ 'mipInt' ].value = LOD_MAX - lodIn;
+		blurUniforms[ 'inputEncoding' ].value = ENCODINGS[ targetIn.texture.encoding ];
+		blurUniforms[ 'outputEncoding' ].value = ENCODINGS[ targetIn.texture.encoding ];
+
+		var outputSize = _sizeLods[ lodOut ];
+		var x = 3 * Math.max( 0, SIZE_MAX - 2 * outputSize );
+		var y = ( lodOut === 0 ? 0 : 2 * SIZE_MAX ) +
+	  2 * outputSize *
+		  ( lodOut > LOD_MAX - LOD_MIN ? lodOut - LOD_MAX + LOD_MIN : 0 );
 
-	function getShader() {
+		_renderer.setRenderTarget( targetOut );
+		_setViewport( x, y, 3 * outputSize, 2 * outputSize );
+		_renderer.render( blurScene, _flatCamera );
 
-		var shaderMaterial = new THREE.ShaderMaterial( {
+	}
+
+	function _getBlurShader( maxSamples ) {
+
+		var weights = new Float32Array( maxSamples );
+		var poleAxis = new THREE.Vector3( 0, 1, 0 );
+		var shaderMaterial = new THREE.RawShaderMaterial( {
 
-			defines: {
-				"SAMPLES_PER_LEVEL": 20,
+			defines: { 'n': maxSamples },
+
+			uniforms: {
+				'envMap': { value: null },
+				'samples': { value: 1 },
+				'weights': { value: weights },
+				'latitudinal': { value: false },
+				'dTheta': { value: 0 },
+				'mipInt': { value: 0 },
+				'poleAxis': { value: poleAxis },
+				'inputEncoding': { value: ENCODINGS[ THREE.LinearEncoding ] },
+				'outputEncoding': { value: ENCODINGS[ THREE.LinearEncoding ] }
 			},
 
+			vertexShader: _getCommonVertexShader(),
+
+			fragmentShader: `
+precision mediump float;
+precision mediump int;
+varying vec3 vOutputDirection;
+uniform sampler2D envMap;
+uniform int samples;
+uniform float weights[n];
+uniform bool latitudinal;
+uniform float dTheta;
+uniform float mipInt;
+uniform vec3 poleAxis;
+
+${_getEncodings()}
+
+#define ENVMAP_TYPE_CUBE_UV
+#include <cube_uv_reflection_fragment>
+
+void main() {
+	gl_FragColor = vec4(0.0);
+    for (int i = 0; i < n; i++) {
+      if (i >= samples)
+        break;
+      for (int dir = -1; dir < 2; dir += 2) {
+        if (i == 0 && dir == 1)
+          continue;
+        vec3 axis = latitudinal ? poleAxis : cross(poleAxis, vOutputDirection);
+        if (all(equal(axis, vec3(0.0))))
+          axis = cross(vec3(0.0, 1.0, 0.0), vOutputDirection);
+        axis = normalize(axis);
+        float theta = dTheta * float(dir * i);
+        float cosTheta = cos(theta);
+        // Rodrigues' axis-angle rotation
+        vec3 sampleDirection = vOutputDirection * cosTheta 
+            + cross(axis, vOutputDirection) * sin(theta) 
+            + axis * dot(axis, vOutputDirection) * (1.0 - cosTheta);
+        gl_FragColor.rgb +=
+            weights[i] * bilinearCubeUV(envMap, sampleDirection, mipInt);
+      }
+    }
+  	gl_FragColor = linearToOutputTexel(gl_FragColor);
+}
+     		`,
+
+			blending: THREE.NoBlending,
+			depthTest: false,
+	   		depthWrite: false
+
+		} );
+
+		shaderMaterial.type = 'SphericalGaussianBlur';
+
+		return shaderMaterial;
+
+	}
+
+	function _getEquirectShader() {
+
+		var texelSize = new THREE.Vector2( 1, 1 );
+		var shaderMaterial = new THREE.RawShaderMaterial( {
+
 			uniforms: {
-				"faceIndex": { value: 0 },
-				"roughness": { value: 0.5 },
-				"mapSize": { value: 0.5 },
-				"envMap": { value: null },
-				"tFlip": { value: - 1 },
+				'envMap': { value: null },
+				'texelSize': { value: texelSize },
+				'inputEncoding': { value: ENCODINGS[ THREE.LinearEncoding ] },
+				'outputEncoding': { value: ENCODINGS[ THREE.LinearEncoding ] }
 			},
 
-			vertexShader:
-				"varying vec2 vUv;\n\
-				void main() {\n\
-					vUv = uv;\n\
-					gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n\
-				}",
-
-			fragmentShader:
-				"#include <common>\n\
-				varying vec2 vUv;\n\
-				uniform int faceIndex;\n\
-				uniform float roughness;\n\
-				uniform samplerCube envMap;\n\
-				uniform float mapSize;\n\
-				uniform float tFlip;\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\
-				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\
-					} 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\
-					vec3 correctedDirection = vec3( tFlip * sampleDirection.x, sampleDirection.yz );\n\
-					mat3 vecSpace = matrixFromVector( normalize( correctedDirection ) );\n\
-					vec3 rgbColor = vec3(0.0);\n\
-					const int NumSamples = SAMPLES_PER_LEVEL;\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 r = rand(vec2(sini, cosi));\n\
-						vect = ImportanceSampleGGX(vec2(float(i) / float(NumSamples), r), 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.NoBlending
+			vertexShader: _getCommonVertexShader(),
+
+			fragmentShader: `
+precision mediump float;
+precision mediump int;
+varying vec3 vOutputDirection;
+uniform sampler2D envMap;
+uniform vec2 texelSize;
+
+${_getEncodings()}
+
+#define RECIPROCAL_PI 0.31830988618
+#define RECIPROCAL_PI2 0.15915494
+
+void main() {
+	gl_FragColor = vec4(0.0);
+	vec3 outputDirection = normalize(vOutputDirection);
+	vec2 uv;
+	uv.y = asin(clamp(outputDirection.y, -1.0, 1.0)) * RECIPROCAL_PI + 0.5;
+	uv.x = atan(outputDirection.z, outputDirection.x) * RECIPROCAL_PI2 + 0.5;
+	vec2 f = fract(uv / texelSize - 0.5);
+	uv -= f * texelSize;
+	vec3 tl = envMapTexelToLinear(texture2D(envMap, uv)).rgb;
+	uv.x += texelSize.x;
+	vec3 tr = envMapTexelToLinear(texture2D(envMap, uv)).rgb;
+	uv.y += texelSize.y;
+	vec3 br = envMapTexelToLinear(texture2D(envMap, uv)).rgb;
+	uv.x -= texelSize.x;
+	vec3 bl = envMapTexelToLinear(texture2D(envMap, uv)).rgb;
+	vec3 tm = mix(tl, tr, f.x);
+	vec3 bm = mix(bl, br, f.x);
+	gl_FragColor.rgb = mix(tm, bm, f.y);
+  	gl_FragColor = linearToOutputTexel(gl_FragColor);
+}
+     		`,
+
+			blending: THREE.NoBlending,
+			depthTest: false,
+	   		depthWrite: false
 
 		} );
 
-		shaderMaterial.type = 'PMREMGenerator';
+		shaderMaterial.type = 'EquirectangularToCubeUV';
 
 		return shaderMaterial;
 
 	}
 
+	function _getCubemapShader() {
+
+		var shaderMaterial = new THREE.RawShaderMaterial( {
+
+			uniforms: {
+				'envMap': { value: null },
+				'inputEncoding': { value: ENCODINGS[ THREE.LinearEncoding ] },
+				'outputEncoding': { value: ENCODINGS[ THREE.LinearEncoding ] }
+			},
+
+			vertexShader: _getCommonVertexShader(),
+
+			fragmentShader: `
+precision mediump float;
+precision mediump int;
+varying vec3 vOutputDirection;
+uniform samplerCube envMap;
+
+${_getEncodings()}
+
+void main() {
+	gl_FragColor = vec4(0.0);
+	gl_FragColor.rgb = envMapTexelToLinear(textureCube(envMap, vec3( - vOutputDirection.x, vOutputDirection.yz ))).rgb;
+  	gl_FragColor = linearToOutputTexel(gl_FragColor);
+}
+     		`,
+
+			blending: THREE.NoBlending,
+			depthTest: false,
+	   		depthWrite: false
+
+		} );
+
+		shaderMaterial.type = 'CubemapToCubeUV';
+
+		return shaderMaterial;
+
+	}
+
+	function _getCommonVertexShader() {
+
+		return `
+precision mediump float;
+precision mediump int;
+attribute vec3 position;
+attribute vec2 uv;
+attribute float faceIndex;
+varying vec3 vOutputDirection;
+vec3 getDirection(vec2 uv, float face) {
+	uv = 2.0 * uv - 1.0;
+	vec3 direction = vec3(uv, 1.0);
+	if (face == 0.0) {
+		direction = direction.zyx;
+		direction.z *= -1.0;
+	} else if (face == 1.0) {
+		direction = direction.xzy;
+		direction.z *= -1.0;
+	} else if (face == 3.0) {
+		direction = direction.zyx;
+		direction.x *= -1.0;
+	} else if (face == 4.0) {
+		direction = direction.xzy;
+		direction.y *= -1.0;
+	} else if (face == 5.0) {
+		direction.xz *= -1.0;
+	}
+	return direction;
+}
+void main() {
+	vOutputDirection = getDirection(uv, faceIndex);
+	gl_Position = vec4( position, 1.0 );
+}
+		`;
+
+	}
+
+	function _getEncodings() {
+
+		return `
+uniform int inputEncoding;
+uniform int outputEncoding;
+
+#include <encodings_pars_fragment>
+
+vec4 inputTexelToLinear(vec4 value){
+    if(inputEncoding == 0){
+        return value;
+    }else if(inputEncoding == 1){
+        return sRGBToLinear(value);
+    }else if(inputEncoding == 2){
+        return RGBEToLinear(value);
+    }else if(inputEncoding == 3){
+        return RGBMToLinear(value, 7.0);
+    }else if(inputEncoding == 4){
+        return RGBMToLinear(value, 16.0);
+    }else if(inputEncoding == 5){
+        return RGBDToLinear(value, 256.0);
+    }else{
+        return GammaToLinear(value, 2.2);
+    }
+}
+
+vec4 linearToOutputTexel(vec4 value){
+    if(outputEncoding == 0){
+        return value;
+    }else if(outputEncoding == 1){
+        return LinearTosRGB(value);
+    }else if(outputEncoding == 2){
+        return LinearToRGBE(value);
+    }else if(outputEncoding == 3){
+        return LinearToRGBM(value, 7.0);
+    }else if(outputEncoding == 4){
+        return LinearToRGBM(value, 16.0);
+    }else if(outputEncoding == 5){
+        return LinearToRGBD(value, 256.0);
+    }else{
+        return LinearToGamma(value, 2.2);
+    }
+}
+
+vec4 envMapTexelToLinear(vec4 color) {
+  return inputTexelToLinear(color);
+}
+		`;
+
+	}
+
 	return PMREMGenerator;
 
 } )();

+ 0 - 11
examples/jsm/pmrem/PMREMCubeUVPacker.d.ts

@@ -1,11 +0,0 @@
-import { CubeTexture, Renderer, WebGLRenderTarget } from '../../../src/Three';
-
-export class PMREMCubeUVPacker {
-
-	CubeUVRenderTarget:WebGLRenderTarget;
-
-	constructor( cubeTextureLods: CubeTexture[] );
-	update( renderer:Renderer ): void;
-	dispose(): void;
-
-}

+ 0 - 253
examples/jsm/pmrem/PMREMCubeUVPacker.js

@@ -1,253 +0,0 @@
-/**
- * @author Prashant Sharma / spidersharma03
- * @author Ben Houston / bhouston, https://clara.io
- *
- * This class takes the cube lods(corresponding to different roughness values), and creates a single cubeUV
- * Texture. The format for a given roughness set of faces is simply::
- * +X+Y+Z
- * -X-Y-Z
- * For every roughness a mip map chain is also saved, which is essential to remove the texture artifacts due to
- * minification.
- * Right now for every face a PlaneMesh is drawn, which leads to a lot of geometry draw calls, but can be replaced
- * later by drawing a single buffer and by sending the appropriate faceIndex via vertex attributes.
- * The arrangement of the faces is fixed, as assuming this arrangement, the sampling function has been written.
- */
-
-import {
-	BackSide,
-	CubeUVReflectionMapping,
-	LinearFilter,
-	LinearToneMapping,
-	Mesh,
-	NoBlending,
-	OrthographicCamera,
-	PlaneBufferGeometry,
-	RGBEEncoding,
-	RGBM16Encoding,
-	Scene,
-	ShaderMaterial,
-	Vector2,
-	Vector3,
-	WebGLRenderTarget
-} from "../../../build/three.module.js";
-
-var PMREMCubeUVPacker = ( function () {
-
-	var camera = new OrthographicCamera();
-	var scene = new Scene();
-	var shader = getShader();
-
-	var PMREMCubeUVPacker = function ( cubeTextureLods ) {
-
-		this.cubeLods = cubeTextureLods;
-		var size = cubeTextureLods[ 0 ].width * 4;
-
-		var sourceTexture = cubeTextureLods[ 0 ].texture;
-		var params = {
-			format: sourceTexture.format,
-			magFilter: sourceTexture.magFilter,
-			minFilter: sourceTexture.minFilter,
-			type: sourceTexture.type,
-			generateMipmaps: sourceTexture.generateMipmaps,
-			anisotropy: sourceTexture.anisotropy,
-			encoding: ( sourceTexture.encoding === RGBEEncoding ) ? RGBM16Encoding : sourceTexture.encoding
-		};
-
-		if ( params.encoding === RGBM16Encoding ) {
-
-			params.magFilter = LinearFilter;
-			params.minFilter = LinearFilter;
-
-		}
-
-		this.CubeUVRenderTarget = new WebGLRenderTarget( size, size, params );
-		this.CubeUVRenderTarget.texture.name = "PMREMCubeUVPacker.cubeUv";
-		this.CubeUVRenderTarget.texture.mapping = CubeUVReflectionMapping;
-
-		this.objects = [];
-
-		var geometry = new PlaneBufferGeometry( 1, 1 );
-
-		var faceOffsets = [];
-		faceOffsets.push( new Vector2( 0, 0 ) );
-		faceOffsets.push( new Vector2( 1, 0 ) );
-		faceOffsets.push( new Vector2( 2, 0 ) );
-		faceOffsets.push( new Vector2( 0, 1 ) );
-		faceOffsets.push( new Vector2( 1, 1 ) );
-		faceOffsets.push( new Vector2( 2, 1 ) );
-
-		var textureResolution = size;
-		size = cubeTextureLods[ 0 ].width;
-
-		var offset2 = 0;
-		var c = 4.0;
-		this.numLods = Math.log( cubeTextureLods[ 0 ].width ) / Math.log( 2 ) - 2; // IE11 doesn't support Math.log2
-		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 = shader.clone();
-					material.uniforms[ 'envMap' ].value = this.cubeLods[ i ].texture;
-					material.envMap = this.cubeLods[ i ].texture;
-					material.uniforms[ 'faceIndex' ].value = k;
-					material.uniforms[ 'mapSize' ].value = mipSize;
-
-					var planeMesh = new Mesh( geometry, material );
-					planeMesh.position.x = faceOffsets[ k ].x * mipSize - offset1 + mipOffsetX;
-					planeMesh.position.y = faceOffsets[ k ].y * mipSize - offset1 + offset2 + mipOffsetY;
-					planeMesh.material.side = BackSide;
-					planeMesh.scale.setScalar( mipSize );
-					this.objects.push( planeMesh );
-
-				}
-				mipOffsetY += 1.75 * mipSize;
-				mipOffsetX += 1.25 * mipSize;
-				mipSize /= 2;
-
-			}
-			offset2 += 2 * size;
-			if ( size > 16 ) size /= 2;
-
-		}
-
-	};
-
-	PMREMCubeUVPacker.prototype = {
-
-		constructor: PMREMCubeUVPacker,
-
-		update: function ( renderer ) {
-
-			var size = this.cubeLods[ 0 ].width * 4;
-			// top and bottom are swapped for some reason?
-			camera.left = - size * 0.5;
-			camera.right = size * 0.5;
-			camera.top = - size * 0.5;
-			camera.bottom = size * 0.5;
-			camera.near = 0;
-			camera.far = 1;
-			camera.updateProjectionMatrix();
-
-			for ( var i = 0; i < this.objects.length; i ++ ) {
-
-				scene.add( this.objects[ i ] );
-
-			}
-
-			var gammaInput = renderer.gammaInput;
-			var gammaOutput = renderer.gammaOutput;
-			var toneMapping = renderer.toneMapping;
-			var toneMappingExposure = renderer.toneMappingExposure;
-			var currentRenderTarget = renderer.getRenderTarget();
-
-			renderer.gammaInput = false;
-			renderer.gammaOutput = false;
-			renderer.toneMapping = LinearToneMapping;
-			renderer.toneMappingExposure = 1.0;
-			renderer.setRenderTarget( this.CubeUVRenderTarget );
-			renderer.render( scene, camera );
-
-			renderer.setRenderTarget( currentRenderTarget );
-			renderer.toneMapping = toneMapping;
-			renderer.toneMappingExposure = toneMappingExposure;
-			renderer.gammaInput = gammaInput;
-			renderer.gammaOutput = gammaOutput;
-
-			for ( var i = 0; i < this.objects.length; i ++ ) {
-
-				scene.remove( this.objects[ i ] );
-
-			}
-
-		},
-
-		dispose: function () {
-
-			for ( var i = 0, l = this.objects.length; i < l; i ++ ) {
-
-				this.objects[ i ].material.dispose();
-
-			}
-
-			this.objects[ 0 ].geometry.dispose();
-
-		}
-
-	};
-
-	function getShader() {
-
-		var shaderMaterial = new ShaderMaterial( {
-
-			uniforms: {
-				"faceIndex": { value: 0 },
-				"mapSize": { value: 0 },
-				"envMap": { value: null },
-				"testColor": { value: new 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 );\
-        }",
-
-			blending: NoBlending
-
-		} );
-
-		shaderMaterial.type = 'PMREMCubeUVPacker';
-
-		return shaderMaterial;
-
-	}
-
-
-	return PMREMCubeUVPacker;
-
-} )();
-
-export { PMREMCubeUVPacker };

+ 8 - 9
examples/jsm/pmrem/PMREMGenerator.d.ts

@@ -1,18 +1,17 @@
 import {
-	Renderer,
-	RenderTarget,
+	WebGLRenderer,
+	WebGLRenderTarget,
 	Texture,
-	CubeTexture
+	CubeTexture,
+	Scene
 } from '../../../src/Three';
 
 export class PMREMGenerator {
 
-	cubeLods:CubeTexture[];
-
-	constructor( sourceTexture:Texture, samplesPerLevel?:number, resolution?:number );
-	update( renderer:Renderer ): void;
-	renderToCubeMapTarget( renderer:Renderer, renderTarget:any ): void;
-	renderToCubeMapTargetFace( renderer:Renderer, renderTarget:RenderTarget, faceIndex:number ): void;
+	constructor( renderer:WebGLRenderer );
+	fromScene( scene:Scene, sigma?:number, near?:number, far?:number ): WebGLRenderTarget;
+	fromEquirectangular( equirectangular:Texture ): WebGLRenderTarget;
+	fromCubemap( cubemap:CubeTexture ): WebGLRenderTarget;
 	dispose(): void;
 
 }

+ 712 - 229
examples/jsm/pmrem/PMREMGenerator.js

@@ -1,310 +1,793 @@
 /**
- * @author Prashant Sharma / spidersharma03
- * @author Ben Houston / bhouston, https://clara.io
+ * @author Emmett Lalish / elalish
  *
- * To avoid cube map seams, I create an extra pixel around each face. This way when the cube map is
- * sampled by an application later(with a little care by sampling the centre of the texel), the extra 1 border
- *	of pixels makes sure that there is no seams artifacts present. This works perfectly for cubeUV format as
- *	well where the 6 faces can be arranged in any manner whatsoever.
- * Code in the beginning of fragment shader's main function does this job for a given resolution.
- *	Run Scene_PMREM_Test.html in the examples directory to see the sampling from the cube lods generated
- *	by this class.
+ * This class generates a Prefiltered, Mipmapped Radiance Environment Map
+ * (PMREM) from a cubeMap environment texture. This allows different levels of
+ * blur to be quickly accessed based on material roughness. It is packed into a
+ * special CubeUV format that allows us to perform custom interpolation so that
+ * we can support nonlinear formats such as RGBE. Unlike a traditional mipmap
+ * chain, it only goes down to the LOD_MIN level (above), and then creates extra
+ * even more filtered 'mips' at the same LOD_MIN resolution, associated with
+ * higher roughness levels. In this way we maintain resolution to smoothly
+ * interpolate diffuse lighting while limiting sampling computation.
  */
 
 import {
-	DoubleSide,
+	BufferAttribute,
+	BufferGeometry,
+	CubeUVReflectionMapping,
 	GammaEncoding,
 	LinearEncoding,
-	LinearFilter,
 	LinearToneMapping,
 	Mesh,
 	NearestFilter,
 	NoBlending,
 	OrthographicCamera,
-	PlaneBufferGeometry,
+	PerspectiveCamera,
+	RGBDEncoding,
+	RGBEEncoding,
+	RGBEFormat,
+	RGBM16Encoding,
+	RGBM7Encoding,
+	RawShaderMaterial,
 	Scene,
-	ShaderMaterial,
-	WebGLRenderTargetCube,
+	UnsignedByteType,
+	Vector2,
+	Vector3,
+	WebGLRenderTarget,
 	sRGBEncoding
 } from "../../../build/three.module.js";
 
 var PMREMGenerator = ( function () {
 
-	var shader = getShader();
-	var camera = new OrthographicCamera( - 1, 1, 1, - 1, 0.0, 1000 );
-	var scene = new Scene();
-	var planeMesh = new Mesh( new PlaneBufferGeometry( 2, 2, 0 ), shader );
-	planeMesh.material.side = DoubleSide;
-	scene.add( planeMesh );
-	scene.add( camera );
+	var LOD_MIN = 4;
+	var LOD_MAX = 8;
+	var SIZE_MAX = Math.pow( 2, LOD_MAX );
+	// The standard deviations (radians) associated with the extra mips. These are
+	// chosen to approximate a Trowbridge-Reitz distribution function times the
+	// geometric shadowing function. These sigma values squared must match the
+	// variance #defines in cube_uv_reflection_fragment.glsl.js.
+	var EXTRA_LOD_SIGMA = [ 0.125, 0.215, 0.35, 0.446, 0.526, 0.582 ];
+	var TOTAL_LODS = LOD_MAX - LOD_MIN + 1 + EXTRA_LOD_SIGMA.length;
+	// The maximum length of the blur for loop. Smaller sigmas will use fewer
+	// samples and exit early, but not recompile the shader.
+	var MAX_SAMPLES = 20;
+	var ENCODINGS = {
+		[ LinearEncoding ]: 0,
+		[ sRGBEncoding ]: 1,
+		[ RGBEEncoding ]: 2,
+		[ RGBM7Encoding ]: 3,
+		[ RGBM16Encoding ]: 4,
+		[ RGBDEncoding ]: 5,
+		[ GammaEncoding ]: 6
+	  };
+
+	var _flatCamera = new OrthographicCamera();
+	var _blurMaterial = _getBlurShader( MAX_SAMPLES );
+	var _equirectShader = null;
+	var _cubemapShader = null;
+
+	var { _lodPlanes, _sizeLods, _sigmas } = _createPlanes();
+	var _pingPongRenderTarget = null;
+	var _renderer = null;
+
+	// Golden Ratio
+	var PHI = ( 1 + Math.sqrt( 5 ) ) / 2;
+	var INV_PHI = 1 / PHI;
+	// Vertices of a dodecahedron (except the opposites, which represent the
+	// same axis), used as axis directions evenly spread on a sphere.
+	var _axisDirections = [
+		new Vector3( 1, 1, 1 ),
+		new Vector3( - 1, 1, 1 ),
+		new Vector3( 1, 1, - 1 ),
+		new Vector3( - 1, 1, - 1 ),
+		new Vector3( 0, PHI, INV_PHI ),
+		new Vector3( 0, PHI, - INV_PHI ),
+		new Vector3( INV_PHI, 0, PHI ),
+		new Vector3( - INV_PHI, 0, PHI ),
+		new Vector3( PHI, INV_PHI, 0 ),
+		new Vector3( - PHI, INV_PHI, 0 ) ];
+
+	var PMREMGenerator = function ( renderer ) {
+
+		_renderer = renderer;
 
-	var PMREMGenerator = function ( sourceTexture, samplesPerLevel, resolution ) {
+	};
+
+	PMREMGenerator.prototype = {
+
+		constructor: PMREMGenerator,
+
+		/**
+		 * Generates a PMREM from a supplied Scene, which can be faster than using an
+		 * image if networking bandwidth is low. Optional sigma specifies a blur radius
+		 * in radians to be applied to the scene before PMREM generation. Optional near
+		 * and far planes ensure the scene is rendered in its entirety (the cubeCamera
+		 * is placed at the origin).
+		 */
+		fromScene: function ( scene, sigma = 0, near = 0.1, far = 100 ) {
+
+			var cubeUVRenderTarget = _allocateTargets();
+			_sceneToCubeUV( scene, near, far, cubeUVRenderTarget );
+			if ( sigma > 0 ) {
+
+				_blur( cubeUVRenderTarget, 0, 0, sigma );
+
+			}
+			_applyPMREM( cubeUVRenderTarget );
+			_cleanup();
+
+			return cubeUVRenderTarget;
+
+		},
+
+		/**
+		 * Generates a PMREM from an equirectangular texture, which can be either LDR
+		 * (RGBFormat) or HDR (RGBEFormat). The ideal input image size is 1k (1024 x 512),
+		 * as this matches best with the 256 x 256 cubemap output.
+		 */
+		fromEquirectangular: function ( equirectangular ) {
+
+			equirectangular.magFilter = NearestFilter;
+			equirectangular.minFilter = NearestFilter;
+			equirectangular.generateMipmaps = false;
+
+			return this.fromCubemap( equirectangular );
+
+		},
+
+		/**
+		 * Generates a PMREM from an cubemap texture, which can be either LDR
+		 * (RGBFormat) or HDR (RGBEFormat). The ideal input cube size is 256 x 256,
+		 * as this matches best with the 256 x 256 cubemap output.
+		 */
+		fromCubemap: function ( cubemap ) {
+
+			var cubeUVRenderTarget = _allocateTargets( cubemap );
+			_textureToCubeUV( cubemap, cubeUVRenderTarget );
+			_applyPMREM( cubeUVRenderTarget );
+			_cleanup();
+
+			return cubeUVRenderTarget;
+
+		},
+
+		/**
+		 * Disposes of the PMREMGenerator's internal memory. Note that PMREMGenerator is a static class,
+		 * so you should not need more than one PMREMGenerator object. If you do, calling dispose() on
+		 * one of them will cause any others to also become unusable.
+		 */
+		dispose: function () {
+
+			_blurMaterial.dispose();
+			if ( _cubemapShader != null ) _cubemapShader.dispose();
+			if ( _equirectShader != null ) _equirectShader.dispose();
+			var plane;
+			for ( plane of _lodPlanes ) {
+
+				plane.dispose();
+
+			}
+
+		},
+
+	};
+
+	function _createPlanes() {
+
+		var _lodPlanes = [];
+		var _sizeLods = [];
+		var _sigmas = [];
 
-		this.sourceTexture = sourceTexture;
-		this.resolution = ( resolution !== undefined ) ? resolution : 256; // NODE: 256 is currently hard coded in the glsl code for performance reasons
-		this.samplesPerLevel = ( samplesPerLevel !== undefined ) ? samplesPerLevel : 32;
+		var lod = LOD_MAX;
+		for ( var i = 0; i < TOTAL_LODS; i ++ ) {
 
-		var monotonicEncoding = ( this.sourceTexture.encoding === LinearEncoding ) ||
-			( this.sourceTexture.encoding === GammaEncoding ) || ( this.sourceTexture.encoding === sRGBEncoding );
+			var sizeLod = Math.pow( 2, lod );
+			_sizeLods.push( sizeLod );
+			var sigma = 1.0 / sizeLod;
+			if ( i > LOD_MAX - LOD_MIN ) {
 
-		this.sourceTexture.minFilter = ( monotonicEncoding ) ? LinearFilter : NearestFilter;
-		this.sourceTexture.magFilter = ( monotonicEncoding ) ? LinearFilter : NearestFilter;
-		this.sourceTexture.generateMipmaps = this.sourceTexture.generateMipmaps && monotonicEncoding;
+				sigma = EXTRA_LOD_SIGMA[ i - LOD_MAX + LOD_MIN - 1 ];
 
-		this.cubeLods = [];
+			} else if ( i == 0 ) {
+
+				sigma = 0;
+
+			}
+			_sigmas.push( sigma );
+
+			var texelSize = 1.0 / ( sizeLod - 1 );
+			var min = - texelSize / 2;
+			var max = 1 + texelSize / 2;
+			var uv1 = [ min, min, max, min, max, max, min, min, max, max, min, max ];
+
+			var cubeFaces = 6;
+			var vertices = 6;
+			var positionSize = 3;
+			var uvSize = 2;
+			var faceIndexSize = 1;
+
+			var position = new Float32Array( positionSize * vertices * cubeFaces );
+			var uv = new Float32Array( uvSize * vertices * cubeFaces );
+			var faceIndex = new Float32Array( faceIndexSize * vertices * cubeFaces );
+
+			for ( var face = 0; face < cubeFaces; face ++ ) {
+
+				var x = ( face % 3 ) * 2 / 3 - 1;
+				var y = face > 2 ? 0 : - 1;
+				var coordinates = [
+					[ x, y, 0 ],
+					[ x + 2 / 3, y, 0 ],
+					[ x + 2 / 3, y + 1, 0 ],
+					[ x, y, 0 ],
+					[ x + 2 / 3, y + 1, 0 ],
+					[ x, y + 1, 0 ]
+				];
+				position.set( [].concat( ...coordinates ),
+					positionSize * vertices * face );
+				uv.set( uv1, uvSize * vertices * face );
+				var fill = [ face, face, face, face, face, face ];
+				faceIndex.set( fill, faceIndexSize * vertices * face );
+
+			}
+			var planes = new BufferGeometry();
+			planes.setAttribute(
+				'position', new BufferAttribute( position, positionSize ) );
+			planes.setAttribute( 'uv', new BufferAttribute( uv, uvSize ) );
+			planes.setAttribute(
+				'faceIndex', new BufferAttribute( faceIndex, faceIndexSize ) );
+			_lodPlanes.push( planes );
+
+			if ( lod > LOD_MIN ) {
+
+				lod --;
+
+			}
+
+		}
+		return { _lodPlanes, _sizeLods, _sigmas };
+
+	}
+
+	function _allocateTargets( equirectangular ) {
 
-		var size = this.resolution;
 		var params = {
-			format: this.sourceTexture.format,
-			magFilter: this.sourceTexture.magFilter,
-			minFilter: this.sourceTexture.minFilter,
-			type: this.sourceTexture.type,
-			generateMipmaps: this.sourceTexture.generateMipmaps,
-			anisotropy: this.sourceTexture.anisotropy,
-			encoding: this.sourceTexture.encoding
+		  magFilter: NearestFilter,
+		  minFilter: NearestFilter,
+		  generateMipmaps: false,
+		  type: equirectangular ? equirectangular.type : UnsignedByteType,
+		  format: equirectangular ? equirectangular.format : RGBEFormat,
+		  encoding: equirectangular ? equirectangular.encoding : RGBEEncoding,
+		  depthBuffer: false,
+		  stencilBuffer: false
 		};
+		var cubeUVRenderTarget = _createRenderTarget(
+			{ ...params, depthBuffer: ( equirectangular ? false : true ) } );
+		_pingPongRenderTarget = _createRenderTarget( params );
+		return cubeUVRenderTarget;
+
+	}
+
+	function _cleanup() {
 
-		// how many LODs fit in the given CubeUV Texture.
-		this.numLods = Math.log( size ) / Math.log( 2 ) - 2; // IE11 doesn't support Math.log2
+		_pingPongRenderTarget.dispose();
+		_renderer.setRenderTarget( null );
+		var size = _renderer.getSize( new Vector2() );
+		_renderer.setViewport( 0, 0, size.x, size.y );
 
-		for ( var i = 0; i < this.numLods; i ++ ) {
+	}
 
-			var renderTarget = new WebGLRenderTargetCube( size, size, params );
-			renderTarget.texture.name = "PMREMGenerator.cube" + i;
-			this.cubeLods.push( renderTarget );
-			size = Math.max( 16, size / 2 );
+	function _sceneToCubeUV( scene, near, far, cubeUVRenderTarget ) {
+
+		var fov = 90;
+		var aspect = 1;
+		var cubeCamera = new PerspectiveCamera( fov, aspect, near, far );
+		var upSign = [ 1, 1, 1, 1, - 1, 1 ];
+		var forwardSign = [ 1, 1, - 1, - 1, - 1, 1 ];
+
+		var gammaOutput = _renderer.gammaOutput;
+		var toneMapping = _renderer.toneMapping;
+		var toneMappingExposure = _renderer.toneMappingExposure;
+		var clearColor = _renderer.getClearColor();
+		var clearAlpha = _renderer.getClearAlpha();
+
+		_renderer.toneMapping = LinearToneMapping;
+		_renderer.toneMappingExposure = 1.0;
+		_renderer.gammaOutput = false;
+		scene.scale.z *= - 1;
+
+		var background = scene.background;
+		if ( background && background.isColor ) {
+
+			background.convertSRGBToLinear();
+			// Convert linear to RGBE
+			var maxComponent = Math.max( background.r, background.g, background.b );
+			var fExp = Math.min( Math.max( Math.ceil( Math.log2( maxComponent ) ), - 128.0 ), 127.0 );
+			background = background.multiplyScalar( Math.pow( 2.0, - fExp ) );
+			var alpha = ( fExp + 128.0 ) / 255.0;
+			_renderer.setClearColor( background, alpha );
+			scene.background = null;
 
 		}
 
-	};
+		_renderer.setRenderTarget( cubeUVRenderTarget );
+		for ( var i = 0; i < 6; i ++ ) {
 
-	PMREMGenerator.prototype = {
+			var col = i % 3;
+			if ( col == 0 ) {
 
-		constructor: PMREMGenerator,
+				cubeCamera.up.set( 0, upSign[ i ], 0 );
+				cubeCamera.lookAt( forwardSign[ i ], 0, 0 );
 
-		/*
-		 * Prashant Sharma / spidersharma03: More thought and work is needed here.
-		 * Right now it's a kind of a hack to use the previously convolved map to convolve the current one.
-		 * I tried to use the original map to convolve all the lods, but for many textures(specially the high frequency)
-		 * even a high number of samples(1024) dosen't lead to satisfactory results.
-		 * By using the previous convolved maps, a lower number of samples are generally sufficient(right now 32, which
-		 * gives okay results unless we see the reflection very carefully, or zoom in too much), however the math
-		 * goes wrong as the distribution function tries to sample a larger area than what it should be. So I simply scaled
-		 * the roughness by 0.9(totally empirical) to try to visually match the original result.
-		 * The condition "if(i <5)" is also an attemt to make the result match the original result.
-		 * This method requires the most amount of thinking I guess. Here is a paper which we could try to implement in future::
-		 * https://developer.nvidia.com/gpugems/GPUGems3/gpugems3_ch20.html
-		 */
-		update: function ( renderer ) {
+			} else if ( col == 1 ) {
+
+				cubeCamera.up.set( 0, 0, upSign[ i ] );
+				cubeCamera.lookAt( 0, forwardSign[ i ], 0 );
 
-			// Texture should only be flipped for CubeTexture, not for
-			// a Texture created via WebGLRenderTargetCube.
-			var tFlip = ( this.sourceTexture.isCubeTexture ) ? - 1 : 1;
+			} else {
 
-			shader.defines[ 'SAMPLES_PER_LEVEL' ] = this.samplesPerLevel;
-			shader.uniforms[ 'faceIndex' ].value = 0;
-			shader.uniforms[ 'envMap' ].value = this.sourceTexture;
-			shader.envMap = this.sourceTexture;
-			shader.needsUpdate = true;
+				cubeCamera.up.set( 0, upSign[ i ], 0 );
+				cubeCamera.lookAt( 0, 0, forwardSign[ i ] );
+
+			}
+			_setViewport(
+				col * SIZE_MAX, i > 2 ? SIZE_MAX : 0, SIZE_MAX, SIZE_MAX );
+			_renderer.render( scene, cubeCamera );
 
-			var gammaInput = renderer.gammaInput;
-			var gammaOutput = renderer.gammaOutput;
-			var toneMapping = renderer.toneMapping;
-			var toneMappingExposure = renderer.toneMappingExposure;
-			var currentRenderTarget = renderer.getRenderTarget();
+		}
 
-			renderer.toneMapping = LinearToneMapping;
-			renderer.toneMappingExposure = 1.0;
-			renderer.gammaInput = false;
-			renderer.gammaOutput = false;
+		_renderer.toneMapping = toneMapping;
+		_renderer.toneMappingExposure = toneMappingExposure;
+		_renderer.gammaOutput = gammaOutput;
+		_renderer.setClearColor( clearColor, clearAlpha );
+		scene.scale.z *= - 1;
 
-			for ( var i = 0; i < this.numLods; i ++ ) {
+	}
 
-				var r = i / ( this.numLods - 1 );
-				shader.uniforms[ 'roughness' ].value = r * 0.9; // see comment above, pragmatic choice
-				// Only apply the tFlip for the first LOD
-				shader.uniforms[ 'tFlip' ].value = ( i == 0 ) ? tFlip : 1;
-				var size = this.cubeLods[ i ].width;
-				shader.uniforms[ 'mapSize' ].value = size;
-				this.renderToCubeMapTarget( renderer, this.cubeLods[ i ] );
+	function _textureToCubeUV( texture, cubeUVRenderTarget ) {
 
-				if ( i < 5 ) shader.uniforms[ 'envMap' ].value = this.cubeLods[ i ].texture;
+		var scene = new Scene();
+		if ( texture.isCubeTexture ) {
 
-			}
+			if ( _cubemapShader == null ) {
 
-			renderer.setRenderTarget( currentRenderTarget );
-			renderer.toneMapping = toneMapping;
-			renderer.toneMappingExposure = toneMappingExposure;
-			renderer.gammaInput = gammaInput;
-			renderer.gammaOutput = gammaOutput;
+				_cubemapShader = _getCubemapShader();
 
-		},
+			}
 
-		renderToCubeMapTarget: function ( renderer, renderTarget ) {
+		} else {
 
-			for ( var i = 0; i < 6; i ++ ) {
+			if ( _equirectShader == null ) {
 
-				this.renderToCubeMapTargetFace( renderer, renderTarget, i );
+				_equirectShader = _getEquirectShader();
 
 			}
 
-		},
+		}
+		var material = texture.isCubeTexture ? _cubemapShader : _equirectShader;
+		scene.add( new Mesh( _lodPlanes[ 0 ], material ) );
+		var uniforms = material.uniforms;
 
-		renderToCubeMapTargetFace: function ( renderer, renderTarget, faceIndex ) {
+		uniforms[ 'envMap' ].value = texture;
+		if ( ! texture.isCubeTexture ) {
 
-			shader.uniforms[ 'faceIndex' ].value = faceIndex;
-			renderer.setRenderTarget( renderTarget, faceIndex );
-			renderer.clear();
-			renderer.render( scene, camera );
+			uniforms[ 'texelSize' ].value.set( 1.0 / texture.image.width, 1.0 / texture.image.height );
 
-		},
+		}
+		uniforms[ 'inputEncoding' ].value = ENCODINGS[ texture.encoding ];
+		uniforms[ 'outputEncoding' ].value = ENCODINGS[ texture.encoding ];
 
-		dispose: function () {
+		_renderer.setRenderTarget( cubeUVRenderTarget );
+		_setViewport( 0, 0, 3 * SIZE_MAX, 2 * SIZE_MAX );
+		_renderer.render( scene, _flatCamera );
+
+	}
+
+	function _createRenderTarget( params ) {
+
+		var cubeUVRenderTarget =
+		new WebGLRenderTarget( 3 * SIZE_MAX, 3 * SIZE_MAX, params );
+		cubeUVRenderTarget.texture.mapping = CubeUVReflectionMapping;
+		cubeUVRenderTarget.texture.name = 'PMREM.cubeUv';
+		return cubeUVRenderTarget;
 
-			for ( var i = 0, l = this.cubeLods.length; i < l; i ++ ) {
+	}
+
+	function _setViewport( x, y, width, height ) {
+
+		var dpr = _renderer.getPixelRatio();
+		_renderer.setViewport( x / dpr, y / dpr, width / dpr, height / dpr );
+
+	}
+
+	function _applyPMREM( cubeUVRenderTarget ) {
+
+		var autoClear = _renderer.autoClear;
+		_renderer.autoClear = false;
+
+	  	for ( var i = 1; i < TOTAL_LODS; i ++ ) {
+
+			var sigma = Math.sqrt(
+				_sigmas[ i ] * _sigmas[ i ] -
+			_sigmas[ i - 1 ] * _sigmas[ i - 1 ] );
+			var poleAxis =
+			_axisDirections[ ( i - 1 ) % _axisDirections.length ];
+			_blur( cubeUVRenderTarget, i - 1, i, sigma, poleAxis );
+
+		}
+
+		_renderer.autoClear = autoClear;
+
+	}
+
+	/**
+   * This is a two-pass Gaussian blur for a cubemap. Normally this is done
+   * vertically and horizontally, but this breaks down on a cube. Here we apply
+   * the blur latitudinally (around the poles), and then longitudinally (towards
+   * the poles) to approximate the orthogonally-separable blur. It is least
+   * accurate at the poles, but still does a decent job.
+   */
+	function _blur( cubeUVRenderTarget, lodIn, lodOut, sigma, poleAxis ) {
+
+		_halfBlur(
+			cubeUVRenderTarget,
+			_pingPongRenderTarget,
+			lodIn,
+			lodOut,
+			sigma,
+			'latitudinal',
+			poleAxis );
+
+		_halfBlur(
+			_pingPongRenderTarget,
+			cubeUVRenderTarget,
+			lodOut,
+			lodOut,
+			sigma,
+			'longitudinal',
+			poleAxis );
 
-				this.cubeLods[ i ].dispose();
+	}
+
+	function _halfBlur( targetIn, targetOut, lodIn, lodOut, sigmaRadians, direction, poleAxis ) {
+
+		if ( direction !== 'latitudinal' && direction !== 'longitudinal' ) {
+
+			console.error(
+				'blur direction must be either latitudinal or longitudinal!' );
+
+		}
+
+		// Number of standard deviations at which to cut off the discrete approximation.
+		var STANDARD_DEVIATIONS = 3;
+
+		var blurScene = new Scene();
+		blurScene.add( new Mesh( _lodPlanes[ lodOut ], _blurMaterial ) );
+		var blurUniforms = _blurMaterial.uniforms;
+
+		var pixels = _sizeLods[ lodIn ] - 1;
+		var radiansPerPixel = isFinite( sigmaRadians ) ? Math.PI / ( 2 * pixels ) : 2 * Math.PI / ( 2 * MAX_SAMPLES - 1 );
+		var sigmaPixels = sigmaRadians / radiansPerPixel;
+		var samples = isFinite( sigmaRadians ) ? 1 + Math.floor( STANDARD_DEVIATIONS * sigmaPixels ) : MAX_SAMPLES;
+
+		if ( samples > MAX_SAMPLES ) {
+
+			console.warn( `sigmaRadians, ${
+				sigmaRadians}, is too large and will clip, as it requested ${
+				samples} samples when the maximum is set to ${MAX_SAMPLES}` );
+
+		}
+
+		var weights = [];
+		var sum = 0;
+		for ( var i = 0; i < MAX_SAMPLES; ++ i ) {
+
+			var x = i / sigmaPixels;
+			var weight = Math.exp( - x * x / 2 );
+			weights.push( weight );
+			if ( i == 0 ) {
+
+	  			 sum += weight;
+
+			} else if ( i < samples ) {
+
+	  			sum += 2 * weight;
 
 			}
 
-			shader.dispose();
+		}
+		weights = weights.map( w => w / sum );
 
-		},
+		blurUniforms[ 'envMap' ].value = targetIn.texture;
+		blurUniforms[ 'samples' ].value = samples;
+		blurUniforms[ 'weights' ].value = weights;
+		blurUniforms[ 'latitudinal' ].value = direction === 'latitudinal';
+		if ( poleAxis ) {
 
-	};
+			blurUniforms[ 'poleAxis' ].value = poleAxis;
+
+		}
+		blurUniforms[ 'dTheta' ].value = radiansPerPixel;
+		blurUniforms[ 'mipInt' ].value = LOD_MAX - lodIn;
+		blurUniforms[ 'inputEncoding' ].value = ENCODINGS[ targetIn.texture.encoding ];
+		blurUniforms[ 'outputEncoding' ].value = ENCODINGS[ targetIn.texture.encoding ];
+
+		var outputSize = _sizeLods[ lodOut ];
+		var x = 3 * Math.max( 0, SIZE_MAX - 2 * outputSize );
+		var y = ( lodOut === 0 ? 0 : 2 * SIZE_MAX ) +
+	  2 * outputSize *
+		  ( lodOut > LOD_MAX - LOD_MIN ? lodOut - LOD_MAX + LOD_MIN : 0 );
 
-	function getShader() {
+		_renderer.setRenderTarget( targetOut );
+		_setViewport( x, y, 3 * outputSize, 2 * outputSize );
+		_renderer.render( blurScene, _flatCamera );
 
-		var shaderMaterial = new ShaderMaterial( {
+	}
+
+	function _getBlurShader( maxSamples ) {
+
+		var weights = new Float32Array( maxSamples );
+		var poleAxis = new Vector3( 0, 1, 0 );
+		var shaderMaterial = new RawShaderMaterial( {
 
-			defines: {
-				"SAMPLES_PER_LEVEL": 20,
+			defines: { 'n': maxSamples },
+
+			uniforms: {
+				'envMap': { value: null },
+				'samples': { value: 1 },
+				'weights': { value: weights },
+				'latitudinal': { value: false },
+				'dTheta': { value: 0 },
+				'mipInt': { value: 0 },
+				'poleAxis': { value: poleAxis },
+				'inputEncoding': { value: ENCODINGS[ LinearEncoding ] },
+				'outputEncoding': { value: ENCODINGS[ LinearEncoding ] }
 			},
 
+			vertexShader: _getCommonVertexShader(),
+
+			fragmentShader: `
+precision mediump float;
+precision mediump int;
+varying vec3 vOutputDirection;
+uniform sampler2D envMap;
+uniform int samples;
+uniform float weights[n];
+uniform bool latitudinal;
+uniform float dTheta;
+uniform float mipInt;
+uniform vec3 poleAxis;
+
+${_getEncodings()}
+
+#define ENVMAP_TYPE_CUBE_UV
+#include <cube_uv_reflection_fragment>
+
+void main() {
+	gl_FragColor = vec4(0.0);
+    for (int i = 0; i < n; i++) {
+      if (i >= samples)
+        break;
+      for (int dir = -1; dir < 2; dir += 2) {
+        if (i == 0 && dir == 1)
+          continue;
+        vec3 axis = latitudinal ? poleAxis : cross(poleAxis, vOutputDirection);
+        if (all(equal(axis, vec3(0.0))))
+          axis = cross(vec3(0.0, 1.0, 0.0), vOutputDirection);
+        axis = normalize(axis);
+        float theta = dTheta * float(dir * i);
+        float cosTheta = cos(theta);
+        // Rodrigues' axis-angle rotation
+        vec3 sampleDirection = vOutputDirection * cosTheta 
+            + cross(axis, vOutputDirection) * sin(theta) 
+            + axis * dot(axis, vOutputDirection) * (1.0 - cosTheta);
+        gl_FragColor.rgb +=
+            weights[i] * bilinearCubeUV(envMap, sampleDirection, mipInt);
+      }
+    }
+  	gl_FragColor = linearToOutputTexel(gl_FragColor);
+}
+     		`,
+
+			blending: NoBlending,
+			depthTest: false,
+	   		depthWrite: false
+
+		} );
+
+		shaderMaterial.type = 'SphericalGaussianBlur';
+
+		return shaderMaterial;
+
+	}
+
+	function _getEquirectShader() {
+
+		var texelSize = new Vector2( 1, 1 );
+		var shaderMaterial = new RawShaderMaterial( {
+
 			uniforms: {
-				"faceIndex": { value: 0 },
-				"roughness": { value: 0.5 },
-				"mapSize": { value: 0.5 },
-				"envMap": { value: null },
-				"tFlip": { value: - 1 },
+				'envMap': { value: null },
+				'texelSize': { value: texelSize },
+				'inputEncoding': { value: ENCODINGS[ LinearEncoding ] },
+				'outputEncoding': { value: ENCODINGS[ LinearEncoding ] }
 			},
 
-			vertexShader:
-				"varying vec2 vUv;\n\
-				void main() {\n\
-					vUv = uv;\n\
-					gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n\
-				}",
-
-			fragmentShader:
-				"#include <common>\n\
-				varying vec2 vUv;\n\
-				uniform int faceIndex;\n\
-				uniform float roughness;\n\
-				uniform samplerCube envMap;\n\
-				uniform float mapSize;\n\
-				uniform float tFlip;\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\
-				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\
-					} 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\
-					vec3 correctedDirection = vec3( tFlip * sampleDirection.x, sampleDirection.yz );\n\
-					mat3 vecSpace = matrixFromVector( normalize( correctedDirection ) );\n\
-					vec3 rgbColor = vec3(0.0);\n\
-					const int NumSamples = SAMPLES_PER_LEVEL;\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 r = rand(vec2(sini, cosi));\n\
-						vect = ImportanceSampleGGX(vec2(float(i) / float(NumSamples), r), 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: NoBlending
+			vertexShader: _getCommonVertexShader(),
+
+			fragmentShader: `
+precision mediump float;
+precision mediump int;
+varying vec3 vOutputDirection;
+uniform sampler2D envMap;
+uniform vec2 texelSize;
+
+${_getEncodings()}
+
+#define RECIPROCAL_PI 0.31830988618
+#define RECIPROCAL_PI2 0.15915494
+
+void main() {
+	gl_FragColor = vec4(0.0);
+	vec3 outputDirection = normalize(vOutputDirection);
+	vec2 uv;
+	uv.y = asin(clamp(outputDirection.y, -1.0, 1.0)) * RECIPROCAL_PI + 0.5;
+	uv.x = atan(outputDirection.z, outputDirection.x) * RECIPROCAL_PI2 + 0.5;
+	vec2 f = fract(uv / texelSize - 0.5);
+	uv -= f * texelSize;
+	vec3 tl = envMapTexelToLinear(texture2D(envMap, uv)).rgb;
+	uv.x += texelSize.x;
+	vec3 tr = envMapTexelToLinear(texture2D(envMap, uv)).rgb;
+	uv.y += texelSize.y;
+	vec3 br = envMapTexelToLinear(texture2D(envMap, uv)).rgb;
+	uv.x -= texelSize.x;
+	vec3 bl = envMapTexelToLinear(texture2D(envMap, uv)).rgb;
+	vec3 tm = mix(tl, tr, f.x);
+	vec3 bm = mix(bl, br, f.x);
+	gl_FragColor.rgb = mix(tm, bm, f.y);
+  	gl_FragColor = linearToOutputTexel(gl_FragColor);
+}
+     		`,
+
+			blending: NoBlending,
+			depthTest: false,
+	   		depthWrite: false
 
 		} );
 
-		shaderMaterial.type = 'PMREMGenerator';
+		shaderMaterial.type = 'EquirectangularToCubeUV';
 
 		return shaderMaterial;
 
 	}
 
+	function _getCubemapShader() {
+
+		var shaderMaterial = new RawShaderMaterial( {
+
+			uniforms: {
+				'envMap': { value: null },
+				'inputEncoding': { value: ENCODINGS[ LinearEncoding ] },
+				'outputEncoding': { value: ENCODINGS[ LinearEncoding ] }
+			},
+
+			vertexShader: _getCommonVertexShader(),
+
+			fragmentShader: `
+precision mediump float;
+precision mediump int;
+varying vec3 vOutputDirection;
+uniform samplerCube envMap;
+
+${_getEncodings()}
+
+void main() {
+	gl_FragColor = vec4(0.0);
+	gl_FragColor.rgb = envMapTexelToLinear(textureCube(envMap, vec3( - vOutputDirection.x, vOutputDirection.yz ))).rgb;
+  	gl_FragColor = linearToOutputTexel(gl_FragColor);
+}
+     		`,
+
+			blending: NoBlending,
+			depthTest: false,
+	   		depthWrite: false
+
+		} );
+
+		shaderMaterial.type = 'CubemapToCubeUV';
+
+		return shaderMaterial;
+
+	}
+
+	function _getCommonVertexShader() {
+
+		return `
+precision mediump float;
+precision mediump int;
+attribute vec3 position;
+attribute vec2 uv;
+attribute float faceIndex;
+varying vec3 vOutputDirection;
+vec3 getDirection(vec2 uv, float face) {
+	uv = 2.0 * uv - 1.0;
+	vec3 direction = vec3(uv, 1.0);
+	if (face == 0.0) {
+		direction = direction.zyx;
+		direction.z *= -1.0;
+	} else if (face == 1.0) {
+		direction = direction.xzy;
+		direction.z *= -1.0;
+	} else if (face == 3.0) {
+		direction = direction.zyx;
+		direction.x *= -1.0;
+	} else if (face == 4.0) {
+		direction = direction.xzy;
+		direction.y *= -1.0;
+	} else if (face == 5.0) {
+		direction.xz *= -1.0;
+	}
+	return direction;
+}
+void main() {
+	vOutputDirection = getDirection(uv, faceIndex);
+	gl_Position = vec4( position, 1.0 );
+}
+		`;
+
+	}
+
+	function _getEncodings() {
+
+		return `
+uniform int inputEncoding;
+uniform int outputEncoding;
+
+#include <encodings_pars_fragment>
+
+vec4 inputTexelToLinear(vec4 value){
+    if(inputEncoding == 0){
+        return value;
+    }else if(inputEncoding == 1){
+        return sRGBToLinear(value);
+    }else if(inputEncoding == 2){
+        return RGBEToLinear(value);
+    }else if(inputEncoding == 3){
+        return RGBMToLinear(value, 7.0);
+    }else if(inputEncoding == 4){
+        return RGBMToLinear(value, 16.0);
+    }else if(inputEncoding == 5){
+        return RGBDToLinear(value, 256.0);
+    }else{
+        return GammaToLinear(value, 2.2);
+    }
+}
+
+vec4 linearToOutputTexel(vec4 value){
+    if(outputEncoding == 0){
+        return value;
+    }else if(outputEncoding == 1){
+        return LinearTosRGB(value);
+    }else if(outputEncoding == 2){
+        return LinearToRGBE(value);
+    }else if(outputEncoding == 3){
+        return LinearToRGBM(value, 7.0);
+    }else if(outputEncoding == 4){
+        return LinearToRGBM(value, 16.0);
+    }else if(outputEncoding == 5){
+        return LinearToRGBD(value, 256.0);
+    }else{
+        return LinearToGamma(value, 2.2);
+    }
+}
+
+vec4 envMapTexelToLinear(vec4 color) {
+  return inputTexelToLinear(color);
+}
+		`;
+
+	}
+
 	return PMREMGenerator;
 
 } )();

BIN
examples/textures/equirectangular/pedestrian_overpass_1k.hdr


BIN
examples/textures/equirectangular/pedestrian_overpass_2k.hdr


BIN
examples/textures/equirectangular/venice_sunset_1k.hdr


BIN
examples/textures/equirectangular/venice_sunset_2k.hdr


+ 13 - 31
examples/webgl_furnace_test.html

@@ -29,7 +29,6 @@
 			import * as THREE from '../build/three.module.js';
 
 			import { PMREMGenerator } from './jsm/pmrem/PMREMGenerator.js';
-			import { PMREMCubeUVPacker } from './jsm/pmrem/PMREMCubeUVPacker.js';
 
 			var scene, camera, renderer, envMap, radianceMap;
 			var right = 8;
@@ -75,8 +74,8 @@
 
 				// camera
 
-				camera = new THREE.OrthographicCamera( - right, right, right / aspect, - right / aspect, 1, 30 );
-				camera.position.set( 0, 0, 9 );
+				camera = new THREE.PerspectiveCamera( 40, aspect, 1, 30 );
+				camera.position.set( 0, 0, 18 );
 
 			}
 
@@ -110,34 +109,16 @@
 
 			function createEnvironment() {
 
-				var envScene = new THREE.Scene();
-				envScene.background = new THREE.Color( 0xcccccc );
-
-				var cubeCamera = new THREE.CubeCamera( 1, 100, 256, 256 );
-				cubeCamera.update( renderer, envScene );
-
-				envMap = cubeCamera.renderTarget.texture;
-
-				scene.background = envScene.background;
-
-			}
-
-			function getRadiance() {
-
 				return new Promise( function ( resolve ) {
 
-					var pmremGenerator = new PMREMGenerator( envMap );
-					pmremGenerator.update( renderer );
-
-					var pmremCubeUVPacker = new PMREMCubeUVPacker( pmremGenerator.cubeLods );
-					pmremCubeUVPacker.update( renderer );
-
-					var cubeRenderTarget = pmremCubeUVPacker.CubeUVRenderTarget;
+					var envScene = new THREE.Scene();
+					envScene.background = new THREE.Color( 0xcccccc );
 
+					var pmremGenerator = new PMREMGenerator( renderer );
+					radianceMap = pmremGenerator.fromScene( envScene ).texture;
 					pmremGenerator.dispose();
-					pmremCubeUVPacker.dispose();
 
-					radianceMap = cubeRenderTarget.texture;
+					scene.background = radianceMap;
 
 					resolve();
 
@@ -147,11 +128,13 @@
 
 			function onResize() {
 
-				var aspect = window.innerWidth / window.innerHeight;
-				camera.top = right / aspect;
-				camera.bottom = - camera.top;
+				var width = window.innerWidth;
+				var height = window.innerHeight;
+
+				camera.aspect = width / height;
 				camera.updateProjectionMatrix();
-				renderer.setSize( window.innerWidth, window.innerHeight );
+
+				renderer.setSize( width, height );
 
 			}
 
@@ -170,7 +153,6 @@
 			Promise.resolve()
 				.then( init )
 				.then( createEnvironment )
-				.then( getRadiance )
 				.then( createObjects )
 				.then( animate );
 

+ 5 - 18
examples/webgl_loader_gltf.html

@@ -25,7 +25,6 @@
 			import { GLTFLoader } from './jsm/loaders/GLTFLoader.js';
 			import { RGBELoader } from './jsm/loaders/RGBELoader.js';
 			import { PMREMGenerator } from './jsm/pmrem/PMREMGenerator.js';
-			import { PMREMCubeUVPacker } from './jsm/pmrem/PMREMCubeUVPacker.js';
 
 			var container, stats, controls;
 			var camera, scene, renderer;
@@ -46,22 +45,13 @@
 				new RGBELoader()
 					.setDataType( THREE.UnsignedByteType )
 					.setPath( 'textures/equirectangular/' )
-					.load( 'pedestrian_overpass_2k.hdr', function ( texture ) {
+					.load( 'pedestrian_overpass_1k.hdr', function ( texture ) {
 
-						var options = {
-							minFilter: texture.minFilter,
-							magFilter: texture.magFilter
-						};
-
-						scene.background = new THREE.WebGLRenderTargetCube( 1024, 1024, options ).fromEquirectangularTexture( renderer, texture );
-
-						var pmremGenerator = new PMREMGenerator( scene.background.texture );
-						pmremGenerator.update( renderer );
-
-						var pmremCubeUVPacker = new PMREMCubeUVPacker( pmremGenerator.cubeLods );
-						pmremCubeUVPacker.update( renderer );
+						var pmremGenerator = new PMREMGenerator( renderer );
+						var envMap = pmremGenerator.fromEquirectangular( texture ).texture;
+						pmremGenerator.dispose();
 
-						var envMap = pmremCubeUVPacker.CubeUVRenderTarget.texture;
+						scene.background = envMap;
 
 						// model
 
@@ -82,9 +72,6 @@
 
 						} );
 
-						pmremGenerator.dispose();
-						pmremCubeUVPacker.dispose();
-
 					} );
 
 				renderer = new THREE.WebGLRenderer( { antialias: true } );

+ 5 - 18
examples/webgl_loader_gltf_extensions.html

@@ -27,7 +27,6 @@
 			import { DRACOLoader } from './jsm/loaders/DRACOLoader.js';
 			import { RGBELoader } from './jsm/loaders/RGBELoader.js';
 			import { PMREMGenerator } from './jsm/pmrem/PMREMGenerator.js';
-			import { PMREMCubeUVPacker } from './jsm/pmrem/PMREMCubeUVPacker.js';
 
 			var orbitControls;
 			var container, camera, scene, renderer, loader;
@@ -156,25 +155,13 @@
 				new RGBELoader()
 					.setDataType( THREE.UnsignedByteType )
 					.setPath( 'textures/equirectangular/' )
-					.load( 'venice_sunset_2k.hdr', function ( texture ) {
-
-						var options = {
-							minFilter: texture.minFilter,
-							magFilter: texture.magFilter
-						};
-
-						background = new THREE.WebGLRenderTargetCube( 1024, 1024, options ).fromEquirectangularTexture( renderer, texture );
-
-						var pmremGenerator = new PMREMGenerator( background.texture );
-						pmremGenerator.update( renderer );
-
-						var pmremCubeUVPacker = new PMREMCubeUVPacker( pmremGenerator.cubeLods );
-						pmremCubeUVPacker.update( renderer );
-
-						envMap = pmremCubeUVPacker.CubeUVRenderTarget.texture;
+					.load( 'venice_sunset_1k.hdr', function ( texture ) {
 
+						var pmremGenerator = new PMREMGenerator( renderer );
+						envMap = pmremGenerator.fromEquirectangular( texture ).texture;
 						pmremGenerator.dispose();
-						pmremCubeUVPacker.dispose();
+
+						background = envMap;
 
 						//
 

+ 4 - 14
examples/webgl_materials_cars.html

@@ -43,7 +43,6 @@
 			import { RGBELoader } from './jsm/loaders/RGBELoader.js';
 
 			import { PMREMGenerator } from './jsm/pmrem/PMREMGenerator.js';
-			import { PMREMCubeUVPacker } from './jsm/pmrem/PMREMCubeUVPacker.js';
 
 			var camera, scene, renderer;
 			var stats, carModel, materialsLib, envMap;
@@ -92,20 +91,11 @@
 							magFilter: texture.magFilter
 						};
 
-						scene.background = new THREE.WebGLRenderTargetCube( 1024, 1024, options ).fromEquirectangularTexture( renderer, texture );
-
-						envMap = scene.background;
-
-						var pmremGenerator = new PMREMGenerator( scene.background.texture );
-						pmremGenerator.update( renderer );
-
-						var pmremCubeUVPacker = new PMREMCubeUVPacker( pmremGenerator.cubeLods );
-						pmremCubeUVPacker.update( renderer );
-
-						envMap = pmremCubeUVPacker.CubeUVRenderTarget.texture;
-
+						var pmremGenerator = new PMREMGenerator( renderer );
+						envMap = pmremGenerator.fromEquirectangular( texture ).texture;
 						pmremGenerator.dispose();
-						pmremCubeUVPacker.dispose();
+
+						scene.background = envMap;
 
 						//
 

+ 11 - 21
examples/webgl_materials_envmaps_exr.html

@@ -21,7 +21,6 @@
 			import { OrbitControls } from './jsm/controls/OrbitControls.js';
 			import { EXRLoader } from './jsm/loaders/EXRLoader.js';
 			import { PMREMGenerator } from './jsm/pmrem/PMREMGenerator.js';
-			import { PMREMCubeUVPacker } from './jsm/pmrem/PMREMCubeUVPacker.js';
 
 			var params = {
 				envMap: 'EXR',
@@ -73,6 +72,13 @@
 				planeMesh.rotation.x = - Math.PI * 0.5;
 				scene.add( planeMesh );
 
+				var pmremGenerator = new PMREMGenerator( renderer );
+				THREE.DefaultLoadingManager.onLoad = function ( ) {
+
+					pmremGenerator.dispose();
+
+				}
+
 				new EXRLoader()
 					.setDataType( THREE.FloatType )
 					.load( 'textures/piz_compressed.exr', function ( texture ) {
@@ -83,19 +89,11 @@
 							magFilter: THREE.LinearFilter
 						};
 
-						exrBackground = new THREE.WebGLRenderTargetCube( 512, 512, options ).fromEquirectangularTexture( renderer, texture );
+						exrCubeRenderTarget = pmremGenerator.fromEquirectangular( texture );
 
-						var pmremGenerator = new PMREMGenerator( exrBackground.texture );
-						pmremGenerator.update( renderer );
-
-						var pmremCubeUVPacker = new PMREMCubeUVPacker( pmremGenerator.cubeLods );
-						pmremCubeUVPacker.update( renderer );
-
-						exrCubeRenderTarget = pmremCubeUVPacker.CubeUVRenderTarget;
+						exrBackground = exrCubeRenderTarget.texture;
 
 						texture.dispose();
-						pmremGenerator.dispose();
-						pmremCubeUVPacker.dispose();
 
 					} );
 
@@ -103,19 +101,11 @@
 
 					texture.encoding = THREE.sRGBEncoding;
 
-					pngBackground = new THREE.WebGLRenderTargetCube( 512, 512 ).fromEquirectangularTexture( renderer, texture );
-
-					var pmremGenerator = new PMREMGenerator( pngBackground.texture );
-					pmremGenerator.update( renderer );
+					pngCubeRenderTarget = pmremGenerator.fromEquirectangular( texture );
 
-					var pmremCubeUVPacker = new PMREMCubeUVPacker( pmremGenerator.cubeLods );
-					pmremCubeUVPacker.update( renderer );
-
-					pngCubeRenderTarget = pmremCubeUVPacker.CubeUVRenderTarget;
+					pngBackground = pngCubeRenderTarget.texture;
 
 					texture.dispose();
-					pmremGenerator.dispose();
-					pmremCubeUVPacker.dispose();
 
 				} );
 

+ 11 - 33
examples/webgl_materials_envmaps_hdr.html

@@ -24,7 +24,6 @@
 			import { OrbitControls } from './jsm/controls/OrbitControls.js';
 			import { HDRCubeTextureLoader } from './jsm/loaders/HDRCubeTextureLoader.js';
 			import { PMREMGenerator } from './jsm/pmrem/PMREMGenerator.js';
-			import { PMREMCubeUVPacker } from './jsm/pmrem/PMREMCubeUVPacker.js';
 
 			var params = {
 				envMap: 'HDR',
@@ -78,27 +77,24 @@
 				planeMesh.rotation.x = - Math.PI * 0.5;
 				scene.add( planeMesh );
 
-				var hdrUrls = [ 'px.hdr', 'nx.hdr', 'py.hdr', 'ny.hdr', 'pz.hdr', 'nz.hdr' ];
+				var pmremGenerator = new PMREMGenerator( renderer );
+				THREE.DefaultLoadingManager.onLoad = function ( ) {
+
+					pmremGenerator.dispose();
+
+				}
 
+				var hdrUrls = [ 'px.hdr', 'nx.hdr', 'py.hdr', 'ny.hdr', 'pz.hdr', 'nz.hdr' ];
 				hdrCubeMap = new HDRCubeTextureLoader()
 					.setPath( './textures/cube/pisaHDR/' )
 					.setDataType( THREE.UnsignedByteType )
 					.load( hdrUrls, function () {
 
-						var pmremGenerator = new PMREMGenerator( hdrCubeMap );
-						pmremGenerator.update( renderer );
-
-						var pmremCubeUVPacker = new PMREMCubeUVPacker( pmremGenerator.cubeLods );
-						pmremCubeUVPacker.update( renderer );
-
-						hdrCubeRenderTarget = pmremCubeUVPacker.CubeUVRenderTarget;
+						hdrCubeRenderTarget = pmremGenerator.fromCubemap( hdrCubeMap );
 
 						hdrCubeMap.magFilter = THREE.LinearFilter;
 						hdrCubeMap.needsUpdate = true;
 
-						pmremGenerator.dispose();
-						pmremCubeUVPacker.dispose();
-
 					} );
 
 				var ldrUrls = [ 'px.png', 'nx.png', 'py.png', 'ny.png', 'pz.png', 'nz.png' ];
@@ -106,18 +102,9 @@
 					.setPath( './textures/cube/pisa/' )
 					.load( ldrUrls, function () {
 
-						ldrCubeMap.encoding = THREE.sRGBEncoding;
-
-						var pmremGenerator = new PMREMGenerator( ldrCubeMap );
-						pmremGenerator.update( renderer );
-
-						var pmremCubeUVPacker = new PMREMCubeUVPacker( pmremGenerator.cubeLods );
-						pmremCubeUVPacker.update( renderer );
-
-						ldrCubeRenderTarget = pmremCubeUVPacker.CubeUVRenderTarget;
+						ldrCubeMap.encoding = THREE.GammaEncoding;
 
-						pmremGenerator.dispose();
-						pmremCubeUVPacker.dispose();
+						ldrCubeRenderTarget = pmremGenerator.fromCubemap( ldrCubeMap );
 
 					} );
 
@@ -130,20 +117,11 @@
 						rgbmCubeMap.encoding = THREE.RGBM16Encoding;
 						rgbmCubeMap.format = THREE.RGBAFormat;
 
-						var pmremGenerator = new PMREMGenerator( rgbmCubeMap );
-						pmremGenerator.update( renderer );
-
-						var pmremCubeUVPacker = new PMREMCubeUVPacker( pmremGenerator.cubeLods );
-						pmremCubeUVPacker.update( renderer );
-
-						rgbmCubeRenderTarget = pmremCubeUVPacker.CubeUVRenderTarget;
+						rgbmCubeRenderTarget = pmremGenerator.fromCubemap( rgbmCubeMap );
 
 						rgbmCubeMap.magFilter = THREE.LinearFilter;
 						rgbmCubeMap.needsUpdate = true;
 
-						pmremGenerator.dispose();
-						pmremCubeUVPacker.dispose();
-
 					} );
 
 				renderer.setPixelRatio( window.devicePixelRatio );

+ 10 - 31
examples/webgl_materials_envmaps_hdr_nodes.html

@@ -41,7 +41,6 @@
 			import { OrbitControls } from './jsm/controls/OrbitControls.js';
 			import { HDRCubeTextureLoader } from './jsm/loaders/HDRCubeTextureLoader.js';
 			import { PMREMGenerator } from './jsm/pmrem/PMREMGenerator.js';
-			import { PMREMCubeUVPacker } from './jsm/pmrem/PMREMCubeUVPacker.js';
 			import { MeshStandardNodeMaterial } from './jsm/nodes/Nodes.js';
 
 			var params = {
@@ -103,26 +102,24 @@
 				planeMesh.rotation.x = - Math.PI * 0.5;
 				scene.add( planeMesh );
 
+				var pmremGenerator = new PMREMGenerator( renderer );
+				THREE.DefaultLoadingManager.onLoad = function ( ) {
+
+					pmremGenerator.dispose();
+					
+				}
+
 				var hdrUrls = [ 'px.hdr', 'nx.hdr', 'py.hdr', 'ny.hdr', 'pz.hdr', 'nz.hdr' ];
 				hdrCubeMap = new HDRCubeTextureLoader()
 					.setPath( './textures/cube/pisaHDR/' )
 					.setDataType( THREE.UnsignedByteType )
 					.load( hdrUrls, function () {
 
-						var pmremGenerator = new PMREMGenerator( hdrCubeMap );
-						pmremGenerator.update( renderer );
-
-						var pmremCubeUVPacker = new PMREMCubeUVPacker( pmremGenerator.cubeLods );
-						pmremCubeUVPacker.update( renderer );
-
-						hdrCubeRenderTarget = pmremCubeUVPacker.CubeUVRenderTarget;
+						hdrCubeRenderTarget = pmremGenerator.fromCubemap( hdrCubeMap );
 
 						hdrCubeMap.magFilter = THREE.LinearFilter;
 						hdrCubeMap.needsUpdate = true;
 
-						pmremGenerator.dispose();
-						pmremCubeUVPacker.dispose();
-
 					} );
 
 				var ldrUrls = [ 'px.png', 'nx.png', 'py.png', 'ny.png', 'pz.png', 'nz.png' ];
@@ -132,16 +129,7 @@
 
 						ldrCubeMap.encoding = THREE.GammaEncoding;
 
-						var pmremGenerator = new PMREMGenerator( ldrCubeMap );
-						pmremGenerator.update( renderer );
-
-						var pmremCubeUVPacker = new PMREMCubeUVPacker( pmremGenerator.cubeLods );
-						pmremCubeUVPacker.update( renderer );
-
-						ldrCubeRenderTarget = pmremCubeUVPacker.CubeUVRenderTarget;
-
-						pmremGenerator.dispose();
-						pmremCubeUVPacker.dispose();
+						ldrCubeRenderTarget = pmremGenerator.fromCubemap( ldrCubeMap );
 
 					} );
 
@@ -154,20 +142,11 @@
 						rgbmCubeMap.encoding = THREE.RGBM16Encoding;
 						rgbmCubeMap.format = THREE.RGBAFormat;
 
-						var pmremGenerator = new PMREMGenerator( rgbmCubeMap );
-						pmremGenerator.update( renderer );
-
-						var pmremCubeUVPacker = new PMREMCubeUVPacker( pmremGenerator.cubeLods );
-						pmremCubeUVPacker.update( renderer );
-
-						rgbmCubeRenderTarget = pmremCubeUVPacker.CubeUVRenderTarget;
+						rgbmCubeRenderTarget = pmremGenerator.fromCubemap( rgbmCubeMap );
 
 						rgbmCubeMap.magFilter = THREE.LinearFilter;
 						rgbmCubeMap.needsUpdate = true;
 
-						pmremGenerator.dispose();
-						pmremCubeUVPacker.dispose();
-
 					} );
 
 				renderer.setPixelRatio( window.devicePixelRatio );

+ 3 - 10
examples/webgl_materials_envmaps_pmrem_nodes.html

@@ -41,7 +41,6 @@
 			import { OrbitControls } from './jsm/controls/OrbitControls.js';
 			import { HDRCubeTextureLoader } from './jsm/loaders/HDRCubeTextureLoader.js';
 			import { PMREMGenerator } from './jsm/pmrem/PMREMGenerator.js';
-			import { PMREMCubeUVPacker } from './jsm/pmrem/PMREMCubeUVPacker.js';
 
 			import {
 				StandardNodeMaterial,
@@ -74,13 +73,10 @@
 
 				nodeTextureSize.value = textureSize;
 
-				var pmremGenerator = new PMREMGenerator( hdrCubeMap, undefined, textureSize / 4 );
-				pmremGenerator.update( renderer );
-
-				var pmremCubeUVPacker = new PMREMCubeUVPacker( pmremGenerator.cubeLods );
-				pmremCubeUVPacker.update( renderer );
+				var pmremGenerator = new PMREMGenerator( renderer );
+				hdrCubeRenderTarget = pmremGenerator.fromCubemap( hdrCubeMap );
+				pmremGenerator.dispose();
 
-				hdrCubeRenderTarget = pmremCubeUVPacker.CubeUVRenderTarget;
 				nodeTexture.value = hdrCubeRenderTarget.texture;
 
 				hdrCubeMap.magFilter = THREE.LinearFilter;
@@ -89,9 +85,6 @@
 				planeMesh.material.map = hdrCubeRenderTarget.texture;
 				planeMesh.material.needsUpdate = true;
 
-				pmremGenerator.dispose();
-				pmremCubeUVPacker.dispose();
-
 			}
 
 			function init() {

+ 4 - 12
examples/webgl_materials_nodes.html

@@ -24,7 +24,6 @@
 			import { TeapotBufferGeometry } from './jsm/geometries/TeapotBufferGeometry.js';
 
 			import { PMREMGenerator } from './jsm/pmrem/PMREMGenerator.js';
-			import { PMREMCubeUVPacker } from './jsm/pmrem/PMREMCubeUVPacker.js';
 
 			import { NodeMaterialLoader, NodeMaterialLoaderUtils } from './jsm/loaders/NodeMaterialLoader.js';
 
@@ -70,7 +69,7 @@
 
 			}
 
-			var premTexture, pmremCube, pmremGenerator, pmremCubeUVPacker, premSize = 1024;
+			var premTexture, pmremCube, pmremGenerator, premSize = 1024;
 
 			function updatePREM( textureCube ) {
 
@@ -82,24 +81,17 @@
 				var magFilter = pmremCube.magFilter;
 				var generateMipmaps = pmremCube.generateMipmaps;
 
-				pmremGenerator = new PMREMGenerator( pmremCube, undefined, premSize / 4 );
-				pmremGenerator.update( renderer );
-
-				pmremCubeUVPacker = new PMREMCubeUVPacker( pmremGenerator.cubeLods );
-				pmremCubeUVPacker.update( renderer );
+				var pmremGenerator = new PMREMGenerator( renderer );
+				premTexture = pmremGenerator.fromCubemap( pmremCube ).texture;
+				pmremGenerator.dispose();
 
 				pmremCube.minFilter = minFilter;
 				pmremCube.magFilter = magFilter;
 				pmremCube.generateMipmaps = generateMipmaps;
 				pmremCube.needsUpdate = true;
 
-				premTexture = pmremCubeUVPacker.CubeUVRenderTarget.texture;
-
 				library[ premTexture.uuid ] = premTexture;
 
-				pmremGenerator.dispose();
-				pmremCubeUVPacker.dispose();
-
 			}
 
 			var cubemap = function () {

+ 3 - 11
examples/webgl_materials_physical_clearcoat.html

@@ -22,7 +22,6 @@
 		import { OrbitControls } from './jsm/controls/OrbitControls.js';
 		import { HDRCubeTextureLoader } from './jsm/loaders/HDRCubeTextureLoader.js';
 		import { PMREMGenerator } from './jsm/pmrem/PMREMGenerator.js';
-		import { PMREMCubeUVPacker } from './jsm/pmrem/PMREMCubeUVPacker.js';
 
 		var container, stats;
 
@@ -53,13 +52,9 @@
 				.load( [ 'px.hdr', 'nx.hdr', 'py.hdr', 'ny.hdr', 'pz.hdr', 'nz.hdr' ],
 					function ( hdrCubeMap ) {
 
-						var pmremGenerator = new PMREMGenerator( hdrCubeMap );
-						pmremGenerator.update( renderer );
-
-						var pmremCubeUVPacker = new PMREMCubeUVPacker( pmremGenerator.cubeLods );
-						pmremCubeUVPacker.update( renderer );
-
-						var hdrCubeRenderTarget = pmremCubeUVPacker.CubeUVRenderTarget;
+						var pmremGenerator = new PMREMGenerator( renderer );
+						var hdrCubeRenderTarget = pmremGenerator.fromCubemap( hdrCubeMap );
+						pmremGenerator.dispose();
 
 						var geometry = new THREE.SphereBufferGeometry( 80, 64, 32 );
 
@@ -160,9 +155,6 @@
 						hdrCubeMap.needsUpdate = true;
 						scene.background = hdrCubeMap;
 
-						pmremGenerator.dispose();
-						pmremCubeUVPacker.dispose();
-
 					}
 
 				);

+ 3 - 11
examples/webgl_materials_physical_transparency.html

@@ -21,7 +21,6 @@
 			import { OrbitControls } from './jsm/controls/OrbitControls.js';
 			import { HDRCubeTextureLoader } from './jsm/loaders/HDRCubeTextureLoader.js';
 			import { PMREMGenerator } from './jsm/pmrem/PMREMGenerator.js';
-			import { PMREMCubeUVPacker } from './jsm/pmrem/PMREMCubeUVPacker.js';
 
 			var params = {
 				color: 0xffffff,
@@ -74,20 +73,13 @@
 
 				//
 
-				var pmremGenerator = new PMREMGenerator( hdrCubeMap );
-				pmremGenerator.update( renderer );
-
-				var pmremCubeUVPacker = new PMREMCubeUVPacker( pmremGenerator.cubeLods );
-				pmremCubeUVPacker.update( renderer );
-
-				hdrCubeRenderTarget = pmremCubeUVPacker.CubeUVRenderTarget;
+				var pmremGenerator = new PMREMGenerator( renderer );
+				hdrCubeRenderTarget = pmremGenerator.fromCubemap( hdrCubeMap );
+				pmremGenerator.dispose();
 
 				hdrCubeMap.magFilter = THREE.LinearFilter;
 				hdrCubeMap.needsUpdate = true;
 
-				pmremGenerator.dispose();
-				pmremCubeUVPacker.dispose();
-
 				//
 
 				var geometry = new THREE.SphereBufferGeometry( 20, 64, 32 );

+ 3 - 10
examples/webgl_materials_reflectivity.html

@@ -25,7 +25,6 @@
 			import { OBJLoader } from './jsm/loaders/OBJLoader.js';
 			import { HDRCubeTextureLoader } from './jsm/loaders/HDRCubeTextureLoader.js';
 			import { PMREMGenerator } from './jsm/pmrem/PMREMGenerator.js';
-			import { PMREMCubeUVPacker } from './jsm/pmrem/PMREMCubeUVPacker.js';
 
 			var container, stats;
 			var params = {
@@ -129,20 +128,14 @@
 					.setDataType( THREE.UnsignedByteType )
 					.load( hdrUrls, function ( hdrCubeMap ) {
 
-						var pmremGenerator = new PMREMGenerator( hdrCubeMap );
-						pmremGenerator.update( renderer );
-
-						var pmremCubeUVPacker = new PMREMCubeUVPacker( pmremGenerator.cubeLods );
-						pmremCubeUVPacker.update( renderer );
-
-						hdrCubeRenderTarget = pmremCubeUVPacker.CubeUVRenderTarget;
+						var pmremGenerator = new PMREMGenerator( renderer );
+						hdrCubeRenderTarget = pmremGenerator.fromCubemap( hdrCubeMap );
+						pmremGenerator.dispose();
 
 						gemFrontMaterial.envMap = gemBackMaterial.envMap = hdrCubeRenderTarget.texture;
 						gemFrontMaterial.needsUpdate = gemBackMaterial.needsUpdate = true;
 
 						hdrCubeMap.dispose();
-						pmremGenerator.dispose();
-						pmremCubeUVPacker.dispose();
 
 					} );
 

+ 3 - 11
examples/webgl_materials_standard.html

@@ -22,7 +22,6 @@
 			import { TrackballControls } from './jsm/controls/TrackballControls.js';
 			import { OBJLoader } from './jsm/loaders/OBJLoader.js';
 			import { PMREMGenerator } from './jsm/pmrem/PMREMGenerator.js';
-			import { PMREMCubeUVPacker } from './jsm/pmrem/PMREMCubeUVPacker.js';
 
 			var statsEnabled = true;
 
@@ -107,13 +106,9 @@
 						rgbmCubeMap.encoding = THREE.RGBM16Encoding;
 						rgbmCubeMap.format = THREE.RGBAFormat;
 
-						var pmremGenerator = new PMREMGenerator( rgbmCubeMap );
-						pmremGenerator.update( renderer );
-
-						var pmremCubeUVPacker = new PMREMCubeUVPacker( pmremGenerator.cubeLods );
-						pmremCubeUVPacker.update( renderer );
-
-						var rgbmCubeRenderTarget = pmremCubeUVPacker.CubeUVRenderTarget;
+						var pmremGenerator = new PMREMGenerator( renderer );
+						var rgbmCubeRenderTarget = pmremGenerator.fromCubemap( rgbmCubeMap );
+						pmremGenerator.dispose();
 
 						material.envMap = rgbmCubeRenderTarget.texture;
 						material.needsUpdate = true; // is this needed?
@@ -122,9 +117,6 @@
 						rgbmCubeMap.needsUpdate = true;
 						scene.background = rgbmCubeMap;
 
-						pmremGenerator.dispose();
-						pmremCubeUVPacker.dispose();
-
 					} );
 
 				//

+ 3 - 11
examples/webgl_materials_variations_physical.html

@@ -21,7 +21,6 @@
 			import { OrbitControls } from './jsm/controls/OrbitControls.js';
 			import { HDRCubeTextureLoader } from './jsm/loaders/HDRCubeTextureLoader.js';
 			import { PMREMGenerator } from './jsm/pmrem/PMREMGenerator.js';
-			import { PMREMCubeUVPacker } from './jsm/pmrem/PMREMCubeUVPacker.js';
 
 			var container, stats;
 
@@ -65,13 +64,9 @@
 					.setDataType( THREE.UnsignedByteType )
 					.load( hdrUrls, function ( hdrCubeMap ) {
 
-						var pmremGenerator = new PMREMGenerator( hdrCubeMap );
-						pmremGenerator.update( renderer );
-
-						var pmremCubeUVPacker = new PMREMCubeUVPacker( pmremGenerator.cubeLods );
-						pmremCubeUVPacker.update( renderer );
-
-						hdrCubeRenderTarget = pmremCubeUVPacker.CubeUVRenderTarget;
+						var pmremGenerator = new PMREMGenerator( renderer );
+						hdrCubeRenderTarget = pmremGenerator.fromCubemap( hdrCubeMap );
+						pmremGenerator.dispose();
 
 						// Materials
 
@@ -124,9 +119,6 @@
 						hdrCubeMap.needsUpdate = true;
 						scene.background = hdrCubeMap;
 
-						pmremGenerator.dispose();
-						pmremCubeUVPacker.dispose();
-
 					} );
 
 				function addLabel( name, location ) {

+ 3 - 11
examples/webgl_materials_variations_standard.html

@@ -21,7 +21,6 @@
 			import { OrbitControls } from './jsm/controls/OrbitControls.js';
 			import { HDRCubeTextureLoader } from './jsm/loaders/HDRCubeTextureLoader.js';
 			import { PMREMGenerator } from './jsm/pmrem/PMREMGenerator.js';
-			import { PMREMCubeUVPacker } from './jsm/pmrem/PMREMCubeUVPacker.js';
 
 			var container, stats;
 
@@ -71,13 +70,9 @@
 					.setDataType( THREE.UnsignedByteType )
 					.load( hdrUrls, function ( hdrCubeMap ) {
 
-						var pmremGenerator = new PMREMGenerator( hdrCubeMap );
-						pmremGenerator.update( renderer );
-
-						var pmremCubeUVPacker = new PMREMCubeUVPacker( pmremGenerator.cubeLods );
-						pmremCubeUVPacker.update( renderer );
-
-						hdrCubeRenderTarget = pmremCubeUVPacker.CubeUVRenderTarget;
+						var pmremGenerator = new PMREMGenerator( renderer );
+						hdrCubeRenderTarget = pmremGenerator.fromCubemap( hdrCubeMap );
+						pmremGenerator.dispose();
 
 						var bumpScale = 1;
 						var cubeWidth = 400;
@@ -130,9 +125,6 @@
 						hdrCubeMap.needsUpdate = true;
 						scene.background = hdrCubeMap;
 
-						pmremGenerator.dispose();
-						pmremCubeUVPacker.dispose();
-
 					} );
 
 				function addLabel( name, location ) {

+ 2 - 9
examples/webgl_tonemapping.html

@@ -21,7 +21,6 @@
 			import { OrbitControls } from './jsm/controls/OrbitControls.js';
 			import { HDRCubeTextureLoader } from './jsm/loaders/HDRCubeTextureLoader.js';
 			import { PMREMGenerator } from './jsm/pmrem/PMREMGenerator.js';
-			import { PMREMCubeUVPacker } from './jsm/pmrem/PMREMCubeUVPacker.js';
 
 			import { EffectComposer } from './jsm/postprocessing/EffectComposer.js';
 			import { RenderPass } from './jsm/postprocessing/RenderPass.js';
@@ -146,18 +145,12 @@
 					.setDataType( THREE.UnsignedByteType )
 					.load( hdrUrls, function ( hdrCubeMap ) {
 
-						var pmremGenerator = new PMREMGenerator( hdrCubeMap );
-						pmremGenerator.update( renderer );
-
-						var pmremCubeUVPacker = new PMREMCubeUVPacker( pmremGenerator.cubeLods );
-						pmremCubeUVPacker.update( renderer );
-
-						standardMaterial.envMap = pmremCubeUVPacker.CubeUVRenderTarget.texture;
+						var pmremGenerator = new PMREMGenerator( renderer );
+						standardMaterial.envMap = pmremGenerator.fromCubemap( hdrCubeMap ).texture;
 						standardMaterial.needsUpdate = true;
 
 						hdrCubeMap.dispose();
 						pmremGenerator.dispose();
-						pmremCubeUVPacker.dispose();
 
 					} );
 

+ 4 - 0
src/renderers/WebGLRenderer.js

@@ -1990,6 +1990,10 @@ function WebGLRenderer( parameters ) {
 				m_uniforms.color.value.copy( material.color );
 				m_uniforms.opacity.value = material.opacity;
 
+			} else if ( material.envMap ) {
+
+				refreshUniformsCommon( m_uniforms, material );
+
 			}
 
 			// RectAreaLight Texture

+ 145 - 115
src/renderers/shaders/ShaderChunk/cube_uv_reflection_fragment.glsl.js

@@ -1,130 +1,160 @@
 export default /* glsl */`
 #ifdef ENVMAP_TYPE_CUBE_UV
 
-#define cubeUV_textureSize (1024.0)
-
-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;
+#define cubeUV_maxMipLevel 8.0
+#define cubeUV_minMipLevel 4.0
+#define cubeUV_maxTileSize 256.0
+#define cubeUV_minTileSize 16.0
+
+// 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).
+
+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;
 }
-#define cubeUV_maxLods1  (log2(cubeUV_textureSize*0.25) - 1.0)
-#define cubeUV_rangeClamp (exp2((6.0 - 1.0) * 2.0))
 
-vec2 MipLevelInfo( vec3 vec, float roughnessLevel, float roughness ) {
-	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));
+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);
 }
 
-#define cubeUV_maxLods2 (log2(cubeUV_textureSize*0.25) - 2.0)
-#define cubeUV_rcpTextureSize (1.0 / cubeUV_textureSize)
-
-vec2 getCubeUV(vec3 direction, float roughnessLevel, float mipLevel) {
-	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 );
+vec3 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;
+
+  vec3 tl = envMapTexelToLinear(texture2D(envMap, uv)).rgb;
+  uv.x += texelSize;
+  vec3 tr = envMapTexelToLinear(texture2D(envMap, uv)).rgb;
+  uv.y += texelSize;
+  vec3 br = envMapTexelToLinear(texture2D(envMap, uv)).rgb;
+  uv.x -= texelSize;
+  vec3 bl = envMapTexelToLinear(texture2D(envMap, uv)).rgb;
+  vec3 tm = mix(tl, tr, f.x);
+  vec3 bm = mix(bl, br, f.x);
+  return mix(tm, bm, f.y);
 }
 
-#define cubeUV_maxLods3 (log2(cubeUV_textureSize*0.25) - 3.0)
-
-vec4 textureCubeUV( sampler2D envMap, vec3 reflectedDirection, float roughness ) {
-	float roughnessVal = roughness* cubeUV_maxLods3;
-	float r1 = floor(roughnessVal);
-	float r2 = r1 + 1.0;
-	float t = fract(roughnessVal);
-	vec2 mipInfo = MipLevelInfo(reflectedDirection, r1, roughness);
-	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);
-	vec4 color10 = envMapTexelToLinear(texture2D(envMap, uv_10));
-
-	vec2 uv_20 = getCubeUV(reflectedDirection, r2, level0);
-	vec4 color20 = envMapTexelToLinear(texture2D(envMap, uv_20));
+// These defines must match with PMREMGenerator
+
+#define r0 1.0
+#define v0 0.339
+#define m0 -2.0
+#define r1 0.8
+#define v1 0.276
+#define m1 -1.0
+#define r4 0.4
+#define v4 0.046
+#define m4 2.0
+#define r5 0.305
+#define v5 0.016
+#define m5 3.0
+#define r6 0.21
+#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;
+}
 
-	vec4 result = mix(color10, color20, t);
+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;
+}
 
-	return vec4(result.rgb, 1.0);
+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;
 }
 
+vec4 textureCubeUV(sampler2D envMap, vec3 sampleDir, float roughness) {
+  float mip = clamp(roughnessToMip(roughness), m0, cubeUV_maxMipLevel);
+  float mipF = fract(mip);
+  float mipInt = floor(mip);
+
+  vec3 color0 = bilinearCubeUV(envMap, sampleDir, mipInt);
+  if (mipF == 0.0) {
+    return vec4(color0, 1.0);
+  } else {
+    vec3 color1 = bilinearCubeUV(envMap, sampleDir, mipInt + 1.0);
+    return vec4(mix(color0, color1, mipF), 1.0);
+  }
+}
 #endif
 `;

+ 9 - 1
src/renderers/shaders/ShaderChunk/envmap_fragment.glsl.js

@@ -38,6 +38,10 @@ export default /* glsl */`
 
 		vec4 envColor = textureCube( envMap, vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );
 
+	#elif defined( ENVMAP_TYPE_CUBE_UV )
+
+		vec4 envColor = textureCubeUV( envMap, vec3( flipEnvMap * reflectVec.x, reflectVec.yz ), 0.0 );
+
 	#elif defined( ENVMAP_TYPE_EQUIREC )
 
 		vec2 sampleUV;
@@ -64,7 +68,11 @@ export default /* glsl */`
 
 	#endif
 
-	envColor = envMapTexelToLinear( envColor );
+	#ifndef ENVMAP_TYPE_CUBE_UV
+
+		envColor = envMapTexelToLinear( envColor );
+
+	#endif
 
 	#ifdef ENVMAP_BLENDING_MULTIPLY
 

+ 6 - 5
src/renderers/shaders/ShaderLib.js

@@ -236,11 +236,12 @@ var ShaderLib = {
 
 	cube: {
 
-		uniforms: {
-			tCube: { value: null },
-			tFlip: { value: - 1 },
-			opacity: { value: 1.0 }
-		},
+		uniforms: mergeUniforms( [
+			UniformsLib.envmap,
+			{
+				opacity: { value: 1.0 }
+			}
+		] ),
 
 		vertexShader: ShaderChunk.cube_vert,
 		fragmentShader: ShaderChunk.cube_frag

+ 7 - 4
src/renderers/shaders/ShaderLib/cube_frag.glsl.js

@@ -1,15 +1,18 @@
 export default /* glsl */`
-uniform samplerCube tCube;
-uniform float tFlip;
+
+#include <envmap_common_pars_fragment>
 uniform float opacity;
 
 varying vec3 vWorldDirection;
 
+#include <cube_uv_reflection_fragment>
+
 void main() {
 
-	vec4 texColor = textureCube( tCube, vec3( tFlip * vWorldDirection.x, vWorldDirection.yz ) );
+	vec3 vReflect = vWorldDirection;
+	#include <envmap_fragment>
 
-	gl_FragColor = mapTexelToLinear( texColor );
+	gl_FragColor = envColor;
 	gl_FragColor.a *= opacity;
 
 	#include <tonemapping_fragment>

+ 1 - 0
src/renderers/shaders/ShaderLib/meshbasic_frag.glsl.js

@@ -18,6 +18,7 @@ uniform float opacity;
 #include <lightmap_pars_fragment>
 #include <envmap_common_pars_fragment>
 #include <envmap_pars_fragment>
+#include <cube_uv_reflection_fragment>
 #include <fog_pars_fragment>
 #include <specularmap_pars_fragment>
 #include <logdepthbuf_pars_fragment>

+ 1 - 0
src/renderers/shaders/ShaderLib/meshlambert_frag.glsl.js

@@ -25,6 +25,7 @@ varying vec3 vIndirectFront;
 #include <emissivemap_pars_fragment>
 #include <envmap_common_pars_fragment>
 #include <envmap_pars_fragment>
+#include <cube_uv_reflection_fragment>
 #include <bsdfs>
 #include <lights_pars_begin>
 #include <fog_pars_fragment>

+ 1 - 0
src/renderers/shaders/ShaderLib/meshphong_frag.glsl.js

@@ -20,6 +20,7 @@ uniform float opacity;
 #include <emissivemap_pars_fragment>
 #include <envmap_common_pars_fragment>
 #include <envmap_pars_fragment>
+#include <cube_uv_reflection_fragment>
 #include <fog_pars_fragment>
 #include <bsdfs>
 #include <lights_pars_begin>

+ 4 - 5
src/renderers/webgl/WebGLBackground.js

@@ -2,7 +2,7 @@
  * @author mrdoob / http://mrdoob.com/
  */
 
-import { BackSide, FrontSide } from '../../constants.js';
+import { BackSide, FrontSide, CubeUVReflectionMapping } from '../../constants.js';
 import { BoxBufferGeometry } from '../../geometries/BoxGeometry.js';
 import { PlaneBufferGeometry } from '../../geometries/PlaneGeometry.js';
 import { ShaderMaterial } from '../../materials/ShaderMaterial.js';
@@ -60,7 +60,7 @@ function WebGLBackground( renderer, state, objects, premultipliedAlpha ) {
 
 		}
 
-		if ( background && ( background.isCubeTexture || background.isWebGLRenderTargetCube ) ) {
+		if ( background && ( background.isCubeTexture || background.isWebGLRenderTargetCube || background.mapping === CubeUVReflectionMapping ) ) {
 
 			if ( boxMesh === undefined ) {
 
@@ -92,7 +92,7 @@ function WebGLBackground( renderer, state, objects, premultipliedAlpha ) {
 
 					get: function () {
 
-						return this.uniforms.tCube.value;
+						return this.envMap.value;
 
 					}
 
@@ -103,8 +103,7 @@ function WebGLBackground( renderer, state, objects, premultipliedAlpha ) {
 			}
 
 			var texture = background.isWebGLRenderTargetCube ? background.texture : background;
-			boxMesh.material.uniforms.tCube.value = texture;
-			boxMesh.material.uniforms.tFlip.value = ( background.isWebGLRenderTargetCube ) ? 1 : - 1;
+			boxMesh.material.envMap = texture;
 
 			if ( currentBackground !== background ||
 			     currentBackgroundVersion !== texture.version ) {

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

@@ -351,7 +351,7 @@ function generateEnvMapModeDefine( parameters ) {
 
 function generateEnvMapBlendingDefine( parameters ) {
 
-	var envMapBlendingDefine = 'ENVMAP_BLENDING_MULTIPLY';
+	var envMapBlendingDefine = 'ENVMAP_BLENDING_NONE';
 
 	if ( parameters.envMap ) {
 

+ 0 - 1
utils/modularize.js

@@ -142,7 +142,6 @@ var files = [
 	{ path: 'objects/Water.js', dependencies: [], ignoreList: [] },
 	{ path: 'objects/Water2.js', dependencies: [ { name: 'Reflector', path: 'objects/Reflector.js' }, { name: 'Refractor', path: 'objects/Refractor.js' } ], ignoreList: [] },
 
-	{ path: 'pmrem/PMREMCubeUVPacker.js', dependencies: [], ignoreList: [] },
 	{ path: 'pmrem/PMREMGenerator.js', dependencies: [], ignoreList: [] },
 
 	{ path: 'postprocessing/AdaptiveToneMappingPass.js', dependencies: [ { name: 'Pass', path: 'postprocessing/Pass.js' }, { name: 'CopyShader', path: 'shaders/CopyShader.js' }, { name: 'LuminosityShader', path: 'shaders/LuminosityShader.js' }, { name: 'ToneMapShader', path: 'shaders/ToneMapShader.js' } ], ignoreList: [] },