Browse Source

Updated builds.

Mr.doob 5 years ago
parent
commit
c59fa06b30
3 changed files with 3352 additions and 3315 deletions
  1. 1428 1410
      build/three.js
  2. 496 495
      build/three.min.js
  3. 1428 1410
      build/three.module.js

+ 1428 - 1410
build/three.js

@@ -23515,2517 +23515,2535 @@
 	Object.assign( WebXRManager.prototype, EventDispatcher.prototype );
 
 	/**
-	 * @author supereggbert / http://www.paulbrunt.co.uk/
 	 * @author mrdoob / http://mrdoob.com/
-	 * @author alteredq / http://alteredqualia.com/
-	 * @author szimek / https://github.com/szimek/
-	 * @author tschw
 	 */
 
-	function WebGLRenderer( parameters ) {
-
-		parameters = parameters || {};
+	function WebGLMaterials( properties ) {
 
-		var _canvas = parameters.canvas !== undefined ? parameters.canvas : document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ),
-			_context = parameters.context !== undefined ? parameters.context : null,
+		function refreshFogUniforms( uniforms, fog ) {
 
-			_alpha = parameters.alpha !== undefined ? parameters.alpha : false,
-			_depth = parameters.depth !== undefined ? parameters.depth : true,
-			_stencil = parameters.stencil !== undefined ? parameters.stencil : true,
-			_antialias = parameters.antialias !== undefined ? parameters.antialias : false,
-			_premultipliedAlpha = parameters.premultipliedAlpha !== undefined ? parameters.premultipliedAlpha : true,
-			_preserveDrawingBuffer = parameters.preserveDrawingBuffer !== undefined ? parameters.preserveDrawingBuffer : false,
-			_powerPreference = parameters.powerPreference !== undefined ? parameters.powerPreference : 'default',
-			_failIfMajorPerformanceCaveat = parameters.failIfMajorPerformanceCaveat !== undefined ? parameters.failIfMajorPerformanceCaveat : false;
+			uniforms.fogColor.value.copy( fog.color );
 
-		var currentRenderList = null;
-		var currentRenderState = null;
+			if ( fog.isFog ) {
 
-		// public properties
+				uniforms.fogNear.value = fog.near;
+				uniforms.fogFar.value = fog.far;
 
-		this.domElement = _canvas;
+			} else if ( fog.isFogExp2 ) {
 
-		// Debug configuration container
-		this.debug = {
+				uniforms.fogDensity.value = fog.density;
 
-			/**
-			 * Enables error checking and reporting when shader programs are being compiled
-			 * @type {boolean}
-			 */
-			checkShaderErrors: true
-		};
+			}
 
-		// clearing
+		}
 
-		this.autoClear = true;
-		this.autoClearColor = true;
-		this.autoClearDepth = true;
-		this.autoClearStencil = true;
+		function refreshMaterialUniforms( uniforms, material, environment, pixelRatio, height ) {
 
-		// scene graph
+			if ( material.isMeshBasicMaterial ) {
 
-		this.sortObjects = true;
+				refreshUniformsCommon( uniforms, material );
 
-		// user-defined clipping
+			} else if ( material.isMeshLambertMaterial ) {
 
-		this.clippingPlanes = [];
-		this.localClippingEnabled = false;
+				refreshUniformsCommon( uniforms, material );
+				refreshUniformsLambert( uniforms, material );
 
-		// physically based shading
+			} else if ( material.isMeshToonMaterial ) {
 
-		this.gammaFactor = 2.0;	// for backwards compatibility
-		this.outputEncoding = LinearEncoding;
+				refreshUniformsCommon( uniforms, material );
+				refreshUniformsToon( uniforms, material );
 
-		// physical lights
+			} else if ( material.isMeshPhongMaterial ) {
 
-		this.physicallyCorrectLights = false;
+				refreshUniformsCommon( uniforms, material );
+				refreshUniformsPhong( uniforms, material );
 
-		// tone mapping
+			} else if ( material.isMeshStandardMaterial ) {
 
-		this.toneMapping = NoToneMapping;
-		this.toneMappingExposure = 1.0;
-		this.toneMappingWhitePoint = 1.0;
+				refreshUniformsCommon( uniforms, material, environment );
 
-		// morphs
+				if ( material.isMeshPhysicalMaterial ) {
 
-		this.maxMorphTargets = 8;
-		this.maxMorphNormals = 4;
+					refreshUniformsPhysical( uniforms, material, environment );
 
-		// internal properties
+				} else {
 
-		var _this = this,
+					refreshUniformsStandard( uniforms, material, environment );
 
-			_isContextLost = false,
+				}
 
-			// internal state cache
+			} else if ( material.isMeshMatcapMaterial ) {
 
-			_framebuffer = null,
+				refreshUniformsCommon( uniforms, material );
+				refreshUniformsMatcap( uniforms, material );
 
-			_currentActiveCubeFace = 0,
-			_currentActiveMipmapLevel = 0,
-			_currentRenderTarget = null,
-			_currentFramebuffer = null,
-			_currentMaterialId = - 1,
+			} else if ( material.isMeshDepthMaterial ) {
 
-			// geometry and program caching
+				refreshUniformsCommon( uniforms, material );
+				refreshUniformsDepth( uniforms, material );
 
-			_currentGeometryProgram = {
-				geometry: null,
-				program: null,
-				wireframe: false
-			},
+			} else if ( material.isMeshDistanceMaterial ) {
 
-			_currentCamera = null,
-			_currentArrayCamera = null,
+				refreshUniformsCommon( uniforms, material );
+				refreshUniformsDistance( uniforms, material );
 
-			_currentViewport = new Vector4(),
-			_currentScissor = new Vector4(),
-			_currentScissorTest = null,
+			} else if ( material.isMeshNormalMaterial ) {
 
-			//
+				refreshUniformsCommon( uniforms, material );
+				refreshUniformsNormal( uniforms, material );
 
-			_width = _canvas.width,
-			_height = _canvas.height,
+			} else if ( material.isLineBasicMaterial ) {
 
-			_pixelRatio = 1,
-			_opaqueSort = null,
-			_transparentSort = null,
+				refreshUniformsLine( uniforms, material );
 
-			_viewport = new Vector4( 0, 0, _width, _height ),
-			_scissor = new Vector4( 0, 0, _width, _height ),
-			_scissorTest = false,
+				if ( material.isLineDashedMaterial ) {
 
-			// frustum
+					refreshUniformsDash( uniforms, material );
 
-			_frustum = new Frustum(),
+				}
 
-			// clipping
+			} else if ( material.isPointsMaterial ) {
 
-			_clipping = new WebGLClipping(),
-			_clippingEnabled = false,
-			_localClippingEnabled = false,
+				refreshUniformsPoints( uniforms, material, pixelRatio, height );
 
-			// camera matrices cache
+			} else if ( material.isSpriteMaterial ) {
 
-			_projScreenMatrix = new Matrix4(),
+				refreshUniformsSprites( uniforms, material );
 
-			_vector3 = new Vector3();
+			} else if ( material.isShadowMaterial ) {
 
-		function getTargetPixelRatio() {
+				uniforms.color.value.copy( material.color );
+				uniforms.opacity.value = material.opacity;
 
-			return _currentRenderTarget === null ? _pixelRatio : 1;
+			}
 
 		}
 
-		// initialize
-
-		var _gl;
-
-		try {
-
-			var contextAttributes = {
-				alpha: _alpha,
-				depth: _depth,
-				stencil: _stencil,
-				antialias: _antialias,
-				premultipliedAlpha: _premultipliedAlpha,
-				preserveDrawingBuffer: _preserveDrawingBuffer,
-				powerPreference: _powerPreference,
-				failIfMajorPerformanceCaveat: _failIfMajorPerformanceCaveat,
-				xrCompatible: true
-			};
+		function refreshUniformsCommon( uniforms, material, environment ) {
 
-			// event listeners must be registered before WebGL context is created, see #12753
+			uniforms.opacity.value = material.opacity;
 
-			_canvas.addEventListener( 'webglcontextlost', onContextLost, false );
-			_canvas.addEventListener( 'webglcontextrestored', onContextRestore, false );
+			if ( material.color ) {
 
-			_gl = _context || _canvas.getContext( 'webgl', contextAttributes ) || _canvas.getContext( 'experimental-webgl', contextAttributes );
+				uniforms.diffuse.value.copy( material.color );
 
-			if ( _gl === null ) {
+			}
 
-				if ( _canvas.getContext( 'webgl' ) !== null ) {
+			if ( material.emissive ) {
 
-					throw new Error( 'Error creating WebGL context with your selected attributes.' );
+				uniforms.emissive.value.copy( material.emissive ).multiplyScalar( material.emissiveIntensity );
 
-				} else {
+			}
 
-					throw new Error( 'Error creating WebGL context.' );
+			if ( material.map ) {
 
-				}
+				uniforms.map.value = material.map;
 
 			}
 
-			// Some experimental-webgl implementations do not have getShaderPrecisionFormat
+			if ( material.alphaMap ) {
 
-			if ( _gl.getShaderPrecisionFormat === undefined ) {
+				uniforms.alphaMap.value = material.alphaMap;
 
-				_gl.getShaderPrecisionFormat = function () {
+			}
 
-					return { 'rangeMin': 1, 'rangeMax': 1, 'precision': 1 };
+			if ( material.specularMap ) {
 
-				};
+				uniforms.specularMap.value = material.specularMap;
 
 			}
 
-		} catch ( error ) {
+			var envMap = material.envMap || environment;
 
-			console.error( 'THREE.WebGLRenderer: ' + error.message );
-			throw error;
+			if ( envMap ) {
 
-		}
+				uniforms.envMap.value = envMap;
 
-		var extensions, capabilities, state, info;
-		var properties, textures, attributes, geometries, objects;
-		var programCache, renderLists, renderStates;
+				uniforms.flipEnvMap.value = envMap.isCubeTexture ? - 1 : 1;
 
-		var background, morphtargets, bufferRenderer, indexedBufferRenderer;
+				uniforms.reflectivity.value = material.reflectivity;
+				uniforms.refractionRatio.value = material.refractionRatio;
 
-		var utils;
+				uniforms.maxMipLevel.value = properties.get( envMap ).__maxMipLevel;
 
-		function initGLContext() {
+			}
 
-			extensions = new WebGLExtensions( _gl );
+			if ( material.lightMap ) {
 
-			capabilities = new WebGLCapabilities( _gl, extensions, parameters );
+				uniforms.lightMap.value = material.lightMap;
+				uniforms.lightMapIntensity.value = material.lightMapIntensity;
 
-			if ( capabilities.isWebGL2 === false ) {
+			}
 
-				extensions.get( 'WEBGL_depth_texture' );
-				extensions.get( 'OES_texture_float' );
-				extensions.get( 'OES_texture_half_float' );
-				extensions.get( 'OES_texture_half_float_linear' );
-				extensions.get( 'OES_standard_derivatives' );
-				extensions.get( 'OES_element_index_uint' );
-				extensions.get( 'ANGLE_instanced_arrays' );
+			if ( material.aoMap ) {
+
+				uniforms.aoMap.value = material.aoMap;
+				uniforms.aoMapIntensity.value = material.aoMapIntensity;
 
 			}
 
-			extensions.get( 'OES_texture_float_linear' );
+			// uv repeat and offset setting priorities
+			// 1. color map
+			// 2. specular map
+			// 3. normal map
+			// 4. bump map
+			// 5. alpha map
+			// 6. emissive map
 
-			utils = new WebGLUtils( _gl, extensions, capabilities );
+			var uvScaleMap;
 
-			state = new WebGLState( _gl, extensions, capabilities );
-			state.scissor( _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ).floor() );
-			state.viewport( _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ).floor() );
+			if ( material.map ) {
 
-			info = new WebGLInfo( _gl );
-			properties = new WebGLProperties();
-			textures = new WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info );
-			attributes = new WebGLAttributes( _gl, capabilities );
-			geometries = new WebGLGeometries( _gl, attributes, info );
-			objects = new WebGLObjects( _gl, geometries, attributes, info );
-			morphtargets = new WebGLMorphtargets( _gl );
-			programCache = new WebGLPrograms( _this, extensions, capabilities );
-			renderLists = new WebGLRenderLists();
-			renderStates = new WebGLRenderStates();
+				uvScaleMap = material.map;
 
-			background = new WebGLBackground( _this, state, objects, _premultipliedAlpha );
+			} else if ( material.specularMap ) {
 
-			bufferRenderer = new WebGLBufferRenderer( _gl, extensions, info, capabilities );
-			indexedBufferRenderer = new WebGLIndexedBufferRenderer( _gl, extensions, info, capabilities );
+				uvScaleMap = material.specularMap;
 
-			info.programs = programCache.programs;
+			} else if ( material.displacementMap ) {
 
-			_this.capabilities = capabilities;
-			_this.extensions = extensions;
-			_this.properties = properties;
-			_this.renderLists = renderLists;
-			_this.state = state;
-			_this.info = info;
+				uvScaleMap = material.displacementMap;
 
-		}
+			} else if ( material.normalMap ) {
 
-		initGLContext();
+				uvScaleMap = material.normalMap;
 
-		// xr
+			} else if ( material.bumpMap ) {
 
-		var xr = new WebXRManager( _this, _gl );
+				uvScaleMap = material.bumpMap;
 
-		this.xr = xr;
+			} else if ( material.roughnessMap ) {
 
-		// shadow map
+				uvScaleMap = material.roughnessMap;
 
-		var shadowMap = new WebGLShadowMap( _this, objects, capabilities.maxTextureSize );
+			} else if ( material.metalnessMap ) {
 
-		this.shadowMap = shadowMap;
+				uvScaleMap = material.metalnessMap;
 
-		// API
+			} else if ( material.alphaMap ) {
 
-		this.getContext = function () {
+				uvScaleMap = material.alphaMap;
 
-			return _gl;
+			} else if ( material.emissiveMap ) {
 
-		};
+				uvScaleMap = material.emissiveMap;
 
-		this.getContextAttributes = function () {
+			}
 
-			return _gl.getContextAttributes();
+			if ( uvScaleMap !== undefined ) {
 
-		};
+				// backwards compatibility
+				if ( uvScaleMap.isWebGLRenderTarget ) {
 
-		this.forceContextLoss = function () {
+					uvScaleMap = uvScaleMap.texture;
 
-			var extension = extensions.get( 'WEBGL_lose_context' );
-			if ( extension ) { extension.loseContext(); }
+				}
 
-		};
+				if ( uvScaleMap.matrixAutoUpdate === true ) {
 
-		this.forceContextRestore = function () {
+					uvScaleMap.updateMatrix();
 
-			var extension = extensions.get( 'WEBGL_lose_context' );
-			if ( extension ) { extension.restoreContext(); }
+				}
 
-		};
+				uniforms.uvTransform.value.copy( uvScaleMap.matrix );
 
-		this.getPixelRatio = function () {
+			}
 
-			return _pixelRatio;
+			// uv repeat and offset setting priorities for uv2
+			// 1. ao map
+			// 2. light map
 
-		};
+			var uv2ScaleMap;
 
-		this.setPixelRatio = function ( value ) {
+			if ( material.aoMap ) {
 
-			if ( value === undefined ) { return; }
+				uv2ScaleMap = material.aoMap;
 
-			_pixelRatio = value;
+			} else if ( material.lightMap ) {
 
-			this.setSize( _width, _height, false );
+				uv2ScaleMap = material.lightMap;
 
-		};
+			}
 
-		this.getSize = function ( target ) {
+			if ( uv2ScaleMap !== undefined ) {
 
-			if ( target === undefined ) {
+				// backwards compatibility
+				if ( uv2ScaleMap.isWebGLRenderTarget ) {
 
-				console.warn( 'WebGLRenderer: .getsize() now requires a Vector2 as an argument' );
+					uv2ScaleMap = uv2ScaleMap.texture;
 
-				target = new Vector2();
+				}
 
-			}
+				if ( uv2ScaleMap.matrixAutoUpdate === true ) {
 
-			return target.set( _width, _height );
+					uv2ScaleMap.updateMatrix();
 
-		};
+				}
 
-		this.setSize = function ( width, height, updateStyle ) {
+				uniforms.uv2Transform.value.copy( uv2ScaleMap.matrix );
 
-			if ( xr.isPresenting ) {
+			}
 
-				console.warn( 'THREE.WebGLRenderer: Can\'t change size while VR device is presenting.' );
-				return;
+		}
 
-			}
+		function refreshUniformsLine( uniforms, material ) {
 
-			_width = width;
-			_height = height;
+			uniforms.diffuse.value.copy( material.color );
+			uniforms.opacity.value = material.opacity;
 
-			_canvas.width = Math.floor( width * _pixelRatio );
-			_canvas.height = Math.floor( height * _pixelRatio );
+		}
 
-			if ( updateStyle !== false ) {
+		function refreshUniformsDash( uniforms, material ) {
 
-				_canvas.style.width = width + 'px';
-				_canvas.style.height = height + 'px';
+			uniforms.dashSize.value = material.dashSize;
+			uniforms.totalSize.value = material.dashSize + material.gapSize;
+			uniforms.scale.value = material.scale;
 
-			}
+		}
 
-			this.setViewport( 0, 0, width, height );
+		function refreshUniformsPoints( uniforms, material, pixelRatio, height ) {
 
-		};
+			uniforms.diffuse.value.copy( material.color );
+			uniforms.opacity.value = material.opacity;
+			uniforms.size.value = material.size * pixelRatio;
+			uniforms.scale.value = height * 0.5;
 
-		this.getDrawingBufferSize = function ( target ) {
+			if ( material.map ) {
 
-			if ( target === undefined ) {
+				uniforms.map.value = material.map;
 
-				console.warn( 'WebGLRenderer: .getdrawingBufferSize() now requires a Vector2 as an argument' );
+			}
 
-				target = new Vector2();
+			if ( material.alphaMap ) {
+
+				uniforms.alphaMap.value = material.alphaMap;
 
 			}
 
-			return target.set( _width * _pixelRatio, _height * _pixelRatio ).floor();
+			// uv repeat and offset setting priorities
+			// 1. color map
+			// 2. alpha map
 
-		};
+			var uvScaleMap;
 
-		this.setDrawingBufferSize = function ( width, height, pixelRatio ) {
+			if ( material.map ) {
 
-			_width = width;
-			_height = height;
+				uvScaleMap = material.map;
 
-			_pixelRatio = pixelRatio;
+			} else if ( material.alphaMap ) {
 
-			_canvas.width = Math.floor( width * pixelRatio );
-			_canvas.height = Math.floor( height * pixelRatio );
+				uvScaleMap = material.alphaMap;
 
-			this.setViewport( 0, 0, width, height );
+			}
 
-		};
+			if ( uvScaleMap !== undefined ) {
 
-		this.getCurrentViewport = function ( target ) {
+				if ( uvScaleMap.matrixAutoUpdate === true ) {
 
-			if ( target === undefined ) {
+					uvScaleMap.updateMatrix();
 
-				console.warn( 'WebGLRenderer: .getCurrentViewport() now requires a Vector4 as an argument' );
+				}
 
-				target = new Vector4();
+				uniforms.uvTransform.value.copy( uvScaleMap.matrix );
 
 			}
 
-			return target.copy( _currentViewport );
+		}
 
-		};
+		function refreshUniformsSprites( uniforms, material ) {
 
-		this.getViewport = function ( target ) {
+			uniforms.diffuse.value.copy( material.color );
+			uniforms.opacity.value = material.opacity;
+			uniforms.rotation.value = material.rotation;
 
-			return target.copy( _viewport );
+			if ( material.map ) {
 
-		};
+				uniforms.map.value = material.map;
 
-		this.setViewport = function ( x, y, width, height ) {
+			}
 
-			if ( x.isVector4 ) {
+			if ( material.alphaMap ) {
 
-				_viewport.set( x.x, x.y, x.z, x.w );
+				uniforms.alphaMap.value = material.alphaMap;
 
-			} else {
+			}
 
-				_viewport.set( x, y, width, height );
+			// uv repeat and offset setting priorities
+			// 1. color map
+			// 2. alpha map
 
-			}
+			var uvScaleMap;
 
-			state.viewport( _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ).floor() );
+			if ( material.map ) {
 
-		};
+				uvScaleMap = material.map;
 
-		this.getScissor = function ( target ) {
+			} else if ( material.alphaMap ) {
 
-			return target.copy( _scissor );
+				uvScaleMap = material.alphaMap;
 
-		};
+			}
 
-		this.setScissor = function ( x, y, width, height ) {
+			if ( uvScaleMap !== undefined ) {
 
-			if ( x.isVector4 ) {
+				if ( uvScaleMap.matrixAutoUpdate === true ) {
 
-				_scissor.set( x.x, x.y, x.z, x.w );
+					uvScaleMap.updateMatrix();
 
-			} else {
+				}
 
-				_scissor.set( x, y, width, height );
+				uniforms.uvTransform.value.copy( uvScaleMap.matrix );
 
 			}
 
-			state.scissor( _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ).floor() );
+		}
 
-		};
+		function refreshUniformsLambert( uniforms, material ) {
 
-		this.getScissorTest = function () {
+			if ( material.emissiveMap ) {
 
-			return _scissorTest;
+				uniforms.emissiveMap.value = material.emissiveMap;
 
-		};
+			}
 
-		this.setScissorTest = function ( boolean ) {
+		}
 
-			state.setScissorTest( _scissorTest = boolean );
+		function refreshUniformsPhong( uniforms, material ) {
 
-		};
+			uniforms.specular.value.copy( material.specular );
+			uniforms.shininess.value = Math.max( material.shininess, 1e-4 ); // to prevent pow( 0.0, 0.0 )
 
-		this.setOpaqueSort = function ( method ) {
+			if ( material.emissiveMap ) {
 
-			_opaqueSort = method;
+				uniforms.emissiveMap.value = material.emissiveMap;
 
-		};
+			}
 
-		this.setTransparentSort = function ( method ) {
+			if ( material.bumpMap ) {
 
-			_transparentSort = method;
+				uniforms.bumpMap.value = material.bumpMap;
+				uniforms.bumpScale.value = material.bumpScale;
+				if ( material.side === BackSide ) { uniforms.bumpScale.value *= - 1; }
 
-		};
+			}
 
-		// Clearing
+			if ( material.normalMap ) {
 
-		this.getClearColor = function () {
+				uniforms.normalMap.value = material.normalMap;
+				uniforms.normalScale.value.copy( material.normalScale );
+				if ( material.side === BackSide ) { uniforms.normalScale.value.negate(); }
 
-			return background.getClearColor();
+			}
 
-		};
+			if ( material.displacementMap ) {
 
-		this.setClearColor = function () {
+				uniforms.displacementMap.value = material.displacementMap;
+				uniforms.displacementScale.value = material.displacementScale;
+				uniforms.displacementBias.value = material.displacementBias;
 
-			background.setClearColor.apply( background, arguments );
+			}
 
-		};
+		}
 
-		this.getClearAlpha = function () {
+		function refreshUniformsToon( uniforms, material ) {
 
-			return background.getClearAlpha();
+			uniforms.specular.value.copy( material.specular );
+			uniforms.shininess.value = Math.max( material.shininess, 1e-4 ); // to prevent pow( 0.0, 0.0 )
 
-		};
+			if ( material.gradientMap ) {
 
-		this.setClearAlpha = function () {
+				uniforms.gradientMap.value = material.gradientMap;
 
-			background.setClearAlpha.apply( background, arguments );
+			}
 
-		};
+			if ( material.emissiveMap ) {
 
-		this.clear = function ( color, depth, stencil ) {
+				uniforms.emissiveMap.value = material.emissiveMap;
 
-			var bits = 0;
+			}
 
-			if ( color === undefined || color ) { bits |= 16384; }
-			if ( depth === undefined || depth ) { bits |= 256; }
-			if ( stencil === undefined || stencil ) { bits |= 1024; }
+			if ( material.bumpMap ) {
 
-			_gl.clear( bits );
+				uniforms.bumpMap.value = material.bumpMap;
+				uniforms.bumpScale.value = material.bumpScale;
+				if ( material.side === BackSide ) { uniforms.bumpScale.value *= - 1; }
 
-		};
+			}
 
-		this.clearColor = function () {
+			if ( material.normalMap ) {
 
-			this.clear( true, false, false );
+				uniforms.normalMap.value = material.normalMap;
+				uniforms.normalScale.value.copy( material.normalScale );
+				if ( material.side === BackSide ) { uniforms.normalScale.value.negate(); }
 
-		};
+			}
 
-		this.clearDepth = function () {
+			if ( material.displacementMap ) {
 
-			this.clear( false, true, false );
+				uniforms.displacementMap.value = material.displacementMap;
+				uniforms.displacementScale.value = material.displacementScale;
+				uniforms.displacementBias.value = material.displacementBias;
 
-		};
+			}
 
-		this.clearStencil = function () {
+		}
 
-			this.clear( false, false, true );
+		function refreshUniformsStandard( uniforms, material, environment ) {
 
-		};
-
-		//
+			uniforms.roughness.value = material.roughness;
+			uniforms.metalness.value = material.metalness;
 
-		this.dispose = function () {
+			if ( material.roughnessMap ) {
 
-			_canvas.removeEventListener( 'webglcontextlost', onContextLost, false );
-			_canvas.removeEventListener( 'webglcontextrestored', onContextRestore, false );
+				uniforms.roughnessMap.value = material.roughnessMap;
 
-			renderLists.dispose();
-			renderStates.dispose();
-			properties.dispose();
-			objects.dispose();
+			}
 
-			xr.dispose();
+			if ( material.metalnessMap ) {
 
-			animation.stop();
+				uniforms.metalnessMap.value = material.metalnessMap;
 
-		};
+			}
 
-		// Events
+			if ( material.emissiveMap ) {
 
-		function onContextLost( event ) {
+				uniforms.emissiveMap.value = material.emissiveMap;
 
-			event.preventDefault();
+			}
 
-			console.log( 'THREE.WebGLRenderer: Context Lost.' );
+			if ( material.bumpMap ) {
 
-			_isContextLost = true;
+				uniforms.bumpMap.value = material.bumpMap;
+				uniforms.bumpScale.value = material.bumpScale;
+				if ( material.side === BackSide ) { uniforms.bumpScale.value *= - 1; }
 
-		}
+			}
 
-		function onContextRestore( /* event */ ) {
+			if ( material.normalMap ) {
 
-			console.log( 'THREE.WebGLRenderer: Context Restored.' );
+				uniforms.normalMap.value = material.normalMap;
+				uniforms.normalScale.value.copy( material.normalScale );
+				if ( material.side === BackSide ) { uniforms.normalScale.value.negate(); }
 
-			_isContextLost = false;
+			}
 
-			initGLContext();
+			if ( material.displacementMap ) {
 
-		}
+				uniforms.displacementMap.value = material.displacementMap;
+				uniforms.displacementScale.value = material.displacementScale;
+				uniforms.displacementBias.value = material.displacementBias;
 
-		function onMaterialDispose( event ) {
+			}
 
-			var material = event.target;
+			if ( material.envMap || environment ) {
 
-			material.removeEventListener( 'dispose', onMaterialDispose );
+				//uniforms.envMap.value = material.envMap; // part of uniforms common
+				uniforms.envMapIntensity.value = material.envMapIntensity;
 
-			deallocateMaterial( material );
+			}
 
 		}
 
-		// Buffer deallocation
-
-		function deallocateMaterial( material ) {
-
-			releaseMaterialProgramReference( material );
+		function refreshUniformsPhysical( uniforms, material, environment ) {
 
-			properties.remove( material );
+			refreshUniformsStandard( uniforms, material, environment );
 
-		}
+			uniforms.reflectivity.value = material.reflectivity; // also part of uniforms common
 
+			uniforms.clearcoat.value = material.clearcoat;
+			uniforms.clearcoatRoughness.value = material.clearcoatRoughness;
+			if ( material.sheen ) { uniforms.sheen.value.copy( material.sheen ); }
 
-		function releaseMaterialProgramReference( material ) {
+			if ( material.clearcoatMap ) {
 
-			var programInfo = properties.get( material ).program;
+				uniforms.clearcoatMap.value = material.clearcoatMap;
 
-			material.program = undefined;
+			}
 
-			if ( programInfo !== undefined ) {
+			if ( material.clearcoatRoughnessMap ) {
 
-				programCache.releaseProgram( programInfo );
+				uniforms.clearcoatRoughnessMap.value = material.clearcoatRoughnessMap;
 
 			}
 
-		}
-
-		// Buffer rendering
+			if ( material.clearcoatNormalMap ) {
 
-		function renderObjectImmediate( object, program ) {
+				uniforms.clearcoatNormalScale.value.copy( material.clearcoatNormalScale );
+				uniforms.clearcoatNormalMap.value = material.clearcoatNormalMap;
 
-			object.render( function ( object ) {
+				if ( material.side === BackSide ) {
 
-				_this.renderBufferImmediate( object, program );
+					uniforms.clearcoatNormalScale.value.negate();
 
-			} );
+				}
 
-		}
+			}
 
-		this.renderBufferImmediate = function ( object, program ) {
+			uniforms.transparency.value = material.transparency;
 
-			state.initAttributes();
+		}
 
-			var buffers = properties.get( object );
+		function refreshUniformsMatcap( uniforms, material ) {
 
-			if ( object.hasPositions && ! buffers.position ) { buffers.position = _gl.createBuffer(); }
-			if ( object.hasNormals && ! buffers.normal ) { buffers.normal = _gl.createBuffer(); }
-			if ( object.hasUvs && ! buffers.uv ) { buffers.uv = _gl.createBuffer(); }
-			if ( object.hasColors && ! buffers.color ) { buffers.color = _gl.createBuffer(); }
+			if ( material.matcap ) {
 
-			var programAttributes = program.getAttributes();
+				uniforms.matcap.value = material.matcap;
 
-			if ( object.hasPositions ) {
+			}
 
-				_gl.bindBuffer( 34962, buffers.position );
-				_gl.bufferData( 34962, object.positionArray, 35048 );
+			if ( material.bumpMap ) {
 
-				state.enableAttribute( programAttributes.position );
-				_gl.vertexAttribPointer( programAttributes.position, 3, 5126, false, 0, 0 );
+				uniforms.bumpMap.value = material.bumpMap;
+				uniforms.bumpScale.value = material.bumpScale;
+				if ( material.side === BackSide ) { uniforms.bumpScale.value *= - 1; }
 
 			}
 
-			if ( object.hasNormals ) {
-
-				_gl.bindBuffer( 34962, buffers.normal );
-				_gl.bufferData( 34962, object.normalArray, 35048 );
+			if ( material.normalMap ) {
 
-				state.enableAttribute( programAttributes.normal );
-				_gl.vertexAttribPointer( programAttributes.normal, 3, 5126, false, 0, 0 );
+				uniforms.normalMap.value = material.normalMap;
+				uniforms.normalScale.value.copy( material.normalScale );
+				if ( material.side === BackSide ) { uniforms.normalScale.value.negate(); }
 
 			}
 
-			if ( object.hasUvs ) {
-
-				_gl.bindBuffer( 34962, buffers.uv );
-				_gl.bufferData( 34962, object.uvArray, 35048 );
+			if ( material.displacementMap ) {
 
-				state.enableAttribute( programAttributes.uv );
-				_gl.vertexAttribPointer( programAttributes.uv, 2, 5126, false, 0, 0 );
+				uniforms.displacementMap.value = material.displacementMap;
+				uniforms.displacementScale.value = material.displacementScale;
+				uniforms.displacementBias.value = material.displacementBias;
 
 			}
 
-			if ( object.hasColors ) {
-
-				_gl.bindBuffer( 34962, buffers.color );
-				_gl.bufferData( 34962, object.colorArray, 35048 );
-
-				state.enableAttribute( programAttributes.color );
-				_gl.vertexAttribPointer( programAttributes.color, 3, 5126, false, 0, 0 );
+		}
 
-			}
+		function refreshUniformsDepth( uniforms, material ) {
 
-			state.disableUnusedAttributes();
+			if ( material.displacementMap ) {
 
-			_gl.drawArrays( 4, 0, object.count );
+				uniforms.displacementMap.value = material.displacementMap;
+				uniforms.displacementScale.value = material.displacementScale;
+				uniforms.displacementBias.value = material.displacementBias;
 
-			object.count = 0;
+			}
 
-		};
+		}
 
-		var tempScene = new Scene();
+		function refreshUniformsDistance( uniforms, material ) {
 
-		this.renderBufferDirect = function ( camera, scene, geometry, material, object, group ) {
+			if ( material.displacementMap ) {
 
-			if ( scene === null ) { scene = tempScene; } // renderBufferDirect second parameter used to be fog (could be null)
+				uniforms.displacementMap.value = material.displacementMap;
+				uniforms.displacementScale.value = material.displacementScale;
+				uniforms.displacementBias.value = material.displacementBias;
 
-			var frontFaceCW = ( object.isMesh && object.matrixWorld.determinant() < 0 );
+			}
 
-			var program = setProgram( camera, scene, material, object );
+			uniforms.referencePosition.value.copy( material.referencePosition );
+			uniforms.nearDistance.value = material.nearDistance;
+			uniforms.farDistance.value = material.farDistance;
 
-			state.setMaterial( material, frontFaceCW );
+		}
 
-			var updateBuffers = false;
+		function refreshUniformsNormal( uniforms, material ) {
 
-			if ( _currentGeometryProgram.geometry !== geometry.id ||
-				_currentGeometryProgram.program !== program.id ||
-				_currentGeometryProgram.wireframe !== ( material.wireframe === true ) ) {
+			if ( material.bumpMap ) {
 
-				_currentGeometryProgram.geometry = geometry.id;
-				_currentGeometryProgram.program = program.id;
-				_currentGeometryProgram.wireframe = material.wireframe === true;
-				updateBuffers = true;
+				uniforms.bumpMap.value = material.bumpMap;
+				uniforms.bumpScale.value = material.bumpScale;
+				if ( material.side === BackSide ) { uniforms.bumpScale.value *= - 1; }
 
 			}
 
-			if ( material.morphTargets || material.morphNormals ) {
-
-				morphtargets.update( object, geometry, material, program );
+			if ( material.normalMap ) {
 
-				updateBuffers = true;
+				uniforms.normalMap.value = material.normalMap;
+				uniforms.normalScale.value.copy( material.normalScale );
+				if ( material.side === BackSide ) { uniforms.normalScale.value.negate(); }
 
 			}
 
-			if ( object.isInstancedMesh === true ) {
+			if ( material.displacementMap ) {
 
-				updateBuffers = true;
+				uniforms.displacementMap.value = material.displacementMap;
+				uniforms.displacementScale.value = material.displacementScale;
+				uniforms.displacementBias.value = material.displacementBias;
 
 			}
 
-			//
+		}
 
-			var index = geometry.index;
-			var position = geometry.attributes.position;
+		return {
+			refreshFogUniforms: refreshFogUniforms,
+			refreshMaterialUniforms: refreshMaterialUniforms
+		};
 
-			//
+	}
 
-			if ( index === null ) {
+	/**
+	 * @author supereggbert / http://www.paulbrunt.co.uk/
+	 * @author mrdoob / http://mrdoob.com/
+	 * @author alteredq / http://alteredqualia.com/
+	 * @author szimek / https://github.com/szimek/
+	 * @author tschw
+	 */
 
-				if ( position === undefined || position.count === 0 ) { return; }
+	function WebGLRenderer( parameters ) {
 
-			} else if ( index.count === 0 ) {
+		parameters = parameters || {};
 
-				return;
+		var _canvas = parameters.canvas !== undefined ? parameters.canvas : document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ),
+			_context = parameters.context !== undefined ? parameters.context : null,
 
-			}
+			_alpha = parameters.alpha !== undefined ? parameters.alpha : false,
+			_depth = parameters.depth !== undefined ? parameters.depth : true,
+			_stencil = parameters.stencil !== undefined ? parameters.stencil : true,
+			_antialias = parameters.antialias !== undefined ? parameters.antialias : false,
+			_premultipliedAlpha = parameters.premultipliedAlpha !== undefined ? parameters.premultipliedAlpha : true,
+			_preserveDrawingBuffer = parameters.preserveDrawingBuffer !== undefined ? parameters.preserveDrawingBuffer : false,
+			_powerPreference = parameters.powerPreference !== undefined ? parameters.powerPreference : 'default',
+			_failIfMajorPerformanceCaveat = parameters.failIfMajorPerformanceCaveat !== undefined ? parameters.failIfMajorPerformanceCaveat : false;
 
-			//
+		var currentRenderList = null;
+		var currentRenderState = null;
 
-			var rangeFactor = 1;
+		// public properties
 
-			if ( material.wireframe === true ) {
+		this.domElement = _canvas;
 
-				index = geometries.getWireframeAttribute( geometry );
-				rangeFactor = 2;
+		// Debug configuration container
+		this.debug = {
 
-			}
+			/**
+			 * Enables error checking and reporting when shader programs are being compiled
+			 * @type {boolean}
+			 */
+			checkShaderErrors: true
+		};
 
-			var attribute;
-			var renderer = bufferRenderer;
+		// clearing
 
-			if ( index !== null ) {
-
-				attribute = attributes.get( index );
+		this.autoClear = true;
+		this.autoClearColor = true;
+		this.autoClearDepth = true;
+		this.autoClearStencil = true;
 
-				renderer = indexedBufferRenderer;
-				renderer.setIndex( attribute );
+		// scene graph
 
-			}
+		this.sortObjects = true;
 
-			if ( updateBuffers ) {
+		// user-defined clipping
 
-				setupVertexAttributes( object, geometry, material, program );
+		this.clippingPlanes = [];
+		this.localClippingEnabled = false;
 
-				if ( index !== null ) {
+		// physically based shading
 
-					_gl.bindBuffer( 34963, attribute.buffer );
+		this.gammaFactor = 2.0;	// for backwards compatibility
+		this.outputEncoding = LinearEncoding;
 
-				}
+		// physical lights
 
-			}
+		this.physicallyCorrectLights = false;
 
-			//
+		// tone mapping
 
-			var dataCount = ( index !== null ) ? index.count : position.count;
+		this.toneMapping = NoToneMapping;
+		this.toneMappingExposure = 1.0;
+		this.toneMappingWhitePoint = 1.0;
 
-			var rangeStart = geometry.drawRange.start * rangeFactor;
-			var rangeCount = geometry.drawRange.count * rangeFactor;
+		// morphs
 
-			var groupStart = group !== null ? group.start * rangeFactor : 0;
-			var groupCount = group !== null ? group.count * rangeFactor : Infinity;
+		this.maxMorphTargets = 8;
+		this.maxMorphNormals = 4;
 
-			var drawStart = Math.max( rangeStart, groupStart );
-			var drawEnd = Math.min( dataCount, rangeStart + rangeCount, groupStart + groupCount ) - 1;
+		// internal properties
 
-			var drawCount = Math.max( 0, drawEnd - drawStart + 1 );
+		var _this = this,
 
-			if ( drawCount === 0 ) { return; }
+			_isContextLost = false,
 
-			//
+			// internal state cache
 
-			if ( object.isMesh ) {
+			_framebuffer = null,
 
-				if ( material.wireframe === true ) {
+			_currentActiveCubeFace = 0,
+			_currentActiveMipmapLevel = 0,
+			_currentRenderTarget = null,
+			_currentFramebuffer = null,
+			_currentMaterialId = - 1,
 
-					state.setLineWidth( material.wireframeLinewidth * getTargetPixelRatio() );
-					renderer.setMode( 1 );
+			// geometry and program caching
 
-				} else {
+			_currentGeometryProgram = {
+				geometry: null,
+				program: null,
+				wireframe: false
+			},
 
-					renderer.setMode( 4 );
+			_currentCamera = null,
+			_currentArrayCamera = null,
 
-				}
+			_currentViewport = new Vector4(),
+			_currentScissor = new Vector4(),
+			_currentScissorTest = null,
 
-			} else if ( object.isLine ) {
+			//
 
-				var lineWidth = material.linewidth;
+			_width = _canvas.width,
+			_height = _canvas.height,
 
-				if ( lineWidth === undefined ) { lineWidth = 1; } // Not using Line*Material
+			_pixelRatio = 1,
+			_opaqueSort = null,
+			_transparentSort = null,
 
-				state.setLineWidth( lineWidth * getTargetPixelRatio() );
+			_viewport = new Vector4( 0, 0, _width, _height ),
+			_scissor = new Vector4( 0, 0, _width, _height ),
+			_scissorTest = false,
 
-				if ( object.isLineSegments ) {
+			// frustum
 
-					renderer.setMode( 1 );
+			_frustum = new Frustum(),
 
-				} else if ( object.isLineLoop ) {
+			// clipping
 
-					renderer.setMode( 2 );
+			_clipping = new WebGLClipping(),
+			_clippingEnabled = false,
+			_localClippingEnabled = false,
 
-				} else {
+			// camera matrices cache
 
-					renderer.setMode( 3 );
+			_projScreenMatrix = new Matrix4(),
 
-				}
+			_vector3 = new Vector3();
 
-			} else if ( object.isPoints ) {
+		function getTargetPixelRatio() {
 
-				renderer.setMode( 0 );
+			return _currentRenderTarget === null ? _pixelRatio : 1;
 
-			} else if ( object.isSprite ) {
+		}
 
-				renderer.setMode( 4 );
+		// initialize
 
-			}
+		var _gl;
 
-			if ( object.isInstancedMesh ) {
+		try {
 
-				renderer.renderInstances( geometry, drawStart, drawCount, object.count );
+			var contextAttributes = {
+				alpha: _alpha,
+				depth: _depth,
+				stencil: _stencil,
+				antialias: _antialias,
+				premultipliedAlpha: _premultipliedAlpha,
+				preserveDrawingBuffer: _preserveDrawingBuffer,
+				powerPreference: _powerPreference,
+				failIfMajorPerformanceCaveat: _failIfMajorPerformanceCaveat,
+				xrCompatible: true
+			};
 
-			} else if ( geometry.isInstancedBufferGeometry ) {
+			// event listeners must be registered before WebGL context is created, see #12753
 
-				renderer.renderInstances( geometry, drawStart, drawCount, geometry.maxInstancedCount );
+			_canvas.addEventListener( 'webglcontextlost', onContextLost, false );
+			_canvas.addEventListener( 'webglcontextrestored', onContextRestore, false );
 
-			} else {
+			_gl = _context || _canvas.getContext( 'webgl', contextAttributes ) || _canvas.getContext( 'experimental-webgl', contextAttributes );
 
-				renderer.render( drawStart, drawCount );
+			if ( _gl === null ) {
 
-			}
+				if ( _canvas.getContext( 'webgl' ) !== null ) {
 
-		};
+					throw new Error( 'Error creating WebGL context with your selected attributes.' );
 
-		function setupVertexAttributes( object, geometry, material, program ) {
+				} else {
 
-			if ( capabilities.isWebGL2 === false && ( object.isInstancedMesh || geometry.isInstancedBufferGeometry ) ) {
+					throw new Error( 'Error creating WebGL context.' );
 
-				if ( extensions.get( 'ANGLE_instanced_arrays' ) === null ) { return; }
+				}
 
 			}
 
-			state.initAttributes();
+			// Some experimental-webgl implementations do not have getShaderPrecisionFormat
 
-			var geometryAttributes = geometry.attributes;
+			if ( _gl.getShaderPrecisionFormat === undefined ) {
 
-			var programAttributes = program.getAttributes();
+				_gl.getShaderPrecisionFormat = function () {
 
-			var materialDefaultAttributeValues = material.defaultAttributeValues;
+					return { 'rangeMin': 1, 'rangeMax': 1, 'precision': 1 };
 
-			for ( var name in programAttributes ) {
+				};
 
-				var programAttribute = programAttributes[ name ];
+			}
 
-				if ( programAttribute >= 0 ) {
+		} catch ( error ) {
 
-					var geometryAttribute = geometryAttributes[ name ];
+			console.error( 'THREE.WebGLRenderer: ' + error.message );
+			throw error;
 
-					if ( geometryAttribute !== undefined ) {
+		}
 
-						var normalized = geometryAttribute.normalized;
-						var size = geometryAttribute.itemSize;
+		var extensions, capabilities, state, info;
+		var properties, textures, attributes, geometries, objects;
+		var programCache, materials, renderLists, renderStates;
 
-						var attribute = attributes.get( geometryAttribute );
+		var background, morphtargets, bufferRenderer, indexedBufferRenderer;
 
-						// TODO Attribute may not be available on context restore
+		var utils;
 
-						if ( attribute === undefined ) { continue; }
+		function initGLContext() {
 
-						var buffer = attribute.buffer;
-						var type = attribute.type;
-						var bytesPerElement = attribute.bytesPerElement;
+			extensions = new WebGLExtensions( _gl );
 
-						if ( geometryAttribute.isInterleavedBufferAttribute ) {
+			capabilities = new WebGLCapabilities( _gl, extensions, parameters );
 
-							var data = geometryAttribute.data;
-							var stride = data.stride;
-							var offset = geometryAttribute.offset;
+			if ( capabilities.isWebGL2 === false ) {
 
-							if ( data && data.isInstancedInterleavedBuffer ) {
+				extensions.get( 'WEBGL_depth_texture' );
+				extensions.get( 'OES_texture_float' );
+				extensions.get( 'OES_texture_half_float' );
+				extensions.get( 'OES_texture_half_float_linear' );
+				extensions.get( 'OES_standard_derivatives' );
+				extensions.get( 'OES_element_index_uint' );
+				extensions.get( 'ANGLE_instanced_arrays' );
 
-								state.enableAttributeAndDivisor( programAttribute, data.meshPerAttribute );
+			}
 
-								if ( geometry.maxInstancedCount === undefined ) {
+			extensions.get( 'OES_texture_float_linear' );
 
-									geometry.maxInstancedCount = data.meshPerAttribute * data.count;
+			utils = new WebGLUtils( _gl, extensions, capabilities );
 
-								}
+			state = new WebGLState( _gl, extensions, capabilities );
+			state.scissor( _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ).floor() );
+			state.viewport( _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ).floor() );
 
-							} else {
+			info = new WebGLInfo( _gl );
+			properties = new WebGLProperties();
+			textures = new WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info );
+			attributes = new WebGLAttributes( _gl, capabilities );
+			geometries = new WebGLGeometries( _gl, attributes, info );
+			objects = new WebGLObjects( _gl, geometries, attributes, info );
+			morphtargets = new WebGLMorphtargets( _gl );
+			programCache = new WebGLPrograms( _this, extensions, capabilities );
+			materials = new WebGLMaterials( properties );
+			renderLists = new WebGLRenderLists();
+			renderStates = new WebGLRenderStates();
 
-								state.enableAttribute( programAttribute );
+			background = new WebGLBackground( _this, state, objects, _premultipliedAlpha );
 
-							}
+			bufferRenderer = new WebGLBufferRenderer( _gl, extensions, info, capabilities );
+			indexedBufferRenderer = new WebGLIndexedBufferRenderer( _gl, extensions, info, capabilities );
 
-							_gl.bindBuffer( 34962, buffer );
-							state.vertexAttribPointer( programAttribute, size, type, normalized, stride * bytesPerElement, offset * bytesPerElement );
+			info.programs = programCache.programs;
 
-						} else {
+			_this.capabilities = capabilities;
+			_this.extensions = extensions;
+			_this.properties = properties;
+			_this.renderLists = renderLists;
+			_this.state = state;
+			_this.info = info;
 
-							if ( geometryAttribute.isInstancedBufferAttribute ) {
+		}
 
-								state.enableAttributeAndDivisor( programAttribute, geometryAttribute.meshPerAttribute );
+		initGLContext();
 
-								if ( geometry.maxInstancedCount === undefined ) {
+		// xr
 
-									geometry.maxInstancedCount = geometryAttribute.meshPerAttribute * geometryAttribute.count;
+		var xr = new WebXRManager( _this, _gl );
 
-								}
+		this.xr = xr;
 
-							} else {
+		// shadow map
 
-								state.enableAttribute( programAttribute );
+		var shadowMap = new WebGLShadowMap( _this, objects, capabilities.maxTextureSize );
 
-							}
+		this.shadowMap = shadowMap;
 
-							_gl.bindBuffer( 34962, buffer );
-							state.vertexAttribPointer( programAttribute, size, type, normalized, 0, 0 );
+		// API
 
-						}
+		this.getContext = function () {
 
-					} else if ( name === 'instanceMatrix' ) {
+			return _gl;
 
-						var attribute = attributes.get( object.instanceMatrix );
+		};
 
-						// TODO Attribute may not be available on context restore
+		this.getContextAttributes = function () {
 
-						if ( attribute === undefined ) { continue; }
+			return _gl.getContextAttributes();
 
-						var buffer = attribute.buffer;
-						var type = attribute.type;
+		};
 
-						state.enableAttributeAndDivisor( programAttribute + 0, 1 );
-						state.enableAttributeAndDivisor( programAttribute + 1, 1 );
-						state.enableAttributeAndDivisor( programAttribute + 2, 1 );
-						state.enableAttributeAndDivisor( programAttribute + 3, 1 );
+		this.forceContextLoss = function () {
 
-						_gl.bindBuffer( 34962, buffer );
+			var extension = extensions.get( 'WEBGL_lose_context' );
+			if ( extension ) { extension.loseContext(); }
 
-						_gl.vertexAttribPointer( programAttribute + 0, 4, type, false, 64, 0 );
-						_gl.vertexAttribPointer( programAttribute + 1, 4, type, false, 64, 16 );
-						_gl.vertexAttribPointer( programAttribute + 2, 4, type, false, 64, 32 );
-						_gl.vertexAttribPointer( programAttribute + 3, 4, type, false, 64, 48 );
+		};
 
-					} else if ( materialDefaultAttributeValues !== undefined ) {
+		this.forceContextRestore = function () {
 
-						var value = materialDefaultAttributeValues[ name ];
+			var extension = extensions.get( 'WEBGL_lose_context' );
+			if ( extension ) { extension.restoreContext(); }
 
-						if ( value !== undefined ) {
+		};
 
-							switch ( value.length ) {
+		this.getPixelRatio = function () {
 
-								case 2:
-									_gl.vertexAttrib2fv( programAttribute, value );
-									break;
+			return _pixelRatio;
 
-								case 3:
-									_gl.vertexAttrib3fv( programAttribute, value );
-									break;
+		};
 
-								case 4:
-									_gl.vertexAttrib4fv( programAttribute, value );
-									break;
+		this.setPixelRatio = function ( value ) {
 
-								default:
-									_gl.vertexAttrib1fv( programAttribute, value );
+			if ( value === undefined ) { return; }
 
-							}
+			_pixelRatio = value;
 
-						}
+			this.setSize( _width, _height, false );
 
-					}
+		};
 
-				}
+		this.getSize = function ( target ) {
 
-			}
+			if ( target === undefined ) {
 
-			state.disableUnusedAttributes();
+				console.warn( 'WebGLRenderer: .getsize() now requires a Vector2 as an argument' );
 
-		}
+				target = new Vector2();
 
-		// Compile
+			}
 
-		this.compile = function ( scene, camera ) {
+			return target.set( _width, _height );
 
-			currentRenderState = renderStates.get( scene, camera );
-			currentRenderState.init();
+		};
 
-			scene.traverse( function ( object ) {
+		this.setSize = function ( width, height, updateStyle ) {
 
-				if ( object.isLight ) {
+			if ( xr.isPresenting ) {
 
-					currentRenderState.pushLight( object );
+				console.warn( 'THREE.WebGLRenderer: Can\'t change size while VR device is presenting.' );
+				return;
 
-					if ( object.castShadow ) {
+			}
 
-						currentRenderState.pushShadow( object );
+			_width = width;
+			_height = height;
 
-					}
+			_canvas.width = Math.floor( width * _pixelRatio );
+			_canvas.height = Math.floor( height * _pixelRatio );
 
-				}
+			if ( updateStyle !== false ) {
 
-			} );
+				_canvas.style.width = width + 'px';
+				_canvas.style.height = height + 'px';
 
-			currentRenderState.setupLights( camera );
+			}
 
-			var compiled = {};
+			this.setViewport( 0, 0, width, height );
 
-			scene.traverse( function ( object ) {
+		};
 
-				if ( object.material ) {
+		this.getDrawingBufferSize = function ( target ) {
 
-					if ( Array.isArray( object.material ) ) {
+			if ( target === undefined ) {
 
-						for ( var i = 0; i < object.material.length; i ++ ) {
+				console.warn( 'WebGLRenderer: .getdrawingBufferSize() now requires a Vector2 as an argument' );
 
-							if ( object.material[ i ].uuid in compiled === false ) {
+				target = new Vector2();
 
-								initMaterial( object.material[ i ], scene, object );
-								compiled[ object.material[ i ].uuid ] = true;
+			}
 
-							}
+			return target.set( _width * _pixelRatio, _height * _pixelRatio ).floor();
 
-						}
+		};
 
-					} else if ( object.material.uuid in compiled === false ) {
+		this.setDrawingBufferSize = function ( width, height, pixelRatio ) {
 
-						initMaterial( object.material, scene, object );
-						compiled[ object.material.uuid ] = true;
+			_width = width;
+			_height = height;
 
-					}
+			_pixelRatio = pixelRatio;
 
-				}
+			_canvas.width = Math.floor( width * pixelRatio );
+			_canvas.height = Math.floor( height * pixelRatio );
 
-			} );
+			this.setViewport( 0, 0, width, height );
 
 		};
 
-		// Animation Loop
-
-		var onAnimationFrameCallback = null;
+		this.getCurrentViewport = function ( target ) {
 
-		function onAnimationFrame( time ) {
+			if ( target === undefined ) {
 
-			if ( xr.isPresenting ) { return; }
-			if ( onAnimationFrameCallback ) { onAnimationFrameCallback( time ); }
+				console.warn( 'WebGLRenderer: .getCurrentViewport() now requires a Vector4 as an argument' );
 
-		}
+				target = new Vector4();
 
-		var animation = new WebGLAnimation();
-		animation.setAnimationLoop( onAnimationFrame );
+			}
 
-		if ( typeof window !== 'undefined' ) { animation.setContext( window ); }
+			return target.copy( _currentViewport );
 
-		this.setAnimationLoop = function ( callback ) {
+		};
 
-			onAnimationFrameCallback = callback;
-			xr.setAnimationLoop( callback );
+		this.getViewport = function ( target ) {
 
-			animation.start();
+			return target.copy( _viewport );
 
 		};
 
-		// Rendering
+		this.setViewport = function ( x, y, width, height ) {
 
-		this.render = function ( scene, camera ) {
+			if ( x.isVector4 ) {
 
-			var renderTarget, forceClear;
+				_viewport.set( x.x, x.y, x.z, x.w );
 
-			if ( arguments[ 2 ] !== undefined ) {
+			} else {
 
-				console.warn( 'THREE.WebGLRenderer.render(): the renderTarget argument has been removed. Use .setRenderTarget() instead.' );
-				renderTarget = arguments[ 2 ];
+				_viewport.set( x, y, width, height );
 
 			}
 
-			if ( arguments[ 3 ] !== undefined ) {
+			state.viewport( _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ).floor() );
 
-				console.warn( 'THREE.WebGLRenderer.render(): the forceClear argument has been removed. Use .clear() instead.' );
-				forceClear = arguments[ 3 ];
+		};
 
-			}
+		this.getScissor = function ( target ) {
 
-			if ( ! ( camera && camera.isCamera ) ) {
+			return target.copy( _scissor );
 
-				console.error( 'THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.' );
-				return;
+		};
 
-			}
+		this.setScissor = function ( x, y, width, height ) {
 
-			if ( _isContextLost ) { return; }
+			if ( x.isVector4 ) {
 
-			// reset caching for this frame
+				_scissor.set( x.x, x.y, x.z, x.w );
 
-			_currentGeometryProgram.geometry = null;
-			_currentGeometryProgram.program = null;
-			_currentGeometryProgram.wireframe = false;
-			_currentMaterialId = - 1;
-			_currentCamera = null;
+			} else {
 
-			// update scene graph
+				_scissor.set( x, y, width, height );
 
-			if ( scene.autoUpdate === true ) { scene.updateMatrixWorld(); }
+			}
 
-			// update camera matrices and frustum
+			state.scissor( _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ).floor() );
 
-			if ( camera.parent === null ) { camera.updateMatrixWorld(); }
+		};
 
-			if ( xr.enabled && xr.isPresenting ) {
+		this.getScissorTest = function () {
 
-				camera = xr.getCamera( camera );
+			return _scissorTest;
 
-			}
+		};
 
-			//
-			scene.onBeforeRender( _this, scene, camera, renderTarget || _currentRenderTarget );
+		this.setScissorTest = function ( boolean ) {
 
-			currentRenderState = renderStates.get( scene, camera );
-			currentRenderState.init();
+			state.setScissorTest( _scissorTest = boolean );
 
-			_projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse );
-			_frustum.setFromProjectionMatrix( _projScreenMatrix );
+		};
 
-			_localClippingEnabled = this.localClippingEnabled;
-			_clippingEnabled = _clipping.init( this.clippingPlanes, _localClippingEnabled, camera );
+		this.setOpaqueSort = function ( method ) {
 
-			currentRenderList = renderLists.get( scene, camera );
-			currentRenderList.init();
+			_opaqueSort = method;
 
-			projectObject( scene, camera, 0, _this.sortObjects );
+		};
 
-			currentRenderList.finish();
+		this.setTransparentSort = function ( method ) {
 
-			if ( _this.sortObjects === true ) {
+			_transparentSort = method;
 
-				currentRenderList.sort( _opaqueSort, _transparentSort );
+		};
 
-			}
+		// Clearing
 
-			//
+		this.getClearColor = function () {
 
-			if ( _clippingEnabled ) { _clipping.beginShadows(); }
+			return background.getClearColor();
 
-			var shadowsArray = currentRenderState.state.shadowsArray;
+		};
 
-			shadowMap.render( shadowsArray, scene, camera );
+		this.setClearColor = function () {
 
-			currentRenderState.setupLights( camera );
+			background.setClearColor.apply( background, arguments );
 
-			if ( _clippingEnabled ) { _clipping.endShadows(); }
+		};
 
-			//
+		this.getClearAlpha = function () {
 
-			if ( this.info.autoReset ) { this.info.reset(); }
+			return background.getClearAlpha();
 
-			if ( renderTarget !== undefined ) {
+		};
 
-				this.setRenderTarget( renderTarget );
+		this.setClearAlpha = function () {
 
-			}
+			background.setClearAlpha.apply( background, arguments );
 
-			//
+		};
 
-			background.render( currentRenderList, scene, camera, forceClear );
+		this.clear = function ( color, depth, stencil ) {
 
-			// render scene
+			var bits = 0;
 
-			var opaqueObjects = currentRenderList.opaque;
-			var transparentObjects = currentRenderList.transparent;
+			if ( color === undefined || color ) { bits |= 16384; }
+			if ( depth === undefined || depth ) { bits |= 256; }
+			if ( stencil === undefined || stencil ) { bits |= 1024; }
 
-			if ( scene.overrideMaterial ) {
+			_gl.clear( bits );
 
-				var overrideMaterial = scene.overrideMaterial;
+		};
 
-				if ( opaqueObjects.length ) { renderObjects( opaqueObjects, scene, camera, overrideMaterial ); }
-				if ( transparentObjects.length ) { renderObjects( transparentObjects, scene, camera, overrideMaterial ); }
+		this.clearColor = function () {
 
-			} else {
+			this.clear( true, false, false );
 
-				// opaque pass (front-to-back order)
+		};
 
-				if ( opaqueObjects.length ) { renderObjects( opaqueObjects, scene, camera ); }
+		this.clearDepth = function () {
 
-				// transparent pass (back-to-front order)
+			this.clear( false, true, false );
 
-				if ( transparentObjects.length ) { renderObjects( transparentObjects, scene, camera ); }
+		};
 
-			}
+		this.clearStencil = function () {
 
-			//
+			this.clear( false, false, true );
 
-			scene.onAfterRender( _this, scene, camera );
+		};
 
-			//
+		//
 
-			if ( _currentRenderTarget !== null ) {
+		this.dispose = function () {
 
-				// Generate mipmap if we're using any kind of mipmap filtering
+			_canvas.removeEventListener( 'webglcontextlost', onContextLost, false );
+			_canvas.removeEventListener( 'webglcontextrestored', onContextRestore, false );
 
-				textures.updateRenderTargetMipmap( _currentRenderTarget );
+			renderLists.dispose();
+			renderStates.dispose();
+			properties.dispose();
+			objects.dispose();
 
-				// resolve multisample renderbuffers to a single-sample texture if necessary
+			xr.dispose();
 
-				textures.updateMultisampleRenderTarget( _currentRenderTarget );
+			animation.stop();
 
-			}
+		};
 
-			// Ensure depth buffer writing is enabled so it can be cleared on next render
+		// Events
 
-			state.buffers.depth.setTest( true );
-			state.buffers.depth.setMask( true );
-			state.buffers.color.setMask( true );
+		function onContextLost( event ) {
 
-			state.setPolygonOffset( false );
+			event.preventDefault();
 
-			// _gl.finish();
+			console.log( 'THREE.WebGLRenderer: Context Lost.' );
 
-			currentRenderList = null;
-			currentRenderState = null;
+			_isContextLost = true;
 
-		};
+		}
 
-		function projectObject( object, camera, groupOrder, sortObjects ) {
+		function onContextRestore( /* event */ ) {
 
-			if ( object.visible === false ) { return; }
+			console.log( 'THREE.WebGLRenderer: Context Restored.' );
 
-			var visible = object.layers.test( camera.layers );
+			_isContextLost = false;
 
-			if ( visible ) {
+			initGLContext();
 
-				if ( object.isGroup ) {
+		}
 
-					groupOrder = object.renderOrder;
+		function onMaterialDispose( event ) {
 
-				} else if ( object.isLOD ) {
+			var material = event.target;
 
-					if ( object.autoUpdate === true ) { object.update( camera ); }
+			material.removeEventListener( 'dispose', onMaterialDispose );
 
-				} else if ( object.isLight ) {
+			deallocateMaterial( material );
 
-					currentRenderState.pushLight( object );
+		}
 
-					if ( object.castShadow ) {
+		// Buffer deallocation
 
-						currentRenderState.pushShadow( object );
+		function deallocateMaterial( material ) {
 
-					}
+			releaseMaterialProgramReference( material );
 
-				} else if ( object.isSprite ) {
+			properties.remove( material );
 
-					if ( ! object.frustumCulled || _frustum.intersectsSprite( object ) ) {
+		}
 
-						if ( sortObjects ) {
 
-							_vector3.setFromMatrixPosition( object.matrixWorld )
-								.applyMatrix4( _projScreenMatrix );
+		function releaseMaterialProgramReference( material ) {
 
-						}
+			var programInfo = properties.get( material ).program;
 
-						var geometry = objects.update( object );
-						var material = object.material;
+			material.program = undefined;
 
-						if ( material.visible ) {
+			if ( programInfo !== undefined ) {
 
-							currentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null );
+				programCache.releaseProgram( programInfo );
 
-						}
+			}
 
-					}
+		}
 
-				} else if ( object.isImmediateRenderObject ) {
+		// Buffer rendering
 
-					if ( sortObjects ) {
+		function renderObjectImmediate( object, program ) {
 
-						_vector3.setFromMatrixPosition( object.matrixWorld )
-							.applyMatrix4( _projScreenMatrix );
+			object.render( function ( object ) {
 
-					}
+				_this.renderBufferImmediate( object, program );
 
-					currentRenderList.push( object, null, object.material, groupOrder, _vector3.z, null );
+			} );
 
-				} else if ( object.isMesh || object.isLine || object.isPoints ) {
+		}
 
-					if ( object.isSkinnedMesh ) {
+		this.renderBufferImmediate = function ( object, program ) {
 
-						// update skeleton only once in a frame
+			state.initAttributes();
 
-						if ( object.skeleton.frame !== info.render.frame ) {
+			var buffers = properties.get( object );
 
-							object.skeleton.update();
-							object.skeleton.frame = info.render.frame;
+			if ( object.hasPositions && ! buffers.position ) { buffers.position = _gl.createBuffer(); }
+			if ( object.hasNormals && ! buffers.normal ) { buffers.normal = _gl.createBuffer(); }
+			if ( object.hasUvs && ! buffers.uv ) { buffers.uv = _gl.createBuffer(); }
+			if ( object.hasColors && ! buffers.color ) { buffers.color = _gl.createBuffer(); }
 
-						}
+			var programAttributes = program.getAttributes();
 
-					}
+			if ( object.hasPositions ) {
 
-					if ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) {
+				_gl.bindBuffer( 34962, buffers.position );
+				_gl.bufferData( 34962, object.positionArray, 35048 );
 
-						if ( sortObjects ) {
+				state.enableAttribute( programAttributes.position );
+				_gl.vertexAttribPointer( programAttributes.position, 3, 5126, false, 0, 0 );
 
-							_vector3.setFromMatrixPosition( object.matrixWorld )
-								.applyMatrix4( _projScreenMatrix );
+			}
 
-						}
+			if ( object.hasNormals ) {
 
-						var geometry = objects.update( object );
-						var material = object.material;
+				_gl.bindBuffer( 34962, buffers.normal );
+				_gl.bufferData( 34962, object.normalArray, 35048 );
 
-						if ( Array.isArray( material ) ) {
+				state.enableAttribute( programAttributes.normal );
+				_gl.vertexAttribPointer( programAttributes.normal, 3, 5126, false, 0, 0 );
 
-							var groups = geometry.groups;
+			}
 
-							for ( var i = 0, l = groups.length; i < l; i ++ ) {
+			if ( object.hasUvs ) {
 
-								var group = groups[ i ];
-								var groupMaterial = material[ group.materialIndex ];
+				_gl.bindBuffer( 34962, buffers.uv );
+				_gl.bufferData( 34962, object.uvArray, 35048 );
 
-								if ( groupMaterial && groupMaterial.visible ) {
+				state.enableAttribute( programAttributes.uv );
+				_gl.vertexAttribPointer( programAttributes.uv, 2, 5126, false, 0, 0 );
 
-									currentRenderList.push( object, geometry, groupMaterial, groupOrder, _vector3.z, group );
+			}
 
-								}
+			if ( object.hasColors ) {
 
-							}
+				_gl.bindBuffer( 34962, buffers.color );
+				_gl.bufferData( 34962, object.colorArray, 35048 );
 
-						} else if ( material.visible ) {
+				state.enableAttribute( programAttributes.color );
+				_gl.vertexAttribPointer( programAttributes.color, 3, 5126, false, 0, 0 );
 
-							currentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null );
+			}
 
-						}
+			state.disableUnusedAttributes();
 
-					}
+			_gl.drawArrays( 4, 0, object.count );
 
-				}
+			object.count = 0;
 
-			}
+		};
 
-			var children = object.children;
+		var tempScene = new Scene();
 
-			for ( var i = 0, l = children.length; i < l; i ++ ) {
+		this.renderBufferDirect = function ( camera, scene, geometry, material, object, group ) {
 
-				projectObject( children[ i ], camera, groupOrder, sortObjects );
+			if ( scene === null ) { scene = tempScene; } // renderBufferDirect second parameter used to be fog (could be null)
 
-			}
+			var frontFaceCW = ( object.isMesh && object.matrixWorld.determinant() < 0 );
 
-		}
+			var program = setProgram( camera, scene, material, object );
 
-		function renderObjects( renderList, scene, camera, overrideMaterial ) {
+			state.setMaterial( material, frontFaceCW );
 
-			for ( var i = 0, l = renderList.length; i < l; i ++ ) {
+			var updateBuffers = false;
 
-				var renderItem = renderList[ i ];
+			if ( _currentGeometryProgram.geometry !== geometry.id ||
+				_currentGeometryProgram.program !== program.id ||
+				_currentGeometryProgram.wireframe !== ( material.wireframe === true ) ) {
 
-				var object = renderItem.object;
-				var geometry = renderItem.geometry;
-				var material = overrideMaterial === undefined ? renderItem.material : overrideMaterial;
-				var group = renderItem.group;
+				_currentGeometryProgram.geometry = geometry.id;
+				_currentGeometryProgram.program = program.id;
+				_currentGeometryProgram.wireframe = material.wireframe === true;
+				updateBuffers = true;
 
-				if ( camera.isArrayCamera ) {
+			}
 
-					_currentArrayCamera = camera;
+			if ( material.morphTargets || material.morphNormals ) {
 
-					var cameras = camera.cameras;
+				morphtargets.update( object, geometry, material, program );
 
-					for ( var j = 0, jl = cameras.length; j < jl; j ++ ) {
+				updateBuffers = true;
 
-						var camera2 = cameras[ j ];
+			}
 
-						if ( object.layers.test( camera2.layers ) ) {
+			if ( object.isInstancedMesh === true ) {
 
-							state.viewport( _currentViewport.copy( camera2.viewport ) );
+				updateBuffers = true;
 
-							currentRenderState.setupLights( camera2 );
+			}
 
-							renderObject( object, scene, camera2, geometry, material, group );
+			//
 
-						}
+			var index = geometry.index;
+			var position = geometry.attributes.position;
 
-					}
+			//
 
-				} else {
+			if ( index === null ) {
 
-					_currentArrayCamera = null;
+				if ( position === undefined || position.count === 0 ) { return; }
 
-					renderObject( object, scene, camera, geometry, material, group );
+			} else if ( index.count === 0 ) {
 
-				}
+				return;
 
 			}
 
-		}
-
-		function renderObject( object, scene, camera, geometry, material, group ) {
-
-			object.onBeforeRender( _this, scene, camera, geometry, material, group );
-			currentRenderState = renderStates.get( scene, _currentArrayCamera || camera );
+			//
 
-			object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld );
-			object.normalMatrix.getNormalMatrix( object.modelViewMatrix );
+			var rangeFactor = 1;
 
-			if ( object.isImmediateRenderObject ) {
+			if ( material.wireframe === true ) {
 
-				var program = setProgram( camera, scene, material, object );
+				index = geometries.getWireframeAttribute( geometry );
+				rangeFactor = 2;
 
-				state.setMaterial( material );
+			}
 
-				_currentGeometryProgram.geometry = null;
-				_currentGeometryProgram.program = null;
-				_currentGeometryProgram.wireframe = false;
+			var attribute;
+			var renderer = bufferRenderer;
 
-				renderObjectImmediate( object, program );
+			if ( index !== null ) {
 
-			} else {
+				attribute = attributes.get( index );
 
-				_this.renderBufferDirect( camera, scene, geometry, material, object, group );
+				renderer = indexedBufferRenderer;
+				renderer.setIndex( attribute );
 
 			}
 
-			object.onAfterRender( _this, scene, camera, geometry, material, group );
-			currentRenderState = renderStates.get( scene, _currentArrayCamera || camera );
-
-		}
+			if ( updateBuffers ) {
 
-		function initMaterial( material, scene, object ) {
+				setupVertexAttributes( object, geometry, material, program );
 
-			var materialProperties = properties.get( material );
+				if ( index !== null ) {
 
-			var lights = currentRenderState.state.lights;
-			var shadowsArray = currentRenderState.state.shadowsArray;
+					_gl.bindBuffer( 34963, attribute.buffer );
 
-			var lightsStateVersion = lights.state.version;
+				}
 
-			var parameters = programCache.getParameters( material, lights.state, shadowsArray, scene, _clipping.numPlanes, _clipping.numIntersection, object );
-			var programCacheKey = programCache.getProgramCacheKey( parameters );
+			}
 
-			var program = materialProperties.program;
-			var programChange = true;
+			//
 
-			if ( program === undefined ) {
+			var dataCount = ( index !== null ) ? index.count : position.count;
 
-				// new material
-				material.addEventListener( 'dispose', onMaterialDispose );
+			var rangeStart = geometry.drawRange.start * rangeFactor;
+			var rangeCount = geometry.drawRange.count * rangeFactor;
 
-			} else if ( program.cacheKey !== programCacheKey ) {
+			var groupStart = group !== null ? group.start * rangeFactor : 0;
+			var groupCount = group !== null ? group.count * rangeFactor : Infinity;
 
-				// changed glsl or parameters
-				releaseMaterialProgramReference( material );
+			var drawStart = Math.max( rangeStart, groupStart );
+			var drawEnd = Math.min( dataCount, rangeStart + rangeCount, groupStart + groupCount ) - 1;
 
-			} else if ( materialProperties.lightsStateVersion !== lightsStateVersion ) {
+			var drawCount = Math.max( 0, drawEnd - drawStart + 1 );
 
-				materialProperties.lightsStateVersion = lightsStateVersion;
+			if ( drawCount === 0 ) { return; }
 
-				programChange = false;
+			//
 
-			} else if ( parameters.shaderID !== undefined ) {
+			if ( object.isMesh ) {
 
-				// same glsl and uniform list
-				return;
+				if ( material.wireframe === true ) {
 
-			} else {
+					state.setLineWidth( material.wireframeLinewidth * getTargetPixelRatio() );
+					renderer.setMode( 1 );
 
-				// only rebuild uniform list
-				programChange = false;
+				} else {
 
-			}
+					renderer.setMode( 4 );
 
-			if ( programChange ) {
+				}
 
-				program = programCache.acquireProgram( parameters, programCacheKey );
+			} else if ( object.isLine ) {
 
-				materialProperties.program = program;
-				materialProperties.uniforms = parameters.uniforms;
-				materialProperties.outputEncoding = parameters.outputEncoding;
-				material.program = program;
+				var lineWidth = material.linewidth;
 
-			}
+				if ( lineWidth === undefined ) { lineWidth = 1; } // Not using Line*Material
 
-			var programAttributes = program.getAttributes();
+				state.setLineWidth( lineWidth * getTargetPixelRatio() );
 
-			if ( material.morphTargets ) {
+				if ( object.isLineSegments ) {
 
-				material.numSupportedMorphTargets = 0;
+					renderer.setMode( 1 );
 
-				for ( var i = 0; i < _this.maxMorphTargets; i ++ ) {
+				} else if ( object.isLineLoop ) {
 
-					if ( programAttributes[ 'morphTarget' + i ] >= 0 ) {
+					renderer.setMode( 2 );
 
-						material.numSupportedMorphTargets ++;
+				} else {
 
-					}
+					renderer.setMode( 3 );
 
 				}
 
-			}
+			} else if ( object.isPoints ) {
 
-			if ( material.morphNormals ) {
+				renderer.setMode( 0 );
 
-				material.numSupportedMorphNormals = 0;
+			} else if ( object.isSprite ) {
 
-				for ( var i = 0; i < _this.maxMorphNormals; i ++ ) {
+				renderer.setMode( 4 );
 
-					if ( programAttributes[ 'morphNormal' + i ] >= 0 ) {
+			}
 
-						material.numSupportedMorphNormals ++;
+			if ( object.isInstancedMesh ) {
 
-					}
+				renderer.renderInstances( geometry, drawStart, drawCount, object.count );
 
-				}
+			} else if ( geometry.isInstancedBufferGeometry ) {
+
+				renderer.renderInstances( geometry, drawStart, drawCount, geometry.maxInstancedCount );
+
+			} else {
+
+				renderer.render( drawStart, drawCount );
 
 			}
 
-			var uniforms = materialProperties.uniforms;
+		};
 
-			if ( ! material.isShaderMaterial &&
-				! material.isRawShaderMaterial ||
-				material.clipping === true ) {
+		function setupVertexAttributes( object, geometry, material, program ) {
 
-				materialProperties.numClippingPlanes = _clipping.numPlanes;
-				materialProperties.numIntersection = _clipping.numIntersection;
-				uniforms.clippingPlanes = _clipping.uniform;
+			if ( capabilities.isWebGL2 === false && ( object.isInstancedMesh || geometry.isInstancedBufferGeometry ) ) {
+
+				if ( extensions.get( 'ANGLE_instanced_arrays' ) === null ) { return; }
 
 			}
 
-			materialProperties.environment = material.isMeshStandardMaterial ? scene.environment : null;
-			materialProperties.fog = scene.fog;
+			state.initAttributes();
 
-			// store the light setup it was created for
+			var geometryAttributes = geometry.attributes;
 
-			materialProperties.needsLights = materialNeedsLights( material );
-			materialProperties.lightsStateVersion = lightsStateVersion;
+			var programAttributes = program.getAttributes();
 
-			if ( materialProperties.needsLights ) {
+			var materialDefaultAttributeValues = material.defaultAttributeValues;
 
-				// wire up the material to this renderer's lighting state
+			for ( var name in programAttributes ) {
 
-				uniforms.ambientLightColor.value = lights.state.ambient;
-				uniforms.lightProbe.value = lights.state.probe;
-				uniforms.directionalLights.value = lights.state.directional;
-				uniforms.directionalLightShadows.value = lights.state.directionalShadow;
-				uniforms.spotLights.value = lights.state.spot;
-				uniforms.spotLightShadows.value = lights.state.spotShadow;
-				uniforms.rectAreaLights.value = lights.state.rectArea;
-				uniforms.pointLights.value = lights.state.point;
-				uniforms.pointLightShadows.value = lights.state.pointShadow;
-				uniforms.hemisphereLights.value = lights.state.hemi;
+				var programAttribute = programAttributes[ name ];
 
-				uniforms.directionalShadowMap.value = lights.state.directionalShadowMap;
-				uniforms.directionalShadowMatrix.value = lights.state.directionalShadowMatrix;
-				uniforms.spotShadowMap.value = lights.state.spotShadowMap;
-				uniforms.spotShadowMatrix.value = lights.state.spotShadowMatrix;
-				uniforms.pointShadowMap.value = lights.state.pointShadowMap;
-				uniforms.pointShadowMatrix.value = lights.state.pointShadowMatrix;
-				// TODO (abelnation): add area lights shadow info to uniforms
+				if ( programAttribute >= 0 ) {
 
-			}
+					var geometryAttribute = geometryAttributes[ name ];
 
-			var progUniforms = materialProperties.program.getUniforms(),
-				uniformsList =
-					WebGLUniforms.seqWithValue( progUniforms.seq, uniforms );
+					if ( geometryAttribute !== undefined ) {
 
-			materialProperties.uniformsList = uniformsList;
+						var normalized = geometryAttribute.normalized;
+						var size = geometryAttribute.itemSize;
 
-		}
+						var attribute = attributes.get( geometryAttribute );
 
-		function setProgram( camera, scene, material, object ) {
+						// TODO Attribute may not be available on context restore
 
-			textures.resetTextureUnits();
+						if ( attribute === undefined ) { continue; }
 
-			var fog = scene.fog;
-			var environment = material.isMeshStandardMaterial ? scene.environment : null;
-			var encoding = ( _currentRenderTarget === null ) ? _this.outputEncoding : _currentRenderTarget.texture.encoding;
+						var buffer = attribute.buffer;
+						var type = attribute.type;
+						var bytesPerElement = attribute.bytesPerElement;
 
-			var materialProperties = properties.get( material );
-			var lights = currentRenderState.state.lights;
+						if ( geometryAttribute.isInterleavedBufferAttribute ) {
 
-			if ( _clippingEnabled ) {
+							var data = geometryAttribute.data;
+							var stride = data.stride;
+							var offset = geometryAttribute.offset;
 
-				if ( _localClippingEnabled || camera !== _currentCamera ) {
+							if ( data && data.isInstancedInterleavedBuffer ) {
 
-					var useCache =
-						camera === _currentCamera &&
-						material.id === _currentMaterialId;
+								state.enableAttributeAndDivisor( programAttribute, data.meshPerAttribute );
 
-					// we might want to call this function with some ClippingGroup
-					// object instead of the material, once it becomes feasible
-					// (#8465, #8379)
-					_clipping.setState(
-						material.clippingPlanes, material.clipIntersection, material.clipShadows,
-						camera, materialProperties, useCache );
+								if ( geometry.maxInstancedCount === undefined ) {
 
-				}
+									geometry.maxInstancedCount = data.meshPerAttribute * data.count;
 
-			}
+								}
 
-			if ( material.version === materialProperties.__version ) {
+							} else {
 
-				if ( materialProperties.program === undefined ) {
+								state.enableAttribute( programAttribute );
 
-					initMaterial( material, scene, object );
+							}
 
-				} else if ( material.fog && materialProperties.fog !== fog ) {
+							_gl.bindBuffer( 34962, buffer );
+							state.vertexAttribPointer( programAttribute, size, type, normalized, stride * bytesPerElement, offset * bytesPerElement );
 
-					initMaterial( material, scene, object );
+						} else {
 
-				} else if ( materialProperties.environment !== environment ) {
+							if ( geometryAttribute.isInstancedBufferAttribute ) {
 
-					initMaterial( material, scene, object );
+								state.enableAttributeAndDivisor( programAttribute, geometryAttribute.meshPerAttribute );
 
-				} else if ( materialProperties.needsLights && ( materialProperties.lightsStateVersion !== lights.state.version ) ) {
+								if ( geometry.maxInstancedCount === undefined ) {
 
-					initMaterial( material, scene, object );
+									geometry.maxInstancedCount = geometryAttribute.meshPerAttribute * geometryAttribute.count;
 
-				} else if ( materialProperties.numClippingPlanes !== undefined &&
-					( materialProperties.numClippingPlanes !== _clipping.numPlanes ||
-					materialProperties.numIntersection !== _clipping.numIntersection ) ) {
+								}
 
-					initMaterial( material, scene, object );
+							} else {
 
-				} else if ( materialProperties.outputEncoding !== encoding ) {
+								state.enableAttribute( programAttribute );
 
-					initMaterial( material, scene, object );
+							}
 
-				}
+							_gl.bindBuffer( 34962, buffer );
+							state.vertexAttribPointer( programAttribute, size, type, normalized, 0, 0 );
 
-			} else {
+						}
 
-				initMaterial( material, scene, object );
-				materialProperties.__version = material.version;
+					} else if ( name === 'instanceMatrix' ) {
 
-			}
+						var attribute = attributes.get( object.instanceMatrix );
 
-			var refreshProgram = false;
-			var refreshMaterial = false;
-			var refreshLights = false;
+						// TODO Attribute may not be available on context restore
 
-			var program = materialProperties.program,
-				p_uniforms = program.getUniforms(),
-				m_uniforms = materialProperties.uniforms;
+						if ( attribute === undefined ) { continue; }
 
-			if ( state.useProgram( program.program ) ) {
+						var buffer = attribute.buffer;
+						var type = attribute.type;
 
-				refreshProgram = true;
-				refreshMaterial = true;
-				refreshLights = true;
+						state.enableAttributeAndDivisor( programAttribute + 0, 1 );
+						state.enableAttributeAndDivisor( programAttribute + 1, 1 );
+						state.enableAttributeAndDivisor( programAttribute + 2, 1 );
+						state.enableAttributeAndDivisor( programAttribute + 3, 1 );
 
-			}
+						_gl.bindBuffer( 34962, buffer );
 
-			if ( material.id !== _currentMaterialId ) {
+						_gl.vertexAttribPointer( programAttribute + 0, 4, type, false, 64, 0 );
+						_gl.vertexAttribPointer( programAttribute + 1, 4, type, false, 64, 16 );
+						_gl.vertexAttribPointer( programAttribute + 2, 4, type, false, 64, 32 );
+						_gl.vertexAttribPointer( programAttribute + 3, 4, type, false, 64, 48 );
 
-				_currentMaterialId = material.id;
+					} else if ( materialDefaultAttributeValues !== undefined ) {
 
-				refreshMaterial = true;
+						var value = materialDefaultAttributeValues[ name ];
 
-			}
+						if ( value !== undefined ) {
 
-			if ( refreshProgram || _currentCamera !== camera ) {
+							switch ( value.length ) {
 
-				p_uniforms.setValue( _gl, 'projectionMatrix', camera.projectionMatrix );
+								case 2:
+									_gl.vertexAttrib2fv( programAttribute, value );
+									break;
 
-				if ( capabilities.logarithmicDepthBuffer ) {
+								case 3:
+									_gl.vertexAttrib3fv( programAttribute, value );
+									break;
 
-					p_uniforms.setValue( _gl, 'logDepthBufFC',
-						2.0 / ( Math.log( camera.far + 1.0 ) / Math.LN2 ) );
+								case 4:
+									_gl.vertexAttrib4fv( programAttribute, value );
+									break;
+
+								default:
+									_gl.vertexAttrib1fv( programAttribute, value );
+
+							}
+
+						}
+
+					}
 
 				}
 
-				if ( _currentCamera !== camera ) {
+			}
 
-					_currentCamera = camera;
+			state.disableUnusedAttributes();
 
-					// lighting uniforms depend on the camera so enforce an update
-					// now, in case this material supports lights - or later, when
-					// the next material that does gets activated:
+		}
 
-					refreshMaterial = true;		// set to true on material change
-					refreshLights = true;		// remains set until update done
+		// Compile
 
-				}
+		this.compile = function ( scene, camera ) {
 
-				// load material specific uniforms
-				// (shader material also gets them for the sake of genericity)
+			currentRenderState = renderStates.get( scene, camera );
+			currentRenderState.init();
 
-				if ( material.isShaderMaterial ||
-					material.isMeshPhongMaterial ||
-					material.isMeshToonMaterial ||
-					material.isMeshStandardMaterial ||
-					material.envMap ) {
+			scene.traverse( function ( object ) {
 
-					var uCamPos = p_uniforms.map.cameraPosition;
+				if ( object.isLight ) {
 
-					if ( uCamPos !== undefined ) {
+					currentRenderState.pushLight( object );
 
-						uCamPos.setValue( _gl,
-							_vector3.setFromMatrixPosition( camera.matrixWorld ) );
+					if ( object.castShadow ) {
+
+						currentRenderState.pushShadow( object );
 
 					}
 
 				}
 
-				if ( material.isMeshPhongMaterial ||
-					material.isMeshToonMaterial ||
-					material.isMeshLambertMaterial ||
-					material.isMeshBasicMaterial ||
-					material.isMeshStandardMaterial ||
-					material.isShaderMaterial ) {
+			} );
 
-					p_uniforms.setValue( _gl, 'isOrthographic', camera.isOrthographicCamera === true );
+			currentRenderState.setupLights( camera );
 
-				}
+			var compiled = {};
 
-				if ( material.isMeshPhongMaterial ||
-					material.isMeshToonMaterial ||
-					material.isMeshLambertMaterial ||
-					material.isMeshBasicMaterial ||
-					material.isMeshStandardMaterial ||
-					material.isShaderMaterial ||
-					material.skinning ) {
+			scene.traverse( function ( object ) {
 
-					p_uniforms.setValue( _gl, 'viewMatrix', camera.matrixWorldInverse );
+				if ( object.material ) {
+
+					if ( Array.isArray( object.material ) ) {
+
+						for ( var i = 0; i < object.material.length; i ++ ) {
+
+							if ( object.material[ i ].uuid in compiled === false ) {
+
+								initMaterial( object.material[ i ], scene, object );
+								compiled[ object.material[ i ].uuid ] = true;
+
+							}
+
+						}
+
+					} else if ( object.material.uuid in compiled === false ) {
+
+						initMaterial( object.material, scene, object );
+						compiled[ object.material.uuid ] = true;
+
+					}
 
 				}
 
-			}
+			} );
 
-			// skinning uniforms must be set even if material didn't change
-			// auto-setting of texture unit for bone texture must go before other textures
-			// otherwise textures used for skinning can take over texture units reserved for other material textures
+		};
 
-			if ( material.skinning ) {
+		// Animation Loop
 
-				p_uniforms.setOptional( _gl, object, 'bindMatrix' );
-				p_uniforms.setOptional( _gl, object, 'bindMatrixInverse' );
+		var onAnimationFrameCallback = null;
 
-				var skeleton = object.skeleton;
+		function onAnimationFrame( time ) {
 
-				if ( skeleton ) {
+			if ( xr.isPresenting ) { return; }
+			if ( onAnimationFrameCallback ) { onAnimationFrameCallback( time ); }
 
-					var bones = skeleton.bones;
+		}
 
-					if ( capabilities.floatVertexTextures ) {
+		var animation = new WebGLAnimation();
+		animation.setAnimationLoop( onAnimationFrame );
 
-						if ( skeleton.boneTexture === undefined ) {
+		if ( typeof window !== 'undefined' ) { animation.setContext( window ); }
 
-							// layout (1 matrix = 4 pixels)
-							//      RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4)
-							//  with  8x8  pixel texture max   16 bones * 4 pixels =  (8 * 8)
-							//       16x16 pixel texture max   64 bones * 4 pixels = (16 * 16)
-							//       32x32 pixel texture max  256 bones * 4 pixels = (32 * 32)
-							//       64x64 pixel texture max 1024 bones * 4 pixels = (64 * 64)
+		this.setAnimationLoop = function ( callback ) {
 
+			onAnimationFrameCallback = callback;
+			xr.setAnimationLoop( callback );
 
-							var size = Math.sqrt( bones.length * 4 ); // 4 pixels needed for 1 matrix
-							size = MathUtils.ceilPowerOfTwo( size );
-							size = Math.max( size, 4 );
+			animation.start();
 
-							var boneMatrices = new Float32Array( size * size * 4 ); // 4 floats per RGBA pixel
-							boneMatrices.set( skeleton.boneMatrices ); // copy current values
+		};
 
-							var boneTexture = new DataTexture( boneMatrices, size, size, RGBAFormat, FloatType );
+		// Rendering
 
-							skeleton.boneMatrices = boneMatrices;
-							skeleton.boneTexture = boneTexture;
-							skeleton.boneTextureSize = size;
+		this.render = function ( scene, camera ) {
 
-						}
+			var renderTarget, forceClear;
 
-						p_uniforms.setValue( _gl, 'boneTexture', skeleton.boneTexture, textures );
-						p_uniforms.setValue( _gl, 'boneTextureSize', skeleton.boneTextureSize );
+			if ( arguments[ 2 ] !== undefined ) {
 
-					} else {
+				console.warn( 'THREE.WebGLRenderer.render(): the renderTarget argument has been removed. Use .setRenderTarget() instead.' );
+				renderTarget = arguments[ 2 ];
 
-						p_uniforms.setOptional( _gl, skeleton, 'boneMatrices' );
+			}
 
-					}
+			if ( arguments[ 3 ] !== undefined ) {
 
-				}
+				console.warn( 'THREE.WebGLRenderer.render(): the forceClear argument has been removed. Use .clear() instead.' );
+				forceClear = arguments[ 3 ];
 
 			}
 
-			if ( refreshMaterial || materialProperties.receiveShadow !== object.receiveShadow ) {
+			if ( ! ( camera && camera.isCamera ) ) {
 
-				materialProperties.receiveShadow = object.receiveShadow;
-				p_uniforms.setValue( _gl, 'receiveShadow', object.receiveShadow );
+				console.error( 'THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.' );
+				return;
+
+			}
+
+			if ( _isContextLost ) { return; }
+
+			// reset caching for this frame
+
+			_currentGeometryProgram.geometry = null;
+			_currentGeometryProgram.program = null;
+			_currentGeometryProgram.wireframe = false;
+			_currentMaterialId = - 1;
+			_currentCamera = null;
+
+			// update scene graph
+
+			if ( scene.autoUpdate === true ) { scene.updateMatrixWorld(); }
+
+			// update camera matrices and frustum
 
-			}
+			if ( camera.parent === null ) { camera.updateMatrixWorld(); }
 
-			if ( refreshMaterial ) {
+			if ( xr.enabled && xr.isPresenting ) {
 
-				p_uniforms.setValue( _gl, 'toneMappingExposure', _this.toneMappingExposure );
-				p_uniforms.setValue( _gl, 'toneMappingWhitePoint', _this.toneMappingWhitePoint );
+				camera = xr.getCamera( camera );
 
-				if ( materialProperties.needsLights ) {
+			}
 
-					// the current material requires lighting info
+			//
+			scene.onBeforeRender( _this, scene, camera, renderTarget || _currentRenderTarget );
 
-					// note: all lighting uniforms are always set correctly
-					// they simply reference the renderer's state for their
-					// values
-					//
-					// use the current material's .needsUpdate flags to set
-					// the GL state when required
+			currentRenderState = renderStates.get( scene, camera );
+			currentRenderState.init();
 
-					markUniformsLightsNeedsUpdate( m_uniforms, refreshLights );
+			_projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse );
+			_frustum.setFromProjectionMatrix( _projScreenMatrix );
 
-				}
+			_localClippingEnabled = this.localClippingEnabled;
+			_clippingEnabled = _clipping.init( this.clippingPlanes, _localClippingEnabled, camera );
 
-				// refresh uniforms common to several materials
+			currentRenderList = renderLists.get( scene, camera );
+			currentRenderList.init();
 
-				if ( fog && material.fog ) {
+			projectObject( scene, camera, 0, _this.sortObjects );
 
-					refreshUniformsFog( m_uniforms, fog );
+			currentRenderList.finish();
 
-				}
+			if ( _this.sortObjects === true ) {
 
-				if ( material.isMeshBasicMaterial ) {
+				currentRenderList.sort( _opaqueSort, _transparentSort );
 
-					refreshUniformsCommon( m_uniforms, material );
+			}
 
-				} else if ( material.isMeshLambertMaterial ) {
+			//
 
-					refreshUniformsCommon( m_uniforms, material );
-					refreshUniformsLambert( m_uniforms, material );
+			if ( _clippingEnabled ) { _clipping.beginShadows(); }
 
-				} else if ( material.isMeshToonMaterial ) {
+			var shadowsArray = currentRenderState.state.shadowsArray;
 
-					refreshUniformsCommon( m_uniforms, material );
-					refreshUniformsToon( m_uniforms, material );
+			shadowMap.render( shadowsArray, scene, camera );
 
-				} else if ( material.isMeshPhongMaterial ) {
+			currentRenderState.setupLights( camera );
 
-					refreshUniformsCommon( m_uniforms, material );
-					refreshUniformsPhong( m_uniforms, material );
+			if ( _clippingEnabled ) { _clipping.endShadows(); }
 
-				} else if ( material.isMeshStandardMaterial ) {
+			//
 
-					refreshUniformsCommon( m_uniforms, material, environment );
+			if ( this.info.autoReset ) { this.info.reset(); }
 
-					if ( material.isMeshPhysicalMaterial ) {
+			if ( renderTarget !== undefined ) {
 
-						refreshUniformsPhysical( m_uniforms, material, environment );
+				this.setRenderTarget( renderTarget );
 
-					} else {
+			}
 
-						refreshUniformsStandard( m_uniforms, material, environment );
+			//
 
-					}
+			background.render( currentRenderList, scene, camera, forceClear );
 
-				} else if ( material.isMeshMatcapMaterial ) {
+			// render scene
 
-					refreshUniformsCommon( m_uniforms, material );
-					refreshUniformsMatcap( m_uniforms, material );
+			var opaqueObjects = currentRenderList.opaque;
+			var transparentObjects = currentRenderList.transparent;
 
-				} else if ( material.isMeshDepthMaterial ) {
+			if ( scene.overrideMaterial ) {
 
-					refreshUniformsCommon( m_uniforms, material );
-					refreshUniformsDepth( m_uniforms, material );
+				var overrideMaterial = scene.overrideMaterial;
 
-				} else if ( material.isMeshDistanceMaterial ) {
+				if ( opaqueObjects.length ) { renderObjects( opaqueObjects, scene, camera, overrideMaterial ); }
+				if ( transparentObjects.length ) { renderObjects( transparentObjects, scene, camera, overrideMaterial ); }
 
-					refreshUniformsCommon( m_uniforms, material );
-					refreshUniformsDistance( m_uniforms, material );
+			} else {
 
-				} else if ( material.isMeshNormalMaterial ) {
+				// opaque pass (front-to-back order)
 
-					refreshUniformsCommon( m_uniforms, material );
-					refreshUniformsNormal( m_uniforms, material );
+				if ( opaqueObjects.length ) { renderObjects( opaqueObjects, scene, camera ); }
 
-				} else if ( material.isLineBasicMaterial ) {
+				// transparent pass (back-to-front order)
 
-					refreshUniformsLine( m_uniforms, material );
+				if ( transparentObjects.length ) { renderObjects( transparentObjects, scene, camera ); }
 
-					if ( material.isLineDashedMaterial ) {
+			}
 
-						refreshUniformsDash( m_uniforms, material );
+			//
 
-					}
+			scene.onAfterRender( _this, scene, camera );
 
-				} else if ( material.isPointsMaterial ) {
+			//
 
-					refreshUniformsPoints( m_uniforms, material );
+			if ( _currentRenderTarget !== null ) {
 
-				} else if ( material.isSpriteMaterial ) {
+				// Generate mipmap if we're using any kind of mipmap filtering
 
-					refreshUniformsSprites( m_uniforms, material );
+				textures.updateRenderTargetMipmap( _currentRenderTarget );
 
-				} else if ( material.isShadowMaterial ) {
+				// resolve multisample renderbuffers to a single-sample texture if necessary
 
-					m_uniforms.color.value.copy( material.color );
-					m_uniforms.opacity.value = material.opacity;
+				textures.updateMultisampleRenderTarget( _currentRenderTarget );
 
-				}
+			}
 
-				// RectAreaLight Texture
-				// TODO (mrdoob): Find a nicer implementation
+			// Ensure depth buffer writing is enabled so it can be cleared on next render
 
-				if ( m_uniforms.ltc_1 !== undefined ) { m_uniforms.ltc_1.value = UniformsLib.LTC_1; }
-				if ( m_uniforms.ltc_2 !== undefined ) { m_uniforms.ltc_2.value = UniformsLib.LTC_2; }
+			state.buffers.depth.setTest( true );
+			state.buffers.depth.setMask( true );
+			state.buffers.color.setMask( true );
 
-				WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, textures );
+			state.setPolygonOffset( false );
 
-				if ( material.isShaderMaterial ) {
+			// _gl.finish();
 
-					material.uniformsNeedUpdate = false; // #15581
+			currentRenderList = null;
+			currentRenderState = null;
 
-				}
+		};
 
-			}
+		function projectObject( object, camera, groupOrder, sortObjects ) {
 
-			if ( material.isShaderMaterial && material.uniformsNeedUpdate === true ) {
+			if ( object.visible === false ) { return; }
 
-				WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, textures );
-				material.uniformsNeedUpdate = false;
+			var visible = object.layers.test( camera.layers );
 
-			}
+			if ( visible ) {
 
-			if ( material.isSpriteMaterial ) {
+				if ( object.isGroup ) {
 
-				p_uniforms.setValue( _gl, 'center', object.center );
+					groupOrder = object.renderOrder;
 
-			}
+				} else if ( object.isLOD ) {
 
-			// common matrices
+					if ( object.autoUpdate === true ) { object.update( camera ); }
 
-			p_uniforms.setValue( _gl, 'modelViewMatrix', object.modelViewMatrix );
-			p_uniforms.setValue( _gl, 'normalMatrix', object.normalMatrix );
-			p_uniforms.setValue( _gl, 'modelMatrix', object.matrixWorld );
+				} else if ( object.isLight ) {
 
-			return program;
+					currentRenderState.pushLight( object );
 
-		}
+					if ( object.castShadow ) {
 
-		// Uniforms (refresh uniforms objects)
+						currentRenderState.pushShadow( object );
 
-		function refreshUniformsCommon( uniforms, material, environment ) {
+					}
 
-			uniforms.opacity.value = material.opacity;
+				} else if ( object.isSprite ) {
 
-			if ( material.color ) {
+					if ( ! object.frustumCulled || _frustum.intersectsSprite( object ) ) {
 
-				uniforms.diffuse.value.copy( material.color );
+						if ( sortObjects ) {
 
-			}
+							_vector3.setFromMatrixPosition( object.matrixWorld )
+								.applyMatrix4( _projScreenMatrix );
 
-			if ( material.emissive ) {
+						}
 
-				uniforms.emissive.value.copy( material.emissive ).multiplyScalar( material.emissiveIntensity );
+						var geometry = objects.update( object );
+						var material = object.material;
 
-			}
+						if ( material.visible ) {
 
-			if ( material.map ) {
+							currentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null );
 
-				uniforms.map.value = material.map;
+						}
 
-			}
+					}
 
-			if ( material.alphaMap ) {
+				} else if ( object.isImmediateRenderObject ) {
 
-				uniforms.alphaMap.value = material.alphaMap;
+					if ( sortObjects ) {
 
-			}
+						_vector3.setFromMatrixPosition( object.matrixWorld )
+							.applyMatrix4( _projScreenMatrix );
 
-			if ( material.specularMap ) {
+					}
 
-				uniforms.specularMap.value = material.specularMap;
+					currentRenderList.push( object, null, object.material, groupOrder, _vector3.z, null );
 
-			}
+				} else if ( object.isMesh || object.isLine || object.isPoints ) {
 
-			var envMap = material.envMap || environment;
+					if ( object.isSkinnedMesh ) {
 
-			if ( envMap ) {
+						// update skeleton only once in a frame
 
-				uniforms.envMap.value = envMap;
+						if ( object.skeleton.frame !== info.render.frame ) {
 
-				uniforms.flipEnvMap.value = envMap.isCubeTexture ? - 1 : 1;
+							object.skeleton.update();
+							object.skeleton.frame = info.render.frame;
 
-				uniforms.reflectivity.value = material.reflectivity;
-				uniforms.refractionRatio.value = material.refractionRatio;
+						}
 
-				uniforms.maxMipLevel.value = properties.get( envMap ).__maxMipLevel;
+					}
 
-			}
+					if ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) {
 
-			if ( material.lightMap ) {
+						if ( sortObjects ) {
 
-				uniforms.lightMap.value = material.lightMap;
-				uniforms.lightMapIntensity.value = material.lightMapIntensity;
+							_vector3.setFromMatrixPosition( object.matrixWorld )
+								.applyMatrix4( _projScreenMatrix );
 
-			}
+						}
 
-			if ( material.aoMap ) {
+						var geometry = objects.update( object );
+						var material = object.material;
 
-				uniforms.aoMap.value = material.aoMap;
-				uniforms.aoMapIntensity.value = material.aoMapIntensity;
+						if ( Array.isArray( material ) ) {
 
-			}
+							var groups = geometry.groups;
 
-			// uv repeat and offset setting priorities
-			// 1. color map
-			// 2. specular map
-			// 3. normal map
-			// 4. bump map
-			// 5. alpha map
-			// 6. emissive map
+							for ( var i = 0, l = groups.length; i < l; i ++ ) {
 
-			var uvScaleMap;
+								var group = groups[ i ];
+								var groupMaterial = material[ group.materialIndex ];
 
-			if ( material.map ) {
+								if ( groupMaterial && groupMaterial.visible ) {
 
-				uvScaleMap = material.map;
+									currentRenderList.push( object, geometry, groupMaterial, groupOrder, _vector3.z, group );
 
-			} else if ( material.specularMap ) {
+								}
 
-				uvScaleMap = material.specularMap;
+							}
 
-			} else if ( material.displacementMap ) {
+						} else if ( material.visible ) {
 
-				uvScaleMap = material.displacementMap;
+							currentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null );
 
-			} else if ( material.normalMap ) {
+						}
 
-				uvScaleMap = material.normalMap;
+					}
 
-			} else if ( material.bumpMap ) {
+				}
 
-				uvScaleMap = material.bumpMap;
+			}
 
-			} else if ( material.roughnessMap ) {
+			var children = object.children;
 
-				uvScaleMap = material.roughnessMap;
+			for ( var i = 0, l = children.length; i < l; i ++ ) {
 
-			} else if ( material.metalnessMap ) {
+				projectObject( children[ i ], camera, groupOrder, sortObjects );
 
-				uvScaleMap = material.metalnessMap;
+			}
 
-			} else if ( material.alphaMap ) {
+		}
 
-				uvScaleMap = material.alphaMap;
+		function renderObjects( renderList, scene, camera, overrideMaterial ) {
 
-			} else if ( material.emissiveMap ) {
+			for ( var i = 0, l = renderList.length; i < l; i ++ ) {
 
-				uvScaleMap = material.emissiveMap;
+				var renderItem = renderList[ i ];
 
-			}
+				var object = renderItem.object;
+				var geometry = renderItem.geometry;
+				var material = overrideMaterial === undefined ? renderItem.material : overrideMaterial;
+				var group = renderItem.group;
 
-			if ( uvScaleMap !== undefined ) {
+				if ( camera.isArrayCamera ) {
 
-				// backwards compatibility
-				if ( uvScaleMap.isWebGLRenderTarget ) {
+					_currentArrayCamera = camera;
 
-					uvScaleMap = uvScaleMap.texture;
+					var cameras = camera.cameras;
 
-				}
+					for ( var j = 0, jl = cameras.length; j < jl; j ++ ) {
 
-				if ( uvScaleMap.matrixAutoUpdate === true ) {
+						var camera2 = cameras[ j ];
 
-					uvScaleMap.updateMatrix();
+						if ( object.layers.test( camera2.layers ) ) {
 
-				}
+							state.viewport( _currentViewport.copy( camera2.viewport ) );
 
-				uniforms.uvTransform.value.copy( uvScaleMap.matrix );
+							currentRenderState.setupLights( camera2 );
 
-			}
+							renderObject( object, scene, camera2, geometry, material, group );
 
-			// uv repeat and offset setting priorities for uv2
-			// 1. ao map
-			// 2. light map
+						}
 
-			var uv2ScaleMap;
+					}
 
-			if ( material.aoMap ) {
+				} else {
 
-				uv2ScaleMap = material.aoMap;
+					_currentArrayCamera = null;
 
-			} else if ( material.lightMap ) {
+					renderObject( object, scene, camera, geometry, material, group );
 
-				uv2ScaleMap = material.lightMap;
+				}
 
 			}
 
-			if ( uv2ScaleMap !== undefined ) {
+		}
 
-				// backwards compatibility
-				if ( uv2ScaleMap.isWebGLRenderTarget ) {
+		function renderObject( object, scene, camera, geometry, material, group ) {
 
-					uv2ScaleMap = uv2ScaleMap.texture;
+			object.onBeforeRender( _this, scene, camera, geometry, material, group );
+			currentRenderState = renderStates.get( scene, _currentArrayCamera || camera );
 
-				}
+			object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld );
+			object.normalMatrix.getNormalMatrix( object.modelViewMatrix );
 
-				if ( uv2ScaleMap.matrixAutoUpdate === true ) {
+			if ( object.isImmediateRenderObject ) {
 
-					uv2ScaleMap.updateMatrix();
+				var program = setProgram( camera, scene, material, object );
 
-				}
+				state.setMaterial( material );
 
-				uniforms.uv2Transform.value.copy( uv2ScaleMap.matrix );
+				_currentGeometryProgram.geometry = null;
+				_currentGeometryProgram.program = null;
+				_currentGeometryProgram.wireframe = false;
 
-			}
+				renderObjectImmediate( object, program );
 
-		}
+			} else {
 
-		function refreshUniformsLine( uniforms, material ) {
+				_this.renderBufferDirect( camera, scene, geometry, material, object, group );
 
-			uniforms.diffuse.value.copy( material.color );
-			uniforms.opacity.value = material.opacity;
+			}
+
+			object.onAfterRender( _this, scene, camera, geometry, material, group );
+			currentRenderState = renderStates.get( scene, _currentArrayCamera || camera );
 
 		}
 
-		function refreshUniformsDash( uniforms, material ) {
+		function initMaterial( material, scene, object ) {
 
-			uniforms.dashSize.value = material.dashSize;
-			uniforms.totalSize.value = material.dashSize + material.gapSize;
-			uniforms.scale.value = material.scale;
+			var materialProperties = properties.get( material );
 
-		}
+			var lights = currentRenderState.state.lights;
+			var shadowsArray = currentRenderState.state.shadowsArray;
 
-		function refreshUniformsPoints( uniforms, material ) {
+			var lightsStateVersion = lights.state.version;
 
-			uniforms.diffuse.value.copy( material.color );
-			uniforms.opacity.value = material.opacity;
-			uniforms.size.value = material.size * _pixelRatio;
-			uniforms.scale.value = _height * 0.5;
+			var parameters = programCache.getParameters( material, lights.state, shadowsArray, scene, _clipping.numPlanes, _clipping.numIntersection, object );
+			var programCacheKey = programCache.getProgramCacheKey( parameters );
 
-			if ( material.map ) {
+			var program = materialProperties.program;
+			var programChange = true;
 
-				uniforms.map.value = material.map;
+			if ( program === undefined ) {
 
-			}
+				// new material
+				material.addEventListener( 'dispose', onMaterialDispose );
 
-			if ( material.alphaMap ) {
+			} else if ( program.cacheKey !== programCacheKey ) {
 
-				uniforms.alphaMap.value = material.alphaMap;
+				// changed glsl or parameters
+				releaseMaterialProgramReference( material );
 
-			}
+			} else if ( materialProperties.lightsStateVersion !== lightsStateVersion ) {
 
-			// uv repeat and offset setting priorities
-			// 1. color map
-			// 2. alpha map
+				materialProperties.lightsStateVersion = lightsStateVersion;
 
-			var uvScaleMap;
+				programChange = false;
 
-			if ( material.map ) {
+			} else if ( parameters.shaderID !== undefined ) {
 
-				uvScaleMap = material.map;
+				// same glsl and uniform list
+				return;
 
-			} else if ( material.alphaMap ) {
+			} else {
 
-				uvScaleMap = material.alphaMap;
+				// only rebuild uniform list
+				programChange = false;
 
 			}
 
-			if ( uvScaleMap !== undefined ) {
-
-				if ( uvScaleMap.matrixAutoUpdate === true ) {
-
-					uvScaleMap.updateMatrix();
+			if ( programChange ) {
 
-				}
+				program = programCache.acquireProgram( parameters, programCacheKey );
 
-				uniforms.uvTransform.value.copy( uvScaleMap.matrix );
+				materialProperties.program = program;
+				materialProperties.uniforms = parameters.uniforms;
+				materialProperties.outputEncoding = parameters.outputEncoding;
+				material.program = program;
 
 			}
 
-		}
+			var programAttributes = program.getAttributes();
 
-		function refreshUniformsSprites( uniforms, material ) {
+			if ( material.morphTargets ) {
 
-			uniforms.diffuse.value.copy( material.color );
-			uniforms.opacity.value = material.opacity;
-			uniforms.rotation.value = material.rotation;
+				material.numSupportedMorphTargets = 0;
 
-			if ( material.map ) {
+				for ( var i = 0; i < _this.maxMorphTargets; i ++ ) {
 
-				uniforms.map.value = material.map;
+					if ( programAttributes[ 'morphTarget' + i ] >= 0 ) {
 
-			}
+						material.numSupportedMorphTargets ++;
 
-			if ( material.alphaMap ) {
+					}
 
-				uniforms.alphaMap.value = material.alphaMap;
+				}
 
 			}
 
-			// uv repeat and offset setting priorities
-			// 1. color map
-			// 2. alpha map
-
-			var uvScaleMap;
+			if ( material.morphNormals ) {
 
-			if ( material.map ) {
+				material.numSupportedMorphNormals = 0;
 
-				uvScaleMap = material.map;
+				for ( var i = 0; i < _this.maxMorphNormals; i ++ ) {
 
-			} else if ( material.alphaMap ) {
+					if ( programAttributes[ 'morphNormal' + i ] >= 0 ) {
 
-				uvScaleMap = material.alphaMap;
+						material.numSupportedMorphNormals ++;
 
-			}
+					}
 
-			if ( uvScaleMap !== undefined ) {
+				}
 
-				if ( uvScaleMap.matrixAutoUpdate === true ) {
+			}
 
-					uvScaleMap.updateMatrix();
+			var uniforms = materialProperties.uniforms;
 
-				}
+			if ( ! material.isShaderMaterial &&
+				! material.isRawShaderMaterial ||
+				material.clipping === true ) {
 
-				uniforms.uvTransform.value.copy( uvScaleMap.matrix );
+				materialProperties.numClippingPlanes = _clipping.numPlanes;
+				materialProperties.numIntersection = _clipping.numIntersection;
+				uniforms.clippingPlanes = _clipping.uniform;
 
 			}
 
-		}
+			materialProperties.environment = material.isMeshStandardMaterial ? scene.environment : null;
+			materialProperties.fog = scene.fog;
 
-		function refreshUniformsFog( uniforms, fog ) {
+			// store the light setup it was created for
 
-			uniforms.fogColor.value.copy( fog.color );
+			materialProperties.needsLights = materialNeedsLights( material );
+			materialProperties.lightsStateVersion = lightsStateVersion;
 
-			if ( fog.isFog ) {
+			if ( materialProperties.needsLights ) {
 
-				uniforms.fogNear.value = fog.near;
-				uniforms.fogFar.value = fog.far;
+				// wire up the material to this renderer's lighting state
 
-			} else if ( fog.isFogExp2 ) {
+				uniforms.ambientLightColor.value = lights.state.ambient;
+				uniforms.lightProbe.value = lights.state.probe;
+				uniforms.directionalLights.value = lights.state.directional;
+				uniforms.directionalLightShadows.value = lights.state.directionalShadow;
+				uniforms.spotLights.value = lights.state.spot;
+				uniforms.spotLightShadows.value = lights.state.spotShadow;
+				uniforms.rectAreaLights.value = lights.state.rectArea;
+				uniforms.pointLights.value = lights.state.point;
+				uniforms.pointLightShadows.value = lights.state.pointShadow;
+				uniforms.hemisphereLights.value = lights.state.hemi;
 
-				uniforms.fogDensity.value = fog.density;
+				uniforms.directionalShadowMap.value = lights.state.directionalShadowMap;
+				uniforms.directionalShadowMatrix.value = lights.state.directionalShadowMatrix;
+				uniforms.spotShadowMap.value = lights.state.spotShadowMap;
+				uniforms.spotShadowMatrix.value = lights.state.spotShadowMatrix;
+				uniforms.pointShadowMap.value = lights.state.pointShadowMap;
+				uniforms.pointShadowMatrix.value = lights.state.pointShadowMatrix;
+				// TODO (abelnation): add area lights shadow info to uniforms
 
 			}
 
+			var progUniforms = materialProperties.program.getUniforms(),
+				uniformsList =
+					WebGLUniforms.seqWithValue( progUniforms.seq, uniforms );
+
+			materialProperties.uniformsList = uniformsList;
+
 		}
 
-		function refreshUniformsLambert( uniforms, material ) {
+		function setProgram( camera, scene, material, object ) {
 
-			if ( material.emissiveMap ) {
+			textures.resetTextureUnits();
 
-				uniforms.emissiveMap.value = material.emissiveMap;
+			var fog = scene.fog;
+			var environment = material.isMeshStandardMaterial ? scene.environment : null;
+			var encoding = ( _currentRenderTarget === null ) ? _this.outputEncoding : _currentRenderTarget.texture.encoding;
 
-			}
+			var materialProperties = properties.get( material );
+			var lights = currentRenderState.state.lights;
 
-		}
+			if ( _clippingEnabled ) {
 
-		function refreshUniformsPhong( uniforms, material ) {
+				if ( _localClippingEnabled || camera !== _currentCamera ) {
 
-			uniforms.specular.value.copy( material.specular );
-			uniforms.shininess.value = Math.max( material.shininess, 1e-4 ); // to prevent pow( 0.0, 0.0 )
+					var useCache =
+						camera === _currentCamera &&
+						material.id === _currentMaterialId;
 
-			if ( material.emissiveMap ) {
+					// we might want to call this function with some ClippingGroup
+					// object instead of the material, once it becomes feasible
+					// (#8465, #8379)
+					_clipping.setState(
+						material.clippingPlanes, material.clipIntersection, material.clipShadows,
+						camera, materialProperties, useCache );
 
-				uniforms.emissiveMap.value = material.emissiveMap;
+				}
 
 			}
 
-			if ( material.bumpMap ) {
-
-				uniforms.bumpMap.value = material.bumpMap;
-				uniforms.bumpScale.value = material.bumpScale;
-				if ( material.side === BackSide ) { uniforms.bumpScale.value *= - 1; }
+			if ( material.version === materialProperties.__version ) {
 
-			}
+				if ( materialProperties.program === undefined ) {
 
-			if ( material.normalMap ) {
+					initMaterial( material, scene, object );
 
-				uniforms.normalMap.value = material.normalMap;
-				uniforms.normalScale.value.copy( material.normalScale );
-				if ( material.side === BackSide ) { uniforms.normalScale.value.negate(); }
+				} else if ( material.fog && materialProperties.fog !== fog ) {
 
-			}
+					initMaterial( material, scene, object );
 
-			if ( material.displacementMap ) {
+				} else if ( materialProperties.environment !== environment ) {
 
-				uniforms.displacementMap.value = material.displacementMap;
-				uniforms.displacementScale.value = material.displacementScale;
-				uniforms.displacementBias.value = material.displacementBias;
+					initMaterial( material, scene, object );
 
-			}
+				} else if ( materialProperties.needsLights && ( materialProperties.lightsStateVersion !== lights.state.version ) ) {
 
-		}
+					initMaterial( material, scene, object );
 
-		function refreshUniformsToon( uniforms, material ) {
+				} else if ( materialProperties.numClippingPlanes !== undefined &&
+					( materialProperties.numClippingPlanes !== _clipping.numPlanes ||
+					materialProperties.numIntersection !== _clipping.numIntersection ) ) {
 
-			uniforms.specular.value.copy( material.specular );
-			uniforms.shininess.value = Math.max( material.shininess, 1e-4 ); // to prevent pow( 0.0, 0.0 )
+					initMaterial( material, scene, object );
 
-			if ( material.gradientMap ) {
+				} else if ( materialProperties.outputEncoding !== encoding ) {
 
-				uniforms.gradientMap.value = material.gradientMap;
+					initMaterial( material, scene, object );
 
-			}
+				}
 
-			if ( material.emissiveMap ) {
+			} else {
 
-				uniforms.emissiveMap.value = material.emissiveMap;
+				initMaterial( material, scene, object );
+				materialProperties.__version = material.version;
 
 			}
 
-			if ( material.bumpMap ) {
-
-				uniforms.bumpMap.value = material.bumpMap;
-				uniforms.bumpScale.value = material.bumpScale;
-				if ( material.side === BackSide ) { uniforms.bumpScale.value *= - 1; }
+			var refreshProgram = false;
+			var refreshMaterial = false;
+			var refreshLights = false;
 
-			}
+			var program = materialProperties.program,
+				p_uniforms = program.getUniforms(),
+				m_uniforms = materialProperties.uniforms;
 
-			if ( material.normalMap ) {
+			if ( state.useProgram( program.program ) ) {
 
-				uniforms.normalMap.value = material.normalMap;
-				uniforms.normalScale.value.copy( material.normalScale );
-				if ( material.side === BackSide ) { uniforms.normalScale.value.negate(); }
+				refreshProgram = true;
+				refreshMaterial = true;
+				refreshLights = true;
 
 			}
 
-			if ( material.displacementMap ) {
+			if ( material.id !== _currentMaterialId ) {
 
-				uniforms.displacementMap.value = material.displacementMap;
-				uniforms.displacementScale.value = material.displacementScale;
-				uniforms.displacementBias.value = material.displacementBias;
+				_currentMaterialId = material.id;
+
+				refreshMaterial = true;
 
 			}
 
-		}
+			if ( refreshProgram || _currentCamera !== camera ) {
 
-		function refreshUniformsStandard( uniforms, material, environment ) {
+				p_uniforms.setValue( _gl, 'projectionMatrix', camera.projectionMatrix );
 
-			uniforms.roughness.value = material.roughness;
-			uniforms.metalness.value = material.metalness;
+				if ( capabilities.logarithmicDepthBuffer ) {
 
-			if ( material.roughnessMap ) {
+					p_uniforms.setValue( _gl, 'logDepthBufFC',
+						2.0 / ( Math.log( camera.far + 1.0 ) / Math.LN2 ) );
 
-				uniforms.roughnessMap.value = material.roughnessMap;
+				}
 
-			}
+				if ( _currentCamera !== camera ) {
 
-			if ( material.metalnessMap ) {
+					_currentCamera = camera;
 
-				uniforms.metalnessMap.value = material.metalnessMap;
+					// lighting uniforms depend on the camera so enforce an update
+					// now, in case this material supports lights - or later, when
+					// the next material that does gets activated:
 
-			}
+					refreshMaterial = true;		// set to true on material change
+					refreshLights = true;		// remains set until update done
 
-			if ( material.emissiveMap ) {
+				}
 
-				uniforms.emissiveMap.value = material.emissiveMap;
+				// load material specific uniforms
+				// (shader material also gets them for the sake of genericity)
 
-			}
+				if ( material.isShaderMaterial ||
+					material.isMeshPhongMaterial ||
+					material.isMeshToonMaterial ||
+					material.isMeshStandardMaterial ||
+					material.envMap ) {
 
-			if ( material.bumpMap ) {
+					var uCamPos = p_uniforms.map.cameraPosition;
 
-				uniforms.bumpMap.value = material.bumpMap;
-				uniforms.bumpScale.value = material.bumpScale;
-				if ( material.side === BackSide ) { uniforms.bumpScale.value *= - 1; }
+					if ( uCamPos !== undefined ) {
 
-			}
+						uCamPos.setValue( _gl,
+							_vector3.setFromMatrixPosition( camera.matrixWorld ) );
 
-			if ( material.normalMap ) {
+					}
 
-				uniforms.normalMap.value = material.normalMap;
-				uniforms.normalScale.value.copy( material.normalScale );
-				if ( material.side === BackSide ) { uniforms.normalScale.value.negate(); }
+				}
 
-			}
+				if ( material.isMeshPhongMaterial ||
+					material.isMeshToonMaterial ||
+					material.isMeshLambertMaterial ||
+					material.isMeshBasicMaterial ||
+					material.isMeshStandardMaterial ||
+					material.isShaderMaterial ) {
 
-			if ( material.displacementMap ) {
+					p_uniforms.setValue( _gl, 'isOrthographic', camera.isOrthographicCamera === true );
 
-				uniforms.displacementMap.value = material.displacementMap;
-				uniforms.displacementScale.value = material.displacementScale;
-				uniforms.displacementBias.value = material.displacementBias;
+				}
 
-			}
+				if ( material.isMeshPhongMaterial ||
+					material.isMeshToonMaterial ||
+					material.isMeshLambertMaterial ||
+					material.isMeshBasicMaterial ||
+					material.isMeshStandardMaterial ||
+					material.isShaderMaterial ||
+					material.skinning ) {
 
-			if ( material.envMap || environment ) {
+					p_uniforms.setValue( _gl, 'viewMatrix', camera.matrixWorldInverse );
 
-				//uniforms.envMap.value = material.envMap; // part of uniforms common
-				uniforms.envMapIntensity.value = material.envMapIntensity;
+				}
 
 			}
 
-		}
-
-		function refreshUniformsPhysical( uniforms, material, environment ) {
-
-			refreshUniformsStandard( uniforms, material, environment );
+			// skinning uniforms must be set even if material didn't change
+			// auto-setting of texture unit for bone texture must go before other textures
+			// otherwise textures used for skinning can take over texture units reserved for other material textures
 
-			uniforms.reflectivity.value = material.reflectivity; // also part of uniforms common
+			if ( material.skinning ) {
 
-			uniforms.clearcoat.value = material.clearcoat;
-			uniforms.clearcoatRoughness.value = material.clearcoatRoughness;
-			if ( material.sheen ) { uniforms.sheen.value.copy( material.sheen ); }
+				p_uniforms.setOptional( _gl, object, 'bindMatrix' );
+				p_uniforms.setOptional( _gl, object, 'bindMatrixInverse' );
 
-			if ( material.clearcoatMap ) {
+				var skeleton = object.skeleton;
 
-				uniforms.clearcoatMap.value = material.clearcoatMap;
+				if ( skeleton ) {
 
-			}
+					var bones = skeleton.bones;
 
-			if ( material.clearcoatRoughnessMap ) {
+					if ( capabilities.floatVertexTextures ) {
 
-				uniforms.clearcoatRoughnessMap.value = material.clearcoatRoughnessMap;
+						if ( skeleton.boneTexture === undefined ) {
 
-			}
+							// layout (1 matrix = 4 pixels)
+							//      RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4)
+							//  with  8x8  pixel texture max   16 bones * 4 pixels =  (8 * 8)
+							//       16x16 pixel texture max   64 bones * 4 pixels = (16 * 16)
+							//       32x32 pixel texture max  256 bones * 4 pixels = (32 * 32)
+							//       64x64 pixel texture max 1024 bones * 4 pixels = (64 * 64)
 
-			if ( material.clearcoatNormalMap ) {
 
-				uniforms.clearcoatNormalScale.value.copy( material.clearcoatNormalScale );
-				uniforms.clearcoatNormalMap.value = material.clearcoatNormalMap;
+							var size = Math.sqrt( bones.length * 4 ); // 4 pixels needed for 1 matrix
+							size = MathUtils.ceilPowerOfTwo( size );
+							size = Math.max( size, 4 );
 
-				if ( material.side === BackSide ) {
+							var boneMatrices = new Float32Array( size * size * 4 ); // 4 floats per RGBA pixel
+							boneMatrices.set( skeleton.boneMatrices ); // copy current values
 
-					uniforms.clearcoatNormalScale.value.negate();
+							var boneTexture = new DataTexture( boneMatrices, size, size, RGBAFormat, FloatType );
 
-				}
+							skeleton.boneMatrices = boneMatrices;
+							skeleton.boneTexture = boneTexture;
+							skeleton.boneTextureSize = size;
 
-			}
+						}
 
-			uniforms.transparency.value = material.transparency;
+						p_uniforms.setValue( _gl, 'boneTexture', skeleton.boneTexture, textures );
+						p_uniforms.setValue( _gl, 'boneTextureSize', skeleton.boneTextureSize );
 
-		}
+					} else {
 
-		function refreshUniformsMatcap( uniforms, material ) {
+						p_uniforms.setOptional( _gl, skeleton, 'boneMatrices' );
 
-			if ( material.matcap ) {
+					}
 
-				uniforms.matcap.value = material.matcap;
+				}
 
 			}
 
-			if ( material.bumpMap ) {
+			if ( refreshMaterial || materialProperties.receiveShadow !== object.receiveShadow ) {
 
-				uniforms.bumpMap.value = material.bumpMap;
-				uniforms.bumpScale.value = material.bumpScale;
-				if ( material.side === BackSide ) { uniforms.bumpScale.value *= - 1; }
+				materialProperties.receiveShadow = object.receiveShadow;
+				p_uniforms.setValue( _gl, 'receiveShadow', object.receiveShadow );
 
 			}
 
-			if ( material.normalMap ) {
+			if ( refreshMaterial ) {
 
-				uniforms.normalMap.value = material.normalMap;
-				uniforms.normalScale.value.copy( material.normalScale );
-				if ( material.side === BackSide ) { uniforms.normalScale.value.negate(); }
+				p_uniforms.setValue( _gl, 'toneMappingExposure', _this.toneMappingExposure );
+				p_uniforms.setValue( _gl, 'toneMappingWhitePoint', _this.toneMappingWhitePoint );
 
-			}
+				if ( materialProperties.needsLights ) {
 
-			if ( material.displacementMap ) {
+					// the current material requires lighting info
 
-				uniforms.displacementMap.value = material.displacementMap;
-				uniforms.displacementScale.value = material.displacementScale;
-				uniforms.displacementBias.value = material.displacementBias;
+					// note: all lighting uniforms are always set correctly
+					// they simply reference the renderer's state for their
+					// values
+					//
+					// use the current material's .needsUpdate flags to set
+					// the GL state when required
 
-			}
+					markUniformsLightsNeedsUpdate( m_uniforms, refreshLights );
 
-		}
+				}
 
-		function refreshUniformsDepth( uniforms, material ) {
+				// refresh uniforms common to several materials
 
-			if ( material.displacementMap ) {
+				if ( fog && material.fog ) {
 
-				uniforms.displacementMap.value = material.displacementMap;
-				uniforms.displacementScale.value = material.displacementScale;
-				uniforms.displacementBias.value = material.displacementBias;
+					materials.refreshFogUniforms( m_uniforms, fog );
 
-			}
+				}
 
-		}
+				materials.refreshMaterialUniforms( m_uniforms, material, environment, _pixelRatio, _height );
 
-		function refreshUniformsDistance( uniforms, material ) {
+				// RectAreaLight Texture
+				// TODO (mrdoob): Find a nicer implementation
 
-			if ( material.displacementMap ) {
+				if ( m_uniforms.ltc_1 !== undefined ) { m_uniforms.ltc_1.value = UniformsLib.LTC_1; }
+				if ( m_uniforms.ltc_2 !== undefined ) { m_uniforms.ltc_2.value = UniformsLib.LTC_2; }
 
-				uniforms.displacementMap.value = material.displacementMap;
-				uniforms.displacementScale.value = material.displacementScale;
-				uniforms.displacementBias.value = material.displacementBias;
+				WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, textures );
 
-			}
+				if ( material.isShaderMaterial ) {
 
-			uniforms.referencePosition.value.copy( material.referencePosition );
-			uniforms.nearDistance.value = material.nearDistance;
-			uniforms.farDistance.value = material.farDistance;
+					material.uniformsNeedUpdate = false; // #15581
 
-		}
+				}
 
-		function refreshUniformsNormal( uniforms, material ) {
+			}
 
-			if ( material.bumpMap ) {
+			if ( material.isShaderMaterial && material.uniformsNeedUpdate === true ) {
 
-				uniforms.bumpMap.value = material.bumpMap;
-				uniforms.bumpScale.value = material.bumpScale;
-				if ( material.side === BackSide ) { uniforms.bumpScale.value *= - 1; }
+				WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, textures );
+				material.uniformsNeedUpdate = false;
 
 			}
 
-			if ( material.normalMap ) {
+			if ( material.isSpriteMaterial ) {
 
-				uniforms.normalMap.value = material.normalMap;
-				uniforms.normalScale.value.copy( material.normalScale );
-				if ( material.side === BackSide ) { uniforms.normalScale.value.negate(); }
+				p_uniforms.setValue( _gl, 'center', object.center );
 
 			}
 
-			if ( material.displacementMap ) {
+			// common matrices
 
-				uniforms.displacementMap.value = material.displacementMap;
-				uniforms.displacementScale.value = material.displacementScale;
-				uniforms.displacementBias.value = material.displacementBias;
+			p_uniforms.setValue( _gl, 'modelViewMatrix', object.modelViewMatrix );
+			p_uniforms.setValue( _gl, 'normalMatrix', object.normalMatrix );
+			p_uniforms.setValue( _gl, 'modelMatrix', object.matrixWorld );
 
-			}
+			return program;
 
 		}
 

File diff suppressed because it is too large
+ 496 - 495
build/three.min.js


+ 1428 - 1410
build/three.module.js

@@ -23507,2517 +23507,2535 @@ function WebXRManager( renderer, gl ) {
 Object.assign( WebXRManager.prototype, EventDispatcher.prototype );
 
 /**
- * @author supereggbert / http://www.paulbrunt.co.uk/
  * @author mrdoob / http://mrdoob.com/
- * @author alteredq / http://alteredqualia.com/
- * @author szimek / https://github.com/szimek/
- * @author tschw
  */
 
-function WebGLRenderer( parameters ) {
-
-	parameters = parameters || {};
+function WebGLMaterials( properties ) {
 
-	var _canvas = parameters.canvas !== undefined ? parameters.canvas : document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ),
-		_context = parameters.context !== undefined ? parameters.context : null,
+	function refreshFogUniforms( uniforms, fog ) {
 
-		_alpha = parameters.alpha !== undefined ? parameters.alpha : false,
-		_depth = parameters.depth !== undefined ? parameters.depth : true,
-		_stencil = parameters.stencil !== undefined ? parameters.stencil : true,
-		_antialias = parameters.antialias !== undefined ? parameters.antialias : false,
-		_premultipliedAlpha = parameters.premultipliedAlpha !== undefined ? parameters.premultipliedAlpha : true,
-		_preserveDrawingBuffer = parameters.preserveDrawingBuffer !== undefined ? parameters.preserveDrawingBuffer : false,
-		_powerPreference = parameters.powerPreference !== undefined ? parameters.powerPreference : 'default',
-		_failIfMajorPerformanceCaveat = parameters.failIfMajorPerformanceCaveat !== undefined ? parameters.failIfMajorPerformanceCaveat : false;
+		uniforms.fogColor.value.copy( fog.color );
 
-	var currentRenderList = null;
-	var currentRenderState = null;
+		if ( fog.isFog ) {
 
-	// public properties
+			uniforms.fogNear.value = fog.near;
+			uniforms.fogFar.value = fog.far;
 
-	this.domElement = _canvas;
+		} else if ( fog.isFogExp2 ) {
 
-	// Debug configuration container
-	this.debug = {
+			uniforms.fogDensity.value = fog.density;
 
-		/**
-		 * Enables error checking and reporting when shader programs are being compiled
-		 * @type {boolean}
-		 */
-		checkShaderErrors: true
-	};
+		}
 
-	// clearing
+	}
 
-	this.autoClear = true;
-	this.autoClearColor = true;
-	this.autoClearDepth = true;
-	this.autoClearStencil = true;
+	function refreshMaterialUniforms( uniforms, material, environment, pixelRatio, height ) {
 
-	// scene graph
+		if ( material.isMeshBasicMaterial ) {
 
-	this.sortObjects = true;
+			refreshUniformsCommon( uniforms, material );
 
-	// user-defined clipping
+		} else if ( material.isMeshLambertMaterial ) {
 
-	this.clippingPlanes = [];
-	this.localClippingEnabled = false;
+			refreshUniformsCommon( uniforms, material );
+			refreshUniformsLambert( uniforms, material );
 
-	// physically based shading
+		} else if ( material.isMeshToonMaterial ) {
 
-	this.gammaFactor = 2.0;	// for backwards compatibility
-	this.outputEncoding = LinearEncoding;
+			refreshUniformsCommon( uniforms, material );
+			refreshUniformsToon( uniforms, material );
 
-	// physical lights
+		} else if ( material.isMeshPhongMaterial ) {
 
-	this.physicallyCorrectLights = false;
+			refreshUniformsCommon( uniforms, material );
+			refreshUniformsPhong( uniforms, material );
 
-	// tone mapping
+		} else if ( material.isMeshStandardMaterial ) {
 
-	this.toneMapping = NoToneMapping;
-	this.toneMappingExposure = 1.0;
-	this.toneMappingWhitePoint = 1.0;
+			refreshUniformsCommon( uniforms, material, environment );
 
-	// morphs
+			if ( material.isMeshPhysicalMaterial ) {
 
-	this.maxMorphTargets = 8;
-	this.maxMorphNormals = 4;
+				refreshUniformsPhysical( uniforms, material, environment );
 
-	// internal properties
+			} else {
 
-	var _this = this,
+				refreshUniformsStandard( uniforms, material, environment );
 
-		_isContextLost = false,
+			}
 
-		// internal state cache
+		} else if ( material.isMeshMatcapMaterial ) {
 
-		_framebuffer = null,
+			refreshUniformsCommon( uniforms, material );
+			refreshUniformsMatcap( uniforms, material );
 
-		_currentActiveCubeFace = 0,
-		_currentActiveMipmapLevel = 0,
-		_currentRenderTarget = null,
-		_currentFramebuffer = null,
-		_currentMaterialId = - 1,
+		} else if ( material.isMeshDepthMaterial ) {
 
-		// geometry and program caching
+			refreshUniformsCommon( uniforms, material );
+			refreshUniformsDepth( uniforms, material );
 
-		_currentGeometryProgram = {
-			geometry: null,
-			program: null,
-			wireframe: false
-		},
+		} else if ( material.isMeshDistanceMaterial ) {
 
-		_currentCamera = null,
-		_currentArrayCamera = null,
+			refreshUniformsCommon( uniforms, material );
+			refreshUniformsDistance( uniforms, material );
 
-		_currentViewport = new Vector4(),
-		_currentScissor = new Vector4(),
-		_currentScissorTest = null,
+		} else if ( material.isMeshNormalMaterial ) {
 
-		//
+			refreshUniformsCommon( uniforms, material );
+			refreshUniformsNormal( uniforms, material );
 
-		_width = _canvas.width,
-		_height = _canvas.height,
+		} else if ( material.isLineBasicMaterial ) {
 
-		_pixelRatio = 1,
-		_opaqueSort = null,
-		_transparentSort = null,
+			refreshUniformsLine( uniforms, material );
 
-		_viewport = new Vector4( 0, 0, _width, _height ),
-		_scissor = new Vector4( 0, 0, _width, _height ),
-		_scissorTest = false,
+			if ( material.isLineDashedMaterial ) {
 
-		// frustum
+				refreshUniformsDash( uniforms, material );
 
-		_frustum = new Frustum(),
+			}
 
-		// clipping
+		} else if ( material.isPointsMaterial ) {
 
-		_clipping = new WebGLClipping(),
-		_clippingEnabled = false,
-		_localClippingEnabled = false,
+			refreshUniformsPoints( uniforms, material, pixelRatio, height );
 
-		// camera matrices cache
+		} else if ( material.isSpriteMaterial ) {
 
-		_projScreenMatrix = new Matrix4(),
+			refreshUniformsSprites( uniforms, material );
 
-		_vector3 = new Vector3();
+		} else if ( material.isShadowMaterial ) {
 
-	function getTargetPixelRatio() {
+			uniforms.color.value.copy( material.color );
+			uniforms.opacity.value = material.opacity;
 
-		return _currentRenderTarget === null ? _pixelRatio : 1;
+		}
 
 	}
 
-	// initialize
-
-	var _gl;
-
-	try {
-
-		var contextAttributes = {
-			alpha: _alpha,
-			depth: _depth,
-			stencil: _stencil,
-			antialias: _antialias,
-			premultipliedAlpha: _premultipliedAlpha,
-			preserveDrawingBuffer: _preserveDrawingBuffer,
-			powerPreference: _powerPreference,
-			failIfMajorPerformanceCaveat: _failIfMajorPerformanceCaveat,
-			xrCompatible: true
-		};
+	function refreshUniformsCommon( uniforms, material, environment ) {
 
-		// event listeners must be registered before WebGL context is created, see #12753
+		uniforms.opacity.value = material.opacity;
 
-		_canvas.addEventListener( 'webglcontextlost', onContextLost, false );
-		_canvas.addEventListener( 'webglcontextrestored', onContextRestore, false );
+		if ( material.color ) {
 
-		_gl = _context || _canvas.getContext( 'webgl', contextAttributes ) || _canvas.getContext( 'experimental-webgl', contextAttributes );
+			uniforms.diffuse.value.copy( material.color );
 
-		if ( _gl === null ) {
+		}
 
-			if ( _canvas.getContext( 'webgl' ) !== null ) {
+		if ( material.emissive ) {
 
-				throw new Error( 'Error creating WebGL context with your selected attributes.' );
+			uniforms.emissive.value.copy( material.emissive ).multiplyScalar( material.emissiveIntensity );
 
-			} else {
+		}
 
-				throw new Error( 'Error creating WebGL context.' );
+		if ( material.map ) {
 
-			}
+			uniforms.map.value = material.map;
 
 		}
 
-		// Some experimental-webgl implementations do not have getShaderPrecisionFormat
+		if ( material.alphaMap ) {
 
-		if ( _gl.getShaderPrecisionFormat === undefined ) {
+			uniforms.alphaMap.value = material.alphaMap;
 
-			_gl.getShaderPrecisionFormat = function () {
+		}
 
-				return { 'rangeMin': 1, 'rangeMax': 1, 'precision': 1 };
+		if ( material.specularMap ) {
 
-			};
+			uniforms.specularMap.value = material.specularMap;
 
 		}
 
-	} catch ( error ) {
+		var envMap = material.envMap || environment;
 
-		console.error( 'THREE.WebGLRenderer: ' + error.message );
-		throw error;
+		if ( envMap ) {
 
-	}
+			uniforms.envMap.value = envMap;
 
-	var extensions, capabilities, state, info;
-	var properties, textures, attributes, geometries, objects;
-	var programCache, renderLists, renderStates;
+			uniforms.flipEnvMap.value = envMap.isCubeTexture ? - 1 : 1;
 
-	var background, morphtargets, bufferRenderer, indexedBufferRenderer;
+			uniforms.reflectivity.value = material.reflectivity;
+			uniforms.refractionRatio.value = material.refractionRatio;
 
-	var utils;
+			uniforms.maxMipLevel.value = properties.get( envMap ).__maxMipLevel;
 
-	function initGLContext() {
+		}
 
-		extensions = new WebGLExtensions( _gl );
+		if ( material.lightMap ) {
 
-		capabilities = new WebGLCapabilities( _gl, extensions, parameters );
+			uniforms.lightMap.value = material.lightMap;
+			uniforms.lightMapIntensity.value = material.lightMapIntensity;
 
-		if ( capabilities.isWebGL2 === false ) {
+		}
 
-			extensions.get( 'WEBGL_depth_texture' );
-			extensions.get( 'OES_texture_float' );
-			extensions.get( 'OES_texture_half_float' );
-			extensions.get( 'OES_texture_half_float_linear' );
-			extensions.get( 'OES_standard_derivatives' );
-			extensions.get( 'OES_element_index_uint' );
-			extensions.get( 'ANGLE_instanced_arrays' );
+		if ( material.aoMap ) {
+
+			uniforms.aoMap.value = material.aoMap;
+			uniforms.aoMapIntensity.value = material.aoMapIntensity;
 
 		}
 
-		extensions.get( 'OES_texture_float_linear' );
+		// uv repeat and offset setting priorities
+		// 1. color map
+		// 2. specular map
+		// 3. normal map
+		// 4. bump map
+		// 5. alpha map
+		// 6. emissive map
 
-		utils = new WebGLUtils( _gl, extensions, capabilities );
+		var uvScaleMap;
 
-		state = new WebGLState( _gl, extensions, capabilities );
-		state.scissor( _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ).floor() );
-		state.viewport( _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ).floor() );
+		if ( material.map ) {
 
-		info = new WebGLInfo( _gl );
-		properties = new WebGLProperties();
-		textures = new WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info );
-		attributes = new WebGLAttributes( _gl, capabilities );
-		geometries = new WebGLGeometries( _gl, attributes, info );
-		objects = new WebGLObjects( _gl, geometries, attributes, info );
-		morphtargets = new WebGLMorphtargets( _gl );
-		programCache = new WebGLPrograms( _this, extensions, capabilities );
-		renderLists = new WebGLRenderLists();
-		renderStates = new WebGLRenderStates();
+			uvScaleMap = material.map;
 
-		background = new WebGLBackground( _this, state, objects, _premultipliedAlpha );
+		} else if ( material.specularMap ) {
 
-		bufferRenderer = new WebGLBufferRenderer( _gl, extensions, info, capabilities );
-		indexedBufferRenderer = new WebGLIndexedBufferRenderer( _gl, extensions, info, capabilities );
+			uvScaleMap = material.specularMap;
 
-		info.programs = programCache.programs;
+		} else if ( material.displacementMap ) {
 
-		_this.capabilities = capabilities;
-		_this.extensions = extensions;
-		_this.properties = properties;
-		_this.renderLists = renderLists;
-		_this.state = state;
-		_this.info = info;
+			uvScaleMap = material.displacementMap;
 
-	}
+		} else if ( material.normalMap ) {
 
-	initGLContext();
+			uvScaleMap = material.normalMap;
 
-	// xr
+		} else if ( material.bumpMap ) {
 
-	var xr = new WebXRManager( _this, _gl );
+			uvScaleMap = material.bumpMap;
 
-	this.xr = xr;
+		} else if ( material.roughnessMap ) {
 
-	// shadow map
+			uvScaleMap = material.roughnessMap;
 
-	var shadowMap = new WebGLShadowMap( _this, objects, capabilities.maxTextureSize );
+		} else if ( material.metalnessMap ) {
 
-	this.shadowMap = shadowMap;
+			uvScaleMap = material.metalnessMap;
 
-	// API
+		} else if ( material.alphaMap ) {
 
-	this.getContext = function () {
+			uvScaleMap = material.alphaMap;
 
-		return _gl;
+		} else if ( material.emissiveMap ) {
 
-	};
+			uvScaleMap = material.emissiveMap;
 
-	this.getContextAttributes = function () {
+		}
 
-		return _gl.getContextAttributes();
+		if ( uvScaleMap !== undefined ) {
 
-	};
+			// backwards compatibility
+			if ( uvScaleMap.isWebGLRenderTarget ) {
 
-	this.forceContextLoss = function () {
+				uvScaleMap = uvScaleMap.texture;
 
-		var extension = extensions.get( 'WEBGL_lose_context' );
-		if ( extension ) extension.loseContext();
+			}
 
-	};
+			if ( uvScaleMap.matrixAutoUpdate === true ) {
 
-	this.forceContextRestore = function () {
+				uvScaleMap.updateMatrix();
 
-		var extension = extensions.get( 'WEBGL_lose_context' );
-		if ( extension ) extension.restoreContext();
+			}
 
-	};
+			uniforms.uvTransform.value.copy( uvScaleMap.matrix );
 
-	this.getPixelRatio = function () {
+		}
 
-		return _pixelRatio;
+		// uv repeat and offset setting priorities for uv2
+		// 1. ao map
+		// 2. light map
 
-	};
+		var uv2ScaleMap;
 
-	this.setPixelRatio = function ( value ) {
+		if ( material.aoMap ) {
 
-		if ( value === undefined ) return;
+			uv2ScaleMap = material.aoMap;
 
-		_pixelRatio = value;
+		} else if ( material.lightMap ) {
 
-		this.setSize( _width, _height, false );
+			uv2ScaleMap = material.lightMap;
 
-	};
+		}
 
-	this.getSize = function ( target ) {
+		if ( uv2ScaleMap !== undefined ) {
 
-		if ( target === undefined ) {
+			// backwards compatibility
+			if ( uv2ScaleMap.isWebGLRenderTarget ) {
 
-			console.warn( 'WebGLRenderer: .getsize() now requires a Vector2 as an argument' );
+				uv2ScaleMap = uv2ScaleMap.texture;
 
-			target = new Vector2();
+			}
 
-		}
+			if ( uv2ScaleMap.matrixAutoUpdate === true ) {
 
-		return target.set( _width, _height );
+				uv2ScaleMap.updateMatrix();
 
-	};
+			}
 
-	this.setSize = function ( width, height, updateStyle ) {
+			uniforms.uv2Transform.value.copy( uv2ScaleMap.matrix );
 
-		if ( xr.isPresenting ) {
+		}
 
-			console.warn( 'THREE.WebGLRenderer: Can\'t change size while VR device is presenting.' );
-			return;
+	}
 
-		}
+	function refreshUniformsLine( uniforms, material ) {
 
-		_width = width;
-		_height = height;
+		uniforms.diffuse.value.copy( material.color );
+		uniforms.opacity.value = material.opacity;
 
-		_canvas.width = Math.floor( width * _pixelRatio );
-		_canvas.height = Math.floor( height * _pixelRatio );
+	}
 
-		if ( updateStyle !== false ) {
+	function refreshUniformsDash( uniforms, material ) {
 
-			_canvas.style.width = width + 'px';
-			_canvas.style.height = height + 'px';
+		uniforms.dashSize.value = material.dashSize;
+		uniforms.totalSize.value = material.dashSize + material.gapSize;
+		uniforms.scale.value = material.scale;
 
-		}
+	}
 
-		this.setViewport( 0, 0, width, height );
+	function refreshUniformsPoints( uniforms, material, pixelRatio, height ) {
 
-	};
+		uniforms.diffuse.value.copy( material.color );
+		uniforms.opacity.value = material.opacity;
+		uniforms.size.value = material.size * pixelRatio;
+		uniforms.scale.value = height * 0.5;
 
-	this.getDrawingBufferSize = function ( target ) {
+		if ( material.map ) {
 
-		if ( target === undefined ) {
+			uniforms.map.value = material.map;
 
-			console.warn( 'WebGLRenderer: .getdrawingBufferSize() now requires a Vector2 as an argument' );
+		}
 
-			target = new Vector2();
+		if ( material.alphaMap ) {
+
+			uniforms.alphaMap.value = material.alphaMap;
 
 		}
 
-		return target.set( _width * _pixelRatio, _height * _pixelRatio ).floor();
+		// uv repeat and offset setting priorities
+		// 1. color map
+		// 2. alpha map
 
-	};
+		var uvScaleMap;
 
-	this.setDrawingBufferSize = function ( width, height, pixelRatio ) {
+		if ( material.map ) {
 
-		_width = width;
-		_height = height;
+			uvScaleMap = material.map;
 
-		_pixelRatio = pixelRatio;
+		} else if ( material.alphaMap ) {
 
-		_canvas.width = Math.floor( width * pixelRatio );
-		_canvas.height = Math.floor( height * pixelRatio );
+			uvScaleMap = material.alphaMap;
 
-		this.setViewport( 0, 0, width, height );
+		}
 
-	};
+		if ( uvScaleMap !== undefined ) {
 
-	this.getCurrentViewport = function ( target ) {
+			if ( uvScaleMap.matrixAutoUpdate === true ) {
 
-		if ( target === undefined ) {
+				uvScaleMap.updateMatrix();
 
-			console.warn( 'WebGLRenderer: .getCurrentViewport() now requires a Vector4 as an argument' );
+			}
 
-			target = new Vector4();
+			uniforms.uvTransform.value.copy( uvScaleMap.matrix );
 
 		}
 
-		return target.copy( _currentViewport );
+	}
 
-	};
+	function refreshUniformsSprites( uniforms, material ) {
 
-	this.getViewport = function ( target ) {
+		uniforms.diffuse.value.copy( material.color );
+		uniforms.opacity.value = material.opacity;
+		uniforms.rotation.value = material.rotation;
 
-		return target.copy( _viewport );
+		if ( material.map ) {
 
-	};
+			uniforms.map.value = material.map;
 
-	this.setViewport = function ( x, y, width, height ) {
+		}
 
-		if ( x.isVector4 ) {
+		if ( material.alphaMap ) {
 
-			_viewport.set( x.x, x.y, x.z, x.w );
+			uniforms.alphaMap.value = material.alphaMap;
 
-		} else {
+		}
 
-			_viewport.set( x, y, width, height );
+		// uv repeat and offset setting priorities
+		// 1. color map
+		// 2. alpha map
 
-		}
+		var uvScaleMap;
 
-		state.viewport( _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ).floor() );
+		if ( material.map ) {
 
-	};
+			uvScaleMap = material.map;
 
-	this.getScissor = function ( target ) {
+		} else if ( material.alphaMap ) {
 
-		return target.copy( _scissor );
+			uvScaleMap = material.alphaMap;
 
-	};
+		}
 
-	this.setScissor = function ( x, y, width, height ) {
+		if ( uvScaleMap !== undefined ) {
 
-		if ( x.isVector4 ) {
+			if ( uvScaleMap.matrixAutoUpdate === true ) {
 
-			_scissor.set( x.x, x.y, x.z, x.w );
+				uvScaleMap.updateMatrix();
 
-		} else {
+			}
 
-			_scissor.set( x, y, width, height );
+			uniforms.uvTransform.value.copy( uvScaleMap.matrix );
 
 		}
 
-		state.scissor( _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ).floor() );
+	}
 
-	};
+	function refreshUniformsLambert( uniforms, material ) {
 
-	this.getScissorTest = function () {
+		if ( material.emissiveMap ) {
 
-		return _scissorTest;
+			uniforms.emissiveMap.value = material.emissiveMap;
 
-	};
+		}
 
-	this.setScissorTest = function ( boolean ) {
+	}
 
-		state.setScissorTest( _scissorTest = boolean );
+	function refreshUniformsPhong( uniforms, material ) {
 
-	};
+		uniforms.specular.value.copy( material.specular );
+		uniforms.shininess.value = Math.max( material.shininess, 1e-4 ); // to prevent pow( 0.0, 0.0 )
 
-	this.setOpaqueSort = function ( method ) {
+		if ( material.emissiveMap ) {
 
-		_opaqueSort = method;
+			uniforms.emissiveMap.value = material.emissiveMap;
 
-	};
+		}
 
-	this.setTransparentSort = function ( method ) {
+		if ( material.bumpMap ) {
 
-		_transparentSort = method;
+			uniforms.bumpMap.value = material.bumpMap;
+			uniforms.bumpScale.value = material.bumpScale;
+			if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1;
 
-	};
+		}
 
-	// Clearing
+		if ( material.normalMap ) {
 
-	this.getClearColor = function () {
+			uniforms.normalMap.value = material.normalMap;
+			uniforms.normalScale.value.copy( material.normalScale );
+			if ( material.side === BackSide ) uniforms.normalScale.value.negate();
 
-		return background.getClearColor();
+		}
 
-	};
+		if ( material.displacementMap ) {
 
-	this.setClearColor = function () {
+			uniforms.displacementMap.value = material.displacementMap;
+			uniforms.displacementScale.value = material.displacementScale;
+			uniforms.displacementBias.value = material.displacementBias;
 
-		background.setClearColor.apply( background, arguments );
+		}
 
-	};
+	}
 
-	this.getClearAlpha = function () {
+	function refreshUniformsToon( uniforms, material ) {
 
-		return background.getClearAlpha();
+		uniforms.specular.value.copy( material.specular );
+		uniforms.shininess.value = Math.max( material.shininess, 1e-4 ); // to prevent pow( 0.0, 0.0 )
 
-	};
+		if ( material.gradientMap ) {
 
-	this.setClearAlpha = function () {
+			uniforms.gradientMap.value = material.gradientMap;
 
-		background.setClearAlpha.apply( background, arguments );
+		}
 
-	};
+		if ( material.emissiveMap ) {
 
-	this.clear = function ( color, depth, stencil ) {
+			uniforms.emissiveMap.value = material.emissiveMap;
 
-		var bits = 0;
+		}
 
-		if ( color === undefined || color ) bits |= 16384;
-		if ( depth === undefined || depth ) bits |= 256;
-		if ( stencil === undefined || stencil ) bits |= 1024;
+		if ( material.bumpMap ) {
 
-		_gl.clear( bits );
+			uniforms.bumpMap.value = material.bumpMap;
+			uniforms.bumpScale.value = material.bumpScale;
+			if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1;
 
-	};
+		}
 
-	this.clearColor = function () {
+		if ( material.normalMap ) {
 
-		this.clear( true, false, false );
+			uniforms.normalMap.value = material.normalMap;
+			uniforms.normalScale.value.copy( material.normalScale );
+			if ( material.side === BackSide ) uniforms.normalScale.value.negate();
 
-	};
+		}
 
-	this.clearDepth = function () {
+		if ( material.displacementMap ) {
 
-		this.clear( false, true, false );
+			uniforms.displacementMap.value = material.displacementMap;
+			uniforms.displacementScale.value = material.displacementScale;
+			uniforms.displacementBias.value = material.displacementBias;
 
-	};
+		}
 
-	this.clearStencil = function () {
+	}
 
-		this.clear( false, false, true );
+	function refreshUniformsStandard( uniforms, material, environment ) {
 
-	};
-
-	//
+		uniforms.roughness.value = material.roughness;
+		uniforms.metalness.value = material.metalness;
 
-	this.dispose = function () {
+		if ( material.roughnessMap ) {
 
-		_canvas.removeEventListener( 'webglcontextlost', onContextLost, false );
-		_canvas.removeEventListener( 'webglcontextrestored', onContextRestore, false );
+			uniforms.roughnessMap.value = material.roughnessMap;
 
-		renderLists.dispose();
-		renderStates.dispose();
-		properties.dispose();
-		objects.dispose();
+		}
 
-		xr.dispose();
+		if ( material.metalnessMap ) {
 
-		animation.stop();
+			uniforms.metalnessMap.value = material.metalnessMap;
 
-	};
+		}
 
-	// Events
+		if ( material.emissiveMap ) {
 
-	function onContextLost( event ) {
+			uniforms.emissiveMap.value = material.emissiveMap;
 
-		event.preventDefault();
+		}
 
-		console.log( 'THREE.WebGLRenderer: Context Lost.' );
+		if ( material.bumpMap ) {
 
-		_isContextLost = true;
+			uniforms.bumpMap.value = material.bumpMap;
+			uniforms.bumpScale.value = material.bumpScale;
+			if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1;
 
-	}
+		}
 
-	function onContextRestore( /* event */ ) {
+		if ( material.normalMap ) {
 
-		console.log( 'THREE.WebGLRenderer: Context Restored.' );
+			uniforms.normalMap.value = material.normalMap;
+			uniforms.normalScale.value.copy( material.normalScale );
+			if ( material.side === BackSide ) uniforms.normalScale.value.negate();
 
-		_isContextLost = false;
+		}
 
-		initGLContext();
+		if ( material.displacementMap ) {
 
-	}
+			uniforms.displacementMap.value = material.displacementMap;
+			uniforms.displacementScale.value = material.displacementScale;
+			uniforms.displacementBias.value = material.displacementBias;
 
-	function onMaterialDispose( event ) {
+		}
 
-		var material = event.target;
+		if ( material.envMap || environment ) {
 
-		material.removeEventListener( 'dispose', onMaterialDispose );
+			//uniforms.envMap.value = material.envMap; // part of uniforms common
+			uniforms.envMapIntensity.value = material.envMapIntensity;
 
-		deallocateMaterial( material );
+		}
 
 	}
 
-	// Buffer deallocation
-
-	function deallocateMaterial( material ) {
-
-		releaseMaterialProgramReference( material );
+	function refreshUniformsPhysical( uniforms, material, environment ) {
 
-		properties.remove( material );
+		refreshUniformsStandard( uniforms, material, environment );
 
-	}
+		uniforms.reflectivity.value = material.reflectivity; // also part of uniforms common
 
+		uniforms.clearcoat.value = material.clearcoat;
+		uniforms.clearcoatRoughness.value = material.clearcoatRoughness;
+		if ( material.sheen ) uniforms.sheen.value.copy( material.sheen );
 
-	function releaseMaterialProgramReference( material ) {
+		if ( material.clearcoatMap ) {
 
-		var programInfo = properties.get( material ).program;
+			uniforms.clearcoatMap.value = material.clearcoatMap;
 
-		material.program = undefined;
+		}
 
-		if ( programInfo !== undefined ) {
+		if ( material.clearcoatRoughnessMap ) {
 
-			programCache.releaseProgram( programInfo );
+			uniforms.clearcoatRoughnessMap.value = material.clearcoatRoughnessMap;
 
 		}
 
-	}
-
-	// Buffer rendering
+		if ( material.clearcoatNormalMap ) {
 
-	function renderObjectImmediate( object, program ) {
+			uniforms.clearcoatNormalScale.value.copy( material.clearcoatNormalScale );
+			uniforms.clearcoatNormalMap.value = material.clearcoatNormalMap;
 
-		object.render( function ( object ) {
+			if ( material.side === BackSide ) {
 
-			_this.renderBufferImmediate( object, program );
+				uniforms.clearcoatNormalScale.value.negate();
 
-		} );
+			}
 
-	}
+		}
 
-	this.renderBufferImmediate = function ( object, program ) {
+		uniforms.transparency.value = material.transparency;
 
-		state.initAttributes();
+	}
 
-		var buffers = properties.get( object );
+	function refreshUniformsMatcap( uniforms, material ) {
 
-		if ( object.hasPositions && ! buffers.position ) buffers.position = _gl.createBuffer();
-		if ( object.hasNormals && ! buffers.normal ) buffers.normal = _gl.createBuffer();
-		if ( object.hasUvs && ! buffers.uv ) buffers.uv = _gl.createBuffer();
-		if ( object.hasColors && ! buffers.color ) buffers.color = _gl.createBuffer();
+		if ( material.matcap ) {
 
-		var programAttributes = program.getAttributes();
+			uniforms.matcap.value = material.matcap;
 
-		if ( object.hasPositions ) {
+		}
 
-			_gl.bindBuffer( 34962, buffers.position );
-			_gl.bufferData( 34962, object.positionArray, 35048 );
+		if ( material.bumpMap ) {
 
-			state.enableAttribute( programAttributes.position );
-			_gl.vertexAttribPointer( programAttributes.position, 3, 5126, false, 0, 0 );
+			uniforms.bumpMap.value = material.bumpMap;
+			uniforms.bumpScale.value = material.bumpScale;
+			if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1;
 
 		}
 
-		if ( object.hasNormals ) {
-
-			_gl.bindBuffer( 34962, buffers.normal );
-			_gl.bufferData( 34962, object.normalArray, 35048 );
+		if ( material.normalMap ) {
 
-			state.enableAttribute( programAttributes.normal );
-			_gl.vertexAttribPointer( programAttributes.normal, 3, 5126, false, 0, 0 );
+			uniforms.normalMap.value = material.normalMap;
+			uniforms.normalScale.value.copy( material.normalScale );
+			if ( material.side === BackSide ) uniforms.normalScale.value.negate();
 
 		}
 
-		if ( object.hasUvs ) {
-
-			_gl.bindBuffer( 34962, buffers.uv );
-			_gl.bufferData( 34962, object.uvArray, 35048 );
+		if ( material.displacementMap ) {
 
-			state.enableAttribute( programAttributes.uv );
-			_gl.vertexAttribPointer( programAttributes.uv, 2, 5126, false, 0, 0 );
+			uniforms.displacementMap.value = material.displacementMap;
+			uniforms.displacementScale.value = material.displacementScale;
+			uniforms.displacementBias.value = material.displacementBias;
 
 		}
 
-		if ( object.hasColors ) {
-
-			_gl.bindBuffer( 34962, buffers.color );
-			_gl.bufferData( 34962, object.colorArray, 35048 );
-
-			state.enableAttribute( programAttributes.color );
-			_gl.vertexAttribPointer( programAttributes.color, 3, 5126, false, 0, 0 );
+	}
 
-		}
+	function refreshUniformsDepth( uniforms, material ) {
 
-		state.disableUnusedAttributes();
+		if ( material.displacementMap ) {
 
-		_gl.drawArrays( 4, 0, object.count );
+			uniforms.displacementMap.value = material.displacementMap;
+			uniforms.displacementScale.value = material.displacementScale;
+			uniforms.displacementBias.value = material.displacementBias;
 
-		object.count = 0;
+		}
 
-	};
+	}
 
-	var tempScene = new Scene();
+	function refreshUniformsDistance( uniforms, material ) {
 
-	this.renderBufferDirect = function ( camera, scene, geometry, material, object, group ) {
+		if ( material.displacementMap ) {
 
-		if ( scene === null ) scene = tempScene; // renderBufferDirect second parameter used to be fog (could be null)
+			uniforms.displacementMap.value = material.displacementMap;
+			uniforms.displacementScale.value = material.displacementScale;
+			uniforms.displacementBias.value = material.displacementBias;
 
-		var frontFaceCW = ( object.isMesh && object.matrixWorld.determinant() < 0 );
+		}
 
-		var program = setProgram( camera, scene, material, object );
+		uniforms.referencePosition.value.copy( material.referencePosition );
+		uniforms.nearDistance.value = material.nearDistance;
+		uniforms.farDistance.value = material.farDistance;
 
-		state.setMaterial( material, frontFaceCW );
+	}
 
-		var updateBuffers = false;
+	function refreshUniformsNormal( uniforms, material ) {
 
-		if ( _currentGeometryProgram.geometry !== geometry.id ||
-			_currentGeometryProgram.program !== program.id ||
-			_currentGeometryProgram.wireframe !== ( material.wireframe === true ) ) {
+		if ( material.bumpMap ) {
 
-			_currentGeometryProgram.geometry = geometry.id;
-			_currentGeometryProgram.program = program.id;
-			_currentGeometryProgram.wireframe = material.wireframe === true;
-			updateBuffers = true;
+			uniforms.bumpMap.value = material.bumpMap;
+			uniforms.bumpScale.value = material.bumpScale;
+			if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1;
 
 		}
 
-		if ( material.morphTargets || material.morphNormals ) {
-
-			morphtargets.update( object, geometry, material, program );
+		if ( material.normalMap ) {
 
-			updateBuffers = true;
+			uniforms.normalMap.value = material.normalMap;
+			uniforms.normalScale.value.copy( material.normalScale );
+			if ( material.side === BackSide ) uniforms.normalScale.value.negate();
 
 		}
 
-		if ( object.isInstancedMesh === true ) {
+		if ( material.displacementMap ) {
 
-			updateBuffers = true;
+			uniforms.displacementMap.value = material.displacementMap;
+			uniforms.displacementScale.value = material.displacementScale;
+			uniforms.displacementBias.value = material.displacementBias;
 
 		}
 
-		//
+	}
 
-		var index = geometry.index;
-		var position = geometry.attributes.position;
+	return {
+		refreshFogUniforms: refreshFogUniforms,
+		refreshMaterialUniforms: refreshMaterialUniforms
+	};
 
-		//
+}
 
-		if ( index === null ) {
+/**
+ * @author supereggbert / http://www.paulbrunt.co.uk/
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ * @author szimek / https://github.com/szimek/
+ * @author tschw
+ */
 
-			if ( position === undefined || position.count === 0 ) return;
+function WebGLRenderer( parameters ) {
 
-		} else if ( index.count === 0 ) {
+	parameters = parameters || {};
 
-			return;
+	var _canvas = parameters.canvas !== undefined ? parameters.canvas : document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ),
+		_context = parameters.context !== undefined ? parameters.context : null,
 
-		}
+		_alpha = parameters.alpha !== undefined ? parameters.alpha : false,
+		_depth = parameters.depth !== undefined ? parameters.depth : true,
+		_stencil = parameters.stencil !== undefined ? parameters.stencil : true,
+		_antialias = parameters.antialias !== undefined ? parameters.antialias : false,
+		_premultipliedAlpha = parameters.premultipliedAlpha !== undefined ? parameters.premultipliedAlpha : true,
+		_preserveDrawingBuffer = parameters.preserveDrawingBuffer !== undefined ? parameters.preserveDrawingBuffer : false,
+		_powerPreference = parameters.powerPreference !== undefined ? parameters.powerPreference : 'default',
+		_failIfMajorPerformanceCaveat = parameters.failIfMajorPerformanceCaveat !== undefined ? parameters.failIfMajorPerformanceCaveat : false;
 
-		//
+	var currentRenderList = null;
+	var currentRenderState = null;
 
-		var rangeFactor = 1;
+	// public properties
 
-		if ( material.wireframe === true ) {
+	this.domElement = _canvas;
 
-			index = geometries.getWireframeAttribute( geometry );
-			rangeFactor = 2;
+	// Debug configuration container
+	this.debug = {
 
-		}
+		/**
+		 * Enables error checking and reporting when shader programs are being compiled
+		 * @type {boolean}
+		 */
+		checkShaderErrors: true
+	};
 
-		var attribute;
-		var renderer = bufferRenderer;
+	// clearing
 
-		if ( index !== null ) {
-
-			attribute = attributes.get( index );
+	this.autoClear = true;
+	this.autoClearColor = true;
+	this.autoClearDepth = true;
+	this.autoClearStencil = true;
 
-			renderer = indexedBufferRenderer;
-			renderer.setIndex( attribute );
+	// scene graph
 
-		}
+	this.sortObjects = true;
 
-		if ( updateBuffers ) {
+	// user-defined clipping
 
-			setupVertexAttributes( object, geometry, material, program );
+	this.clippingPlanes = [];
+	this.localClippingEnabled = false;
 
-			if ( index !== null ) {
+	// physically based shading
 
-				_gl.bindBuffer( 34963, attribute.buffer );
+	this.gammaFactor = 2.0;	// for backwards compatibility
+	this.outputEncoding = LinearEncoding;
 
-			}
+	// physical lights
 
-		}
+	this.physicallyCorrectLights = false;
 
-		//
+	// tone mapping
 
-		var dataCount = ( index !== null ) ? index.count : position.count;
+	this.toneMapping = NoToneMapping;
+	this.toneMappingExposure = 1.0;
+	this.toneMappingWhitePoint = 1.0;
 
-		var rangeStart = geometry.drawRange.start * rangeFactor;
-		var rangeCount = geometry.drawRange.count * rangeFactor;
+	// morphs
 
-		var groupStart = group !== null ? group.start * rangeFactor : 0;
-		var groupCount = group !== null ? group.count * rangeFactor : Infinity;
+	this.maxMorphTargets = 8;
+	this.maxMorphNormals = 4;
 
-		var drawStart = Math.max( rangeStart, groupStart );
-		var drawEnd = Math.min( dataCount, rangeStart + rangeCount, groupStart + groupCount ) - 1;
+	// internal properties
 
-		var drawCount = Math.max( 0, drawEnd - drawStart + 1 );
+	var _this = this,
 
-		if ( drawCount === 0 ) return;
+		_isContextLost = false,
 
-		//
+		// internal state cache
 
-		if ( object.isMesh ) {
+		_framebuffer = null,
 
-			if ( material.wireframe === true ) {
+		_currentActiveCubeFace = 0,
+		_currentActiveMipmapLevel = 0,
+		_currentRenderTarget = null,
+		_currentFramebuffer = null,
+		_currentMaterialId = - 1,
 
-				state.setLineWidth( material.wireframeLinewidth * getTargetPixelRatio() );
-				renderer.setMode( 1 );
+		// geometry and program caching
 
-			} else {
+		_currentGeometryProgram = {
+			geometry: null,
+			program: null,
+			wireframe: false
+		},
 
-				renderer.setMode( 4 );
+		_currentCamera = null,
+		_currentArrayCamera = null,
 
-			}
+		_currentViewport = new Vector4(),
+		_currentScissor = new Vector4(),
+		_currentScissorTest = null,
 
-		} else if ( object.isLine ) {
+		//
 
-			var lineWidth = material.linewidth;
+		_width = _canvas.width,
+		_height = _canvas.height,
 
-			if ( lineWidth === undefined ) lineWidth = 1; // Not using Line*Material
+		_pixelRatio = 1,
+		_opaqueSort = null,
+		_transparentSort = null,
 
-			state.setLineWidth( lineWidth * getTargetPixelRatio() );
+		_viewport = new Vector4( 0, 0, _width, _height ),
+		_scissor = new Vector4( 0, 0, _width, _height ),
+		_scissorTest = false,
 
-			if ( object.isLineSegments ) {
+		// frustum
 
-				renderer.setMode( 1 );
+		_frustum = new Frustum(),
 
-			} else if ( object.isLineLoop ) {
+		// clipping
 
-				renderer.setMode( 2 );
+		_clipping = new WebGLClipping(),
+		_clippingEnabled = false,
+		_localClippingEnabled = false,
 
-			} else {
+		// camera matrices cache
 
-				renderer.setMode( 3 );
+		_projScreenMatrix = new Matrix4(),
 
-			}
+		_vector3 = new Vector3();
 
-		} else if ( object.isPoints ) {
+	function getTargetPixelRatio() {
 
-			renderer.setMode( 0 );
+		return _currentRenderTarget === null ? _pixelRatio : 1;
 
-		} else if ( object.isSprite ) {
+	}
 
-			renderer.setMode( 4 );
+	// initialize
 
-		}
+	var _gl;
 
-		if ( object.isInstancedMesh ) {
+	try {
 
-			renderer.renderInstances( geometry, drawStart, drawCount, object.count );
+		var contextAttributes = {
+			alpha: _alpha,
+			depth: _depth,
+			stencil: _stencil,
+			antialias: _antialias,
+			premultipliedAlpha: _premultipliedAlpha,
+			preserveDrawingBuffer: _preserveDrawingBuffer,
+			powerPreference: _powerPreference,
+			failIfMajorPerformanceCaveat: _failIfMajorPerformanceCaveat,
+			xrCompatible: true
+		};
 
-		} else if ( geometry.isInstancedBufferGeometry ) {
+		// event listeners must be registered before WebGL context is created, see #12753
 
-			renderer.renderInstances( geometry, drawStart, drawCount, geometry.maxInstancedCount );
+		_canvas.addEventListener( 'webglcontextlost', onContextLost, false );
+		_canvas.addEventListener( 'webglcontextrestored', onContextRestore, false );
 
-		} else {
+		_gl = _context || _canvas.getContext( 'webgl', contextAttributes ) || _canvas.getContext( 'experimental-webgl', contextAttributes );
 
-			renderer.render( drawStart, drawCount );
+		if ( _gl === null ) {
 
-		}
+			if ( _canvas.getContext( 'webgl' ) !== null ) {
 
-	};
+				throw new Error( 'Error creating WebGL context with your selected attributes.' );
 
-	function setupVertexAttributes( object, geometry, material, program ) {
+			} else {
 
-		if ( capabilities.isWebGL2 === false && ( object.isInstancedMesh || geometry.isInstancedBufferGeometry ) ) {
+				throw new Error( 'Error creating WebGL context.' );
 
-			if ( extensions.get( 'ANGLE_instanced_arrays' ) === null ) return;
+			}
 
 		}
 
-		state.initAttributes();
+		// Some experimental-webgl implementations do not have getShaderPrecisionFormat
 
-		var geometryAttributes = geometry.attributes;
+		if ( _gl.getShaderPrecisionFormat === undefined ) {
 
-		var programAttributes = program.getAttributes();
+			_gl.getShaderPrecisionFormat = function () {
 
-		var materialDefaultAttributeValues = material.defaultAttributeValues;
+				return { 'rangeMin': 1, 'rangeMax': 1, 'precision': 1 };
 
-		for ( var name in programAttributes ) {
+			};
 
-			var programAttribute = programAttributes[ name ];
+		}
 
-			if ( programAttribute >= 0 ) {
+	} catch ( error ) {
 
-				var geometryAttribute = geometryAttributes[ name ];
+		console.error( 'THREE.WebGLRenderer: ' + error.message );
+		throw error;
 
-				if ( geometryAttribute !== undefined ) {
+	}
 
-					var normalized = geometryAttribute.normalized;
-					var size = geometryAttribute.itemSize;
+	var extensions, capabilities, state, info;
+	var properties, textures, attributes, geometries, objects;
+	var programCache, materials, renderLists, renderStates;
 
-					var attribute = attributes.get( geometryAttribute );
+	var background, morphtargets, bufferRenderer, indexedBufferRenderer;
 
-					// TODO Attribute may not be available on context restore
+	var utils;
 
-					if ( attribute === undefined ) continue;
+	function initGLContext() {
 
-					var buffer = attribute.buffer;
-					var type = attribute.type;
-					var bytesPerElement = attribute.bytesPerElement;
+		extensions = new WebGLExtensions( _gl );
 
-					if ( geometryAttribute.isInterleavedBufferAttribute ) {
+		capabilities = new WebGLCapabilities( _gl, extensions, parameters );
 
-						var data = geometryAttribute.data;
-						var stride = data.stride;
-						var offset = geometryAttribute.offset;
+		if ( capabilities.isWebGL2 === false ) {
 
-						if ( data && data.isInstancedInterleavedBuffer ) {
+			extensions.get( 'WEBGL_depth_texture' );
+			extensions.get( 'OES_texture_float' );
+			extensions.get( 'OES_texture_half_float' );
+			extensions.get( 'OES_texture_half_float_linear' );
+			extensions.get( 'OES_standard_derivatives' );
+			extensions.get( 'OES_element_index_uint' );
+			extensions.get( 'ANGLE_instanced_arrays' );
 
-							state.enableAttributeAndDivisor( programAttribute, data.meshPerAttribute );
+		}
 
-							if ( geometry.maxInstancedCount === undefined ) {
+		extensions.get( 'OES_texture_float_linear' );
 
-								geometry.maxInstancedCount = data.meshPerAttribute * data.count;
+		utils = new WebGLUtils( _gl, extensions, capabilities );
 
-							}
+		state = new WebGLState( _gl, extensions, capabilities );
+		state.scissor( _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ).floor() );
+		state.viewport( _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ).floor() );
 
-						} else {
+		info = new WebGLInfo( _gl );
+		properties = new WebGLProperties();
+		textures = new WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info );
+		attributes = new WebGLAttributes( _gl, capabilities );
+		geometries = new WebGLGeometries( _gl, attributes, info );
+		objects = new WebGLObjects( _gl, geometries, attributes, info );
+		morphtargets = new WebGLMorphtargets( _gl );
+		programCache = new WebGLPrograms( _this, extensions, capabilities );
+		materials = new WebGLMaterials( properties );
+		renderLists = new WebGLRenderLists();
+		renderStates = new WebGLRenderStates();
 
-							state.enableAttribute( programAttribute );
+		background = new WebGLBackground( _this, state, objects, _premultipliedAlpha );
 
-						}
+		bufferRenderer = new WebGLBufferRenderer( _gl, extensions, info, capabilities );
+		indexedBufferRenderer = new WebGLIndexedBufferRenderer( _gl, extensions, info, capabilities );
 
-						_gl.bindBuffer( 34962, buffer );
-						state.vertexAttribPointer( programAttribute, size, type, normalized, stride * bytesPerElement, offset * bytesPerElement );
+		info.programs = programCache.programs;
 
-					} else {
+		_this.capabilities = capabilities;
+		_this.extensions = extensions;
+		_this.properties = properties;
+		_this.renderLists = renderLists;
+		_this.state = state;
+		_this.info = info;
 
-						if ( geometryAttribute.isInstancedBufferAttribute ) {
+	}
 
-							state.enableAttributeAndDivisor( programAttribute, geometryAttribute.meshPerAttribute );
+	initGLContext();
 
-							if ( geometry.maxInstancedCount === undefined ) {
+	// xr
 
-								geometry.maxInstancedCount = geometryAttribute.meshPerAttribute * geometryAttribute.count;
+	var xr = new WebXRManager( _this, _gl );
 
-							}
+	this.xr = xr;
 
-						} else {
+	// shadow map
 
-							state.enableAttribute( programAttribute );
+	var shadowMap = new WebGLShadowMap( _this, objects, capabilities.maxTextureSize );
 
-						}
+	this.shadowMap = shadowMap;
 
-						_gl.bindBuffer( 34962, buffer );
-						state.vertexAttribPointer( programAttribute, size, type, normalized, 0, 0 );
+	// API
 
-					}
+	this.getContext = function () {
 
-				} else if ( name === 'instanceMatrix' ) {
+		return _gl;
 
-					var attribute = attributes.get( object.instanceMatrix );
+	};
 
-					// TODO Attribute may not be available on context restore
+	this.getContextAttributes = function () {
 
-					if ( attribute === undefined ) continue;
+		return _gl.getContextAttributes();
 
-					var buffer = attribute.buffer;
-					var type = attribute.type;
+	};
 
-					state.enableAttributeAndDivisor( programAttribute + 0, 1 );
-					state.enableAttributeAndDivisor( programAttribute + 1, 1 );
-					state.enableAttributeAndDivisor( programAttribute + 2, 1 );
-					state.enableAttributeAndDivisor( programAttribute + 3, 1 );
+	this.forceContextLoss = function () {
 
-					_gl.bindBuffer( 34962, buffer );
+		var extension = extensions.get( 'WEBGL_lose_context' );
+		if ( extension ) extension.loseContext();
 
-					_gl.vertexAttribPointer( programAttribute + 0, 4, type, false, 64, 0 );
-					_gl.vertexAttribPointer( programAttribute + 1, 4, type, false, 64, 16 );
-					_gl.vertexAttribPointer( programAttribute + 2, 4, type, false, 64, 32 );
-					_gl.vertexAttribPointer( programAttribute + 3, 4, type, false, 64, 48 );
+	};
 
-				} else if ( materialDefaultAttributeValues !== undefined ) {
+	this.forceContextRestore = function () {
 
-					var value = materialDefaultAttributeValues[ name ];
+		var extension = extensions.get( 'WEBGL_lose_context' );
+		if ( extension ) extension.restoreContext();
 
-					if ( value !== undefined ) {
+	};
 
-						switch ( value.length ) {
+	this.getPixelRatio = function () {
 
-							case 2:
-								_gl.vertexAttrib2fv( programAttribute, value );
-								break;
+		return _pixelRatio;
 
-							case 3:
-								_gl.vertexAttrib3fv( programAttribute, value );
-								break;
+	};
 
-							case 4:
-								_gl.vertexAttrib4fv( programAttribute, value );
-								break;
+	this.setPixelRatio = function ( value ) {
 
-							default:
-								_gl.vertexAttrib1fv( programAttribute, value );
+		if ( value === undefined ) return;
 
-						}
+		_pixelRatio = value;
 
-					}
+		this.setSize( _width, _height, false );
 
-				}
+	};
 
-			}
+	this.getSize = function ( target ) {
 
-		}
+		if ( target === undefined ) {
 
-		state.disableUnusedAttributes();
+			console.warn( 'WebGLRenderer: .getsize() now requires a Vector2 as an argument' );
 
-	}
+			target = new Vector2();
 
-	// Compile
+		}
 
-	this.compile = function ( scene, camera ) {
+		return target.set( _width, _height );
 
-		currentRenderState = renderStates.get( scene, camera );
-		currentRenderState.init();
+	};
 
-		scene.traverse( function ( object ) {
+	this.setSize = function ( width, height, updateStyle ) {
 
-			if ( object.isLight ) {
+		if ( xr.isPresenting ) {
 
-				currentRenderState.pushLight( object );
+			console.warn( 'THREE.WebGLRenderer: Can\'t change size while VR device is presenting.' );
+			return;
 
-				if ( object.castShadow ) {
+		}
 
-					currentRenderState.pushShadow( object );
+		_width = width;
+		_height = height;
 
-				}
+		_canvas.width = Math.floor( width * _pixelRatio );
+		_canvas.height = Math.floor( height * _pixelRatio );
 
-			}
+		if ( updateStyle !== false ) {
 
-		} );
+			_canvas.style.width = width + 'px';
+			_canvas.style.height = height + 'px';
 
-		currentRenderState.setupLights( camera );
+		}
 
-		var compiled = {};
+		this.setViewport( 0, 0, width, height );
 
-		scene.traverse( function ( object ) {
+	};
 
-			if ( object.material ) {
+	this.getDrawingBufferSize = function ( target ) {
 
-				if ( Array.isArray( object.material ) ) {
+		if ( target === undefined ) {
 
-					for ( var i = 0; i < object.material.length; i ++ ) {
+			console.warn( 'WebGLRenderer: .getdrawingBufferSize() now requires a Vector2 as an argument' );
 
-						if ( object.material[ i ].uuid in compiled === false ) {
+			target = new Vector2();
 
-							initMaterial( object.material[ i ], scene, object );
-							compiled[ object.material[ i ].uuid ] = true;
+		}
 
-						}
+		return target.set( _width * _pixelRatio, _height * _pixelRatio ).floor();
 
-					}
+	};
 
-				} else if ( object.material.uuid in compiled === false ) {
+	this.setDrawingBufferSize = function ( width, height, pixelRatio ) {
 
-					initMaterial( object.material, scene, object );
-					compiled[ object.material.uuid ] = true;
+		_width = width;
+		_height = height;
 
-				}
+		_pixelRatio = pixelRatio;
 
-			}
+		_canvas.width = Math.floor( width * pixelRatio );
+		_canvas.height = Math.floor( height * pixelRatio );
 
-		} );
+		this.setViewport( 0, 0, width, height );
 
 	};
 
-	// Animation Loop
-
-	var onAnimationFrameCallback = null;
+	this.getCurrentViewport = function ( target ) {
 
-	function onAnimationFrame( time ) {
+		if ( target === undefined ) {
 
-		if ( xr.isPresenting ) return;
-		if ( onAnimationFrameCallback ) onAnimationFrameCallback( time );
+			console.warn( 'WebGLRenderer: .getCurrentViewport() now requires a Vector4 as an argument' );
 
-	}
+			target = new Vector4();
 
-	var animation = new WebGLAnimation();
-	animation.setAnimationLoop( onAnimationFrame );
+		}
 
-	if ( typeof window !== 'undefined' ) animation.setContext( window );
+		return target.copy( _currentViewport );
 
-	this.setAnimationLoop = function ( callback ) {
+	};
 
-		onAnimationFrameCallback = callback;
-		xr.setAnimationLoop( callback );
+	this.getViewport = function ( target ) {
 
-		animation.start();
+		return target.copy( _viewport );
 
 	};
 
-	// Rendering
+	this.setViewport = function ( x, y, width, height ) {
 
-	this.render = function ( scene, camera ) {
+		if ( x.isVector4 ) {
 
-		var renderTarget, forceClear;
+			_viewport.set( x.x, x.y, x.z, x.w );
 
-		if ( arguments[ 2 ] !== undefined ) {
+		} else {
 
-			console.warn( 'THREE.WebGLRenderer.render(): the renderTarget argument has been removed. Use .setRenderTarget() instead.' );
-			renderTarget = arguments[ 2 ];
+			_viewport.set( x, y, width, height );
 
 		}
 
-		if ( arguments[ 3 ] !== undefined ) {
+		state.viewport( _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ).floor() );
 
-			console.warn( 'THREE.WebGLRenderer.render(): the forceClear argument has been removed. Use .clear() instead.' );
-			forceClear = arguments[ 3 ];
+	};
 
-		}
+	this.getScissor = function ( target ) {
 
-		if ( ! ( camera && camera.isCamera ) ) {
+		return target.copy( _scissor );
 
-			console.error( 'THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.' );
-			return;
+	};
 
-		}
+	this.setScissor = function ( x, y, width, height ) {
 
-		if ( _isContextLost ) return;
+		if ( x.isVector4 ) {
 
-		// reset caching for this frame
+			_scissor.set( x.x, x.y, x.z, x.w );
 
-		_currentGeometryProgram.geometry = null;
-		_currentGeometryProgram.program = null;
-		_currentGeometryProgram.wireframe = false;
-		_currentMaterialId = - 1;
-		_currentCamera = null;
+		} else {
 
-		// update scene graph
+			_scissor.set( x, y, width, height );
 
-		if ( scene.autoUpdate === true ) scene.updateMatrixWorld();
+		}
 
-		// update camera matrices and frustum
+		state.scissor( _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ).floor() );
 
-		if ( camera.parent === null ) camera.updateMatrixWorld();
+	};
 
-		if ( xr.enabled && xr.isPresenting ) {
+	this.getScissorTest = function () {
 
-			camera = xr.getCamera( camera );
+		return _scissorTest;
 
-		}
+	};
 
-		//
-		scene.onBeforeRender( _this, scene, camera, renderTarget || _currentRenderTarget );
+	this.setScissorTest = function ( boolean ) {
 
-		currentRenderState = renderStates.get( scene, camera );
-		currentRenderState.init();
+		state.setScissorTest( _scissorTest = boolean );
 
-		_projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse );
-		_frustum.setFromProjectionMatrix( _projScreenMatrix );
+	};
 
-		_localClippingEnabled = this.localClippingEnabled;
-		_clippingEnabled = _clipping.init( this.clippingPlanes, _localClippingEnabled, camera );
+	this.setOpaqueSort = function ( method ) {
 
-		currentRenderList = renderLists.get( scene, camera );
-		currentRenderList.init();
+		_opaqueSort = method;
 
-		projectObject( scene, camera, 0, _this.sortObjects );
+	};
 
-		currentRenderList.finish();
+	this.setTransparentSort = function ( method ) {
 
-		if ( _this.sortObjects === true ) {
+		_transparentSort = method;
 
-			currentRenderList.sort( _opaqueSort, _transparentSort );
+	};
 
-		}
+	// Clearing
 
-		//
+	this.getClearColor = function () {
 
-		if ( _clippingEnabled ) _clipping.beginShadows();
+		return background.getClearColor();
 
-		var shadowsArray = currentRenderState.state.shadowsArray;
+	};
 
-		shadowMap.render( shadowsArray, scene, camera );
+	this.setClearColor = function () {
 
-		currentRenderState.setupLights( camera );
+		background.setClearColor.apply( background, arguments );
 
-		if ( _clippingEnabled ) _clipping.endShadows();
+	};
 
-		//
+	this.getClearAlpha = function () {
 
-		if ( this.info.autoReset ) this.info.reset();
+		return background.getClearAlpha();
 
-		if ( renderTarget !== undefined ) {
+	};
 
-			this.setRenderTarget( renderTarget );
+	this.setClearAlpha = function () {
 
-		}
+		background.setClearAlpha.apply( background, arguments );
 
-		//
+	};
 
-		background.render( currentRenderList, scene, camera, forceClear );
+	this.clear = function ( color, depth, stencil ) {
 
-		// render scene
+		var bits = 0;
 
-		var opaqueObjects = currentRenderList.opaque;
-		var transparentObjects = currentRenderList.transparent;
+		if ( color === undefined || color ) bits |= 16384;
+		if ( depth === undefined || depth ) bits |= 256;
+		if ( stencil === undefined || stencil ) bits |= 1024;
 
-		if ( scene.overrideMaterial ) {
+		_gl.clear( bits );
 
-			var overrideMaterial = scene.overrideMaterial;
+	};
 
-			if ( opaqueObjects.length ) renderObjects( opaqueObjects, scene, camera, overrideMaterial );
-			if ( transparentObjects.length ) renderObjects( transparentObjects, scene, camera, overrideMaterial );
+	this.clearColor = function () {
 
-		} else {
+		this.clear( true, false, false );
 
-			// opaque pass (front-to-back order)
+	};
 
-			if ( opaqueObjects.length ) renderObjects( opaqueObjects, scene, camera );
+	this.clearDepth = function () {
 
-			// transparent pass (back-to-front order)
+		this.clear( false, true, false );
 
-			if ( transparentObjects.length ) renderObjects( transparentObjects, scene, camera );
+	};
 
-		}
+	this.clearStencil = function () {
 
-		//
+		this.clear( false, false, true );
 
-		scene.onAfterRender( _this, scene, camera );
+	};
 
-		//
+	//
 
-		if ( _currentRenderTarget !== null ) {
+	this.dispose = function () {
 
-			// Generate mipmap if we're using any kind of mipmap filtering
+		_canvas.removeEventListener( 'webglcontextlost', onContextLost, false );
+		_canvas.removeEventListener( 'webglcontextrestored', onContextRestore, false );
 
-			textures.updateRenderTargetMipmap( _currentRenderTarget );
+		renderLists.dispose();
+		renderStates.dispose();
+		properties.dispose();
+		objects.dispose();
 
-			// resolve multisample renderbuffers to a single-sample texture if necessary
+		xr.dispose();
 
-			textures.updateMultisampleRenderTarget( _currentRenderTarget );
+		animation.stop();
 
-		}
+	};
 
-		// Ensure depth buffer writing is enabled so it can be cleared on next render
+	// Events
 
-		state.buffers.depth.setTest( true );
-		state.buffers.depth.setMask( true );
-		state.buffers.color.setMask( true );
+	function onContextLost( event ) {
 
-		state.setPolygonOffset( false );
+		event.preventDefault();
 
-		// _gl.finish();
+		console.log( 'THREE.WebGLRenderer: Context Lost.' );
 
-		currentRenderList = null;
-		currentRenderState = null;
+		_isContextLost = true;
 
-	};
+	}
 
-	function projectObject( object, camera, groupOrder, sortObjects ) {
+	function onContextRestore( /* event */ ) {
 
-		if ( object.visible === false ) return;
+		console.log( 'THREE.WebGLRenderer: Context Restored.' );
 
-		var visible = object.layers.test( camera.layers );
+		_isContextLost = false;
 
-		if ( visible ) {
+		initGLContext();
 
-			if ( object.isGroup ) {
+	}
 
-				groupOrder = object.renderOrder;
+	function onMaterialDispose( event ) {
 
-			} else if ( object.isLOD ) {
+		var material = event.target;
 
-				if ( object.autoUpdate === true ) object.update( camera );
+		material.removeEventListener( 'dispose', onMaterialDispose );
 
-			} else if ( object.isLight ) {
+		deallocateMaterial( material );
 
-				currentRenderState.pushLight( object );
+	}
 
-				if ( object.castShadow ) {
+	// Buffer deallocation
 
-					currentRenderState.pushShadow( object );
+	function deallocateMaterial( material ) {
 
-				}
+		releaseMaterialProgramReference( material );
 
-			} else if ( object.isSprite ) {
+		properties.remove( material );
 
-				if ( ! object.frustumCulled || _frustum.intersectsSprite( object ) ) {
+	}
 
-					if ( sortObjects ) {
 
-						_vector3.setFromMatrixPosition( object.matrixWorld )
-							.applyMatrix4( _projScreenMatrix );
+	function releaseMaterialProgramReference( material ) {
 
-					}
+		var programInfo = properties.get( material ).program;
 
-					var geometry = objects.update( object );
-					var material = object.material;
+		material.program = undefined;
 
-					if ( material.visible ) {
+		if ( programInfo !== undefined ) {
 
-						currentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null );
+			programCache.releaseProgram( programInfo );
 
-					}
+		}
 
-				}
+	}
 
-			} else if ( object.isImmediateRenderObject ) {
+	// Buffer rendering
 
-				if ( sortObjects ) {
+	function renderObjectImmediate( object, program ) {
 
-					_vector3.setFromMatrixPosition( object.matrixWorld )
-						.applyMatrix4( _projScreenMatrix );
+		object.render( function ( object ) {
 
-				}
+			_this.renderBufferImmediate( object, program );
 
-				currentRenderList.push( object, null, object.material, groupOrder, _vector3.z, null );
+		} );
 
-			} else if ( object.isMesh || object.isLine || object.isPoints ) {
+	}
 
-				if ( object.isSkinnedMesh ) {
+	this.renderBufferImmediate = function ( object, program ) {
 
-					// update skeleton only once in a frame
+		state.initAttributes();
 
-					if ( object.skeleton.frame !== info.render.frame ) {
+		var buffers = properties.get( object );
 
-						object.skeleton.update();
-						object.skeleton.frame = info.render.frame;
+		if ( object.hasPositions && ! buffers.position ) buffers.position = _gl.createBuffer();
+		if ( object.hasNormals && ! buffers.normal ) buffers.normal = _gl.createBuffer();
+		if ( object.hasUvs && ! buffers.uv ) buffers.uv = _gl.createBuffer();
+		if ( object.hasColors && ! buffers.color ) buffers.color = _gl.createBuffer();
 
-					}
+		var programAttributes = program.getAttributes();
 
-				}
+		if ( object.hasPositions ) {
 
-				if ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) {
+			_gl.bindBuffer( 34962, buffers.position );
+			_gl.bufferData( 34962, object.positionArray, 35048 );
 
-					if ( sortObjects ) {
+			state.enableAttribute( programAttributes.position );
+			_gl.vertexAttribPointer( programAttributes.position, 3, 5126, false, 0, 0 );
 
-						_vector3.setFromMatrixPosition( object.matrixWorld )
-							.applyMatrix4( _projScreenMatrix );
+		}
 
-					}
+		if ( object.hasNormals ) {
 
-					var geometry = objects.update( object );
-					var material = object.material;
+			_gl.bindBuffer( 34962, buffers.normal );
+			_gl.bufferData( 34962, object.normalArray, 35048 );
 
-					if ( Array.isArray( material ) ) {
+			state.enableAttribute( programAttributes.normal );
+			_gl.vertexAttribPointer( programAttributes.normal, 3, 5126, false, 0, 0 );
 
-						var groups = geometry.groups;
+		}
 
-						for ( var i = 0, l = groups.length; i < l; i ++ ) {
+		if ( object.hasUvs ) {
 
-							var group = groups[ i ];
-							var groupMaterial = material[ group.materialIndex ];
+			_gl.bindBuffer( 34962, buffers.uv );
+			_gl.bufferData( 34962, object.uvArray, 35048 );
 
-							if ( groupMaterial && groupMaterial.visible ) {
+			state.enableAttribute( programAttributes.uv );
+			_gl.vertexAttribPointer( programAttributes.uv, 2, 5126, false, 0, 0 );
 
-								currentRenderList.push( object, geometry, groupMaterial, groupOrder, _vector3.z, group );
+		}
 
-							}
+		if ( object.hasColors ) {
 
-						}
+			_gl.bindBuffer( 34962, buffers.color );
+			_gl.bufferData( 34962, object.colorArray, 35048 );
 
-					} else if ( material.visible ) {
+			state.enableAttribute( programAttributes.color );
+			_gl.vertexAttribPointer( programAttributes.color, 3, 5126, false, 0, 0 );
 
-						currentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null );
+		}
 
-					}
+		state.disableUnusedAttributes();
 
-				}
+		_gl.drawArrays( 4, 0, object.count );
 
-			}
+		object.count = 0;
 
-		}
+	};
 
-		var children = object.children;
+	var tempScene = new Scene();
 
-		for ( var i = 0, l = children.length; i < l; i ++ ) {
+	this.renderBufferDirect = function ( camera, scene, geometry, material, object, group ) {
 
-			projectObject( children[ i ], camera, groupOrder, sortObjects );
+		if ( scene === null ) scene = tempScene; // renderBufferDirect second parameter used to be fog (could be null)
 
-		}
+		var frontFaceCW = ( object.isMesh && object.matrixWorld.determinant() < 0 );
 
-	}
+		var program = setProgram( camera, scene, material, object );
 
-	function renderObjects( renderList, scene, camera, overrideMaterial ) {
+		state.setMaterial( material, frontFaceCW );
 
-		for ( var i = 0, l = renderList.length; i < l; i ++ ) {
+		var updateBuffers = false;
 
-			var renderItem = renderList[ i ];
+		if ( _currentGeometryProgram.geometry !== geometry.id ||
+			_currentGeometryProgram.program !== program.id ||
+			_currentGeometryProgram.wireframe !== ( material.wireframe === true ) ) {
 
-			var object = renderItem.object;
-			var geometry = renderItem.geometry;
-			var material = overrideMaterial === undefined ? renderItem.material : overrideMaterial;
-			var group = renderItem.group;
+			_currentGeometryProgram.geometry = geometry.id;
+			_currentGeometryProgram.program = program.id;
+			_currentGeometryProgram.wireframe = material.wireframe === true;
+			updateBuffers = true;
 
-			if ( camera.isArrayCamera ) {
+		}
 
-				_currentArrayCamera = camera;
+		if ( material.morphTargets || material.morphNormals ) {
 
-				var cameras = camera.cameras;
+			morphtargets.update( object, geometry, material, program );
 
-				for ( var j = 0, jl = cameras.length; j < jl; j ++ ) {
+			updateBuffers = true;
 
-					var camera2 = cameras[ j ];
+		}
 
-					if ( object.layers.test( camera2.layers ) ) {
+		if ( object.isInstancedMesh === true ) {
 
-						state.viewport( _currentViewport.copy( camera2.viewport ) );
+			updateBuffers = true;
 
-						currentRenderState.setupLights( camera2 );
+		}
 
-						renderObject( object, scene, camera2, geometry, material, group );
+		//
 
-					}
+		var index = geometry.index;
+		var position = geometry.attributes.position;
 
-				}
+		//
 
-			} else {
+		if ( index === null ) {
 
-				_currentArrayCamera = null;
+			if ( position === undefined || position.count === 0 ) return;
 
-				renderObject( object, scene, camera, geometry, material, group );
+		} else if ( index.count === 0 ) {
 
-			}
+			return;
 
 		}
 
-	}
-
-	function renderObject( object, scene, camera, geometry, material, group ) {
-
-		object.onBeforeRender( _this, scene, camera, geometry, material, group );
-		currentRenderState = renderStates.get( scene, _currentArrayCamera || camera );
+		//
 
-		object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld );
-		object.normalMatrix.getNormalMatrix( object.modelViewMatrix );
+		var rangeFactor = 1;
 
-		if ( object.isImmediateRenderObject ) {
+		if ( material.wireframe === true ) {
 
-			var program = setProgram( camera, scene, material, object );
+			index = geometries.getWireframeAttribute( geometry );
+			rangeFactor = 2;
 
-			state.setMaterial( material );
+		}
 
-			_currentGeometryProgram.geometry = null;
-			_currentGeometryProgram.program = null;
-			_currentGeometryProgram.wireframe = false;
+		var attribute;
+		var renderer = bufferRenderer;
 
-			renderObjectImmediate( object, program );
+		if ( index !== null ) {
 
-		} else {
+			attribute = attributes.get( index );
 
-			_this.renderBufferDirect( camera, scene, geometry, material, object, group );
+			renderer = indexedBufferRenderer;
+			renderer.setIndex( attribute );
 
 		}
 
-		object.onAfterRender( _this, scene, camera, geometry, material, group );
-		currentRenderState = renderStates.get( scene, _currentArrayCamera || camera );
-
-	}
+		if ( updateBuffers ) {
 
-	function initMaterial( material, scene, object ) {
+			setupVertexAttributes( object, geometry, material, program );
 
-		var materialProperties = properties.get( material );
+			if ( index !== null ) {
 
-		var lights = currentRenderState.state.lights;
-		var shadowsArray = currentRenderState.state.shadowsArray;
+				_gl.bindBuffer( 34963, attribute.buffer );
 
-		var lightsStateVersion = lights.state.version;
+			}
 
-		var parameters = programCache.getParameters( material, lights.state, shadowsArray, scene, _clipping.numPlanes, _clipping.numIntersection, object );
-		var programCacheKey = programCache.getProgramCacheKey( parameters );
+		}
 
-		var program = materialProperties.program;
-		var programChange = true;
+		//
 
-		if ( program === undefined ) {
+		var dataCount = ( index !== null ) ? index.count : position.count;
 
-			// new material
-			material.addEventListener( 'dispose', onMaterialDispose );
+		var rangeStart = geometry.drawRange.start * rangeFactor;
+		var rangeCount = geometry.drawRange.count * rangeFactor;
 
-		} else if ( program.cacheKey !== programCacheKey ) {
+		var groupStart = group !== null ? group.start * rangeFactor : 0;
+		var groupCount = group !== null ? group.count * rangeFactor : Infinity;
 
-			// changed glsl or parameters
-			releaseMaterialProgramReference( material );
+		var drawStart = Math.max( rangeStart, groupStart );
+		var drawEnd = Math.min( dataCount, rangeStart + rangeCount, groupStart + groupCount ) - 1;
 
-		} else if ( materialProperties.lightsStateVersion !== lightsStateVersion ) {
+		var drawCount = Math.max( 0, drawEnd - drawStart + 1 );
 
-			materialProperties.lightsStateVersion = lightsStateVersion;
+		if ( drawCount === 0 ) return;
 
-			programChange = false;
+		//
 
-		} else if ( parameters.shaderID !== undefined ) {
+		if ( object.isMesh ) {
 
-			// same glsl and uniform list
-			return;
+			if ( material.wireframe === true ) {
 
-		} else {
+				state.setLineWidth( material.wireframeLinewidth * getTargetPixelRatio() );
+				renderer.setMode( 1 );
 
-			// only rebuild uniform list
-			programChange = false;
+			} else {
 
-		}
+				renderer.setMode( 4 );
 
-		if ( programChange ) {
+			}
 
-			program = programCache.acquireProgram( parameters, programCacheKey );
+		} else if ( object.isLine ) {
 
-			materialProperties.program = program;
-			materialProperties.uniforms = parameters.uniforms;
-			materialProperties.outputEncoding = parameters.outputEncoding;
-			material.program = program;
+			var lineWidth = material.linewidth;
 
-		}
+			if ( lineWidth === undefined ) lineWidth = 1; // Not using Line*Material
 
-		var programAttributes = program.getAttributes();
+			state.setLineWidth( lineWidth * getTargetPixelRatio() );
 
-		if ( material.morphTargets ) {
+			if ( object.isLineSegments ) {
 
-			material.numSupportedMorphTargets = 0;
+				renderer.setMode( 1 );
 
-			for ( var i = 0; i < _this.maxMorphTargets; i ++ ) {
+			} else if ( object.isLineLoop ) {
 
-				if ( programAttributes[ 'morphTarget' + i ] >= 0 ) {
+				renderer.setMode( 2 );
 
-					material.numSupportedMorphTargets ++;
+			} else {
 
-				}
+				renderer.setMode( 3 );
 
 			}
 
-		}
+		} else if ( object.isPoints ) {
 
-		if ( material.morphNormals ) {
+			renderer.setMode( 0 );
 
-			material.numSupportedMorphNormals = 0;
+		} else if ( object.isSprite ) {
 
-			for ( var i = 0; i < _this.maxMorphNormals; i ++ ) {
+			renderer.setMode( 4 );
 
-				if ( programAttributes[ 'morphNormal' + i ] >= 0 ) {
+		}
 
-					material.numSupportedMorphNormals ++;
+		if ( object.isInstancedMesh ) {
 
-				}
+			renderer.renderInstances( geometry, drawStart, drawCount, object.count );
 
-			}
+		} else if ( geometry.isInstancedBufferGeometry ) {
+
+			renderer.renderInstances( geometry, drawStart, drawCount, geometry.maxInstancedCount );
+
+		} else {
+
+			renderer.render( drawStart, drawCount );
 
 		}
 
-		var uniforms = materialProperties.uniforms;
+	};
 
-		if ( ! material.isShaderMaterial &&
-			! material.isRawShaderMaterial ||
-			material.clipping === true ) {
+	function setupVertexAttributes( object, geometry, material, program ) {
 
-			materialProperties.numClippingPlanes = _clipping.numPlanes;
-			materialProperties.numIntersection = _clipping.numIntersection;
-			uniforms.clippingPlanes = _clipping.uniform;
+		if ( capabilities.isWebGL2 === false && ( object.isInstancedMesh || geometry.isInstancedBufferGeometry ) ) {
+
+			if ( extensions.get( 'ANGLE_instanced_arrays' ) === null ) return;
 
 		}
 
-		materialProperties.environment = material.isMeshStandardMaterial ? scene.environment : null;
-		materialProperties.fog = scene.fog;
+		state.initAttributes();
 
-		// store the light setup it was created for
+		var geometryAttributes = geometry.attributes;
 
-		materialProperties.needsLights = materialNeedsLights( material );
-		materialProperties.lightsStateVersion = lightsStateVersion;
+		var programAttributes = program.getAttributes();
 
-		if ( materialProperties.needsLights ) {
+		var materialDefaultAttributeValues = material.defaultAttributeValues;
 
-			// wire up the material to this renderer's lighting state
+		for ( var name in programAttributes ) {
 
-			uniforms.ambientLightColor.value = lights.state.ambient;
-			uniforms.lightProbe.value = lights.state.probe;
-			uniforms.directionalLights.value = lights.state.directional;
-			uniforms.directionalLightShadows.value = lights.state.directionalShadow;
-			uniforms.spotLights.value = lights.state.spot;
-			uniforms.spotLightShadows.value = lights.state.spotShadow;
-			uniforms.rectAreaLights.value = lights.state.rectArea;
-			uniforms.pointLights.value = lights.state.point;
-			uniforms.pointLightShadows.value = lights.state.pointShadow;
-			uniforms.hemisphereLights.value = lights.state.hemi;
+			var programAttribute = programAttributes[ name ];
 
-			uniforms.directionalShadowMap.value = lights.state.directionalShadowMap;
-			uniforms.directionalShadowMatrix.value = lights.state.directionalShadowMatrix;
-			uniforms.spotShadowMap.value = lights.state.spotShadowMap;
-			uniforms.spotShadowMatrix.value = lights.state.spotShadowMatrix;
-			uniforms.pointShadowMap.value = lights.state.pointShadowMap;
-			uniforms.pointShadowMatrix.value = lights.state.pointShadowMatrix;
-			// TODO (abelnation): add area lights shadow info to uniforms
+			if ( programAttribute >= 0 ) {
 
-		}
+				var geometryAttribute = geometryAttributes[ name ];
 
-		var progUniforms = materialProperties.program.getUniforms(),
-			uniformsList =
-				WebGLUniforms.seqWithValue( progUniforms.seq, uniforms );
+				if ( geometryAttribute !== undefined ) {
 
-		materialProperties.uniformsList = uniformsList;
+					var normalized = geometryAttribute.normalized;
+					var size = geometryAttribute.itemSize;
 
-	}
+					var attribute = attributes.get( geometryAttribute );
 
-	function setProgram( camera, scene, material, object ) {
+					// TODO Attribute may not be available on context restore
 
-		textures.resetTextureUnits();
+					if ( attribute === undefined ) continue;
 
-		var fog = scene.fog;
-		var environment = material.isMeshStandardMaterial ? scene.environment : null;
-		var encoding = ( _currentRenderTarget === null ) ? _this.outputEncoding : _currentRenderTarget.texture.encoding;
+					var buffer = attribute.buffer;
+					var type = attribute.type;
+					var bytesPerElement = attribute.bytesPerElement;
 
-		var materialProperties = properties.get( material );
-		var lights = currentRenderState.state.lights;
+					if ( geometryAttribute.isInterleavedBufferAttribute ) {
 
-		if ( _clippingEnabled ) {
+						var data = geometryAttribute.data;
+						var stride = data.stride;
+						var offset = geometryAttribute.offset;
 
-			if ( _localClippingEnabled || camera !== _currentCamera ) {
+						if ( data && data.isInstancedInterleavedBuffer ) {
 
-				var useCache =
-					camera === _currentCamera &&
-					material.id === _currentMaterialId;
+							state.enableAttributeAndDivisor( programAttribute, data.meshPerAttribute );
 
-				// we might want to call this function with some ClippingGroup
-				// object instead of the material, once it becomes feasible
-				// (#8465, #8379)
-				_clipping.setState(
-					material.clippingPlanes, material.clipIntersection, material.clipShadows,
-					camera, materialProperties, useCache );
+							if ( geometry.maxInstancedCount === undefined ) {
 
-			}
+								geometry.maxInstancedCount = data.meshPerAttribute * data.count;
 
-		}
+							}
 
-		if ( material.version === materialProperties.__version ) {
+						} else {
 
-			if ( materialProperties.program === undefined ) {
+							state.enableAttribute( programAttribute );
 
-				initMaterial( material, scene, object );
+						}
 
-			} else if ( material.fog && materialProperties.fog !== fog ) {
+						_gl.bindBuffer( 34962, buffer );
+						state.vertexAttribPointer( programAttribute, size, type, normalized, stride * bytesPerElement, offset * bytesPerElement );
 
-				initMaterial( material, scene, object );
+					} else {
 
-			} else if ( materialProperties.environment !== environment ) {
+						if ( geometryAttribute.isInstancedBufferAttribute ) {
 
-				initMaterial( material, scene, object );
+							state.enableAttributeAndDivisor( programAttribute, geometryAttribute.meshPerAttribute );
 
-			} else if ( materialProperties.needsLights && ( materialProperties.lightsStateVersion !== lights.state.version ) ) {
+							if ( geometry.maxInstancedCount === undefined ) {
 
-				initMaterial( material, scene, object );
+								geometry.maxInstancedCount = geometryAttribute.meshPerAttribute * geometryAttribute.count;
 
-			} else if ( materialProperties.numClippingPlanes !== undefined &&
-				( materialProperties.numClippingPlanes !== _clipping.numPlanes ||
-				materialProperties.numIntersection !== _clipping.numIntersection ) ) {
+							}
 
-				initMaterial( material, scene, object );
+						} else {
 
-			} else if ( materialProperties.outputEncoding !== encoding ) {
+							state.enableAttribute( programAttribute );
 
-				initMaterial( material, scene, object );
+						}
 
-			}
+						_gl.bindBuffer( 34962, buffer );
+						state.vertexAttribPointer( programAttribute, size, type, normalized, 0, 0 );
 
-		} else {
+					}
 
-			initMaterial( material, scene, object );
-			materialProperties.__version = material.version;
+				} else if ( name === 'instanceMatrix' ) {
 
-		}
+					var attribute = attributes.get( object.instanceMatrix );
 
-		var refreshProgram = false;
-		var refreshMaterial = false;
-		var refreshLights = false;
+					// TODO Attribute may not be available on context restore
 
-		var program = materialProperties.program,
-			p_uniforms = program.getUniforms(),
-			m_uniforms = materialProperties.uniforms;
+					if ( attribute === undefined ) continue;
 
-		if ( state.useProgram( program.program ) ) {
+					var buffer = attribute.buffer;
+					var type = attribute.type;
 
-			refreshProgram = true;
-			refreshMaterial = true;
-			refreshLights = true;
+					state.enableAttributeAndDivisor( programAttribute + 0, 1 );
+					state.enableAttributeAndDivisor( programAttribute + 1, 1 );
+					state.enableAttributeAndDivisor( programAttribute + 2, 1 );
+					state.enableAttributeAndDivisor( programAttribute + 3, 1 );
 
-		}
+					_gl.bindBuffer( 34962, buffer );
 
-		if ( material.id !== _currentMaterialId ) {
+					_gl.vertexAttribPointer( programAttribute + 0, 4, type, false, 64, 0 );
+					_gl.vertexAttribPointer( programAttribute + 1, 4, type, false, 64, 16 );
+					_gl.vertexAttribPointer( programAttribute + 2, 4, type, false, 64, 32 );
+					_gl.vertexAttribPointer( programAttribute + 3, 4, type, false, 64, 48 );
 
-			_currentMaterialId = material.id;
+				} else if ( materialDefaultAttributeValues !== undefined ) {
 
-			refreshMaterial = true;
+					var value = materialDefaultAttributeValues[ name ];
 
-		}
+					if ( value !== undefined ) {
 
-		if ( refreshProgram || _currentCamera !== camera ) {
+						switch ( value.length ) {
 
-			p_uniforms.setValue( _gl, 'projectionMatrix', camera.projectionMatrix );
+							case 2:
+								_gl.vertexAttrib2fv( programAttribute, value );
+								break;
 
-			if ( capabilities.logarithmicDepthBuffer ) {
+							case 3:
+								_gl.vertexAttrib3fv( programAttribute, value );
+								break;
 
-				p_uniforms.setValue( _gl, 'logDepthBufFC',
-					2.0 / ( Math.log( camera.far + 1.0 ) / Math.LN2 ) );
+							case 4:
+								_gl.vertexAttrib4fv( programAttribute, value );
+								break;
+
+							default:
+								_gl.vertexAttrib1fv( programAttribute, value );
+
+						}
+
+					}
+
+				}
 
 			}
 
-			if ( _currentCamera !== camera ) {
+		}
 
-				_currentCamera = camera;
+		state.disableUnusedAttributes();
 
-				// lighting uniforms depend on the camera so enforce an update
-				// now, in case this material supports lights - or later, when
-				// the next material that does gets activated:
+	}
 
-				refreshMaterial = true;		// set to true on material change
-				refreshLights = true;		// remains set until update done
+	// Compile
 
-			}
+	this.compile = function ( scene, camera ) {
 
-			// load material specific uniforms
-			// (shader material also gets them for the sake of genericity)
+		currentRenderState = renderStates.get( scene, camera );
+		currentRenderState.init();
 
-			if ( material.isShaderMaterial ||
-				material.isMeshPhongMaterial ||
-				material.isMeshToonMaterial ||
-				material.isMeshStandardMaterial ||
-				material.envMap ) {
+		scene.traverse( function ( object ) {
 
-				var uCamPos = p_uniforms.map.cameraPosition;
+			if ( object.isLight ) {
 
-				if ( uCamPos !== undefined ) {
+				currentRenderState.pushLight( object );
 
-					uCamPos.setValue( _gl,
-						_vector3.setFromMatrixPosition( camera.matrixWorld ) );
+				if ( object.castShadow ) {
+
+					currentRenderState.pushShadow( object );
 
 				}
 
 			}
 
-			if ( material.isMeshPhongMaterial ||
-				material.isMeshToonMaterial ||
-				material.isMeshLambertMaterial ||
-				material.isMeshBasicMaterial ||
-				material.isMeshStandardMaterial ||
-				material.isShaderMaterial ) {
+		} );
 
-				p_uniforms.setValue( _gl, 'isOrthographic', camera.isOrthographicCamera === true );
+		currentRenderState.setupLights( camera );
 
-			}
+		var compiled = {};
 
-			if ( material.isMeshPhongMaterial ||
-				material.isMeshToonMaterial ||
-				material.isMeshLambertMaterial ||
-				material.isMeshBasicMaterial ||
-				material.isMeshStandardMaterial ||
-				material.isShaderMaterial ||
-				material.skinning ) {
+		scene.traverse( function ( object ) {
 
-				p_uniforms.setValue( _gl, 'viewMatrix', camera.matrixWorldInverse );
+			if ( object.material ) {
+
+				if ( Array.isArray( object.material ) ) {
+
+					for ( var i = 0; i < object.material.length; i ++ ) {
+
+						if ( object.material[ i ].uuid in compiled === false ) {
+
+							initMaterial( object.material[ i ], scene, object );
+							compiled[ object.material[ i ].uuid ] = true;
+
+						}
+
+					}
+
+				} else if ( object.material.uuid in compiled === false ) {
+
+					initMaterial( object.material, scene, object );
+					compiled[ object.material.uuid ] = true;
+
+				}
 
 			}
 
-		}
+		} );
 
-		// skinning uniforms must be set even if material didn't change
-		// auto-setting of texture unit for bone texture must go before other textures
-		// otherwise textures used for skinning can take over texture units reserved for other material textures
+	};
 
-		if ( material.skinning ) {
+	// Animation Loop
 
-			p_uniforms.setOptional( _gl, object, 'bindMatrix' );
-			p_uniforms.setOptional( _gl, object, 'bindMatrixInverse' );
+	var onAnimationFrameCallback = null;
 
-			var skeleton = object.skeleton;
+	function onAnimationFrame( time ) {
 
-			if ( skeleton ) {
+		if ( xr.isPresenting ) return;
+		if ( onAnimationFrameCallback ) onAnimationFrameCallback( time );
 
-				var bones = skeleton.bones;
+	}
 
-				if ( capabilities.floatVertexTextures ) {
+	var animation = new WebGLAnimation();
+	animation.setAnimationLoop( onAnimationFrame );
 
-					if ( skeleton.boneTexture === undefined ) {
+	if ( typeof window !== 'undefined' ) animation.setContext( window );
 
-						// layout (1 matrix = 4 pixels)
-						//      RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4)
-						//  with  8x8  pixel texture max   16 bones * 4 pixels =  (8 * 8)
-						//       16x16 pixel texture max   64 bones * 4 pixels = (16 * 16)
-						//       32x32 pixel texture max  256 bones * 4 pixels = (32 * 32)
-						//       64x64 pixel texture max 1024 bones * 4 pixels = (64 * 64)
+	this.setAnimationLoop = function ( callback ) {
 
+		onAnimationFrameCallback = callback;
+		xr.setAnimationLoop( callback );
 
-						var size = Math.sqrt( bones.length * 4 ); // 4 pixels needed for 1 matrix
-						size = MathUtils.ceilPowerOfTwo( size );
-						size = Math.max( size, 4 );
+		animation.start();
 
-						var boneMatrices = new Float32Array( size * size * 4 ); // 4 floats per RGBA pixel
-						boneMatrices.set( skeleton.boneMatrices ); // copy current values
+	};
 
-						var boneTexture = new DataTexture( boneMatrices, size, size, RGBAFormat, FloatType );
+	// Rendering
 
-						skeleton.boneMatrices = boneMatrices;
-						skeleton.boneTexture = boneTexture;
-						skeleton.boneTextureSize = size;
+	this.render = function ( scene, camera ) {
 
-					}
+		var renderTarget, forceClear;
 
-					p_uniforms.setValue( _gl, 'boneTexture', skeleton.boneTexture, textures );
-					p_uniforms.setValue( _gl, 'boneTextureSize', skeleton.boneTextureSize );
+		if ( arguments[ 2 ] !== undefined ) {
 
-				} else {
+			console.warn( 'THREE.WebGLRenderer.render(): the renderTarget argument has been removed. Use .setRenderTarget() instead.' );
+			renderTarget = arguments[ 2 ];
 
-					p_uniforms.setOptional( _gl, skeleton, 'boneMatrices' );
+		}
 
-				}
+		if ( arguments[ 3 ] !== undefined ) {
 
-			}
+			console.warn( 'THREE.WebGLRenderer.render(): the forceClear argument has been removed. Use .clear() instead.' );
+			forceClear = arguments[ 3 ];
 
 		}
 
-		if ( refreshMaterial || materialProperties.receiveShadow !== object.receiveShadow ) {
+		if ( ! ( camera && camera.isCamera ) ) {
 
-			materialProperties.receiveShadow = object.receiveShadow;
-			p_uniforms.setValue( _gl, 'receiveShadow', object.receiveShadow );
+			console.error( 'THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.' );
+			return;
+
+		}
+
+		if ( _isContextLost ) return;
+
+		// reset caching for this frame
+
+		_currentGeometryProgram.geometry = null;
+		_currentGeometryProgram.program = null;
+		_currentGeometryProgram.wireframe = false;
+		_currentMaterialId = - 1;
+		_currentCamera = null;
+
+		// update scene graph
+
+		if ( scene.autoUpdate === true ) scene.updateMatrixWorld();
+
+		// update camera matrices and frustum
 
-		}
+		if ( camera.parent === null ) camera.updateMatrixWorld();
 
-		if ( refreshMaterial ) {
+		if ( xr.enabled && xr.isPresenting ) {
 
-			p_uniforms.setValue( _gl, 'toneMappingExposure', _this.toneMappingExposure );
-			p_uniforms.setValue( _gl, 'toneMappingWhitePoint', _this.toneMappingWhitePoint );
+			camera = xr.getCamera( camera );
 
-			if ( materialProperties.needsLights ) {
+		}
 
-				// the current material requires lighting info
+		//
+		scene.onBeforeRender( _this, scene, camera, renderTarget || _currentRenderTarget );
 
-				// note: all lighting uniforms are always set correctly
-				// they simply reference the renderer's state for their
-				// values
-				//
-				// use the current material's .needsUpdate flags to set
-				// the GL state when required
+		currentRenderState = renderStates.get( scene, camera );
+		currentRenderState.init();
 
-				markUniformsLightsNeedsUpdate( m_uniforms, refreshLights );
+		_projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse );
+		_frustum.setFromProjectionMatrix( _projScreenMatrix );
 
-			}
+		_localClippingEnabled = this.localClippingEnabled;
+		_clippingEnabled = _clipping.init( this.clippingPlanes, _localClippingEnabled, camera );
 
-			// refresh uniforms common to several materials
+		currentRenderList = renderLists.get( scene, camera );
+		currentRenderList.init();
 
-			if ( fog && material.fog ) {
+		projectObject( scene, camera, 0, _this.sortObjects );
 
-				refreshUniformsFog( m_uniforms, fog );
+		currentRenderList.finish();
 
-			}
+		if ( _this.sortObjects === true ) {
 
-			if ( material.isMeshBasicMaterial ) {
+			currentRenderList.sort( _opaqueSort, _transparentSort );
 
-				refreshUniformsCommon( m_uniforms, material );
+		}
 
-			} else if ( material.isMeshLambertMaterial ) {
+		//
 
-				refreshUniformsCommon( m_uniforms, material );
-				refreshUniformsLambert( m_uniforms, material );
+		if ( _clippingEnabled ) _clipping.beginShadows();
 
-			} else if ( material.isMeshToonMaterial ) {
+		var shadowsArray = currentRenderState.state.shadowsArray;
 
-				refreshUniformsCommon( m_uniforms, material );
-				refreshUniformsToon( m_uniforms, material );
+		shadowMap.render( shadowsArray, scene, camera );
 
-			} else if ( material.isMeshPhongMaterial ) {
+		currentRenderState.setupLights( camera );
 
-				refreshUniformsCommon( m_uniforms, material );
-				refreshUniformsPhong( m_uniforms, material );
+		if ( _clippingEnabled ) _clipping.endShadows();
 
-			} else if ( material.isMeshStandardMaterial ) {
+		//
 
-				refreshUniformsCommon( m_uniforms, material, environment );
+		if ( this.info.autoReset ) this.info.reset();
 
-				if ( material.isMeshPhysicalMaterial ) {
+		if ( renderTarget !== undefined ) {
 
-					refreshUniformsPhysical( m_uniforms, material, environment );
+			this.setRenderTarget( renderTarget );
 
-				} else {
+		}
 
-					refreshUniformsStandard( m_uniforms, material, environment );
+		//
 
-				}
+		background.render( currentRenderList, scene, camera, forceClear );
 
-			} else if ( material.isMeshMatcapMaterial ) {
+		// render scene
 
-				refreshUniformsCommon( m_uniforms, material );
-				refreshUniformsMatcap( m_uniforms, material );
+		var opaqueObjects = currentRenderList.opaque;
+		var transparentObjects = currentRenderList.transparent;
 
-			} else if ( material.isMeshDepthMaterial ) {
+		if ( scene.overrideMaterial ) {
 
-				refreshUniformsCommon( m_uniforms, material );
-				refreshUniformsDepth( m_uniforms, material );
+			var overrideMaterial = scene.overrideMaterial;
 
-			} else if ( material.isMeshDistanceMaterial ) {
+			if ( opaqueObjects.length ) renderObjects( opaqueObjects, scene, camera, overrideMaterial );
+			if ( transparentObjects.length ) renderObjects( transparentObjects, scene, camera, overrideMaterial );
 
-				refreshUniformsCommon( m_uniforms, material );
-				refreshUniformsDistance( m_uniforms, material );
+		} else {
 
-			} else if ( material.isMeshNormalMaterial ) {
+			// opaque pass (front-to-back order)
 
-				refreshUniformsCommon( m_uniforms, material );
-				refreshUniformsNormal( m_uniforms, material );
+			if ( opaqueObjects.length ) renderObjects( opaqueObjects, scene, camera );
 
-			} else if ( material.isLineBasicMaterial ) {
+			// transparent pass (back-to-front order)
 
-				refreshUniformsLine( m_uniforms, material );
+			if ( transparentObjects.length ) renderObjects( transparentObjects, scene, camera );
 
-				if ( material.isLineDashedMaterial ) {
+		}
 
-					refreshUniformsDash( m_uniforms, material );
+		//
 
-				}
+		scene.onAfterRender( _this, scene, camera );
 
-			} else if ( material.isPointsMaterial ) {
+		//
 
-				refreshUniformsPoints( m_uniforms, material );
+		if ( _currentRenderTarget !== null ) {
 
-			} else if ( material.isSpriteMaterial ) {
+			// Generate mipmap if we're using any kind of mipmap filtering
 
-				refreshUniformsSprites( m_uniforms, material );
+			textures.updateRenderTargetMipmap( _currentRenderTarget );
 
-			} else if ( material.isShadowMaterial ) {
+			// resolve multisample renderbuffers to a single-sample texture if necessary
 
-				m_uniforms.color.value.copy( material.color );
-				m_uniforms.opacity.value = material.opacity;
+			textures.updateMultisampleRenderTarget( _currentRenderTarget );
 
-			}
+		}
 
-			// RectAreaLight Texture
-			// TODO (mrdoob): Find a nicer implementation
+		// Ensure depth buffer writing is enabled so it can be cleared on next render
 
-			if ( m_uniforms.ltc_1 !== undefined ) m_uniforms.ltc_1.value = UniformsLib.LTC_1;
-			if ( m_uniforms.ltc_2 !== undefined ) m_uniforms.ltc_2.value = UniformsLib.LTC_2;
+		state.buffers.depth.setTest( true );
+		state.buffers.depth.setMask( true );
+		state.buffers.color.setMask( true );
 
-			WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, textures );
+		state.setPolygonOffset( false );
 
-			if ( material.isShaderMaterial ) {
+		// _gl.finish();
 
-				material.uniformsNeedUpdate = false; // #15581
+		currentRenderList = null;
+		currentRenderState = null;
 
-			}
+	};
 
-		}
+	function projectObject( object, camera, groupOrder, sortObjects ) {
 
-		if ( material.isShaderMaterial && material.uniformsNeedUpdate === true ) {
+		if ( object.visible === false ) return;
 
-			WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, textures );
-			material.uniformsNeedUpdate = false;
+		var visible = object.layers.test( camera.layers );
 
-		}
+		if ( visible ) {
 
-		if ( material.isSpriteMaterial ) {
+			if ( object.isGroup ) {
 
-			p_uniforms.setValue( _gl, 'center', object.center );
+				groupOrder = object.renderOrder;
 
-		}
+			} else if ( object.isLOD ) {
 
-		// common matrices
+				if ( object.autoUpdate === true ) object.update( camera );
 
-		p_uniforms.setValue( _gl, 'modelViewMatrix', object.modelViewMatrix );
-		p_uniforms.setValue( _gl, 'normalMatrix', object.normalMatrix );
-		p_uniforms.setValue( _gl, 'modelMatrix', object.matrixWorld );
+			} else if ( object.isLight ) {
 
-		return program;
+				currentRenderState.pushLight( object );
 
-	}
+				if ( object.castShadow ) {
 
-	// Uniforms (refresh uniforms objects)
+					currentRenderState.pushShadow( object );
 
-	function refreshUniformsCommon( uniforms, material, environment ) {
+				}
 
-		uniforms.opacity.value = material.opacity;
+			} else if ( object.isSprite ) {
 
-		if ( material.color ) {
+				if ( ! object.frustumCulled || _frustum.intersectsSprite( object ) ) {
 
-			uniforms.diffuse.value.copy( material.color );
+					if ( sortObjects ) {
 
-		}
+						_vector3.setFromMatrixPosition( object.matrixWorld )
+							.applyMatrix4( _projScreenMatrix );
 
-		if ( material.emissive ) {
+					}
 
-			uniforms.emissive.value.copy( material.emissive ).multiplyScalar( material.emissiveIntensity );
+					var geometry = objects.update( object );
+					var material = object.material;
 
-		}
+					if ( material.visible ) {
 
-		if ( material.map ) {
+						currentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null );
 
-			uniforms.map.value = material.map;
+					}
 
-		}
+				}
 
-		if ( material.alphaMap ) {
+			} else if ( object.isImmediateRenderObject ) {
 
-			uniforms.alphaMap.value = material.alphaMap;
+				if ( sortObjects ) {
 
-		}
+					_vector3.setFromMatrixPosition( object.matrixWorld )
+						.applyMatrix4( _projScreenMatrix );
 
-		if ( material.specularMap ) {
+				}
 
-			uniforms.specularMap.value = material.specularMap;
+				currentRenderList.push( object, null, object.material, groupOrder, _vector3.z, null );
 
-		}
+			} else if ( object.isMesh || object.isLine || object.isPoints ) {
 
-		var envMap = material.envMap || environment;
+				if ( object.isSkinnedMesh ) {
 
-		if ( envMap ) {
+					// update skeleton only once in a frame
 
-			uniforms.envMap.value = envMap;
+					if ( object.skeleton.frame !== info.render.frame ) {
 
-			uniforms.flipEnvMap.value = envMap.isCubeTexture ? - 1 : 1;
+						object.skeleton.update();
+						object.skeleton.frame = info.render.frame;
 
-			uniforms.reflectivity.value = material.reflectivity;
-			uniforms.refractionRatio.value = material.refractionRatio;
+					}
 
-			uniforms.maxMipLevel.value = properties.get( envMap ).__maxMipLevel;
+				}
 
-		}
+				if ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) {
 
-		if ( material.lightMap ) {
+					if ( sortObjects ) {
 
-			uniforms.lightMap.value = material.lightMap;
-			uniforms.lightMapIntensity.value = material.lightMapIntensity;
+						_vector3.setFromMatrixPosition( object.matrixWorld )
+							.applyMatrix4( _projScreenMatrix );
 
-		}
+					}
 
-		if ( material.aoMap ) {
+					var geometry = objects.update( object );
+					var material = object.material;
 
-			uniforms.aoMap.value = material.aoMap;
-			uniforms.aoMapIntensity.value = material.aoMapIntensity;
+					if ( Array.isArray( material ) ) {
 
-		}
+						var groups = geometry.groups;
 
-		// uv repeat and offset setting priorities
-		// 1. color map
-		// 2. specular map
-		// 3. normal map
-		// 4. bump map
-		// 5. alpha map
-		// 6. emissive map
+						for ( var i = 0, l = groups.length; i < l; i ++ ) {
 
-		var uvScaleMap;
+							var group = groups[ i ];
+							var groupMaterial = material[ group.materialIndex ];
 
-		if ( material.map ) {
+							if ( groupMaterial && groupMaterial.visible ) {
 
-			uvScaleMap = material.map;
+								currentRenderList.push( object, geometry, groupMaterial, groupOrder, _vector3.z, group );
 
-		} else if ( material.specularMap ) {
+							}
 
-			uvScaleMap = material.specularMap;
+						}
 
-		} else if ( material.displacementMap ) {
+					} else if ( material.visible ) {
 
-			uvScaleMap = material.displacementMap;
+						currentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null );
 
-		} else if ( material.normalMap ) {
+					}
 
-			uvScaleMap = material.normalMap;
+				}
 
-		} else if ( material.bumpMap ) {
+			}
 
-			uvScaleMap = material.bumpMap;
+		}
 
-		} else if ( material.roughnessMap ) {
+		var children = object.children;
 
-			uvScaleMap = material.roughnessMap;
+		for ( var i = 0, l = children.length; i < l; i ++ ) {
 
-		} else if ( material.metalnessMap ) {
+			projectObject( children[ i ], camera, groupOrder, sortObjects );
 
-			uvScaleMap = material.metalnessMap;
+		}
 
-		} else if ( material.alphaMap ) {
+	}
 
-			uvScaleMap = material.alphaMap;
+	function renderObjects( renderList, scene, camera, overrideMaterial ) {
 
-		} else if ( material.emissiveMap ) {
+		for ( var i = 0, l = renderList.length; i < l; i ++ ) {
 
-			uvScaleMap = material.emissiveMap;
+			var renderItem = renderList[ i ];
 
-		}
+			var object = renderItem.object;
+			var geometry = renderItem.geometry;
+			var material = overrideMaterial === undefined ? renderItem.material : overrideMaterial;
+			var group = renderItem.group;
 
-		if ( uvScaleMap !== undefined ) {
+			if ( camera.isArrayCamera ) {
 
-			// backwards compatibility
-			if ( uvScaleMap.isWebGLRenderTarget ) {
+				_currentArrayCamera = camera;
 
-				uvScaleMap = uvScaleMap.texture;
+				var cameras = camera.cameras;
 
-			}
+				for ( var j = 0, jl = cameras.length; j < jl; j ++ ) {
 
-			if ( uvScaleMap.matrixAutoUpdate === true ) {
+					var camera2 = cameras[ j ];
 
-				uvScaleMap.updateMatrix();
+					if ( object.layers.test( camera2.layers ) ) {
 
-			}
+						state.viewport( _currentViewport.copy( camera2.viewport ) );
 
-			uniforms.uvTransform.value.copy( uvScaleMap.matrix );
+						currentRenderState.setupLights( camera2 );
 
-		}
+						renderObject( object, scene, camera2, geometry, material, group );
 
-		// uv repeat and offset setting priorities for uv2
-		// 1. ao map
-		// 2. light map
+					}
 
-		var uv2ScaleMap;
+				}
 
-		if ( material.aoMap ) {
+			} else {
 
-			uv2ScaleMap = material.aoMap;
+				_currentArrayCamera = null;
 
-		} else if ( material.lightMap ) {
+				renderObject( object, scene, camera, geometry, material, group );
 
-			uv2ScaleMap = material.lightMap;
+			}
 
 		}
 
-		if ( uv2ScaleMap !== undefined ) {
+	}
 
-			// backwards compatibility
-			if ( uv2ScaleMap.isWebGLRenderTarget ) {
+	function renderObject( object, scene, camera, geometry, material, group ) {
 
-				uv2ScaleMap = uv2ScaleMap.texture;
+		object.onBeforeRender( _this, scene, camera, geometry, material, group );
+		currentRenderState = renderStates.get( scene, _currentArrayCamera || camera );
 
-			}
+		object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld );
+		object.normalMatrix.getNormalMatrix( object.modelViewMatrix );
 
-			if ( uv2ScaleMap.matrixAutoUpdate === true ) {
+		if ( object.isImmediateRenderObject ) {
 
-				uv2ScaleMap.updateMatrix();
+			var program = setProgram( camera, scene, material, object );
 
-			}
+			state.setMaterial( material );
 
-			uniforms.uv2Transform.value.copy( uv2ScaleMap.matrix );
+			_currentGeometryProgram.geometry = null;
+			_currentGeometryProgram.program = null;
+			_currentGeometryProgram.wireframe = false;
 
-		}
+			renderObjectImmediate( object, program );
 
-	}
+		} else {
 
-	function refreshUniformsLine( uniforms, material ) {
+			_this.renderBufferDirect( camera, scene, geometry, material, object, group );
 
-		uniforms.diffuse.value.copy( material.color );
-		uniforms.opacity.value = material.opacity;
+		}
+
+		object.onAfterRender( _this, scene, camera, geometry, material, group );
+		currentRenderState = renderStates.get( scene, _currentArrayCamera || camera );
 
 	}
 
-	function refreshUniformsDash( uniforms, material ) {
+	function initMaterial( material, scene, object ) {
 
-		uniforms.dashSize.value = material.dashSize;
-		uniforms.totalSize.value = material.dashSize + material.gapSize;
-		uniforms.scale.value = material.scale;
+		var materialProperties = properties.get( material );
 
-	}
+		var lights = currentRenderState.state.lights;
+		var shadowsArray = currentRenderState.state.shadowsArray;
 
-	function refreshUniformsPoints( uniforms, material ) {
+		var lightsStateVersion = lights.state.version;
 
-		uniforms.diffuse.value.copy( material.color );
-		uniforms.opacity.value = material.opacity;
-		uniforms.size.value = material.size * _pixelRatio;
-		uniforms.scale.value = _height * 0.5;
+		var parameters = programCache.getParameters( material, lights.state, shadowsArray, scene, _clipping.numPlanes, _clipping.numIntersection, object );
+		var programCacheKey = programCache.getProgramCacheKey( parameters );
 
-		if ( material.map ) {
+		var program = materialProperties.program;
+		var programChange = true;
 
-			uniforms.map.value = material.map;
+		if ( program === undefined ) {
 
-		}
+			// new material
+			material.addEventListener( 'dispose', onMaterialDispose );
 
-		if ( material.alphaMap ) {
+		} else if ( program.cacheKey !== programCacheKey ) {
 
-			uniforms.alphaMap.value = material.alphaMap;
+			// changed glsl or parameters
+			releaseMaterialProgramReference( material );
 
-		}
+		} else if ( materialProperties.lightsStateVersion !== lightsStateVersion ) {
 
-		// uv repeat and offset setting priorities
-		// 1. color map
-		// 2. alpha map
+			materialProperties.lightsStateVersion = lightsStateVersion;
 
-		var uvScaleMap;
+			programChange = false;
 
-		if ( material.map ) {
+		} else if ( parameters.shaderID !== undefined ) {
 
-			uvScaleMap = material.map;
+			// same glsl and uniform list
+			return;
 
-		} else if ( material.alphaMap ) {
+		} else {
 
-			uvScaleMap = material.alphaMap;
+			// only rebuild uniform list
+			programChange = false;
 
 		}
 
-		if ( uvScaleMap !== undefined ) {
-
-			if ( uvScaleMap.matrixAutoUpdate === true ) {
-
-				uvScaleMap.updateMatrix();
+		if ( programChange ) {
 
-			}
+			program = programCache.acquireProgram( parameters, programCacheKey );
 
-			uniforms.uvTransform.value.copy( uvScaleMap.matrix );
+			materialProperties.program = program;
+			materialProperties.uniforms = parameters.uniforms;
+			materialProperties.outputEncoding = parameters.outputEncoding;
+			material.program = program;
 
 		}
 
-	}
+		var programAttributes = program.getAttributes();
 
-	function refreshUniformsSprites( uniforms, material ) {
+		if ( material.morphTargets ) {
 
-		uniforms.diffuse.value.copy( material.color );
-		uniforms.opacity.value = material.opacity;
-		uniforms.rotation.value = material.rotation;
+			material.numSupportedMorphTargets = 0;
 
-		if ( material.map ) {
+			for ( var i = 0; i < _this.maxMorphTargets; i ++ ) {
 
-			uniforms.map.value = material.map;
+				if ( programAttributes[ 'morphTarget' + i ] >= 0 ) {
 
-		}
+					material.numSupportedMorphTargets ++;
 
-		if ( material.alphaMap ) {
+				}
 
-			uniforms.alphaMap.value = material.alphaMap;
+			}
 
 		}
 
-		// uv repeat and offset setting priorities
-		// 1. color map
-		// 2. alpha map
-
-		var uvScaleMap;
+		if ( material.morphNormals ) {
 
-		if ( material.map ) {
+			material.numSupportedMorphNormals = 0;
 
-			uvScaleMap = material.map;
+			for ( var i = 0; i < _this.maxMorphNormals; i ++ ) {
 
-		} else if ( material.alphaMap ) {
+				if ( programAttributes[ 'morphNormal' + i ] >= 0 ) {
 
-			uvScaleMap = material.alphaMap;
+					material.numSupportedMorphNormals ++;
 
-		}
+				}
 
-		if ( uvScaleMap !== undefined ) {
+			}
 
-			if ( uvScaleMap.matrixAutoUpdate === true ) {
+		}
 
-				uvScaleMap.updateMatrix();
+		var uniforms = materialProperties.uniforms;
 
-			}
+		if ( ! material.isShaderMaterial &&
+			! material.isRawShaderMaterial ||
+			material.clipping === true ) {
 
-			uniforms.uvTransform.value.copy( uvScaleMap.matrix );
+			materialProperties.numClippingPlanes = _clipping.numPlanes;
+			materialProperties.numIntersection = _clipping.numIntersection;
+			uniforms.clippingPlanes = _clipping.uniform;
 
 		}
 
-	}
+		materialProperties.environment = material.isMeshStandardMaterial ? scene.environment : null;
+		materialProperties.fog = scene.fog;
 
-	function refreshUniformsFog( uniforms, fog ) {
+		// store the light setup it was created for
 
-		uniforms.fogColor.value.copy( fog.color );
+		materialProperties.needsLights = materialNeedsLights( material );
+		materialProperties.lightsStateVersion = lightsStateVersion;
 
-		if ( fog.isFog ) {
+		if ( materialProperties.needsLights ) {
 
-			uniforms.fogNear.value = fog.near;
-			uniforms.fogFar.value = fog.far;
+			// wire up the material to this renderer's lighting state
 
-		} else if ( fog.isFogExp2 ) {
+			uniforms.ambientLightColor.value = lights.state.ambient;
+			uniforms.lightProbe.value = lights.state.probe;
+			uniforms.directionalLights.value = lights.state.directional;
+			uniforms.directionalLightShadows.value = lights.state.directionalShadow;
+			uniforms.spotLights.value = lights.state.spot;
+			uniforms.spotLightShadows.value = lights.state.spotShadow;
+			uniforms.rectAreaLights.value = lights.state.rectArea;
+			uniforms.pointLights.value = lights.state.point;
+			uniforms.pointLightShadows.value = lights.state.pointShadow;
+			uniforms.hemisphereLights.value = lights.state.hemi;
 
-			uniforms.fogDensity.value = fog.density;
+			uniforms.directionalShadowMap.value = lights.state.directionalShadowMap;
+			uniforms.directionalShadowMatrix.value = lights.state.directionalShadowMatrix;
+			uniforms.spotShadowMap.value = lights.state.spotShadowMap;
+			uniforms.spotShadowMatrix.value = lights.state.spotShadowMatrix;
+			uniforms.pointShadowMap.value = lights.state.pointShadowMap;
+			uniforms.pointShadowMatrix.value = lights.state.pointShadowMatrix;
+			// TODO (abelnation): add area lights shadow info to uniforms
 
 		}
 
+		var progUniforms = materialProperties.program.getUniforms(),
+			uniformsList =
+				WebGLUniforms.seqWithValue( progUniforms.seq, uniforms );
+
+		materialProperties.uniformsList = uniformsList;
+
 	}
 
-	function refreshUniformsLambert( uniforms, material ) {
+	function setProgram( camera, scene, material, object ) {
 
-		if ( material.emissiveMap ) {
+		textures.resetTextureUnits();
 
-			uniforms.emissiveMap.value = material.emissiveMap;
+		var fog = scene.fog;
+		var environment = material.isMeshStandardMaterial ? scene.environment : null;
+		var encoding = ( _currentRenderTarget === null ) ? _this.outputEncoding : _currentRenderTarget.texture.encoding;
 
-		}
+		var materialProperties = properties.get( material );
+		var lights = currentRenderState.state.lights;
 
-	}
+		if ( _clippingEnabled ) {
 
-	function refreshUniformsPhong( uniforms, material ) {
+			if ( _localClippingEnabled || camera !== _currentCamera ) {
 
-		uniforms.specular.value.copy( material.specular );
-		uniforms.shininess.value = Math.max( material.shininess, 1e-4 ); // to prevent pow( 0.0, 0.0 )
+				var useCache =
+					camera === _currentCamera &&
+					material.id === _currentMaterialId;
 
-		if ( material.emissiveMap ) {
+				// we might want to call this function with some ClippingGroup
+				// object instead of the material, once it becomes feasible
+				// (#8465, #8379)
+				_clipping.setState(
+					material.clippingPlanes, material.clipIntersection, material.clipShadows,
+					camera, materialProperties, useCache );
 
-			uniforms.emissiveMap.value = material.emissiveMap;
+			}
 
 		}
 
-		if ( material.bumpMap ) {
-
-			uniforms.bumpMap.value = material.bumpMap;
-			uniforms.bumpScale.value = material.bumpScale;
-			if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1;
+		if ( material.version === materialProperties.__version ) {
 
-		}
+			if ( materialProperties.program === undefined ) {
 
-		if ( material.normalMap ) {
+				initMaterial( material, scene, object );
 
-			uniforms.normalMap.value = material.normalMap;
-			uniforms.normalScale.value.copy( material.normalScale );
-			if ( material.side === BackSide ) uniforms.normalScale.value.negate();
+			} else if ( material.fog && materialProperties.fog !== fog ) {
 
-		}
+				initMaterial( material, scene, object );
 
-		if ( material.displacementMap ) {
+			} else if ( materialProperties.environment !== environment ) {
 
-			uniforms.displacementMap.value = material.displacementMap;
-			uniforms.displacementScale.value = material.displacementScale;
-			uniforms.displacementBias.value = material.displacementBias;
+				initMaterial( material, scene, object );
 
-		}
+			} else if ( materialProperties.needsLights && ( materialProperties.lightsStateVersion !== lights.state.version ) ) {
 
-	}
+				initMaterial( material, scene, object );
 
-	function refreshUniformsToon( uniforms, material ) {
+			} else if ( materialProperties.numClippingPlanes !== undefined &&
+				( materialProperties.numClippingPlanes !== _clipping.numPlanes ||
+				materialProperties.numIntersection !== _clipping.numIntersection ) ) {
 
-		uniforms.specular.value.copy( material.specular );
-		uniforms.shininess.value = Math.max( material.shininess, 1e-4 ); // to prevent pow( 0.0, 0.0 )
+				initMaterial( material, scene, object );
 
-		if ( material.gradientMap ) {
+			} else if ( materialProperties.outputEncoding !== encoding ) {
 
-			uniforms.gradientMap.value = material.gradientMap;
+				initMaterial( material, scene, object );
 
-		}
+			}
 
-		if ( material.emissiveMap ) {
+		} else {
 
-			uniforms.emissiveMap.value = material.emissiveMap;
+			initMaterial( material, scene, object );
+			materialProperties.__version = material.version;
 
 		}
 
-		if ( material.bumpMap ) {
-
-			uniforms.bumpMap.value = material.bumpMap;
-			uniforms.bumpScale.value = material.bumpScale;
-			if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1;
+		var refreshProgram = false;
+		var refreshMaterial = false;
+		var refreshLights = false;
 
-		}
+		var program = materialProperties.program,
+			p_uniforms = program.getUniforms(),
+			m_uniforms = materialProperties.uniforms;
 
-		if ( material.normalMap ) {
+		if ( state.useProgram( program.program ) ) {
 
-			uniforms.normalMap.value = material.normalMap;
-			uniforms.normalScale.value.copy( material.normalScale );
-			if ( material.side === BackSide ) uniforms.normalScale.value.negate();
+			refreshProgram = true;
+			refreshMaterial = true;
+			refreshLights = true;
 
 		}
 
-		if ( material.displacementMap ) {
+		if ( material.id !== _currentMaterialId ) {
 
-			uniforms.displacementMap.value = material.displacementMap;
-			uniforms.displacementScale.value = material.displacementScale;
-			uniforms.displacementBias.value = material.displacementBias;
+			_currentMaterialId = material.id;
+
+			refreshMaterial = true;
 
 		}
 
-	}
+		if ( refreshProgram || _currentCamera !== camera ) {
 
-	function refreshUniformsStandard( uniforms, material, environment ) {
+			p_uniforms.setValue( _gl, 'projectionMatrix', camera.projectionMatrix );
 
-		uniforms.roughness.value = material.roughness;
-		uniforms.metalness.value = material.metalness;
+			if ( capabilities.logarithmicDepthBuffer ) {
 
-		if ( material.roughnessMap ) {
+				p_uniforms.setValue( _gl, 'logDepthBufFC',
+					2.0 / ( Math.log( camera.far + 1.0 ) / Math.LN2 ) );
 
-			uniforms.roughnessMap.value = material.roughnessMap;
+			}
 
-		}
+			if ( _currentCamera !== camera ) {
 
-		if ( material.metalnessMap ) {
+				_currentCamera = camera;
 
-			uniforms.metalnessMap.value = material.metalnessMap;
+				// lighting uniforms depend on the camera so enforce an update
+				// now, in case this material supports lights - or later, when
+				// the next material that does gets activated:
 
-		}
+				refreshMaterial = true;		// set to true on material change
+				refreshLights = true;		// remains set until update done
 
-		if ( material.emissiveMap ) {
+			}
 
-			uniforms.emissiveMap.value = material.emissiveMap;
+			// load material specific uniforms
+			// (shader material also gets them for the sake of genericity)
 
-		}
+			if ( material.isShaderMaterial ||
+				material.isMeshPhongMaterial ||
+				material.isMeshToonMaterial ||
+				material.isMeshStandardMaterial ||
+				material.envMap ) {
 
-		if ( material.bumpMap ) {
+				var uCamPos = p_uniforms.map.cameraPosition;
 
-			uniforms.bumpMap.value = material.bumpMap;
-			uniforms.bumpScale.value = material.bumpScale;
-			if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1;
+				if ( uCamPos !== undefined ) {
 
-		}
+					uCamPos.setValue( _gl,
+						_vector3.setFromMatrixPosition( camera.matrixWorld ) );
 
-		if ( material.normalMap ) {
+				}
 
-			uniforms.normalMap.value = material.normalMap;
-			uniforms.normalScale.value.copy( material.normalScale );
-			if ( material.side === BackSide ) uniforms.normalScale.value.negate();
+			}
 
-		}
+			if ( material.isMeshPhongMaterial ||
+				material.isMeshToonMaterial ||
+				material.isMeshLambertMaterial ||
+				material.isMeshBasicMaterial ||
+				material.isMeshStandardMaterial ||
+				material.isShaderMaterial ) {
 
-		if ( material.displacementMap ) {
+				p_uniforms.setValue( _gl, 'isOrthographic', camera.isOrthographicCamera === true );
 
-			uniforms.displacementMap.value = material.displacementMap;
-			uniforms.displacementScale.value = material.displacementScale;
-			uniforms.displacementBias.value = material.displacementBias;
+			}
 
-		}
+			if ( material.isMeshPhongMaterial ||
+				material.isMeshToonMaterial ||
+				material.isMeshLambertMaterial ||
+				material.isMeshBasicMaterial ||
+				material.isMeshStandardMaterial ||
+				material.isShaderMaterial ||
+				material.skinning ) {
 
-		if ( material.envMap || environment ) {
+				p_uniforms.setValue( _gl, 'viewMatrix', camera.matrixWorldInverse );
 
-			//uniforms.envMap.value = material.envMap; // part of uniforms common
-			uniforms.envMapIntensity.value = material.envMapIntensity;
+			}
 
 		}
 
-	}
-
-	function refreshUniformsPhysical( uniforms, material, environment ) {
-
-		refreshUniformsStandard( uniforms, material, environment );
+		// skinning uniforms must be set even if material didn't change
+		// auto-setting of texture unit for bone texture must go before other textures
+		// otherwise textures used for skinning can take over texture units reserved for other material textures
 
-		uniforms.reflectivity.value = material.reflectivity; // also part of uniforms common
+		if ( material.skinning ) {
 
-		uniforms.clearcoat.value = material.clearcoat;
-		uniforms.clearcoatRoughness.value = material.clearcoatRoughness;
-		if ( material.sheen ) uniforms.sheen.value.copy( material.sheen );
+			p_uniforms.setOptional( _gl, object, 'bindMatrix' );
+			p_uniforms.setOptional( _gl, object, 'bindMatrixInverse' );
 
-		if ( material.clearcoatMap ) {
+			var skeleton = object.skeleton;
 
-			uniforms.clearcoatMap.value = material.clearcoatMap;
+			if ( skeleton ) {
 
-		}
+				var bones = skeleton.bones;
 
-		if ( material.clearcoatRoughnessMap ) {
+				if ( capabilities.floatVertexTextures ) {
 
-			uniforms.clearcoatRoughnessMap.value = material.clearcoatRoughnessMap;
+					if ( skeleton.boneTexture === undefined ) {
 
-		}
+						// layout (1 matrix = 4 pixels)
+						//      RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4)
+						//  with  8x8  pixel texture max   16 bones * 4 pixels =  (8 * 8)
+						//       16x16 pixel texture max   64 bones * 4 pixels = (16 * 16)
+						//       32x32 pixel texture max  256 bones * 4 pixels = (32 * 32)
+						//       64x64 pixel texture max 1024 bones * 4 pixels = (64 * 64)
 
-		if ( material.clearcoatNormalMap ) {
 
-			uniforms.clearcoatNormalScale.value.copy( material.clearcoatNormalScale );
-			uniforms.clearcoatNormalMap.value = material.clearcoatNormalMap;
+						var size = Math.sqrt( bones.length * 4 ); // 4 pixels needed for 1 matrix
+						size = MathUtils.ceilPowerOfTwo( size );
+						size = Math.max( size, 4 );
 
-			if ( material.side === BackSide ) {
+						var boneMatrices = new Float32Array( size * size * 4 ); // 4 floats per RGBA pixel
+						boneMatrices.set( skeleton.boneMatrices ); // copy current values
 
-				uniforms.clearcoatNormalScale.value.negate();
+						var boneTexture = new DataTexture( boneMatrices, size, size, RGBAFormat, FloatType );
 
-			}
+						skeleton.boneMatrices = boneMatrices;
+						skeleton.boneTexture = boneTexture;
+						skeleton.boneTextureSize = size;
 
-		}
+					}
 
-		uniforms.transparency.value = material.transparency;
+					p_uniforms.setValue( _gl, 'boneTexture', skeleton.boneTexture, textures );
+					p_uniforms.setValue( _gl, 'boneTextureSize', skeleton.boneTextureSize );
 
-	}
+				} else {
 
-	function refreshUniformsMatcap( uniforms, material ) {
+					p_uniforms.setOptional( _gl, skeleton, 'boneMatrices' );
 
-		if ( material.matcap ) {
+				}
 
-			uniforms.matcap.value = material.matcap;
+			}
 
 		}
 
-		if ( material.bumpMap ) {
+		if ( refreshMaterial || materialProperties.receiveShadow !== object.receiveShadow ) {
 
-			uniforms.bumpMap.value = material.bumpMap;
-			uniforms.bumpScale.value = material.bumpScale;
-			if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1;
+			materialProperties.receiveShadow = object.receiveShadow;
+			p_uniforms.setValue( _gl, 'receiveShadow', object.receiveShadow );
 
 		}
 
-		if ( material.normalMap ) {
+		if ( refreshMaterial ) {
 
-			uniforms.normalMap.value = material.normalMap;
-			uniforms.normalScale.value.copy( material.normalScale );
-			if ( material.side === BackSide ) uniforms.normalScale.value.negate();
+			p_uniforms.setValue( _gl, 'toneMappingExposure', _this.toneMappingExposure );
+			p_uniforms.setValue( _gl, 'toneMappingWhitePoint', _this.toneMappingWhitePoint );
 
-		}
+			if ( materialProperties.needsLights ) {
 
-		if ( material.displacementMap ) {
+				// the current material requires lighting info
 
-			uniforms.displacementMap.value = material.displacementMap;
-			uniforms.displacementScale.value = material.displacementScale;
-			uniforms.displacementBias.value = material.displacementBias;
+				// note: all lighting uniforms are always set correctly
+				// they simply reference the renderer's state for their
+				// values
+				//
+				// use the current material's .needsUpdate flags to set
+				// the GL state when required
 
-		}
+				markUniformsLightsNeedsUpdate( m_uniforms, refreshLights );
 
-	}
+			}
 
-	function refreshUniformsDepth( uniforms, material ) {
+			// refresh uniforms common to several materials
 
-		if ( material.displacementMap ) {
+			if ( fog && material.fog ) {
 
-			uniforms.displacementMap.value = material.displacementMap;
-			uniforms.displacementScale.value = material.displacementScale;
-			uniforms.displacementBias.value = material.displacementBias;
+				materials.refreshFogUniforms( m_uniforms, fog );
 
-		}
+			}
 
-	}
+			materials.refreshMaterialUniforms( m_uniforms, material, environment, _pixelRatio, _height );
 
-	function refreshUniformsDistance( uniforms, material ) {
+			// RectAreaLight Texture
+			// TODO (mrdoob): Find a nicer implementation
 
-		if ( material.displacementMap ) {
+			if ( m_uniforms.ltc_1 !== undefined ) m_uniforms.ltc_1.value = UniformsLib.LTC_1;
+			if ( m_uniforms.ltc_2 !== undefined ) m_uniforms.ltc_2.value = UniformsLib.LTC_2;
 
-			uniforms.displacementMap.value = material.displacementMap;
-			uniforms.displacementScale.value = material.displacementScale;
-			uniforms.displacementBias.value = material.displacementBias;
+			WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, textures );
 
-		}
+			if ( material.isShaderMaterial ) {
 
-		uniforms.referencePosition.value.copy( material.referencePosition );
-		uniforms.nearDistance.value = material.nearDistance;
-		uniforms.farDistance.value = material.farDistance;
+				material.uniformsNeedUpdate = false; // #15581
 
-	}
+			}
 
-	function refreshUniformsNormal( uniforms, material ) {
+		}
 
-		if ( material.bumpMap ) {
+		if ( material.isShaderMaterial && material.uniformsNeedUpdate === true ) {
 
-			uniforms.bumpMap.value = material.bumpMap;
-			uniforms.bumpScale.value = material.bumpScale;
-			if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1;
+			WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, textures );
+			material.uniformsNeedUpdate = false;
 
 		}
 
-		if ( material.normalMap ) {
+		if ( material.isSpriteMaterial ) {
 
-			uniforms.normalMap.value = material.normalMap;
-			uniforms.normalScale.value.copy( material.normalScale );
-			if ( material.side === BackSide ) uniforms.normalScale.value.negate();
+			p_uniforms.setValue( _gl, 'center', object.center );
 
 		}
 
-		if ( material.displacementMap ) {
+		// common matrices
 
-			uniforms.displacementMap.value = material.displacementMap;
-			uniforms.displacementScale.value = material.displacementScale;
-			uniforms.displacementBias.value = material.displacementBias;
+		p_uniforms.setValue( _gl, 'modelViewMatrix', object.modelViewMatrix );
+		p_uniforms.setValue( _gl, 'normalMatrix', object.normalMatrix );
+		p_uniforms.setValue( _gl, 'modelMatrix', object.matrixWorld );
 
-		}
+		return program;
 
 	}
 

Some files were not shown because too many files changed in this diff