diff --git a/examples/webxr_xr_ballshooter.html b/examples/webxr_xr_ballshooter.html index d462e5f01b6b40..5188c1d00adfc2 100644 --- a/examples/webxr_xr_ballshooter.html +++ b/examples/webxr_xr_ballshooter.html @@ -93,7 +93,7 @@ // - renderer = new THREE.WebGLRenderer( { antialias: true } ); + renderer = new THREE.WebGLRenderer( { antialias: true, multiviewStereo: true } ); renderer.setPixelRatio( window.devicePixelRatio ); renderer.setSize( window.innerWidth, window.innerHeight ); renderer.xr.enabled = true; diff --git a/src/renderers/WebGLMultiviewRenderTarget.js b/src/renderers/WebGLMultiviewRenderTarget.js new file mode 100644 index 00000000000000..016d40ecf09ddd --- /dev/null +++ b/src/renderers/WebGLMultiviewRenderTarget.js @@ -0,0 +1,35 @@ +/** + * @author fernandojsg / http://fernandojsg.com + * @author Takahiro https://github.com/takahirox + */ + +import { WebGLRenderTarget } from './WebGLRenderTarget.js'; + +class WebGLMultiviewRenderTarget extends WebGLRenderTarget { + + constructor( width, height, numViews, options = {} ) { + + super( width, height, options ); + + this.depthBuffer = false; + this.stencilBuffer = false; + + this.numViews = numViews; + + } + + copy( source ) { + + super.copy( source ); + + this.numViews = source.numViews; + + return this; + + } + +} + +WebGLMultiviewRenderTarget.prototype.isWebGLMultiviewRenderTarget = true; + +export { WebGLMultiviewRenderTarget }; diff --git a/src/renderers/WebGLRenderer.js b/src/renderers/WebGLRenderer.js index 1f5270573b614e..a333b1d375226b 100644 --- a/src/renderers/WebGLRenderer.js +++ b/src/renderers/WebGLRenderer.js @@ -51,6 +51,7 @@ import { WebGLState } from './webgl/WebGLState.js'; import { WebGLTextures } from './webgl/WebGLTextures.js'; import { WebGLUniforms } from './webgl/WebGLUniforms.js'; import { WebGLUtils } from './webgl/WebGLUtils.js'; +import { WebGLMultiview } from './webgl/WebGLMultiview.js'; import { WebXRManager } from './webxr/WebXRManager.js'; import { WebGLMaterials } from './webgl/WebGLMaterials.js'; import { WebGLUniformsGroups } from './webgl/WebGLUniformsGroups.js'; @@ -79,6 +80,7 @@ class WebGLRenderer { preserveDrawingBuffer = false, powerPreference = 'default', failIfMajorPerformanceCaveat = false, + multiviewStereo = false, } = parameters; this.isWebGLRenderer = true; @@ -303,6 +305,7 @@ class WebGLRenderer { let extensions, capabilities, state, info; let properties, textures, cubemaps, cubeuvmaps, attributes, geometries, objects; let programCache, materials, renderLists, renderStates, clipping, shadowMap; + let multiview; let background, morphtargets, bufferRenderer, indexedBufferRenderer; @@ -336,6 +339,7 @@ class WebGLRenderer { renderLists = new WebGLRenderLists(); renderStates = new WebGLRenderStates( extensions, capabilities ); background = new WebGLBackground( _this, cubemaps, cubeuvmaps, state, objects, _alpha, premultipliedAlpha ); + multiview = new WebGLMultiview( _this, extensions, _gl ); shadowMap = new WebGLShadowMap( _this, objects, capabilities ); uniformsGroups = new WebGLUniformsGroups( _gl, info, capabilities, state ); @@ -358,7 +362,7 @@ class WebGLRenderer { // xr - const xr = new WebXRManager( _this, _gl ); + const xr = new WebXRManager( _this, _gl, extensions, multiviewStereo ); this.xr = xr; @@ -1101,13 +1105,23 @@ class WebGLRenderer { if ( camera.isArrayCamera ) { - const cameras = camera.cameras; + if ( xr.enabled && xr.isMultiview ) { - for ( let i = 0, l = cameras.length; i < l; i ++ ) { + textures.deferTextureUploads = true; - const camera2 = cameras[ i ]; + renderScene( currentRenderList, scene, camera, camera.cameras[ 0 ].viewport ); - renderScene( currentRenderList, scene, camera2, camera2.viewport ); + } else { + + const cameras = camera.cameras; + + for ( let i = 0, l = cameras.length; i < l; i ++ ) { + + const camera2 = cameras[ i ]; + + renderScene( currentRenderList, scene, camera2, camera2.viewport ); + + } } @@ -1135,6 +1149,8 @@ class WebGLRenderer { if ( scene.isScene === true ) scene.onAfterRender( _this, scene, camera ); + textures.runDeferredUploads(); + // _gl.finish(); bindingStates.resetDefaultState(); @@ -1590,6 +1606,7 @@ class WebGLRenderer { materialProperties.vertexAlphas = parameters.vertexAlphas; materialProperties.vertexTangents = parameters.vertexTangents; materialProperties.toneMapping = parameters.toneMapping; + materialProperties.numMultiviewViews = parameters.numMultiviewViews; } @@ -1609,7 +1626,7 @@ class WebGLRenderer { const morphNormals = !! geometry.morphAttributes.normal; const morphColors = !! geometry.morphAttributes.color; const toneMapping = material.toneMapped ? _this.toneMapping : NoToneMapping; - + const numMultiviewViews = _currentRenderTarget && _currentRenderTarget.isWebGLMultiviewRenderTarget ? _currentRenderTarget.numViews : 0; const morphAttribute = geometry.morphAttributes.position || geometry.morphAttributes.normal || geometry.morphAttributes.color; const morphTargetsCount = ( morphAttribute !== undefined ) ? morphAttribute.length : 0; @@ -1705,6 +1722,10 @@ class WebGLRenderer { needsProgramChange = true; + } else if ( materialProperties.numMultiviewViews !== numMultiviewViews ) { + + needsProgramChange = true; + } } else { @@ -1749,7 +1770,15 @@ class WebGLRenderer { 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 ) { @@ -1811,7 +1840,15 @@ class WebGLRenderer { material.isShadowMaterial || object.isSkinnedMesh ) { - p_uniforms.setValue( _gl, 'viewMatrix', camera.matrixWorldInverse ); + if ( program.numMultiviewViews > 0 ) { + + multiview.updateCameraViewMatricesUniform( camera, p_uniforms ); + + } else { + + p_uniforms.setValue( _gl, 'viewMatrix', camera.matrixWorldInverse ); + + } } @@ -1920,8 +1957,17 @@ class WebGLRenderer { // 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 ); // UBOs @@ -2005,20 +2051,16 @@ class WebGLRenderer { const renderTargetProperties = properties.get( renderTarget ); renderTargetProperties.__hasExternalTextures = true; - if ( renderTargetProperties.__hasExternalTextures ) { - - renderTargetProperties.__autoAllocateDepthBuffer = depthTexture === undefined; + renderTargetProperties.__autoAllocateDepthBuffer = depthTexture === undefined; - if ( ! renderTargetProperties.__autoAllocateDepthBuffer ) { + if ( ! renderTargetProperties.__autoAllocateDepthBuffer && ! _currentRenderTarget.isWebGLMultiviewRenderTarget ) { - // The multisample_render_to_texture extension doesn't work properly if there - // are midframe flushes and an external depth buffer. Disable use of the extension. - if ( extensions.has( 'WEBGL_multisampled_render_to_texture' ) === true ) { + // The multisample_render_to_texture extension doesn't work properly if there + // are midframe flushes and an external depth buffer. Disable use of the extension. + if ( extensions.has( 'WEBGL_multisampled_render_to_texture' ) === true ) { - console.warn( 'THREE.WebGLRenderer: Render-to-texture extension was disabled because an external texture was provided' ); - renderTargetProperties.__useRenderToTexture = false; - - } + console.warn( 'THREE.WebGLRenderer: Render-to-texture extension was disabled because an external texture was provided' ); + renderTargetProperties.__useRenderToTexture = false; } diff --git a/src/renderers/webgl/WebGLBackground.js b/src/renderers/webgl/WebGLBackground.js index a82640f1508799..781aefce56e702 100644 --- a/src/renderers/webgl/WebGLBackground.js +++ b/src/renderers/webgl/WebGLBackground.js @@ -76,7 +76,7 @@ function WebGLBackground( renderer, cubemaps, cubeuvmaps, state, objects, alpha, if ( boxMesh === undefined ) { boxMesh = new Mesh( - new BoxGeometry( 1, 1, 1 ), + new BoxGeometry( 10000, 10000, 10000 ), new ShaderMaterial( { name: 'BackgroundCubeMaterial', uniforms: cloneUniforms( ShaderLib.backgroundCube.uniforms ), diff --git a/src/renderers/webgl/WebGLLights.js b/src/renderers/webgl/WebGLLights.js index de48b309fee46e..38118c26a0ac87 100644 --- a/src/renderers/webgl/WebGLLights.js +++ b/src/renderers/webgl/WebGLLights.js @@ -484,6 +484,8 @@ function WebGLLights( extensions, capabilities ) { const viewMatrix = camera.matrixWorldInverse; + // TODO here's where i need to fix the lights. + for ( let i = 0, l = lights.length; i < l; i ++ ) { const light = lights[ i ]; diff --git a/src/renderers/webgl/WebGLMultiview.js b/src/renderers/webgl/WebGLMultiview.js new file mode 100644 index 00000000000000..8cb4173e90359c --- /dev/null +++ b/src/renderers/webgl/WebGLMultiview.js @@ -0,0 +1,101 @@ +/** + * @author fernandojsg / http://fernandojsg.com + * @author Takahiro https://github.com/takahirox + */ +import { Matrix3 } from '../../math/Matrix3.js'; +import { Matrix4 } from '../../math/Matrix4.js'; + + +class WebGLMultiview { + + constructor( renderer, extensions, gl ) { + + this.renderer = renderer; + + this.DEFAULT_NUMVIEWS = 2; + this.maxNumViews = 0; + this.gl = gl; + + this.extensions = extensions; + + this.available = this.extensions.has( 'OCULUS_multiview' ); + + if ( this.available ) { + + const extension = this.extensions.get( 'OCULUS_multiview' ); + + this.maxNumViews = this.gl.getParameter( extension.MAX_VIEWS_OVR ); + + this.mat4 = []; + this.mat3 = []; + this.cameraArray = []; + + for ( var i = 0; i < this.maxNumViews; i ++ ) { + + this.mat4[ i ] = new Matrix4(); + this.mat3[ i ] = new Matrix3(); + + } + + } + + } + + // + getCameraArray( camera ) { + + if ( camera.isArrayCamera ) return camera.cameras; + + this.cameraArray[ 0 ] = camera; + + return this.cameraArray; + + } + + updateCameraProjectionMatricesUniform( camera, uniforms ) { + + var cameras = this.getCameraArray( camera ); + + for ( var i = 0; i < cameras.length; i ++ ) { + + this.mat4[ i ].copy( cameras[ i ].projectionMatrix ); + + } + + uniforms.setValue( this.gl, 'projectionMatrices', this.mat4 ); + + } + + updateCameraViewMatricesUniform( camera, uniforms ) { + + var cameras = this.getCameraArray( camera ); + + for ( var i = 0; i < cameras.length; i ++ ) { + + this.mat4[ i ].copy( cameras[ i ].matrixWorldInverse ); + + } + + uniforms.setValue( this.gl, 'viewMatrices', this.mat4 ); + + } + + updateObjectMatricesUniforms( object, camera, uniforms ) { + + var cameras = this.getCameraArray( camera ); + + for ( var i = 0; i < cameras.length; i ++ ) { + + this.mat4[ i ].multiplyMatrices( cameras[ i ].matrixWorldInverse, object.matrixWorld ); + this.mat3[ i ].getNormalMatrix( this.mat4[ i ] ); + + } + + uniforms.setValue( this.gl, 'modelViewMatrices', this.mat4 ); + uniforms.setValue( this.gl, 'normalMatrices', this.mat3 ); + + } + +} + +export { WebGLMultiview }; diff --git a/src/renderers/webgl/WebGLProgram.js b/src/renderers/webgl/WebGLProgram.js index 332652bee95ae4..c50ac732258b85 100644 --- a/src/renderers/webgl/WebGLProgram.js +++ b/src/renderers/webgl/WebGLProgram.js @@ -414,6 +414,8 @@ function WebGLProgram( renderer, cacheKey, parameters, bindingStates ) { let prefixVertex, prefixFragment; let versionString = parameters.glslVersion ? '#version ' + parameters.glslVersion + '\n' : ''; + var numMultiviewViews = parameters.numMultiviewViews; + if ( parameters.isRawShaderMaterial ) { prefixVertex = [ @@ -793,6 +795,53 @@ function WebGLProgram( renderer, cacheKey, parameters, bindingStates ) { '#define textureCubeGradEXT textureGrad' ].join( '\n' ) + '\n' + prefixFragment; + // Multiview + + if ( numMultiviewViews > 0 ) { + + // TODO: fix light transforms here? + + prefixVertex = [ + '#extension GL_OVR_multiview : require', + 'layout(num_views = ' + numMultiviewViews + ') in;', + '#define VIEW_ID gl_ViewID_OVR' + ].join( '\n' ) + '\n' + prefixVertex; + + prefixVertex = prefixVertex.replace( + [ + 'uniform mat4 modelViewMatrix;', + 'uniform mat4 projectionMatrix;', + 'uniform mat4 viewMatrix;', + 'uniform mat3 normalMatrix;' + ].join( '\n' ), + [ + 'uniform mat4 modelViewMatrices[' + numMultiviewViews + '];', + 'uniform mat4 projectionMatrices[' + numMultiviewViews + '];', + 'uniform mat4 viewMatrices[' + numMultiviewViews + '];', + 'uniform mat3 normalMatrices[' + numMultiviewViews + '];', + + '#define modelViewMatrix modelViewMatrices[VIEW_ID]', + '#define projectionMatrix projectionMatrices[VIEW_ID]', + '#define viewMatrix viewMatrices[VIEW_ID]', + '#define normalMatrix normalMatrices[VIEW_ID]' + ].join( '\n' ) + ); + + prefixFragment = [ + '#extension GL_OVR_multiview : require', + '#define VIEW_ID gl_ViewID_OVR' + ].join( '\n' ) + '\n' + prefixFragment; + + prefixFragment = prefixFragment.replace( + 'uniform mat4 viewMatrix;', + [ + 'uniform mat4 viewMatrices[' + numMultiviewViews + '];', + '#define viewMatrix viewMatrices[VIEW_ID]' + ].join( '\n' ) + ); + + } + } const vertexGlsl = versionString + prefixVertex + vertexShader; @@ -956,6 +1005,7 @@ function WebGLProgram( renderer, cacheKey, parameters, bindingStates ) { this.program = program; this.vertexShader = glVertexShader; this.fragmentShader = glFragmentShader; + this.numMultiviewViews = numMultiviewViews; return this; diff --git a/src/renderers/webgl/WebGLPrograms.js b/src/renderers/webgl/WebGLPrograms.js index fe6fcad03b97ac..f0b523cf9db2ea 100644 --- a/src/renderers/webgl/WebGLPrograms.js +++ b/src/renderers/webgl/WebGLPrograms.js @@ -106,6 +106,8 @@ function WebGLPrograms( renderer, cubemaps, cubeuvmaps, extensions, capabilities const currentRenderTarget = renderer.getRenderTarget(); + const numMultiviewViews = currentRenderTarget && currentRenderTarget.isWebGLMultiviewRenderTarget ? currentRenderTarget.numViews : 0; + const IS_INSTANCEDMESH = object.isInstancedMesh === true; const HAS_MAP = !! material.map; @@ -178,6 +180,8 @@ function WebGLPrograms( renderer, cubemaps, cubeuvmaps, extensions, capabilities instancingColor: IS_INSTANCEDMESH && object.instanceColor !== null, supportsVertexTextures: SUPPORTS_VERTEX_TEXTURES, + numMultiviewViews: numMultiviewViews, + outputColorSpace: ( currentRenderTarget === null ) ? renderer.outputColorSpace : ( currentRenderTarget.isXRRenderTarget === true ? currentRenderTarget.texture.colorSpace : LinearSRGBColorSpace ), map: HAS_MAP, @@ -509,6 +513,8 @@ function WebGLPrograms( renderer, cubemaps, cubeuvmaps, extensions, capabilities _programLayers.enable( 17 ); if ( parameters.pointsUvs ) _programLayers.enable( 18 ); + if ( parameters.numMultiviewViews ) + _programLayers.enable( 19 ); array.push( _programLayers.mask ); diff --git a/src/renderers/webgl/WebGLTextures.js b/src/renderers/webgl/WebGLTextures.js index 11ba4c408631d0..29feafdd6a428e 100644 --- a/src/renderers/webgl/WebGLTextures.js +++ b/src/renderers/webgl/WebGLTextures.js @@ -12,12 +12,15 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, const maxSamples = capabilities.maxSamples; const multisampledRTTExt = extensions.has( 'WEBGL_multisampled_render_to_texture' ) ? extensions.get( 'WEBGL_multisampled_render_to_texture' ) : null; const supportsInvalidateFramebuffer = typeof navigator === 'undefined' ? false : /OculusBrowser/g.test( navigator.userAgent ); + const multiviewExt = extensions.has( 'OCULUS_multiview' ) ? extensions.get( 'OCULUS_multiview' ) : null; const _videoTextures = new WeakMap(); let _canvas; const _sources = new WeakMap(); // maps WebglTexture objects to instances of Source + let _deferredUploads = []; + // cordova iOS (as of 5.0) still uses UIWebView, which provides OffscreenCanvas, // also OffscreenCanvas.getContext("webgl"), but not OffscreenCanvas.getContext("2d")! // Some implementations may only implement OffscreenCanvas partially (e.g. lacking 2d). @@ -454,8 +457,11 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, } else { - uploadTexture( textureProperties, texture, slot ); - return; + if ( this.uploadTexture( textureProperties, texture, slot ) ) { + + return; + + } } @@ -471,7 +477,7 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, if ( texture.version > 0 && textureProperties.__version !== texture.version ) { - uploadTexture( textureProperties, texture, slot ); + this.uploadTexture( textureProperties, texture, slot ); return; } @@ -486,7 +492,7 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, if ( texture.version > 0 && textureProperties.__version !== texture.version ) { - uploadTexture( textureProperties, texture, slot ); + this.uploadTexture( textureProperties, texture, slot ); return; } @@ -544,7 +550,7 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, wrappingToGL[ texture.wrapS ] ); _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, wrappingToGL[ texture.wrapT ] ); - if ( textureType === _gl.TEXTURE_3D || textureType === _gl.TEXTURE_2D_ARRAY ) { + if ( ( textureType === _gl.TEXTURE_3D || textureType === _gl.TEXTURE_2D_ARRAY ) && texture.wrapR !== undefined ) { _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_R, wrappingToGL[ texture.wrapR ] ); @@ -688,8 +694,39 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, } + function runDeferredUploads() { + + const previousDeferSetting = this.deferTextureUploads; + this.deferTextureUploads = false; + + for ( const upload of _deferredUploads ) { + + this.uploadTexture( upload.textureProperties, upload.texture, upload.slot ); + upload.texture.isPendingDeferredUpload = false; + + } + + _deferredUploads = []; + + this.deferTextureUploads = previousDeferSetting; + + } + function uploadTexture( textureProperties, texture, slot ) { + if ( this.deferTextureUploads ) { + + if ( ! texture.isPendingDeferredUpload ) { + + texture.isPendingDeferredUpload = true; + _deferredUploads.push( { textureProperties: textureProperties, texture: texture, slot: slot } ); + + } + + return false; + + } + let textureType = _gl.TEXTURE_2D; if ( texture.isDataArrayTexture || texture.isCompressedArrayTexture ) textureType = _gl.TEXTURE_2D_ARRAY; @@ -1102,6 +1139,7 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, } textureProperties.__version = texture.version; + return true; } @@ -1324,7 +1362,11 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, if ( ! renderTargetProperties.__hasExternalTextures ) { - if ( textureTarget === _gl.TEXTURE_3D || textureTarget === _gl.TEXTURE_2D_ARRAY ) { + if ( renderTarget.isWebGLMultiviewRenderTarget === true ) { + + state.texStorage3D( _gl.TEXTURE_2D_ARRAY, 0, glInternalFormat, renderTarget.width, renderTarget.height, renderTarget.numViews ); + + } else if ( textureTarget === _gl.TEXTURE_3D || textureTarget === _gl.TEXTURE_2D_ARRAY ) { state.texImage3D( textureTarget, 0, glInternalFormat, renderTarget.width, renderTarget.height, renderTarget.depth, 0, glFormat, glType, null ); @@ -1338,13 +1380,31 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, state.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); - if ( useMultisampledRTT( renderTarget ) ) { + const multisampled = useMultisampledRTT( renderTarget ); + + if ( renderTarget.isWebGLMultiviewRenderTarget === true ) { + + if ( multisampled ) { - multisampledRTTExt.framebufferTexture2DMultisampleEXT( _gl.FRAMEBUFFER, attachment, textureTarget, properties.get( texture ).__webglTexture, 0, getRenderTargetSamples( renderTarget ) ); + multiviewExt.framebufferTextureMultisampleMultiviewOVR( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, properties.get( texture ).__webglTexture, 0, getRenderTargetSamples( renderTarget ), 0, renderTarget.numViews ); + + } else { + + multiviewExt.framebufferTextureMultiviewOVR( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, properties.get( texture ).__webglTexture, 0, 0, renderTarget.numViews ); + + } } else if ( textureTarget === _gl.TEXTURE_2D || ( textureTarget >= _gl.TEXTURE_CUBE_MAP_POSITIVE_X && textureTarget <= _gl.TEXTURE_CUBE_MAP_NEGATIVE_Z ) ) { // see #24753 - _gl.framebufferTexture2D( _gl.FRAMEBUFFER, attachment, textureTarget, properties.get( texture ).__webglTexture, 0 ); + if ( multisampled ) { + + multisampledRTTExt.framebufferTexture2DMultisampleEXT( _gl.FRAMEBUFFER, attachment, textureTarget, properties.get( texture ).__webglTexture, 0, getRenderTargetSamples( renderTarget ) ); + + } else { + + _gl.framebufferTexture2D( _gl.FRAMEBUFFER, attachment, textureTarget, properties.get( texture ).__webglTexture, 0 ); + + } } @@ -1358,7 +1418,59 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, _gl.bindRenderbuffer( _gl.RENDERBUFFER, renderbuffer ); - if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) { + if ( renderTarget.isWebGLMultiviewRenderTarget === true ) { + + const useMultisample = useMultisampledRTT( renderTarget ); + const numViews = renderTarget.numViews; + + const depthTexture = renderTarget.depthTexture; + let glInternalFormat = _gl.DEPTH_COMPONENT24; + let glDepthAttachment = _gl.DEPTH_ATTACHMENT; + + if ( depthTexture && depthTexture.isDepthTexture ) { + + if ( depthTexture.type === FloatType ) { + + glInternalFormat = _gl.DEPTH_COMPONENT32F; + + } else if ( depthTexture.type === UnsignedInt248Type ) { + + glInternalFormat = _gl.DEPTH24_STENCIL8; + glDepthAttachment = _gl.DEPTH_STENCIL_ATTACHMENT; + + } + + // we're defaulting to _gl.DEPTH_COMPONENT24 so don't assign here + // or else DeepScan will complain + + // else if ( depthTexture.type === UnsignedIntType ) { + + // glInternalFormat = _gl.DEPTH_COMPONENT24; + + // } + + } + + let depthStencilTexture = properties.get( renderTarget.depthTexture ).__webglTexture; + if ( depthStencilTexture === undefined ) { + + depthStencilTexture = _gl.createTexture(); + _gl.bindTexture( _gl.TEXTURE_2D_ARRAY, depthStencilTexture ); + _gl.texStorage3D( _gl.TEXTURE_2D_ARRAY, 1, glInternalFormat, renderTarget.width, renderTarget.height, numViews ); + + } + + if ( useMultisample ) { + + multiviewExt.framebufferTextureMultisampleMultiviewOVR( _gl.FRAMEBUFFER, glDepthAttachment, depthStencilTexture, 0, getRenderTargetSamples( renderTarget ), 0, numViews ); + + } else { + + multiviewExt.framebufferTextureMultiviewOVR( _gl.FRAMEBUFFER, glDepthAttachment, depthStencilTexture, 0, 0, numViews ); + + } + + } else if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) { let glInternalFormat = _gl.DEPTH_COMPONENT16; @@ -1481,38 +1593,85 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, } - setTexture2D( renderTarget.depthTexture, 0 ); + if ( renderTarget.depthTexture.image.depth != 1 ) { + + this.setTexture2DArray( renderTarget.depthTexture, 0 ); + + } else { + + this.setTexture2D( renderTarget.depthTexture, 0 ); + + } const webglDepthTexture = properties.get( renderTarget.depthTexture ).__webglTexture; const samples = getRenderTargetSamples( renderTarget ); - if ( renderTarget.depthTexture.format === DepthFormat ) { + if ( renderTarget.isWebGLMultiviewRenderTarget === true ) { - if ( useMultisampledRTT( renderTarget ) ) { + const useMultisample = useMultisampledRTT( renderTarget ); + const numViews = renderTarget.numViews; - multisampledRTTExt.framebufferTexture2DMultisampleEXT( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0, samples ); + if ( renderTarget.depthTexture.format === DepthFormat ) { - } else { + if ( useMultisample ) { - _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0 ); + multiviewExt.framebufferTextureMultisampleMultiviewOVR( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, webglDepthTexture, 0, samples, 0, numViews ); - } + } else { + + multiviewExt.framebufferTextureMultiviewOVR( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, webglDepthTexture, 0, 0, numViews ); - } else if ( renderTarget.depthTexture.format === DepthStencilFormat ) { + } + + } else if ( renderTarget.depthTexture.format === DepthStencilFormat ) { + + if ( useMultisample ) { + + multiviewExt.framebufferTextureMultisampleMultiviewOVR( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, webglDepthTexture, 0, samples, 0, numViews ); + + } else { - if ( useMultisampledRTT( renderTarget ) ) { + multiviewExt.framebufferTextureMultiviewOVR( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, webglDepthTexture, 0, 0, numViews ); - multisampledRTTExt.framebufferTexture2DMultisampleEXT( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0, samples ); + } } else { - _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0 ); + throw new Error( 'Unknown depthTexture format' ); } } else { - throw new Error( 'Unknown depthTexture format' ); + if ( renderTarget.depthTexture.format === DepthFormat ) { + + if ( useMultisampledRTT( renderTarget ) ) { + + multisampledRTTExt.framebufferTexture2DMultisampleEXT( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0, samples ); + + } else { + + _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0 ); + + } + + } else if ( renderTarget.depthTexture.format === DepthStencilFormat ) { + + if ( useMultisampledRTT( renderTarget ) ) { + + multisampledRTTExt.framebufferTexture2DMultisampleEXT( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0, samples ); + + } else { + + _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0 ); + + } + + } else { + + throw new Error( 'Unknown depthTexture format' ); + + } } @@ -1528,7 +1687,7 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, if ( isCube ) throw new Error( 'target.depthTexture not supported in Cube render targets' ); - setupDepthTexture( renderTargetProperties.__webglFramebuffer, renderTarget ); + this.setupDepthTexture( renderTargetProperties.__webglFramebuffer, renderTarget ); } else { @@ -1565,13 +1724,13 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, if ( colorTexture !== undefined ) { - setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, renderTarget.texture, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_2D ); + this.setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, renderTarget.texture, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_2D ); } if ( depthTexture !== undefined ) { - setupDepthRenderbuffer( renderTarget ); + this.setupDepthRenderbuffer( renderTarget ); } @@ -1751,6 +1910,12 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, } + if ( renderTarget.isWebGLMultiviewRenderTarget === true ) { + + glTextureType = _gl.TEXTURE_2D_ARRAY; + + } + state.bindTexture( glTextureType, textureProperties.__webglTexture ); setTextureParameters( glTextureType, texture, supportsMips ); setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, texture, _gl.COLOR_ATTACHMENT0, glTextureType ); @@ -1767,9 +1932,9 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, // Setup depth and stencil buffers - if ( renderTarget.depthBuffer ) { + if ( renderTarget.depthBuffer || renderTarget.isWebGLMultiviewRenderTarget === true ) { - setupDepthRenderbuffer( renderTarget ); + this.setupDepthRenderbuffer( renderTarget ); } @@ -2005,12 +2170,15 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, this.setTexture3D = setTexture3D; this.setTextureCube = setTextureCube; this.rebindTextures = rebindTextures; + this.uploadTexture = uploadTexture; this.setupRenderTarget = setupRenderTarget; this.updateRenderTargetMipmap = updateRenderTargetMipmap; this.updateMultisampleRenderTarget = updateMultisampleRenderTarget; + this.setupDepthTexture = setupDepthTexture; this.setupDepthRenderbuffer = setupDepthRenderbuffer; this.setupFrameBufferTexture = setupFrameBufferTexture; this.useMultisampledRTT = useMultisampledRTT; + this.runDeferredUploads = runDeferredUploads; } diff --git a/src/renderers/webxr/WebXRManager.js b/src/renderers/webxr/WebXRManager.js index c4adaa7e437a72..bae9a6b78ace70 100644 --- a/src/renderers/webxr/WebXRManager.js +++ b/src/renderers/webxr/WebXRManager.js @@ -6,13 +6,14 @@ import { Vector4 } from '../../math/Vector4.js'; import { RAD2DEG } from '../../math/MathUtils.js'; import { WebGLAnimation } from '../webgl/WebGLAnimation.js'; import { WebGLRenderTarget } from '../WebGLRenderTarget.js'; +import { WebGLMultiviewRenderTarget } from '../WebGLMultiviewRenderTarget.js'; import { WebXRController } from './WebXRController.js'; import { DepthTexture } from '../../textures/DepthTexture.js'; import { DepthFormat, DepthStencilFormat, RGBAFormat, UnsignedByteType, UnsignedIntType, UnsignedInt248Type } from '../../constants.js'; class WebXRManager extends EventDispatcher { - constructor( renderer, gl ) { + constructor( renderer, gl, extensions, useMultiview ) { super(); @@ -68,6 +69,7 @@ class WebXRManager extends EventDispatcher { this.enabled = false; this.isPresenting = false; + this.isMultiview = false; this.getController = function ( index ) { @@ -305,29 +307,50 @@ class WebXRManager extends EventDispatcher { } + scope.isMultiview = useMultiview && extensions.has( 'OCULUS_multiview' ); + const projectionlayerInit = { colorFormat: gl.RGBA8, depthFormat: glDepthFormat, scaleFactor: framebufferScaleFactor }; + if ( scope.isMultiview ) { + + projectionlayerInit.textureType = 'texture-array'; + + } + glBinding = new XRWebGLBinding( session, gl ); glProjLayer = glBinding.createProjectionLayer( projectionlayerInit ); session.updateRenderState( { layers: [ glProjLayer ] } ); + const rtOptions = { + format: RGBAFormat, + type: UnsignedByteType, + depthTexture: new DepthTexture( glProjLayer.textureWidth, glProjLayer.textureHeight, depthType, undefined, undefined, undefined, undefined, undefined, undefined, depthFormat ), + stencilBuffer: attributes.stencil, + encoding: renderer.outputEncoding, + samples: attributes.antialias ? 4 : 0 + }; - newRenderTarget = new WebGLRenderTarget( - glProjLayer.textureWidth, - glProjLayer.textureHeight, - { - format: RGBAFormat, - type: UnsignedByteType, - depthTexture: new DepthTexture( glProjLayer.textureWidth, glProjLayer.textureHeight, depthType, undefined, undefined, undefined, undefined, undefined, undefined, depthFormat ), - stencilBuffer: attributes.stencil, - colorSpace: renderer.outputColorSpace, - samples: attributes.antialias ? 4 : 0 - } ); + if ( scope.isMultiview ) { + + const extension = extensions.get( 'OCULUS_multiview' ); + + this.maxNumViews = gl.getParameter( extension.MAX_VIEWS_OVR ); + + newRenderTarget = new WebGLMultiviewRenderTarget( glProjLayer.textureWidth, glProjLayer.textureHeight, 2, rtOptions ); + + } else { + + newRenderTarget = new WebGLRenderTarget( + glProjLayer.textureWidth, + glProjLayer.textureHeight, + rtOptions ); + + } const renderTargetProperties = renderer.properties.get( newRenderTarget ); renderTargetProperties.__ignoreDepthValues = glProjLayer.ignoreDepthValues;