ソースを参照

Merge branch 'dev' into dev38

Mr.doob 5 年 前
コミット
7efa2d4bac

+ 431 - 19
build/three.js

@@ -15549,6 +15549,10 @@
 
 		var maxSamples = isWebGL2 ? gl.getParameter( 36183 ) : 0;
 
+		var multiviewExt = extensions.get( 'OVR_multiview2' );
+		var multiview = isWebGL2 && !! multiviewExt && ! gl.getContextAttributes().antialias;
+		var maxMultiviewViews = multiview ? gl.getParameter( multiviewExt.MAX_VIEWS_OVR ) : 0;
+
 		return {
 
 			isWebGL2: isWebGL2,
@@ -15573,7 +15577,10 @@
 			floatFragmentTextures: floatFragmentTextures,
 			floatVertexTextures: floatVertexTextures,
 
-			maxSamples: maxSamples
+			maxSamples: maxSamples,
+
+			multiview: multiview,
+			maxMultiviewViews: maxMultiviewViews
 
 		};
 
@@ -17603,6 +17610,9 @@
 
 		var prefixVertex, prefixFragment;
 
+		var renderTarget = renderer.getRenderTarget();
+		var numMultiviewViews = renderTarget && renderTarget.isWebGLMultiviewRenderTarget ? renderTarget.numViews : 0;
+
 		if ( material.isRawShaderMaterial ) {
 
 			prefixVertex = [
@@ -17690,14 +17700,27 @@
 
 				parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '',
 				parameters.logarithmicDepthBuffer && ( capabilities.isWebGL2 || extensions.get( 'EXT_frag_depth' ) ) ? '#define USE_LOGDEPTHBUF_EXT' : '',
-
 				'uniform mat4 modelMatrix;',
-				'uniform mat4 modelViewMatrix;',
-				'uniform mat4 projectionMatrix;',
-				'uniform mat4 viewMatrix;',
-				'uniform mat3 normalMatrix;',
 				'uniform vec3 cameraPosition;',
 
+				numMultiviewViews > 0 ? [
+					'uniform mat4 modelViewMatrices[' + numMultiviewViews + '];',
+					'uniform mat3 normalMatrices[' + numMultiviewViews + '];',
+					'uniform mat4 viewMatrices[' + numMultiviewViews + '];',
+					'uniform mat4 projectionMatrices[' + numMultiviewViews + '];',
+
+					'#define modelViewMatrix modelViewMatrices[VIEW_ID]',
+					'#define normalMatrix normalMatrices[VIEW_ID]',
+					'#define viewMatrix viewMatrices[VIEW_ID]',
+					'#define projectionMatrix projectionMatrices[VIEW_ID]'
+
+				].join( '\n' ) : [
+
+					'uniform mat4 modelViewMatrix;',
+					'uniform mat4 projectionMatrix;',
+					'uniform mat4 viewMatrix;',
+					'uniform mat3 normalMatrix;' ].join( '\n' ),
+
 				'attribute vec3 position;',
 				'attribute vec3 normal;',
 				'attribute vec2 uv;',
@@ -17814,9 +17837,15 @@
 
 				( ( material.extensions ? material.extensions.shaderTextureLOD : false ) || parameters.envMap ) && ( capabilities.isWebGL2 || extensions.get( 'EXT_shader_texture_lod' ) ) ? '#define TEXTURE_LOD_EXT' : '',
 
-				'uniform mat4 viewMatrix;',
 				'uniform vec3 cameraPosition;',
 
+				numMultiviewViews > 0 ? [
+
+					'uniform mat4 viewMatrices[' + numMultiviewViews + '];',
+					'#define viewMatrix viewMatrices[VIEW_ID]'
+
+				].join( '\n' ) : 'uniform mat4 viewMatrix;',
+
 				( parameters.toneMapping !== NoToneMapping ) ? '#define TONE_MAPPING' : '',
 				( parameters.toneMapping !== NoToneMapping ) ? ShaderChunk[ 'tonemapping_pars_fragment' ] : '', // this code is required here because it is used by the toneMapping() function defined below
 				( parameters.toneMapping !== NoToneMapping ) ? getToneMappingFunction( 'toneMapping', parameters.toneMapping ) : '',
@@ -17870,6 +17899,15 @@
 			// GLSL 3.0 conversion
 			prefixVertex = [
 				'#version 300 es\n',
+
+				numMultiviewViews > 0 ? [
+
+					'#extension GL_OVR_multiview2 : require',
+					'layout(num_views = ' + numMultiviewViews + ') in;',
+					'#define VIEW_ID gl_ViewID_OVR'
+
+				].join( '\n' ) : '',
+
 				'#define attribute in',
 				'#define varying out',
 				'#define texture2D texture'
@@ -17877,6 +17915,12 @@
 
 			prefixFragment = [
 				'#version 300 es\n',
+				numMultiviewViews > 0 ? [
+
+					'#extension GL_OVR_multiview2 : require',
+					'#define VIEW_ID gl_ViewID_OVR'
+
+				].join( '\n' ) : '',
 				'#define varying in',
 				isGLSL3ShaderMaterial ? '' : 'out highp vec4 pc_fragColor;',
 				isGLSL3ShaderMaterial ? '' : '#define gl_FragColor pc_fragColor',
@@ -18034,6 +18078,7 @@
 		this.program = program;
 		this.vertexShader = glVertexShader;
 		this.fragmentShader = glFragmentShader;
+		this.numMultiviewViews = numMultiviewViews;
 
 		return this;
 
@@ -20964,6 +21009,21 @@
 
 			}
 
+			if ( renderTarget.isWebGLMultiviewRenderTarget ) {
+
+				_gl.deleteTexture( renderTargetProperties.__webglColorTexture );
+				_gl.deleteTexture( renderTargetProperties.__webglDepthStencilTexture );
+
+				info.memory.textures -= 2;
+
+				for ( var i = 0, il = renderTargetProperties.__webglViewFramebuffers.length; i < il; i ++ ) {
+
+					_gl.deleteFramebuffer( renderTargetProperties.__webglViewFramebuffers[ i ] );
+
+				}
+
+			}
+
 			properties.remove( renderTarget.texture );
 			properties.remove( renderTarget );
 
@@ -21650,6 +21710,7 @@
 
 			var isCube = ( renderTarget.isWebGLRenderTargetCube === true );
 			var isMultisample = ( renderTarget.isWebGLMultisampleRenderTarget === true );
+			var isMultiview = ( renderTarget.isWebGLMultiviewRenderTarget === true );
 			var supportsMips = isPowerOfTwo( renderTarget ) || capabilities.isWebGL2;
 
 			// Setup framebuffer
@@ -21702,6 +21763,56 @@
 
 					}
 
+				} else if ( isMultiview ) {
+
+					if ( capabilities.multiview ) {
+
+						var width = renderTarget.width;
+						var height = renderTarget.height;
+						var numViews = renderTarget.numViews;
+
+						_gl.bindFramebuffer( 36160, renderTargetProperties.__webglFramebuffer );
+
+						var ext = extensions.get( 'OVR_multiview2' );
+
+						info.memory.textures += 2;
+
+						var colorTexture = _gl.createTexture();
+						_gl.bindTexture( 35866, colorTexture );
+						_gl.texParameteri( 35866, 10240, 9728 );
+						_gl.texParameteri( 35866, 10241, 9728 );
+						_gl.texImage3D( 35866, 0, 32856, width, height, numViews, 0, 6408, 5121, null );
+						ext.framebufferTextureMultiviewOVR( 36160, 36064, colorTexture, 0, 0, numViews );
+
+						var depthStencilTexture = _gl.createTexture();
+						_gl.bindTexture( 35866, depthStencilTexture );
+						_gl.texParameteri( 35866, 10240, 9728 );
+						_gl.texParameteri( 35866, 10241, 9728 );
+						_gl.texImage3D( 35866, 0, 35056, width, height, numViews, 0, 34041, 34042, null );
+						ext.framebufferTextureMultiviewOVR( 36160, 33306, depthStencilTexture, 0, 0, numViews );
+
+						var viewFramebuffers = new Array( numViews );
+						for ( var i = 0; i < numViews; ++ i ) {
+
+							viewFramebuffers[ i ] = _gl.createFramebuffer();
+							_gl.bindFramebuffer( 36160, viewFramebuffers[ i ] );
+							_gl.framebufferTextureLayer( 36160, 36064, colorTexture, 0, i );
+
+						}
+
+						renderTargetProperties.__webglColorTexture = colorTexture;
+						renderTargetProperties.__webglDepthStencilTexture = depthStencilTexture;
+						renderTargetProperties.__webglViewFramebuffers = viewFramebuffers;
+
+						_gl.bindFramebuffer( 36160, null );
+						_gl.bindTexture( 35866, null );
+
+					} else {
+
+						console.warn( 'THREE.WebGLRenderer: WebGLMultiviewRenderTarget can only be used with WebGL2 and Multiview extension support.' );
+
+					}
+
 				}
 
 			}
@@ -21727,7 +21838,7 @@
 
 				state.bindTexture( 34067, null );
 
-			} else {
+			} else if ( ! isMultiview ) {
 
 				state.bindTexture( 3553, textureProperties.__webglTexture );
 				setTextureParameters( 3553, renderTarget.texture, supportsMips );
@@ -22064,6 +22175,258 @@
 
 	}
 
+	/**
+	 * @author fernandojsg / http://fernandojsg.com
+	 * @author Takahiro https://github.com/takahirox
+	 */
+
+	function WebGLMultiviewRenderTarget( width, height, numViews, options ) {
+
+		WebGLRenderTarget.call( this, width, height, options );
+
+		this.depthBuffer = false;
+		this.stencilBuffer = false;
+
+		this.numViews = numViews;
+
+	}
+
+	WebGLMultiviewRenderTarget.prototype = Object.assign( Object.create( WebGLRenderTarget.prototype ), {
+
+		constructor: WebGLMultiviewRenderTarget,
+
+		isWebGLMultiviewRenderTarget: true,
+
+		copy: function ( source ) {
+
+			WebGLRenderTarget.prototype.copy.call( this, source );
+
+			this.numViews = source.numViews;
+
+			return this;
+
+		},
+
+		setNumViews: function ( numViews ) {
+
+			if ( this.numViews !== numViews ) {
+
+				this.numViews = numViews;
+				this.dispose();
+
+			}
+
+			return this;
+
+		}
+
+	} );
+
+	/**
+	 * @author fernandojsg / http://fernandojsg.com
+	 * @author Takahiro https://github.com/takahirox
+	 */
+
+	function WebGLMultiview( renderer, gl ) {
+
+		var DEFAULT_NUMVIEWS = 2;
+
+		var capabilities = renderer.capabilities;
+		var properties = renderer.properties;
+
+		var maxNumViews = capabilities.maxMultiviewViews;
+
+		var renderTarget, currentRenderTarget;
+		var mat3, mat4, cameraArray, renderSize;
+
+		function getCameraArray( camera ) {
+
+			if ( camera.isArrayCamera ) { return camera.cameras; }
+
+			cameraArray[ 0 ] = camera;
+
+			return cameraArray;
+
+		}
+
+		//
+
+		function updateCameraProjectionMatricesUniform( camera, uniforms ) {
+
+			var cameras = getCameraArray( camera );
+
+			for ( var i = 0; i < cameras.length; i ++ ) {
+
+				mat4[ i ].copy( cameras[ i ].projectionMatrix );
+
+			}
+
+			uniforms.setValue( gl, 'projectionMatrices', mat4 );
+
+		}
+
+		function updateCameraViewMatricesUniform( camera, uniforms ) {
+
+			var cameras = getCameraArray( camera );
+
+			for ( var i = 0; i < cameras.length; i ++ ) {
+
+				mat4[ i ].copy( cameras[ i ].matrixWorldInverse );
+
+			}
+
+			uniforms.setValue( gl, 'viewMatrices', mat4 );
+
+		}
+
+		function updateObjectMatricesUniforms( object, camera, uniforms ) {
+
+			var cameras = getCameraArray( camera );
+
+			for ( var i = 0; i < cameras.length; i ++ ) {
+
+				mat4[ i ].multiplyMatrices( cameras[ i ].matrixWorldInverse, object.matrixWorld );
+				mat3[ i ].getNormalMatrix( mat4[ i ] );
+
+			}
+
+			uniforms.setValue( gl, 'modelViewMatrices', mat4 );
+			uniforms.setValue( gl, 'normalMatrices', mat3 );
+
+		}
+
+		function isMultiviewCompatible( camera ) {
+
+			if ( ! camera.isArrayCamera ) { return true; }
+
+			var cameras = camera.cameras;
+
+			if ( cameras.length > maxNumViews ) { return false; }
+
+			for ( var i = 1, il = cameras.length; i < il; i ++ ) {
+
+				if ( cameras[ 0 ].viewport.z !== cameras[ i ].viewport.z ||
+					cameras[ 0 ].viewport.w !== cameras[ i ].viewport.w ) { return false; }
+
+			}
+
+			return true;
+
+		}
+
+		function resizeRenderTarget( camera ) {
+
+			if ( currentRenderTarget ) {
+
+				renderSize.set( currentRenderTarget.width, currentRenderTarget.height );
+
+			} else {
+
+				renderer.getDrawingBufferSize( renderSize );
+
+			}
+
+			if ( camera.isArrayCamera ) {
+
+				var viewport = camera.cameras[ 0 ].viewport;
+
+				renderTarget.setSize( viewport.z, viewport.w );
+
+				renderTarget.setNumViews( camera.cameras.length );
+
+			} else {
+
+				renderTarget.setSize( renderSize.x, renderSize.y );
+				renderTarget.setNumViews( DEFAULT_NUMVIEWS );
+
+			}
+
+		}
+
+		function attachCamera( camera ) {
+
+			if ( ! isMultiviewCompatible( camera ) ) { return; }
+
+			currentRenderTarget = renderer.getRenderTarget();
+			resizeRenderTarget( camera );
+			renderer.setRenderTarget( renderTarget );
+
+		}
+
+		function detachCamera( camera ) {
+
+			if ( renderTarget !== renderer.getRenderTarget() ) { return; }
+
+			renderer.setRenderTarget( currentRenderTarget );
+			flush( camera );
+
+		}
+
+		function flush( camera ) {
+
+			var srcRenderTarget = renderTarget;
+			var numViews = srcRenderTarget.numViews;
+
+			var srcFramebuffers = properties.get( srcRenderTarget ).__webglViewFramebuffers;
+
+			var viewWidth = srcRenderTarget.width;
+			var viewHeight = srcRenderTarget.height;
+
+			if ( camera.isArrayCamera ) {
+
+				for ( var i = 0; i < numViews; i ++ ) {
+
+					var viewport = camera.cameras[ i ].viewport;
+
+					var x1 = viewport.x;
+					var y1 = viewport.y;
+					var x2 = x1 + viewport.z;
+					var y2 = y1 + viewport.w;
+
+					gl.bindFramebuffer( 36008, srcFramebuffers[ i ] );
+					gl.blitFramebuffer( 0, 0, viewWidth, viewHeight, x1, y1, x2, y2, 16384, 9728 );
+
+				}
+
+			} else {
+
+				gl.bindFramebuffer( 36008, srcFramebuffers[ 0 ] );
+				gl.blitFramebuffer( 0, 0, viewWidth, viewHeight, 0, 0, renderSize.x, renderSize.y, 16384, 9728 );
+
+			}
+
+		}
+
+
+		if ( renderer.capabilities.multiview ) {
+
+			renderTarget = new WebGLMultiviewRenderTarget( 0, 0, DEFAULT_NUMVIEWS );
+
+			renderSize = new Vector2();
+			mat4 = [];
+			mat3 = [];
+			cameraArray = [];
+
+			var maxViews = capabilities.maxMultiviewViews;
+
+			for ( var i = 0; i < maxViews; i ++ ) {
+
+				mat4[ i ] = new Matrix4();
+				mat3[ i ] = new Matrix3();
+
+			}
+
+		}
+
+
+		this.attachCamera = attachCamera;
+		this.detachCamera = detachCamera;
+		this.updateCameraProjectionMatricesUniform = updateCameraProjectionMatricesUniform;
+		this.updateCameraViewMatricesUniform = updateCameraViewMatricesUniform;
+		this.updateObjectMatricesUniforms = updateObjectMatricesUniforms;
+
+	}
+
 	/**
 	 * @author mrdoob / http://mrdoob.com/
 	 */
@@ -23167,6 +23530,10 @@
 
 		this.vr = vr;
 
+		// Multiview
+
+		var multiview = new WebGLMultiview( _this, _gl );
+
 		// shadow map
 
 		var shadowMap = new WebGLShadowMap( _this, objects, capabilities.maxTextureSize );
@@ -24030,6 +24397,12 @@
 
 			}
 
+			if ( capabilities.multiview ) {
+
+				multiview.attachCamera( camera );
+
+			}
+
 			//
 
 			background.render( currentRenderList, scene, camera, forceClear );
@@ -24084,6 +24457,12 @@
 
 			state.setPolygonOffset( false );
 
+			if ( capabilities.multiview ) {
+
+				multiview.detachCamera( camera );
+
+			}
+
 			if ( vr.enabled ) {
 
 				vr.submitFrame();
@@ -24230,19 +24609,27 @@
 
 					_currentArrayCamera = camera;
 
-					var cameras = camera.cameras;
+					if ( capabilities.multiview ) {
+
+						renderObject(	object, scene, camera, geometry, material, group );
+
+					} else {
+
+						var cameras = camera.cameras;
 
-					for ( var j = 0, jl = cameras.length; j < jl; j ++ ) {
+						for ( var j = 0, jl = cameras.length; j < jl; j ++ ) {
 
-						var camera2 = cameras[ j ];
+							var camera2 = cameras[ j ];
 
-						if ( object.layers.test( camera2.layers ) ) {
+							if ( object.layers.test( camera2.layers ) ) {
 
-							state.viewport( _currentViewport.copy( camera2.viewport ) );
+								state.viewport( _currentViewport.copy( camera2.viewport ) );
 
-							currentRenderState.setupLights( camera2 );
+								currentRenderState.setupLights( camera2 );
 
-							renderObject( object, scene, camera2, geometry, material, group );
+								renderObject( object, scene, camera2, geometry, material, group );
+
+							}
 
 						}
 
@@ -24537,7 +24924,15 @@
 
 			if ( refreshProgram || _currentCamera !== camera ) {
 
-				p_uniforms.setValue( _gl, 'projectionMatrix', camera.projectionMatrix );
+				if ( program.numMultiviewViews > 0 ) {
+
+					multiview.updateCameraProjectionMatricesUniform( camera, p_uniforms );
+
+				} else {
+
+					p_uniforms.setValue( _gl, 'projectionMatrix', camera.projectionMatrix );
+
+				}
 
 				if ( capabilities.logarithmicDepthBuffer ) {
 
@@ -24585,7 +24980,15 @@
 					material.isShaderMaterial ||
 					material.skinning ) {
 
-					p_uniforms.setValue( _gl, 'viewMatrix', camera.matrixWorldInverse );
+					if ( program.numMultiviewViews > 0 ) {
+
+						multiview.updateCameraViewMatricesUniform( camera, p_uniforms );
+
+					} else {
+
+						p_uniforms.setValue( _gl, 'viewMatrix', camera.matrixWorldInverse );
+
+					}
 
 				}
 
@@ -24790,8 +25193,17 @@
 
 			// common matrices
 
-			p_uniforms.setValue( _gl, 'modelViewMatrix', object.modelViewMatrix );
-			p_uniforms.setValue( _gl, 'normalMatrix', object.normalMatrix );
+			if ( program.numMultiviewViews > 0 ) {
+
+				multiview.updateObjectMatricesUniforms( object, camera, p_uniforms );
+
+			} else {
+
+				p_uniforms.setValue( _gl, 'modelViewMatrix', object.modelViewMatrix );
+				p_uniforms.setValue( _gl, 'normalMatrix', object.normalMatrix );
+
+			}
+
 			p_uniforms.setValue( _gl, 'modelMatrix', object.matrixWorld );
 
 			return program;

ファイルの差分が大きいため隠しています
+ 429 - 421
build/three.min.js


+ 433 - 19
build/three.module.js

@@ -15536,6 +15536,10 @@ function WebGLCapabilities( gl, extensions, parameters ) {
 
 	var maxSamples = isWebGL2 ? gl.getParameter( 36183 ) : 0;
 
+	var multiviewExt = extensions.get( 'OVR_multiview2' );
+	var multiview = isWebGL2 && !! multiviewExt && ! gl.getContextAttributes().antialias;
+	var maxMultiviewViews = multiview ? gl.getParameter( multiviewExt.MAX_VIEWS_OVR ) : 0;
+
 	return {
 
 		isWebGL2: isWebGL2,
@@ -15560,7 +15564,10 @@ function WebGLCapabilities( gl, extensions, parameters ) {
 		floatFragmentTextures: floatFragmentTextures,
 		floatVertexTextures: floatVertexTextures,
 
-		maxSamples: maxSamples
+		maxSamples: maxSamples,
+
+		multiview: multiview,
+		maxMultiviewViews: maxMultiviewViews
 
 	};
 
@@ -17590,6 +17597,9 @@ function WebGLProgram( renderer, extensions, code, material, shader, parameters,
 
 	var prefixVertex, prefixFragment;
 
+	var renderTarget = renderer.getRenderTarget();
+	var numMultiviewViews = renderTarget && renderTarget.isWebGLMultiviewRenderTarget ? renderTarget.numViews : 0;
+
 	if ( material.isRawShaderMaterial ) {
 
 		prefixVertex = [
@@ -17677,14 +17687,29 @@ function WebGLProgram( renderer, extensions, code, material, shader, parameters,
 
 			parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '',
 			parameters.logarithmicDepthBuffer && ( capabilities.isWebGL2 || extensions.get( 'EXT_frag_depth' ) ) ? '#define USE_LOGDEPTHBUF_EXT' : '',
-
 			'uniform mat4 modelMatrix;',
-			'uniform mat4 modelViewMatrix;',
-			'uniform mat4 projectionMatrix;',
-			'uniform mat4 viewMatrix;',
-			'uniform mat3 normalMatrix;',
 			'uniform vec3 cameraPosition;',
 
+			numMultiviewViews > 0 ? [
+				'uniform mat4 modelViewMatrices[' + numMultiviewViews + '];',
+				'uniform mat3 normalMatrices[' + numMultiviewViews + '];',
+				'uniform mat4 viewMatrices[' + numMultiviewViews + '];',
+				'uniform mat4 projectionMatrices[' + numMultiviewViews + '];',
+
+				'#define modelViewMatrix modelViewMatrices[VIEW_ID]',
+				'#define normalMatrix normalMatrices[VIEW_ID]',
+				'#define viewMatrix viewMatrices[VIEW_ID]',
+				'#define projectionMatrix projectionMatrices[VIEW_ID]'
+
+			].join( '\n' ) : [
+
+				'uniform mat4 modelViewMatrix;',
+				'uniform mat4 projectionMatrix;',
+				'uniform mat4 viewMatrix;',
+				'uniform mat3 normalMatrix;',
+
+			].join( '\n' ),
+
 			'attribute vec3 position;',
 			'attribute vec3 normal;',
 			'attribute vec2 uv;',
@@ -17801,9 +17826,15 @@ function WebGLProgram( renderer, extensions, code, material, shader, parameters,
 
 			( ( material.extensions ? material.extensions.shaderTextureLOD : false ) || parameters.envMap ) && ( capabilities.isWebGL2 || extensions.get( 'EXT_shader_texture_lod' ) ) ? '#define TEXTURE_LOD_EXT' : '',
 
-			'uniform mat4 viewMatrix;',
 			'uniform vec3 cameraPosition;',
 
+			numMultiviewViews > 0 ? [
+
+				'uniform mat4 viewMatrices[' + numMultiviewViews + '];',
+				'#define viewMatrix viewMatrices[VIEW_ID]'
+
+			].join( '\n' ) : 'uniform mat4 viewMatrix;',
+
 			( parameters.toneMapping !== NoToneMapping ) ? '#define TONE_MAPPING' : '',
 			( parameters.toneMapping !== NoToneMapping ) ? ShaderChunk[ 'tonemapping_pars_fragment' ] : '', // this code is required here because it is used by the toneMapping() function defined below
 			( parameters.toneMapping !== NoToneMapping ) ? getToneMappingFunction( 'toneMapping', parameters.toneMapping ) : '',
@@ -17857,6 +17888,15 @@ function WebGLProgram( renderer, extensions, code, material, shader, parameters,
 		// GLSL 3.0 conversion
 		prefixVertex = [
 			'#version 300 es\n',
+
+			numMultiviewViews > 0 ? [
+
+				'#extension GL_OVR_multiview2 : require',
+				'layout(num_views = ' + numMultiviewViews + ') in;',
+				'#define VIEW_ID gl_ViewID_OVR'
+
+			].join( '\n' ) : '',
+
 			'#define attribute in',
 			'#define varying out',
 			'#define texture2D texture'
@@ -17864,6 +17904,12 @@ function WebGLProgram( renderer, extensions, code, material, shader, parameters,
 
 		prefixFragment = [
 			'#version 300 es\n',
+			numMultiviewViews > 0 ? [
+
+				'#extension GL_OVR_multiview2 : require',
+				'#define VIEW_ID gl_ViewID_OVR'
+
+			].join( '\n' ) : '',
 			'#define varying in',
 			isGLSL3ShaderMaterial ? '' : 'out highp vec4 pc_fragColor;',
 			isGLSL3ShaderMaterial ? '' : '#define gl_FragColor pc_fragColor',
@@ -18021,6 +18067,7 @@ function WebGLProgram( renderer, extensions, code, material, shader, parameters,
 	this.program = program;
 	this.vertexShader = glVertexShader;
 	this.fragmentShader = glFragmentShader;
+	this.numMultiviewViews = numMultiviewViews;
 
 	return this;
 
@@ -20951,6 +20998,21 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,
 
 		}
 
+		if ( renderTarget.isWebGLMultiviewRenderTarget ) {
+
+			_gl.deleteTexture( renderTargetProperties.__webglColorTexture );
+			_gl.deleteTexture( renderTargetProperties.__webglDepthStencilTexture );
+
+			info.memory.textures -= 2;
+
+			for ( var i = 0, il = renderTargetProperties.__webglViewFramebuffers.length; i < il; i ++ ) {
+
+				_gl.deleteFramebuffer( renderTargetProperties.__webglViewFramebuffers[ i ] );
+
+			}
+
+		}
+
 		properties.remove( renderTarget.texture );
 		properties.remove( renderTarget );
 
@@ -21637,6 +21699,7 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,
 
 		var isCube = ( renderTarget.isWebGLRenderTargetCube === true );
 		var isMultisample = ( renderTarget.isWebGLMultisampleRenderTarget === true );
+		var isMultiview = ( renderTarget.isWebGLMultiviewRenderTarget === true );
 		var supportsMips = isPowerOfTwo( renderTarget ) || capabilities.isWebGL2;
 
 		// Setup framebuffer
@@ -21689,6 +21752,56 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,
 
 				}
 
+			} else if ( isMultiview ) {
+
+				if ( capabilities.multiview ) {
+
+					var width = renderTarget.width;
+					var height = renderTarget.height;
+					var numViews = renderTarget.numViews;
+
+					_gl.bindFramebuffer( 36160, renderTargetProperties.__webglFramebuffer );
+
+					var ext = extensions.get( 'OVR_multiview2' );
+
+					info.memory.textures += 2;
+
+					var colorTexture = _gl.createTexture();
+					_gl.bindTexture( 35866, colorTexture );
+					_gl.texParameteri( 35866, 10240, 9728 );
+					_gl.texParameteri( 35866, 10241, 9728 );
+					_gl.texImage3D( 35866, 0, 32856, width, height, numViews, 0, 6408, 5121, null );
+					ext.framebufferTextureMultiviewOVR( 36160, 36064, colorTexture, 0, 0, numViews );
+
+					var depthStencilTexture = _gl.createTexture();
+					_gl.bindTexture( 35866, depthStencilTexture );
+					_gl.texParameteri( 35866, 10240, 9728 );
+					_gl.texParameteri( 35866, 10241, 9728 );
+					_gl.texImage3D( 35866, 0, 35056, width, height, numViews, 0, 34041, 34042, null );
+					ext.framebufferTextureMultiviewOVR( 36160, 33306, depthStencilTexture, 0, 0, numViews );
+
+					var viewFramebuffers = new Array( numViews );
+					for ( var i = 0; i < numViews; ++ i ) {
+
+						viewFramebuffers[ i ] = _gl.createFramebuffer();
+						_gl.bindFramebuffer( 36160, viewFramebuffers[ i ] );
+						_gl.framebufferTextureLayer( 36160, 36064, colorTexture, 0, i );
+
+					}
+
+					renderTargetProperties.__webglColorTexture = colorTexture;
+					renderTargetProperties.__webglDepthStencilTexture = depthStencilTexture;
+					renderTargetProperties.__webglViewFramebuffers = viewFramebuffers;
+
+					_gl.bindFramebuffer( 36160, null );
+					_gl.bindTexture( 35866, null );
+
+				} else {
+
+					console.warn( 'THREE.WebGLRenderer: WebGLMultiviewRenderTarget can only be used with WebGL2 and Multiview extension support.' );
+
+				}
+
 			}
 
 		}
@@ -21714,7 +21827,7 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,
 
 			state.bindTexture( 34067, null );
 
-		} else {
+		} else if ( ! isMultiview ) {
 
 			state.bindTexture( 3553, textureProperties.__webglTexture );
 			setTextureParameters( 3553, renderTarget.texture, supportsMips );
@@ -22051,6 +22164,258 @@ function WebGLUtils( gl, extensions, capabilities ) {
 
 }
 
+/**
+ * @author fernandojsg / http://fernandojsg.com
+ * @author Takahiro https://github.com/takahirox
+ */
+
+function WebGLMultiviewRenderTarget( width, height, numViews, options ) {
+
+	WebGLRenderTarget.call( this, width, height, options );
+
+	this.depthBuffer = false;
+	this.stencilBuffer = false;
+
+	this.numViews = numViews;
+
+}
+
+WebGLMultiviewRenderTarget.prototype = Object.assign( Object.create( WebGLRenderTarget.prototype ), {
+
+	constructor: WebGLMultiviewRenderTarget,
+
+	isWebGLMultiviewRenderTarget: true,
+
+	copy: function ( source ) {
+
+		WebGLRenderTarget.prototype.copy.call( this, source );
+
+		this.numViews = source.numViews;
+
+		return this;
+
+	},
+
+	setNumViews: function ( numViews ) {
+
+		if ( this.numViews !== numViews ) {
+
+			this.numViews = numViews;
+			this.dispose();
+
+		}
+
+		return this;
+
+	}
+
+} );
+
+/**
+ * @author fernandojsg / http://fernandojsg.com
+ * @author Takahiro https://github.com/takahirox
+ */
+
+function WebGLMultiview( renderer, gl ) {
+
+	var DEFAULT_NUMVIEWS = 2;
+
+	var capabilities = renderer.capabilities;
+	var properties = renderer.properties;
+
+	var maxNumViews = capabilities.maxMultiviewViews;
+
+	var renderTarget, currentRenderTarget;
+	var mat3, mat4, cameraArray, renderSize;
+
+	function getCameraArray( camera ) {
+
+		if ( camera.isArrayCamera ) return camera.cameras;
+
+		cameraArray[ 0 ] = camera;
+
+		return cameraArray;
+
+	}
+
+	//
+
+	function updateCameraProjectionMatricesUniform( camera, uniforms ) {
+
+		var cameras = getCameraArray( camera );
+
+		for ( var i = 0; i < cameras.length; i ++ ) {
+
+			mat4[ i ].copy( cameras[ i ].projectionMatrix );
+
+		}
+
+		uniforms.setValue( gl, 'projectionMatrices', mat4 );
+
+	}
+
+	function updateCameraViewMatricesUniform( camera, uniforms ) {
+
+		var cameras = getCameraArray( camera );
+
+		for ( var i = 0; i < cameras.length; i ++ ) {
+
+			mat4[ i ].copy( cameras[ i ].matrixWorldInverse );
+
+		}
+
+		uniforms.setValue( gl, 'viewMatrices', mat4 );
+
+	}
+
+	function updateObjectMatricesUniforms( object, camera, uniforms ) {
+
+		var cameras = getCameraArray( camera );
+
+		for ( var i = 0; i < cameras.length; i ++ ) {
+
+			mat4[ i ].multiplyMatrices( cameras[ i ].matrixWorldInverse, object.matrixWorld );
+			mat3[ i ].getNormalMatrix( mat4[ i ] );
+
+		}
+
+		uniforms.setValue( gl, 'modelViewMatrices', mat4 );
+		uniforms.setValue( gl, 'normalMatrices', mat3 );
+
+	}
+
+	function isMultiviewCompatible( camera ) {
+
+		if ( ! camera.isArrayCamera ) return true;
+
+		var cameras = camera.cameras;
+
+		if ( cameras.length > maxNumViews ) return false;
+
+		for ( var i = 1, il = cameras.length; i < il; i ++ ) {
+
+			if ( cameras[ 0 ].viewport.z !== cameras[ i ].viewport.z ||
+				cameras[ 0 ].viewport.w !== cameras[ i ].viewport.w ) return false;
+
+		}
+
+		return true;
+
+	}
+
+	function resizeRenderTarget( camera ) {
+
+		if ( currentRenderTarget ) {
+
+			renderSize.set( currentRenderTarget.width, currentRenderTarget.height );
+
+		} else {
+
+			renderer.getDrawingBufferSize( renderSize );
+
+		}
+
+		if ( camera.isArrayCamera ) {
+
+			var viewport = camera.cameras[ 0 ].viewport;
+
+			renderTarget.setSize( viewport.z, viewport.w );
+
+			renderTarget.setNumViews( camera.cameras.length );
+
+		} else {
+
+			renderTarget.setSize( renderSize.x, renderSize.y );
+			renderTarget.setNumViews( DEFAULT_NUMVIEWS );
+
+		}
+
+	}
+
+	function attachCamera( camera ) {
+
+		if ( ! isMultiviewCompatible( camera ) ) return;
+
+		currentRenderTarget = renderer.getRenderTarget();
+		resizeRenderTarget( camera );
+		renderer.setRenderTarget( renderTarget );
+
+	}
+
+	function detachCamera( camera ) {
+
+		if ( renderTarget !== renderer.getRenderTarget() ) return;
+
+		renderer.setRenderTarget( currentRenderTarget );
+		flush( camera );
+
+	}
+
+	function flush( camera ) {
+
+		var srcRenderTarget = renderTarget;
+		var numViews = srcRenderTarget.numViews;
+
+		var srcFramebuffers = properties.get( srcRenderTarget ).__webglViewFramebuffers;
+
+		var viewWidth = srcRenderTarget.width;
+		var viewHeight = srcRenderTarget.height;
+
+		if ( camera.isArrayCamera ) {
+
+			for ( var i = 0; i < numViews; i ++ ) {
+
+				var viewport = camera.cameras[ i ].viewport;
+
+				var x1 = viewport.x;
+				var y1 = viewport.y;
+				var x2 = x1 + viewport.z;
+				var y2 = y1 + viewport.w;
+
+				gl.bindFramebuffer( 36008, srcFramebuffers[ i ] );
+				gl.blitFramebuffer( 0, 0, viewWidth, viewHeight, x1, y1, x2, y2, 16384, 9728 );
+
+			}
+
+		} else {
+
+			gl.bindFramebuffer( 36008, srcFramebuffers[ 0 ] );
+			gl.blitFramebuffer( 0, 0, viewWidth, viewHeight, 0, 0, renderSize.x, renderSize.y, 16384, 9728 );
+
+		}
+
+	}
+
+
+	if ( renderer.capabilities.multiview ) {
+
+		renderTarget = new WebGLMultiviewRenderTarget( 0, 0, DEFAULT_NUMVIEWS );
+
+		renderSize = new Vector2();
+		mat4 = [];
+		mat3 = [];
+		cameraArray = [];
+
+		var maxViews = capabilities.maxMultiviewViews;
+
+		for ( var i = 0; i < maxViews; i ++ ) {
+
+			mat4[ i ] = new Matrix4();
+			mat3[ i ] = new Matrix3();
+
+		}
+
+	}
+
+
+	this.attachCamera = attachCamera;
+	this.detachCamera = detachCamera;
+	this.updateCameraProjectionMatricesUniform = updateCameraProjectionMatricesUniform;
+	this.updateCameraViewMatricesUniform = updateCameraViewMatricesUniform;
+	this.updateObjectMatricesUniforms = updateObjectMatricesUniforms;
+
+}
+
 /**
  * @author mrdoob / http://mrdoob.com/
  */
@@ -23154,6 +23519,10 @@ function WebGLRenderer( parameters ) {
 
 	this.vr = vr;
 
+	// Multiview
+
+	var multiview = new WebGLMultiview( _this, _gl );
+
 	// shadow map
 
 	var shadowMap = new WebGLShadowMap( _this, objects, capabilities.maxTextureSize );
@@ -24017,6 +24386,12 @@ function WebGLRenderer( parameters ) {
 
 		}
 
+		if ( capabilities.multiview ) {
+
+			multiview.attachCamera( camera );
+
+		}
+
 		//
 
 		background.render( currentRenderList, scene, camera, forceClear );
@@ -24071,6 +24446,12 @@ function WebGLRenderer( parameters ) {
 
 		state.setPolygonOffset( false );
 
+		if ( capabilities.multiview ) {
+
+			multiview.detachCamera( camera );
+
+		}
+
 		if ( vr.enabled ) {
 
 			vr.submitFrame();
@@ -24217,19 +24598,27 @@ function WebGLRenderer( parameters ) {
 
 				_currentArrayCamera = camera;
 
-				var cameras = camera.cameras;
+				if ( capabilities.multiview ) {
+
+					renderObject(	object, scene, camera, geometry, material, group );
+
+				} else {
+
+					var cameras = camera.cameras;
 
-				for ( var j = 0, jl = cameras.length; j < jl; j ++ ) {
+					for ( var j = 0, jl = cameras.length; j < jl; j ++ ) {
 
-					var camera2 = cameras[ j ];
+						var camera2 = cameras[ j ];
 
-					if ( object.layers.test( camera2.layers ) ) {
+						if ( object.layers.test( camera2.layers ) ) {
 
-						state.viewport( _currentViewport.copy( camera2.viewport ) );
+							state.viewport( _currentViewport.copy( camera2.viewport ) );
 
-						currentRenderState.setupLights( camera2 );
+							currentRenderState.setupLights( camera2 );
 
-						renderObject( object, scene, camera2, geometry, material, group );
+							renderObject( object, scene, camera2, geometry, material, group );
+
+						}
 
 					}
 
@@ -24524,7 +24913,15 @@ function WebGLRenderer( parameters ) {
 
 		if ( refreshProgram || _currentCamera !== camera ) {
 
-			p_uniforms.setValue( _gl, 'projectionMatrix', camera.projectionMatrix );
+			if ( program.numMultiviewViews > 0 ) {
+
+				multiview.updateCameraProjectionMatricesUniform( camera, p_uniforms );
+
+			} else {
+
+				p_uniforms.setValue( _gl, 'projectionMatrix', camera.projectionMatrix );
+
+			}
 
 			if ( capabilities.logarithmicDepthBuffer ) {
 
@@ -24572,7 +24969,15 @@ function WebGLRenderer( parameters ) {
 				material.isShaderMaterial ||
 				material.skinning ) {
 
-				p_uniforms.setValue( _gl, 'viewMatrix', camera.matrixWorldInverse );
+				if ( program.numMultiviewViews > 0 ) {
+
+					multiview.updateCameraViewMatricesUniform( camera, p_uniforms );
+
+				} else {
+
+					p_uniforms.setValue( _gl, 'viewMatrix', camera.matrixWorldInverse );
+
+				}
 
 			}
 
@@ -24777,8 +25182,17 @@ function WebGLRenderer( parameters ) {
 
 		// common matrices
 
-		p_uniforms.setValue( _gl, 'modelViewMatrix', object.modelViewMatrix );
-		p_uniforms.setValue( _gl, 'normalMatrix', object.normalMatrix );
+		if ( program.numMultiviewViews > 0 ) {
+
+			multiview.updateObjectMatricesUniforms( object, camera, p_uniforms );
+
+		} else {
+
+			p_uniforms.setValue( _gl, 'modelViewMatrix', object.modelViewMatrix );
+			p_uniforms.setValue( _gl, 'normalMatrix', object.normalMatrix );
+
+		}
+
 		p_uniforms.setValue( _gl, 'modelMatrix', object.matrixWorld );
 
 		return program;

+ 149 - 0
docs/examples/en/controls/PointerLockControls.html

@@ -0,0 +1,149 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<meta charset="utf-8" />
+		<base href="../../../" />
+		<script src="list.js"></script>
+		<script src="page.js"></script>
+		<link type="text/css" rel="stylesheet" href="page.css" />
+	</head>
+	<body>
+		[page:EventDispatcher] &rarr;
+
+		<h1>[name]</h1>
+
+		<p class="desc">
+		The implementation of this class is based on the [link:https://developer.mozilla.org/en-US/docs/Web/API/Pointer_Lock_API Pointer Lock API].
+		[name] is a perfect choice for first person 3D games.
+		</p>
+
+		<h2>Example</h2>
+
+		<p>[example:misc_controls_pointerlock misc / controls / pointerlock ]</p>
+
+		<code>
+var controls = new PointerLockControls( camera );
+
+// add event listener to show/hide a UI (e.g. the game's menu)
+
+controls.addEventListener( 'lock', function () {
+
+	menu.style.display = 'none';
+
+} );
+
+controls.addEventListener( 'unlock', function () {
+
+	menu.style.display = 'block';
+
+} );
+
+		</code>
+
+		<h2>Constructor</h2>
+
+		<h3>[name]( [param:Camera camera], [param:HTMLDOMElement domElement] )</h3>
+		<p>
+			<p>
+			[page:Camera camera]: The camera of the rendered scene.
+			</p>
+			<p>
+			[page:HTMLDOMElement domElement]: (optional) The HTML element used for event listeners. By default this is the document's body.
+			</p>
+			<p>
+				Creates a new instance of [name].
+			</p>
+		</p>
+
+		<h2>Events</h2>
+
+		<h3>change</h3>
+		<p>
+			Fires when the user moves the mouse.
+		</p>
+
+		<h3>lock</h3>
+		<p>
+			Fires when the pointer lock status is "locked" (in other words: the mouse is captured).
+		</p>
+
+		<h3>unlock</h3>
+		<p>
+			Fires when the pointer lock status is "unlocked" (in other words: the mouse is not captured anymore).
+		</p>
+
+		<h2>Properties</h2>
+
+		<h3>[property:HTMLDOMElement domElement]</h3>
+		<p>
+			The HTMLDOMElement used to listen for mouse / touch events. This must be passed in the constructor; changing it here will
+			not set up new event listeners. Default is the document's body.
+		</p>
+
+
+		<h3>[property:Boolean isLocked]</h3>
+		<p>
+			Whether or not the controls are locked.
+		</p>
+
+		<h2>Methods</h2>
+
+		<p>See the base [page:EventDispatcher] class for common methods.</p>
+
+		<h3>[method:null connect] ()</h3>
+		<p>
+			Adds the event listeners of the controls.
+		</p>
+
+		<h3>[method:null disconnect] ()</h3>
+		<p>
+			Removes the event listeners of the controls.
+		</p>
+
+		<h3>[method:Vector3 getDirection] ( [param:Vector3 target] )</h3>
+		<p>
+			<p>
+				[page:Vector3 target]: The target vector.
+			</p>
+			<p>
+				Returns the look direction of the camera.
+			</p>
+		</p>
+
+		<h3>[method:null lock] ()</h3>
+		<p>
+			Activates the pointer lock.
+		</p>
+
+		<h3>[method:null moveForward] ( [param:Number distance] )</h3>
+		<p>
+			<p>
+				[page:Number distance]: The signed distance.
+			</p>
+			<p>
+				Moves the camera forward parallel to the xz-plane. Assumes camera.up is y-up.
+			</p>
+		</p>
+
+		<h3>[method:null moveRight] ( [param:Number distance] )</h3>
+		<p>
+			<p>
+				[page:Number distance]: The signed distance.
+			</p>
+			<p>
+				Moves the camera sidewards parallel to the xz-plane.
+			</p>
+		</p>
+
+		<h3>[method:null unlock] ()</h3>
+		<p>
+			Exits the pointer lock.
+		</p>
+
+		<h2>Source</h2>
+
+		<p>
+			[link:https://github.com/mrdoob/three.js/blob/master/examples/js/controls/PointerLockControls.js examples/js/controls/PointerLockControls.js]
+		</p>
+	</body>
+</html>

+ 149 - 0
docs/examples/zh/controls/PointerLockControls.html

@@ -0,0 +1,149 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<meta charset="utf-8" />
+		<base href="../../../" />
+		<script src="list.js"></script>
+		<script src="page.js"></script>
+		<link type="text/css" rel="stylesheet" href="page.css" />
+	</head>
+	<body>
+		[page:EventDispatcher] &rarr;
+
+		<h1>[name]</h1>
+
+		<p class="desc">
+		The implementation of this class is based on the [link:https://developer.mozilla.org/en-US/docs/Web/API/Pointer_Lock_API Pointer Lock API].
+		[name] is a perfect choice for first person 3D games.
+		</p>
+
+		<h2>Example</h2>
+
+		<p>[example:misc_controls_pointerlock misc / controls / pointerlock ]</p>
+
+		<code>
+var controls = new PointerLockControls( camera );
+
+// add event listener to show/hide a UI (e.g. the game's menu)
+
+controls.addEventListener( 'lock', function () {
+
+	menu.style.display = 'none';
+
+} );
+
+controls.addEventListener( 'unlock', function () {
+
+	menu.style.display = 'block';
+
+} );
+
+		</code>
+
+		<h2>Constructor</h2>
+
+		<h3>[name]( [param:Camera camera], [param:HTMLDOMElement domElement] )</h3>
+		<p>
+			<p>
+			[page:Camera camera]: The camera of the rendered scene.
+			</p>
+			<p>
+			[page:HTMLDOMElement domElement]: (optional) The HTML element used for event listeners. By default this is the document's body.
+			</p>
+			<p>
+				Creates a new instance of [name].
+			</p>
+		</p>
+
+		<h2>Events</h2>
+
+		<h3>change</h3>
+		<p>
+			Fires when the user moves the mouse.
+		</p>
+
+		<h3>lock</h3>
+		<p>
+			Fires when the pointer lock status is "locked" (in other words: the mouse is captured).
+		</p>
+
+		<h3>unlock</h3>
+		<p>
+			Fires when the pointer lock status is "unlocked" (in other words: the mouse is not captured anymore).
+		</p>
+
+		<h2>Properties</h2>
+
+		<h3>[property:HTMLDOMElement domElement]</h3>
+		<p>
+			The HTMLDOMElement used to listen for mouse / touch events. This must be passed in the constructor; changing it here will
+			not set up new event listeners. Default is the document's body.
+		</p>
+
+
+		<h3>[property:Boolean isLocked]</h3>
+		<p>
+			Whether or not the controls are locked.
+		</p>
+
+		<h2>Methods</h2>
+
+		<p>See the base [page:EventDispatcher] class for common methods.</p>
+
+		<h3>[method:null connect] ()</h3>
+		<p>
+			Adds the event listeners of the controls.
+		</p>
+
+		<h3>[method:null disconnect] ()</h3>
+		<p>
+			Removes the event listeners of the controls.
+		</p>
+
+		<h3>[method:Vector3 getDirection] ( [param:Vector3 target] )</h3>
+		<p>
+			<p>
+				[page:Vector3 target]: The target vector.
+			</p>
+			<p>
+				Returns the look direction of the camera.
+			</p>
+		</p>
+
+		<h3>[method:null lock] ()</h3>
+		<p>
+			Activates the pointer lock.
+		</p>
+
+		<h3>[method:null moveForward] ( [param:Number distance] )</h3>
+		<p>
+			<p>
+				[page:Number distance]: The signed distance.
+			</p>
+			<p>
+				Moves the camera forward parallel to the xz-plane. Assumes camera.up is y-up.
+			</p>
+		</p>
+
+		<h3>[method:null moveRight] ( [param:Number distance] )</h3>
+		<p>
+			<p>
+				[page:Number distance]: The signed distance.
+			</p>
+			<p>
+				Moves the camera sidewards parallel to the xz-plane.
+			</p>
+		</p>
+
+		<h3>[method:null unlock] ()</h3>
+		<p>
+			Exits the pointer lock.
+		</p>
+
+		<h2>Source</h2>
+
+		<p>
+			[link:https://github.com/mrdoob/three.js/blob/master/examples/js/controls/PointerLockControls.js examples/js/controls/PointerLockControls.js]
+		</p>
+	</body>
+</html>

+ 4 - 2
docs/list.js

@@ -357,7 +357,8 @@ var list = {
 				"DeviceOrientationControls": "examples/en/controls/DeviceOrientationControls",
 				"DragControls": "examples/en/controls/DragControls",
 				"FlyControls": "examples/en/controls/FlyControls",
-				"OrbitControls": "examples/en/controls/OrbitControls"
+				"OrbitControls": "examples/en/controls/OrbitControls",
+				"PointerLockControls": "examples/en/controls/PointerLockControls"
 			},
 
 			"Geometries": {
@@ -796,7 +797,8 @@ var list = {
 				"DeviceOrientationControls": "examples/zh/controls/DeviceOrientationControls",
 				"DragControls": "examples/zh/controls/DragControls",
 				"FlyControls": "examples/zh/controls/FlyControls",
-				"OrbitControls": "examples/zh/controls/OrbitControls"
+				"OrbitControls": "examples/zh/controls/OrbitControls",
+				"PointerLockControls": "examples/zh/controls/PointerLockControls"
 			},
 
 			"几何体": {

この差分においてかなりの量のファイルが変更されているため、一部のファイルを表示していません