diff --git a/src/renderers/WebGLRenderer.js b/src/renderers/WebGLRenderer.js index 2367ba7e1e9a99..85fd91f2ba5b64 100644 --- a/src/renderers/WebGLRenderer.js +++ b/src/renderers/WebGLRenderer.js @@ -53,1134 +53,1140 @@ function createCanvasElement() { } -function WebGLRenderer( parameters = {} ) { +class WebGLRenderer { - this.isWebGLRenderer = true; + constructor( parameters = {} ) { - const _canvas = parameters.canvas !== undefined ? parameters.canvas : createCanvasElement(), - _context = parameters.context !== undefined ? parameters.context : null, + const { + canvas = createCanvasElement(), + context = null, + depth = true, + stencil = true, + alpha = false, + antialias = false, + premultipliedAlpha = true, + preserveDrawingBuffer = false, + powerPreference = 'default', + failIfMajorPerformanceCaveat = false, + } = parameters; - _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; + this.isWebGLRenderer = true; - let _alpha; + let _alpha; - if ( _context !== null ) { + if ( context !== null ) { - _alpha = _context.getContextAttributes().alpha; + _alpha = context.getContextAttributes().alpha; - } else { + } else { - _alpha = parameters.alpha !== undefined ? parameters.alpha : false; + _alpha = alpha; - } + } - let currentRenderList = null; - let currentRenderState = null; + let currentRenderList = null; + let currentRenderState = null; - // render() can be called from within a callback triggered by another render. - // We track this so that the nested render call gets its list and state isolated from the parent render call. + // render() can be called from within a callback triggered by another render. + // We track this so that the nested render call gets its list and state isolated from the parent render call. - const renderListStack = []; - const renderStateStack = []; + const renderListStack = []; + const renderStateStack = []; - // public properties + // public properties - this.domElement = _canvas; + this.domElement = canvas; - // Debug configuration container - this.debug = { + // Debug configuration container + this.debug = { - /** - * Enables error checking and reporting when shader programs are being compiled - * @type {boolean} - */ - checkShaderErrors: true - }; + /** + * Enables error checking and reporting when shader programs are being compiled + * @type {boolean} + */ + checkShaderErrors: true + }; - // clearing + // clearing - this.autoClear = true; - this.autoClearColor = true; - this.autoClearDepth = true; - this.autoClearStencil = true; + this.autoClear = true; + this.autoClearColor = true; + this.autoClearDepth = true; + this.autoClearStencil = true; - // scene graph + // scene graph - this.sortObjects = true; + this.sortObjects = true; - // user-defined clipping + // user-defined clipping - this.clippingPlanes = []; - this.localClippingEnabled = false; + this.clippingPlanes = []; + this.localClippingEnabled = false; - // physically based shading + // physically based shading - this.outputEncoding = LinearEncoding; + this.outputEncoding = LinearEncoding; - // physical lights + // physical lights - this.useLegacyLights = true; + this.useLegacyLights = true; - // tone mapping + // tone mapping - this.toneMapping = NoToneMapping; - this.toneMappingExposure = 1.0; + this.toneMapping = NoToneMapping; + this.toneMappingExposure = 1.0; - // internal properties + // internal properties - const _this = this; + const _this = this; - let _isContextLost = false; + let _isContextLost = false; - // internal state cache + // internal state cache - let _currentActiveCubeFace = 0; - let _currentActiveMipmapLevel = 0; - let _currentRenderTarget = null; - let _currentMaterialId = - 1; + let _currentActiveCubeFace = 0; + let _currentActiveMipmapLevel = 0; + let _currentRenderTarget = null; + let _currentMaterialId = - 1; - let _currentCamera = null; + let _currentCamera = null; - const _currentViewport = new Vector4(); - const _currentScissor = new Vector4(); - let _currentScissorTest = null; + const _currentViewport = new Vector4(); + const _currentScissor = new Vector4(); + let _currentScissorTest = null; - // + // - let _width = _canvas.width; - let _height = _canvas.height; + let _width = canvas.width; + let _height = canvas.height; - let _pixelRatio = 1; - let _opaqueSort = null; - let _transparentSort = null; + let _pixelRatio = 1; + let _opaqueSort = null; + let _transparentSort = null; - const _viewport = new Vector4( 0, 0, _width, _height ); - const _scissor = new Vector4( 0, 0, _width, _height ); - let _scissorTest = false; + const _viewport = new Vector4( 0, 0, _width, _height ); + const _scissor = new Vector4( 0, 0, _width, _height ); + let _scissorTest = false; - // frustum + // frustum - const _frustum = new Frustum(); + const _frustum = new Frustum(); - // clipping + // clipping - let _clippingEnabled = false; - let _localClippingEnabled = false; + let _clippingEnabled = false; + let _localClippingEnabled = false; - // transmission + // transmission - let _transmissionRenderTarget = null; + let _transmissionRenderTarget = null; - // camera matrices cache + // camera matrices cache - const _projScreenMatrix = new Matrix4(); + const _projScreenMatrix = new Matrix4(); - const _vector3 = new Vector3(); + const _vector3 = new Vector3(); - const _emptyScene = { background: null, fog: null, environment: null, overrideMaterial: null, isScene: true }; + const _emptyScene = { background: null, fog: null, environment: null, overrideMaterial: null, isScene: true }; - function getTargetPixelRatio() { + function getTargetPixelRatio() { - return _currentRenderTarget === null ? _pixelRatio : 1; + return _currentRenderTarget === null ? _pixelRatio : 1; - } + } - // initialize + // initialize - let _gl = _context; + let _gl = context; - function getContext( contextNames, contextAttributes ) { + function getContext( contextNames, contextAttributes ) { - for ( let i = 0; i < contextNames.length; i ++ ) { + for ( let i = 0; i < contextNames.length; i ++ ) { - const contextName = contextNames[ i ]; - const context = _canvas.getContext( contextName, contextAttributes ); - if ( context !== null ) return context; + const contextName = contextNames[ i ]; + const context = canvas.getContext( contextName, contextAttributes ); + if ( context !== null ) return context; - } + } - return null; + return null; - } + } - try { - - const contextAttributes = { - alpha: true, - depth: _depth, - stencil: _stencil, - antialias: _antialias, - premultipliedAlpha: _premultipliedAlpha, - preserveDrawingBuffer: _preserveDrawingBuffer, - powerPreference: _powerPreference, - failIfMajorPerformanceCaveat: _failIfMajorPerformanceCaveat - }; + try { + + const contextAttributes = { + alpha: true, + depth, + stencil, + antialias, + premultipliedAlpha, + preserveDrawingBuffer, + powerPreference, + failIfMajorPerformanceCaveat, + }; - // OffscreenCanvas does not have setAttribute, see #22811 - if ( 'setAttribute' in _canvas ) _canvas.setAttribute( 'data-engine', `three.js r${REVISION}` ); + // OffscreenCanvas does not have setAttribute, see #22811 + if ( 'setAttribute' in canvas ) canvas.setAttribute( 'data-engine', `three.js r${REVISION}` ); - // event listeners must be registered before WebGL context is created, see #12753 - _canvas.addEventListener( 'webglcontextlost', onContextLost, false ); - _canvas.addEventListener( 'webglcontextrestored', onContextRestore, false ); - _canvas.addEventListener( 'webglcontextcreationerror', onContextCreationError, false ); + // event listeners must be registered before WebGL context is created, see #12753 + canvas.addEventListener( 'webglcontextlost', onContextLost, false ); + canvas.addEventListener( 'webglcontextrestored', onContextRestore, false ); + canvas.addEventListener( 'webglcontextcreationerror', onContextCreationError, false ); - if ( _gl === null ) { + if ( _gl === null ) { - const contextNames = [ 'webgl2', 'webgl', 'experimental-webgl' ]; + const contextNames = [ 'webgl2', 'webgl', 'experimental-webgl' ]; - if ( _this.isWebGL1Renderer === true ) { + if ( _this.isWebGL1Renderer === true ) { - contextNames.shift(); + contextNames.shift(); - } + } - _gl = getContext( contextNames, contextAttributes ); + _gl = getContext( contextNames, contextAttributes ); - if ( _gl === null ) { + if ( _gl === null ) { - if ( getContext( contextNames ) ) { + if ( getContext( contextNames ) ) { - throw new Error( 'Error creating WebGL context with your selected attributes.' ); + throw new Error( 'Error creating WebGL context with your selected attributes.' ); - } else { + } else { + + throw new Error( 'Error creating WebGL context.' ); - throw new Error( 'Error creating WebGL context.' ); + } } } - } + // Some experimental-webgl implementations do not have getShaderPrecisionFormat - // Some experimental-webgl implementations do not have getShaderPrecisionFormat + if ( _gl.getShaderPrecisionFormat === undefined ) { - if ( _gl.getShaderPrecisionFormat === undefined ) { + _gl.getShaderPrecisionFormat = function () { - _gl.getShaderPrecisionFormat = function () { + return { 'rangeMin': 1, 'rangeMax': 1, 'precision': 1 }; - return { 'rangeMin': 1, 'rangeMax': 1, 'precision': 1 }; + }; - }; + } - } + } catch ( error ) { - } catch ( error ) { + console.error( 'THREE.WebGLRenderer: ' + error.message ); + throw error; - console.error( 'THREE.WebGLRenderer: ' + error.message ); - throw error; + } - } + let extensions, capabilities, state, info; + let properties, textures, cubemaps, cubeuvmaps, attributes, geometries, objects; + let programCache, materials, renderLists, renderStates, clipping, shadowMap; - let extensions, capabilities, state, info; - let properties, textures, cubemaps, cubeuvmaps, attributes, geometries, objects; - let programCache, materials, renderLists, renderStates, clipping, shadowMap; + let background, morphtargets, bufferRenderer, indexedBufferRenderer; - let background, morphtargets, bufferRenderer, indexedBufferRenderer; + let utils, bindingStates, uniformsGroups; - let utils, bindingStates, uniformsGroups; + function initGLContext() { - function initGLContext() { + extensions = new WebGLExtensions( _gl ); - extensions = new WebGLExtensions( _gl ); + capabilities = new WebGLCapabilities( _gl, extensions, parameters ); - capabilities = new WebGLCapabilities( _gl, extensions, parameters ); + extensions.init( capabilities ); - extensions.init( capabilities ); + utils = new WebGLUtils( _gl, extensions, capabilities ); - utils = new WebGLUtils( _gl, extensions, capabilities ); + state = new WebGLState( _gl, extensions, capabilities ); - state = new WebGLState( _gl, extensions, capabilities ); + info = new WebGLInfo( _gl ); + properties = new WebGLProperties(); + textures = new WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info ); + cubemaps = new WebGLCubeMaps( _this ); + cubeuvmaps = new WebGLCubeUVMaps( _this ); + attributes = new WebGLAttributes( _gl, capabilities ); + bindingStates = new WebGLBindingStates( _gl, extensions, attributes, capabilities ); + geometries = new WebGLGeometries( _gl, attributes, info, bindingStates ); + objects = new WebGLObjects( _gl, geometries, attributes, info ); + morphtargets = new WebGLMorphtargets( _gl, capabilities, textures ); + clipping = new WebGLClipping( properties ); + programCache = new WebGLPrograms( _this, cubemaps, cubeuvmaps, extensions, capabilities, bindingStates, clipping ); + materials = new WebGLMaterials( _this, properties ); + renderLists = new WebGLRenderLists(); + renderStates = new WebGLRenderStates( extensions, capabilities ); + background = new WebGLBackground( _this, cubemaps, cubeuvmaps, state, objects, _alpha, premultipliedAlpha ); + shadowMap = new WebGLShadowMap( _this, objects, capabilities ); + uniformsGroups = new WebGLUniformsGroups( _gl, info, capabilities, state ); - info = new WebGLInfo( _gl ); - properties = new WebGLProperties(); - textures = new WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info ); - cubemaps = new WebGLCubeMaps( _this ); - cubeuvmaps = new WebGLCubeUVMaps( _this ); - attributes = new WebGLAttributes( _gl, capabilities ); - bindingStates = new WebGLBindingStates( _gl, extensions, attributes, capabilities ); - geometries = new WebGLGeometries( _gl, attributes, info, bindingStates ); - objects = new WebGLObjects( _gl, geometries, attributes, info ); - morphtargets = new WebGLMorphtargets( _gl, capabilities, textures ); - clipping = new WebGLClipping( properties ); - programCache = new WebGLPrograms( _this, cubemaps, cubeuvmaps, extensions, capabilities, bindingStates, clipping ); - materials = new WebGLMaterials( _this, properties ); - renderLists = new WebGLRenderLists(); - renderStates = new WebGLRenderStates( extensions, capabilities ); - background = new WebGLBackground( _this, cubemaps, cubeuvmaps, state, objects, _alpha, _premultipliedAlpha ); - shadowMap = new WebGLShadowMap( _this, objects, capabilities ); - uniformsGroups = new WebGLUniformsGroups( _gl, info, capabilities, state ); + bufferRenderer = new WebGLBufferRenderer( _gl, extensions, info, capabilities ); + indexedBufferRenderer = new WebGLIndexedBufferRenderer( _gl, extensions, info, capabilities ); - bufferRenderer = new WebGLBufferRenderer( _gl, extensions, info, capabilities ); - indexedBufferRenderer = new WebGLIndexedBufferRenderer( _gl, extensions, info, capabilities ); + info.programs = programCache.programs; - info.programs = programCache.programs; + _this.capabilities = capabilities; + _this.extensions = extensions; + _this.properties = properties; + _this.renderLists = renderLists; + _this.shadowMap = shadowMap; + _this.state = state; + _this.info = info; - _this.capabilities = capabilities; - _this.extensions = extensions; - _this.properties = properties; - _this.renderLists = renderLists; - _this.shadowMap = shadowMap; - _this.state = state; - _this.info = info; + } - } + initGLContext(); - initGLContext(); + // xr - // xr + const xr = new WebXRManager( _this, _gl ); - const xr = new WebXRManager( _this, _gl ); + this.xr = xr; - this.xr = xr; + // API - // API + this.getContext = function () { - this.getContext = function () { + return _gl; - return _gl; + }; - }; + this.getContextAttributes = function () { - this.getContextAttributes = function () { + return _gl.getContextAttributes(); - return _gl.getContextAttributes(); + }; - }; + this.forceContextLoss = function () { - this.forceContextLoss = function () { + const extension = extensions.get( 'WEBGL_lose_context' ); + if ( extension ) extension.loseContext(); - const extension = extensions.get( 'WEBGL_lose_context' ); - if ( extension ) extension.loseContext(); + }; - }; + this.forceContextRestore = function () { - this.forceContextRestore = function () { + const extension = extensions.get( 'WEBGL_lose_context' ); + if ( extension ) extension.restoreContext(); - const extension = extensions.get( 'WEBGL_lose_context' ); - if ( extension ) extension.restoreContext(); + }; - }; + this.getPixelRatio = function () { - this.getPixelRatio = function () { + return _pixelRatio; - return _pixelRatio; + }; - }; + this.setPixelRatio = function ( value ) { - this.setPixelRatio = function ( value ) { + if ( value === undefined ) return; - if ( value === undefined ) return; + _pixelRatio = value; - _pixelRatio = value; + this.setSize( _width, _height, false ); - this.setSize( _width, _height, false ); + }; - }; + this.getSize = function ( target ) { - this.getSize = function ( target ) { + return target.set( _width, _height ); - return target.set( _width, _height ); + }; - }; + this.setSize = function ( width, height, updateStyle = true ) { - this.setSize = function ( width, height, updateStyle = true ) { + if ( xr.isPresenting ) { - if ( xr.isPresenting ) { + console.warn( 'THREE.WebGLRenderer: Can\'t change size while VR device is presenting.' ); + return; - console.warn( 'THREE.WebGLRenderer: Can\'t change size while VR device is presenting.' ); - return; + } - } + _width = width; + _height = height; - _width = width; - _height = height; + canvas.width = Math.floor( width * _pixelRatio ); + canvas.height = Math.floor( height * _pixelRatio ); - _canvas.width = Math.floor( width * _pixelRatio ); - _canvas.height = Math.floor( height * _pixelRatio ); + if ( updateStyle === true ) { - if ( updateStyle === true ) { + canvas.style.width = width + 'px'; + canvas.style.height = height + 'px'; - _canvas.style.width = width + 'px'; - _canvas.style.height = height + 'px'; + } - } + this.setViewport( 0, 0, width, height ); - this.setViewport( 0, 0, width, height ); + }; - }; + this.getDrawingBufferSize = function ( target ) { - this.getDrawingBufferSize = function ( target ) { + return target.set( _width * _pixelRatio, _height * _pixelRatio ).floor(); - return target.set( _width * _pixelRatio, _height * _pixelRatio ).floor(); + }; - }; + this.setDrawingBufferSize = function ( width, height, pixelRatio ) { - this.setDrawingBufferSize = function ( width, height, pixelRatio ) { + _width = width; + _height = height; - _width = width; - _height = height; + _pixelRatio = pixelRatio; - _pixelRatio = pixelRatio; + canvas.width = Math.floor( width * pixelRatio ); + canvas.height = Math.floor( height * pixelRatio ); - _canvas.width = Math.floor( width * pixelRatio ); - _canvas.height = Math.floor( height * pixelRatio ); + this.setViewport( 0, 0, width, height ); - this.setViewport( 0, 0, width, height ); + }; - }; + this.getCurrentViewport = function ( target ) { - this.getCurrentViewport = function ( target ) { + return target.copy( _currentViewport ); - return target.copy( _currentViewport ); + }; - }; + this.getViewport = function ( target ) { - this.getViewport = function ( target ) { + return target.copy( _viewport ); - return target.copy( _viewport ); + }; - }; + this.setViewport = function ( x, y, width, height ) { - this.setViewport = function ( x, y, width, height ) { + if ( x.isVector4 ) { - if ( x.isVector4 ) { + _viewport.set( x.x, x.y, x.z, x.w ); - _viewport.set( x.x, x.y, x.z, x.w ); + } else { - } else { + _viewport.set( x, y, width, height ); - _viewport.set( x, y, width, height ); + } - } + state.viewport( _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ).floor() ); - state.viewport( _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ).floor() ); + }; - }; + this.getScissor = function ( target ) { - this.getScissor = function ( target ) { + return target.copy( _scissor ); - return target.copy( _scissor ); + }; - }; + this.setScissor = function ( x, y, width, height ) { - this.setScissor = function ( x, y, width, height ) { + if ( x.isVector4 ) { - if ( x.isVector4 ) { + _scissor.set( x.x, x.y, x.z, x.w ); - _scissor.set( x.x, x.y, x.z, x.w ); + } else { - } else { + _scissor.set( x, y, width, height ); - _scissor.set( x, y, width, height ); + } - } + state.scissor( _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ).floor() ); - state.scissor( _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ).floor() ); + }; - }; + this.getScissorTest = function () { - this.getScissorTest = function () { + return _scissorTest; - return _scissorTest; + }; - }; + this.setScissorTest = function ( boolean ) { - this.setScissorTest = function ( boolean ) { + state.setScissorTest( _scissorTest = boolean ); - state.setScissorTest( _scissorTest = boolean ); + }; - }; + this.setOpaqueSort = function ( method ) { - this.setOpaqueSort = function ( method ) { + _opaqueSort = method; - _opaqueSort = method; + }; - }; + this.setTransparentSort = function ( method ) { - this.setTransparentSort = function ( method ) { + _transparentSort = method; - _transparentSort = method; + }; - }; + // Clearing - // Clearing + this.getClearColor = function ( target ) { - this.getClearColor = function ( target ) { + return target.copy( background.getClearColor() ); - return target.copy( background.getClearColor() ); + }; - }; + this.setClearColor = function () { - this.setClearColor = function () { + background.setClearColor.apply( background, arguments ); - background.setClearColor.apply( background, arguments ); + }; - }; + this.getClearAlpha = function () { - this.getClearAlpha = function () { + return background.getClearAlpha(); - return background.getClearAlpha(); + }; - }; + this.setClearAlpha = function () { - this.setClearAlpha = function () { + background.setClearAlpha.apply( background, arguments ); - background.setClearAlpha.apply( background, arguments ); + }; - }; + this.clear = function ( color = true, depth = true, stencil = true ) { - this.clear = function ( color = true, depth = true, stencil = true ) { + let bits = 0; - let bits = 0; + if ( color ) bits |= _gl.COLOR_BUFFER_BIT; + if ( depth ) bits |= _gl.DEPTH_BUFFER_BIT; + if ( stencil ) bits |= _gl.STENCIL_BUFFER_BIT; - if ( color ) bits |= _gl.COLOR_BUFFER_BIT; - if ( depth ) bits |= _gl.DEPTH_BUFFER_BIT; - if ( stencil ) bits |= _gl.STENCIL_BUFFER_BIT; + _gl.clear( bits ); - _gl.clear( bits ); + }; - }; + this.clearColor = function () { - this.clearColor = function () { + this.clear( true, false, false ); - this.clear( true, false, false ); + }; - }; + this.clearDepth = function () { - this.clearDepth = function () { + this.clear( false, true, false ); - this.clear( false, true, false ); + }; - }; + this.clearStencil = function () { - this.clearStencil = function () { + this.clear( false, false, true ); - this.clear( false, false, true ); + }; - }; + // - // + this.dispose = function () { - this.dispose = function () { + canvas.removeEventListener( 'webglcontextlost', onContextLost, false ); + canvas.removeEventListener( 'webglcontextrestored', onContextRestore, false ); + canvas.removeEventListener( 'webglcontextcreationerror', onContextCreationError, false ); - _canvas.removeEventListener( 'webglcontextlost', onContextLost, false ); - _canvas.removeEventListener( 'webglcontextrestored', onContextRestore, false ); - _canvas.removeEventListener( 'webglcontextcreationerror', onContextCreationError, false ); + renderLists.dispose(); + renderStates.dispose(); + properties.dispose(); + cubemaps.dispose(); + cubeuvmaps.dispose(); + objects.dispose(); + bindingStates.dispose(); + uniformsGroups.dispose(); + programCache.dispose(); - renderLists.dispose(); - renderStates.dispose(); - properties.dispose(); - cubemaps.dispose(); - cubeuvmaps.dispose(); - objects.dispose(); - bindingStates.dispose(); - uniformsGroups.dispose(); - programCache.dispose(); + xr.dispose(); - xr.dispose(); + xr.removeEventListener( 'sessionstart', onXRSessionStart ); + xr.removeEventListener( 'sessionend', onXRSessionEnd ); - xr.removeEventListener( 'sessionstart', onXRSessionStart ); - xr.removeEventListener( 'sessionend', onXRSessionEnd ); + if ( _transmissionRenderTarget ) { - if ( _transmissionRenderTarget ) { + _transmissionRenderTarget.dispose(); + _transmissionRenderTarget = null; - _transmissionRenderTarget.dispose(); - _transmissionRenderTarget = null; + } - } + animation.stop(); - animation.stop(); + }; - }; + // Events - // Events + function onContextLost( event ) { - function onContextLost( event ) { + event.preventDefault(); - event.preventDefault(); + console.log( 'THREE.WebGLRenderer: Context Lost.' ); - console.log( 'THREE.WebGLRenderer: Context Lost.' ); + _isContextLost = true; - _isContextLost = true; + } - } + function onContextRestore( /* event */ ) { - function onContextRestore( /* event */ ) { + console.log( 'THREE.WebGLRenderer: Context Restored.' ); - console.log( 'THREE.WebGLRenderer: Context Restored.' ); + _isContextLost = false; - _isContextLost = false; + const infoAutoReset = info.autoReset; + const shadowMapEnabled = shadowMap.enabled; + const shadowMapAutoUpdate = shadowMap.autoUpdate; + const shadowMapNeedsUpdate = shadowMap.needsUpdate; + const shadowMapType = shadowMap.type; - const infoAutoReset = info.autoReset; - const shadowMapEnabled = shadowMap.enabled; - const shadowMapAutoUpdate = shadowMap.autoUpdate; - const shadowMapNeedsUpdate = shadowMap.needsUpdate; - const shadowMapType = shadowMap.type; + initGLContext(); - initGLContext(); + info.autoReset = infoAutoReset; + shadowMap.enabled = shadowMapEnabled; + shadowMap.autoUpdate = shadowMapAutoUpdate; + shadowMap.needsUpdate = shadowMapNeedsUpdate; + shadowMap.type = shadowMapType; - info.autoReset = infoAutoReset; - shadowMap.enabled = shadowMapEnabled; - shadowMap.autoUpdate = shadowMapAutoUpdate; - shadowMap.needsUpdate = shadowMapNeedsUpdate; - shadowMap.type = shadowMapType; + } - } + function onContextCreationError( event ) { - function onContextCreationError( event ) { + console.error( 'THREE.WebGLRenderer: A WebGL context could not be created. Reason: ', event.statusMessage ); - console.error( 'THREE.WebGLRenderer: A WebGL context could not be created. Reason: ', event.statusMessage ); + } - } + function onMaterialDispose( event ) { - function onMaterialDispose( event ) { + const material = event.target; - const material = event.target; + material.removeEventListener( 'dispose', onMaterialDispose ); - material.removeEventListener( 'dispose', onMaterialDispose ); + deallocateMaterial( material ); - deallocateMaterial( material ); + } - } + // Buffer deallocation - // Buffer deallocation + function deallocateMaterial( material ) { - function deallocateMaterial( material ) { + releaseMaterialProgramReferences( material ); - releaseMaterialProgramReferences( material ); + properties.remove( material ); - properties.remove( material ); + } - } + function releaseMaterialProgramReferences( material ) { - function releaseMaterialProgramReferences( material ) { + const programs = properties.get( material ).programs; - const programs = properties.get( material ).programs; + if ( programs !== undefined ) { - if ( programs !== undefined ) { + programs.forEach( function ( program ) { - programs.forEach( function ( program ) { + programCache.releaseProgram( program ); - programCache.releaseProgram( program ); + } ); - } ); + if ( material.isShaderMaterial ) { - if ( material.isShaderMaterial ) { + programCache.releaseShaderCache( material ); - programCache.releaseShaderCache( material ); + } } } - } + // Buffer rendering - // Buffer rendering + this.renderBufferDirect = function ( camera, scene, geometry, material, object, group ) { - this.renderBufferDirect = function ( camera, scene, geometry, material, object, group ) { + if ( scene === null ) scene = _emptyScene; // renderBufferDirect second parameter used to be fog (could be null) - if ( scene === null ) scene = _emptyScene; // renderBufferDirect second parameter used to be fog (could be null) + const frontFaceCW = ( object.isMesh && object.matrixWorld.determinant() < 0 ); - const frontFaceCW = ( object.isMesh && object.matrixWorld.determinant() < 0 ); + const program = setProgram( camera, scene, geometry, material, object ); - const program = setProgram( camera, scene, geometry, material, object ); + state.setMaterial( material, frontFaceCW ); - state.setMaterial( material, frontFaceCW ); + // - // + let index = geometry.index; + let rangeFactor = 1; - let index = geometry.index; - let rangeFactor = 1; + if ( material.wireframe === true ) { - if ( material.wireframe === true ) { + index = geometries.getWireframeAttribute( geometry ); + rangeFactor = 2; - index = geometries.getWireframeAttribute( geometry ); - rangeFactor = 2; + } - } + // - // + const drawRange = geometry.drawRange; + const position = geometry.attributes.position; - const drawRange = geometry.drawRange; - const position = geometry.attributes.position; + let drawStart = drawRange.start * rangeFactor; + let drawEnd = ( drawRange.start + drawRange.count ) * rangeFactor; - let drawStart = drawRange.start * rangeFactor; - let drawEnd = ( drawRange.start + drawRange.count ) * rangeFactor; + if ( group !== null ) { - if ( group !== null ) { + drawStart = Math.max( drawStart, group.start * rangeFactor ); + drawEnd = Math.min( drawEnd, ( group.start + group.count ) * rangeFactor ); - drawStart = Math.max( drawStart, group.start * rangeFactor ); - drawEnd = Math.min( drawEnd, ( group.start + group.count ) * rangeFactor ); + } - } + if ( index !== null ) { - if ( index !== null ) { + drawStart = Math.max( drawStart, 0 ); + drawEnd = Math.min( drawEnd, index.count ); - drawStart = Math.max( drawStart, 0 ); - drawEnd = Math.min( drawEnd, index.count ); + } else if ( position !== undefined && position !== null ) { - } else if ( position !== undefined && position !== null ) { + drawStart = Math.max( drawStart, 0 ); + drawEnd = Math.min( drawEnd, position.count ); - drawStart = Math.max( drawStart, 0 ); - drawEnd = Math.min( drawEnd, position.count ); + } - } + const drawCount = drawEnd - drawStart; - const drawCount = drawEnd - drawStart; + if ( drawCount < 0 || drawCount === Infinity ) return; - if ( drawCount < 0 || drawCount === Infinity ) return; + // - // + bindingStates.setup( object, material, program, geometry, index ); - bindingStates.setup( object, material, program, geometry, index ); + let attribute; + let renderer = bufferRenderer; - let attribute; - let renderer = bufferRenderer; + if ( index !== null ) { - if ( index !== null ) { + attribute = attributes.get( index ); - attribute = attributes.get( index ); + renderer = indexedBufferRenderer; + renderer.setIndex( attribute ); - renderer = indexedBufferRenderer; - renderer.setIndex( attribute ); + } - } + // - // + if ( object.isMesh ) { - if ( object.isMesh ) { + if ( material.wireframe === true ) { - if ( material.wireframe === true ) { + state.setLineWidth( material.wireframeLinewidth * getTargetPixelRatio() ); + renderer.setMode( _gl.LINES ); - state.setLineWidth( material.wireframeLinewidth * getTargetPixelRatio() ); - renderer.setMode( _gl.LINES ); + } else { - } else { + renderer.setMode( _gl.TRIANGLES ); - renderer.setMode( _gl.TRIANGLES ); + } - } + } else if ( object.isLine ) { - } else if ( object.isLine ) { + let lineWidth = material.linewidth; - let lineWidth = material.linewidth; + if ( lineWidth === undefined ) lineWidth = 1; // Not using Line*Material - if ( lineWidth === undefined ) lineWidth = 1; // Not using Line*Material + state.setLineWidth( lineWidth * getTargetPixelRatio() ); - state.setLineWidth( lineWidth * getTargetPixelRatio() ); + if ( object.isLineSegments ) { - if ( object.isLineSegments ) { + renderer.setMode( _gl.LINES ); - renderer.setMode( _gl.LINES ); + } else if ( object.isLineLoop ) { - } else if ( object.isLineLoop ) { + renderer.setMode( _gl.LINE_LOOP ); - renderer.setMode( _gl.LINE_LOOP ); + } else { - } else { + renderer.setMode( _gl.LINE_STRIP ); - renderer.setMode( _gl.LINE_STRIP ); + } - } + } else if ( object.isPoints ) { - } else if ( object.isPoints ) { + renderer.setMode( _gl.POINTS ); - renderer.setMode( _gl.POINTS ); + } else if ( object.isSprite ) { - } else if ( object.isSprite ) { + renderer.setMode( _gl.TRIANGLES ); - renderer.setMode( _gl.TRIANGLES ); + } - } + if ( object.isInstancedMesh ) { - if ( object.isInstancedMesh ) { + renderer.renderInstances( drawStart, drawCount, object.count ); - renderer.renderInstances( drawStart, drawCount, object.count ); + } else if ( geometry.isInstancedBufferGeometry ) { - } else if ( geometry.isInstancedBufferGeometry ) { + const maxInstanceCount = geometry._maxInstanceCount !== undefined ? geometry._maxInstanceCount : Infinity; + const instanceCount = Math.min( geometry.instanceCount, maxInstanceCount ); - const maxInstanceCount = geometry._maxInstanceCount !== undefined ? geometry._maxInstanceCount : Infinity; - const instanceCount = Math.min( geometry.instanceCount, maxInstanceCount ); + renderer.renderInstances( drawStart, drawCount, instanceCount ); - renderer.renderInstances( drawStart, drawCount, instanceCount ); + } else { - } else { + renderer.render( drawStart, drawCount ); - renderer.render( drawStart, drawCount ); + } - } + }; - }; + // Compile - // Compile + this.compile = function ( scene, camera ) { - this.compile = function ( scene, camera ) { + function prepare( material, scene, object ) { - function prepare( material, scene, object ) { + if ( material.transparent === true && material.side === DoubleSide && material.forceSinglePass === false ) { - if ( material.transparent === true && material.side === DoubleSide && material.forceSinglePass === false ) { + material.side = BackSide; + material.needsUpdate = true; + getProgram( material, scene, object ); - material.side = BackSide; - material.needsUpdate = true; - getProgram( material, scene, object ); + material.side = FrontSide; + material.needsUpdate = true; + getProgram( material, scene, object ); - material.side = FrontSide; - material.needsUpdate = true; - getProgram( material, scene, object ); + material.side = DoubleSide; - material.side = DoubleSide; + } else { - } else { + getProgram( material, scene, object ); - getProgram( material, scene, object ); + } } - } + currentRenderState = renderStates.get( scene ); + currentRenderState.init(); - currentRenderState = renderStates.get( scene ); - currentRenderState.init(); + renderStateStack.push( currentRenderState ); - renderStateStack.push( currentRenderState ); + scene.traverseVisible( function ( object ) { - scene.traverseVisible( function ( object ) { + if ( object.isLight && object.layers.test( camera.layers ) ) { - if ( object.isLight && object.layers.test( camera.layers ) ) { + currentRenderState.pushLight( object ); - currentRenderState.pushLight( object ); + if ( object.castShadow ) { - if ( object.castShadow ) { + currentRenderState.pushShadow( object ); - currentRenderState.pushShadow( object ); + } } - } + } ); - } ); + currentRenderState.setupLights( _this.useLegacyLights ); - currentRenderState.setupLights( _this.useLegacyLights ); + scene.traverse( function ( object ) { - scene.traverse( function ( object ) { + const material = object.material; - const material = object.material; + if ( material ) { - if ( material ) { + if ( Array.isArray( material ) ) { - if ( Array.isArray( material ) ) { + for ( let i = 0; i < material.length; i ++ ) { - for ( let i = 0; i < material.length; i ++ ) { + const material2 = material[ i ]; - const material2 = material[ i ]; + prepare( material2, scene, object ); - prepare( material2, scene, object ); + } - } + } else { - } else { + prepare( material, scene, object ); - prepare( material, scene, object ); + } } - } + } ); - } ); + renderStateStack.pop(); + currentRenderState = null; - renderStateStack.pop(); - currentRenderState = null; + }; - }; + // Animation Loop - // Animation Loop + let onAnimationFrameCallback = null; - let onAnimationFrameCallback = null; + function onAnimationFrame( time ) { - function onAnimationFrame( time ) { + if ( onAnimationFrameCallback ) onAnimationFrameCallback( time ); - if ( onAnimationFrameCallback ) onAnimationFrameCallback( time ); + } - } + function onXRSessionStart() { - function onXRSessionStart() { + animation.stop(); - animation.stop(); + } - } + function onXRSessionEnd() { - function onXRSessionEnd() { + animation.start(); - animation.start(); + } - } + const animation = new WebGLAnimation(); + animation.setAnimationLoop( onAnimationFrame ); - const animation = new WebGLAnimation(); - animation.setAnimationLoop( onAnimationFrame ); + if ( typeof self !== 'undefined' ) animation.setContext( self ); - if ( typeof self !== 'undefined' ) animation.setContext( self ); + this.setAnimationLoop = function ( callback ) { - this.setAnimationLoop = function ( callback ) { + onAnimationFrameCallback = callback; + xr.setAnimationLoop( callback ); - onAnimationFrameCallback = callback; - xr.setAnimationLoop( callback ); + ( callback === null ) ? animation.stop() : animation.start(); - ( callback === null ) ? animation.stop() : animation.start(); + }; - }; + xr.addEventListener( 'sessionstart', onXRSessionStart ); + xr.addEventListener( 'sessionend', onXRSessionEnd ); - xr.addEventListener( 'sessionstart', onXRSessionStart ); - xr.addEventListener( 'sessionend', onXRSessionEnd ); + // Rendering - // Rendering + this.render = function ( scene, camera ) { - this.render = function ( scene, camera ) { + if ( camera !== undefined && camera.isCamera !== true ) { - if ( camera !== undefined && camera.isCamera !== true ) { + console.error( 'THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.' ); + return; - console.error( 'THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.' ); - return; + } - } + if ( _isContextLost === true ) return; - if ( _isContextLost === true ) return; + // update scene graph - // update scene graph + if ( scene.matrixWorldAutoUpdate === true ) scene.updateMatrixWorld(); - if ( scene.matrixWorldAutoUpdate === true ) scene.updateMatrixWorld(); + // update camera matrices and frustum - // update camera matrices and frustum + if ( camera.parent === null && camera.matrixWorldAutoUpdate === true ) camera.updateMatrixWorld(); - if ( camera.parent === null && camera.matrixWorldAutoUpdate === true ) camera.updateMatrixWorld(); + if ( xr.enabled === true && xr.isPresenting === true ) { - if ( xr.enabled === true && xr.isPresenting === true ) { + if ( xr.cameraAutoUpdate === true ) xr.updateCamera( camera ); - if ( xr.cameraAutoUpdate === true ) xr.updateCamera( camera ); + camera = xr.getCamera(); // use XR camera for rendering - camera = xr.getCamera(); // use XR camera for rendering + } - } + // + if ( scene.isScene === true ) scene.onBeforeRender( _this, scene, camera, _currentRenderTarget ); - // - if ( scene.isScene === true ) scene.onBeforeRender( _this, scene, camera, _currentRenderTarget ); + currentRenderState = renderStates.get( scene, renderStateStack.length ); + currentRenderState.init(); - currentRenderState = renderStates.get( scene, renderStateStack.length ); - currentRenderState.init(); + renderStateStack.push( currentRenderState ); - renderStateStack.push( currentRenderState ); + _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); + _frustum.setFromProjectionMatrix( _projScreenMatrix ); - _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); - _frustum.setFromProjectionMatrix( _projScreenMatrix ); + _localClippingEnabled = this.localClippingEnabled; + _clippingEnabled = clipping.init( this.clippingPlanes, _localClippingEnabled ); - _localClippingEnabled = this.localClippingEnabled; - _clippingEnabled = clipping.init( this.clippingPlanes, _localClippingEnabled ); + currentRenderList = renderLists.get( scene, renderListStack.length ); + currentRenderList.init(); - currentRenderList = renderLists.get( scene, renderListStack.length ); - currentRenderList.init(); + renderListStack.push( currentRenderList ); - renderListStack.push( currentRenderList ); + projectObject( scene, camera, 0, _this.sortObjects ); - projectObject( scene, camera, 0, _this.sortObjects ); + currentRenderList.finish(); - currentRenderList.finish(); + if ( _this.sortObjects === true ) { - if ( _this.sortObjects === true ) { + currentRenderList.sort( _opaqueSort, _transparentSort ); - currentRenderList.sort( _opaqueSort, _transparentSort ); + } - } + // - // + if ( _clippingEnabled === true ) clipping.beginShadows(); - if ( _clippingEnabled === true ) clipping.beginShadows(); + const shadowsArray = currentRenderState.state.shadowsArray; - const shadowsArray = currentRenderState.state.shadowsArray; + shadowMap.render( shadowsArray, scene, camera ); - shadowMap.render( shadowsArray, scene, camera ); + if ( _clippingEnabled === true ) clipping.endShadows(); - if ( _clippingEnabled === true ) clipping.endShadows(); + // - // + if ( this.info.autoReset === true ) this.info.reset(); - if ( this.info.autoReset === true ) this.info.reset(); + // - // + background.render( currentRenderList, scene ); - background.render( currentRenderList, scene ); + // render scene - // render scene + currentRenderState.setupLights( _this.useLegacyLights ); - currentRenderState.setupLights( _this.useLegacyLights ); + if ( camera.isArrayCamera ) { - if ( camera.isArrayCamera ) { + const cameras = camera.cameras; - const cameras = camera.cameras; + for ( let i = 0, l = cameras.length; i < l; i ++ ) { - for ( let i = 0, l = cameras.length; i < l; i ++ ) { + const camera2 = cameras[ i ]; - const camera2 = cameras[ i ]; + renderScene( currentRenderList, scene, camera2, camera2.viewport ); - renderScene( currentRenderList, scene, camera2, camera2.viewport ); + } - } + } else { - } else { + renderScene( currentRenderList, scene, camera ); - renderScene( currentRenderList, scene, camera ); + } - } + // - // + if ( _currentRenderTarget !== null ) { - if ( _currentRenderTarget !== null ) { + // resolve multisample renderbuffers to a single-sample texture if necessary - // resolve multisample renderbuffers to a single-sample texture if necessary + textures.updateMultisampleRenderTarget( _currentRenderTarget ); - textures.updateMultisampleRenderTarget( _currentRenderTarget ); + // Generate mipmap if we're using any kind of mipmap filtering - // Generate mipmap if we're using any kind of mipmap filtering + textures.updateRenderTargetMipmap( _currentRenderTarget ); - textures.updateRenderTargetMipmap( _currentRenderTarget ); + } - } + // - // + if ( scene.isScene === true ) scene.onAfterRender( _this, scene, camera ); - if ( scene.isScene === true ) scene.onAfterRender( _this, scene, camera ); + // _gl.finish(); - // _gl.finish(); + bindingStates.resetDefaultState(); + _currentMaterialId = - 1; + _currentCamera = null; - bindingStates.resetDefaultState(); - _currentMaterialId = - 1; - _currentCamera = null; + renderStateStack.pop(); - renderStateStack.pop(); + if ( renderStateStack.length > 0 ) { - if ( renderStateStack.length > 0 ) { + currentRenderState = renderStateStack[ renderStateStack.length - 1 ]; - currentRenderState = renderStateStack[ renderStateStack.length - 1 ]; + } else { - } else { + currentRenderState = null; - currentRenderState = null; + } - } + renderListStack.pop(); - renderListStack.pop(); + if ( renderListStack.length > 0 ) { - if ( renderListStack.length > 0 ) { + currentRenderList = renderListStack[ renderListStack.length - 1 ]; - currentRenderList = renderListStack[ renderListStack.length - 1 ]; + } else { - } else { + currentRenderList = null; - currentRenderList = null; + } - } + }; - }; + function projectObject( object, camera, groupOrder, sortObjects ) { - function projectObject( object, camera, groupOrder, sortObjects ) { + if ( object.visible === false ) return; - if ( object.visible === false ) return; + const visible = object.layers.test( camera.layers ); - const visible = object.layers.test( camera.layers ); + if ( visible ) { - if ( visible ) { + if ( object.isGroup ) { - if ( object.isGroup ) { + groupOrder = object.renderOrder; - groupOrder = object.renderOrder; + } else if ( object.isLOD ) { - } else if ( object.isLOD ) { + if ( object.autoUpdate === true ) object.update( camera ); - if ( object.autoUpdate === true ) object.update( camera ); + } else if ( object.isLight ) { - } else if ( object.isLight ) { + currentRenderState.pushLight( object ); - currentRenderState.pushLight( object ); + if ( object.castShadow ) { - if ( object.castShadow ) { + currentRenderState.pushShadow( object ); - currentRenderState.pushShadow( object ); + } - } + } else if ( object.isSprite ) { - } else if ( object.isSprite ) { + if ( ! object.frustumCulled || _frustum.intersectsSprite( object ) ) { - if ( ! object.frustumCulled || _frustum.intersectsSprite( object ) ) { + if ( sortObjects ) { - if ( sortObjects ) { + _vector3.setFromMatrixPosition( object.matrixWorld ) + .applyMatrix4( _projScreenMatrix ); - _vector3.setFromMatrixPosition( object.matrixWorld ) - .applyMatrix4( _projScreenMatrix ); + } - } + const geometry = objects.update( object ); + const material = object.material; - const geometry = objects.update( object ); - const material = object.material; + if ( material.visible ) { - if ( material.visible ) { + currentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null ); - currentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null ); + } } - } + } else if ( object.isMesh || object.isLine || object.isPoints ) { - } else if ( object.isMesh || object.isLine || object.isPoints ) { + if ( object.isSkinnedMesh ) { - if ( object.isSkinnedMesh ) { + // update skeleton only once in a frame - // update skeleton only once in a frame + if ( object.skeleton.frame !== info.render.frame ) { - if ( object.skeleton.frame !== info.render.frame ) { + object.skeleton.update(); + object.skeleton.frame = info.render.frame; - object.skeleton.update(); - object.skeleton.frame = info.render.frame; + } } - } + if ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) { - if ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) { + if ( sortObjects ) { - if ( sortObjects ) { + _vector3.setFromMatrixPosition( object.matrixWorld ) + .applyMatrix4( _projScreenMatrix ); - _vector3.setFromMatrixPosition( object.matrixWorld ) - .applyMatrix4( _projScreenMatrix ); + } - } + const geometry = objects.update( object ); + const material = object.material; - const geometry = objects.update( object ); - const material = object.material; + if ( Array.isArray( material ) ) { - if ( Array.isArray( material ) ) { + const groups = geometry.groups; - const groups = geometry.groups; + for ( let i = 0, l = groups.length; i < l; i ++ ) { - for ( let i = 0, l = groups.length; i < l; i ++ ) { + const group = groups[ i ]; + const groupMaterial = material[ group.materialIndex ]; - const group = groups[ i ]; - const groupMaterial = material[ group.materialIndex ]; + if ( groupMaterial && groupMaterial.visible ) { - if ( groupMaterial && groupMaterial.visible ) { + currentRenderList.push( object, geometry, groupMaterial, groupOrder, _vector3.z, group ); - currentRenderList.push( object, geometry, groupMaterial, groupOrder, _vector3.z, group ); + } } - } + } else if ( material.visible ) { - } else if ( material.visible ) { + currentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null ); - currentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null ); + } } @@ -1188,1124 +1194,1119 @@ function WebGLRenderer( parameters = {} ) { } - } + const children = object.children; - const children = object.children; + for ( let i = 0, l = children.length; i < l; i ++ ) { - for ( let i = 0, l = children.length; i < l; i ++ ) { + projectObject( children[ i ], camera, groupOrder, sortObjects ); - projectObject( children[ i ], camera, groupOrder, sortObjects ); + } } - } + function renderScene( currentRenderList, scene, camera, viewport ) { - function renderScene( currentRenderList, scene, camera, viewport ) { + const opaqueObjects = currentRenderList.opaque; + const transmissiveObjects = currentRenderList.transmissive; + const transparentObjects = currentRenderList.transparent; - const opaqueObjects = currentRenderList.opaque; - const transmissiveObjects = currentRenderList.transmissive; - const transparentObjects = currentRenderList.transparent; + currentRenderState.setupLightsView( camera ); - currentRenderState.setupLightsView( camera ); + if ( _clippingEnabled === true ) clipping.setGlobalState( _this.clippingPlanes, camera ); - if ( _clippingEnabled === true ) clipping.setGlobalState( _this.clippingPlanes, camera ); + if ( transmissiveObjects.length > 0 ) renderTransmissionPass( opaqueObjects, transmissiveObjects, scene, camera ); - if ( transmissiveObjects.length > 0 ) renderTransmissionPass( opaqueObjects, transmissiveObjects, scene, camera ); + if ( viewport ) state.viewport( _currentViewport.copy( viewport ) ); - if ( viewport ) state.viewport( _currentViewport.copy( viewport ) ); + if ( opaqueObjects.length > 0 ) renderObjects( opaqueObjects, scene, camera ); + if ( transmissiveObjects.length > 0 ) renderObjects( transmissiveObjects, scene, camera ); + if ( transparentObjects.length > 0 ) renderObjects( transparentObjects, scene, camera ); - if ( opaqueObjects.length > 0 ) renderObjects( opaqueObjects, scene, camera ); - if ( transmissiveObjects.length > 0 ) renderObjects( transmissiveObjects, scene, camera ); - if ( transparentObjects.length > 0 ) renderObjects( transparentObjects, scene, camera ); + // Ensure depth buffer writing is enabled so it can be cleared on next render - // Ensure depth buffer writing is enabled so it can be cleared on next render + state.buffers.depth.setTest( true ); + state.buffers.depth.setMask( true ); + state.buffers.color.setMask( true ); - state.buffers.depth.setTest( true ); - state.buffers.depth.setMask( true ); - state.buffers.color.setMask( true ); + state.setPolygonOffset( false ); - state.setPolygonOffset( false ); + } - } + function renderTransmissionPass( opaqueObjects, transmissiveObjects, scene, camera ) { - function renderTransmissionPass( opaqueObjects, transmissiveObjects, scene, camera ) { + if ( _transmissionRenderTarget === null ) { - if ( _transmissionRenderTarget === null ) { + const isWebGL2 = capabilities.isWebGL2; - const isWebGL2 = capabilities.isWebGL2; + _transmissionRenderTarget = new WebGLRenderTarget( 1024, 1024, { + generateMipmaps: true, + type: extensions.has( 'EXT_color_buffer_half_float' ) ? HalfFloatType : UnsignedByteType, + minFilter: LinearMipmapLinearFilter, + samples: ( isWebGL2 && antialias === true ) ? 4 : 0 + } ); - _transmissionRenderTarget = new WebGLRenderTarget( 1024, 1024, { - generateMipmaps: true, - type: extensions.has( 'EXT_color_buffer_half_float' ) ? HalfFloatType : UnsignedByteType, - minFilter: LinearMipmapLinearFilter, - samples: ( isWebGL2 && _antialias === true ) ? 4 : 0 - } ); + // debug - // debug + /* + const geometry = new PlaneGeometry(); + const material = new MeshBasicMaterial( { map: _transmissionRenderTarget.texture } ); - /* - const geometry = new PlaneGeometry(); - const material = new MeshBasicMaterial( { map: _transmissionRenderTarget.texture } ); + const mesh = new Mesh( geometry, material ); + scene.add( mesh ); + */ - const mesh = new Mesh( geometry, material ); - scene.add( mesh ); - */ + } - } + // - // + const currentRenderTarget = _this.getRenderTarget(); + _this.setRenderTarget( _transmissionRenderTarget ); + _this.clear(); - const currentRenderTarget = _this.getRenderTarget(); - _this.setRenderTarget( _transmissionRenderTarget ); - _this.clear(); + // Turn off the features which can affect the frag color for opaque objects pass. + // Otherwise they are applied twice in opaque objects pass and transmission objects pass. + const currentToneMapping = _this.toneMapping; + _this.toneMapping = NoToneMapping; - // Turn off the features which can affect the frag color for opaque objects pass. - // Otherwise they are applied twice in opaque objects pass and transmission objects pass. - const currentToneMapping = _this.toneMapping; - _this.toneMapping = NoToneMapping; + renderObjects( opaqueObjects, scene, camera ); - renderObjects( opaqueObjects, scene, camera ); + textures.updateMultisampleRenderTarget( _transmissionRenderTarget ); + textures.updateRenderTargetMipmap( _transmissionRenderTarget ); - textures.updateMultisampleRenderTarget( _transmissionRenderTarget ); - textures.updateRenderTargetMipmap( _transmissionRenderTarget ); + let renderTargetNeedsUpdate = false; - let renderTargetNeedsUpdate = false; + for ( let i = 0, l = transmissiveObjects.length; i < l; i ++ ) { - for ( let i = 0, l = transmissiveObjects.length; i < l; i ++ ) { + const renderItem = transmissiveObjects[ i ]; - const renderItem = transmissiveObjects[ i ]; + const object = renderItem.object; + const geometry = renderItem.geometry; + const material = renderItem.material; + const group = renderItem.group; - const object = renderItem.object; - const geometry = renderItem.geometry; - const material = renderItem.material; - const group = renderItem.group; + if ( material.side === DoubleSide && object.layers.test( camera.layers ) ) { - if ( material.side === DoubleSide && object.layers.test( camera.layers ) ) { + const currentSide = material.side; - const currentSide = material.side; + material.side = BackSide; + material.needsUpdate = true; - material.side = BackSide; - material.needsUpdate = true; + renderObject( object, scene, camera, geometry, material, group ); - renderObject( object, scene, camera, geometry, material, group ); + material.side = currentSide; + material.needsUpdate = true; - material.side = currentSide; - material.needsUpdate = true; + renderTargetNeedsUpdate = true; - renderTargetNeedsUpdate = true; + } } - } + if ( renderTargetNeedsUpdate === true ) { - if ( renderTargetNeedsUpdate === true ) { + textures.updateMultisampleRenderTarget( _transmissionRenderTarget ); + textures.updateRenderTargetMipmap( _transmissionRenderTarget ); - textures.updateMultisampleRenderTarget( _transmissionRenderTarget ); - textures.updateRenderTargetMipmap( _transmissionRenderTarget ); + } - } + _this.setRenderTarget( currentRenderTarget ); - _this.setRenderTarget( currentRenderTarget ); + _this.toneMapping = currentToneMapping; - _this.toneMapping = currentToneMapping; + } - } + function renderObjects( renderList, scene, camera ) { - function renderObjects( renderList, scene, camera ) { + const overrideMaterial = scene.isScene === true ? scene.overrideMaterial : null; - const overrideMaterial = scene.isScene === true ? scene.overrideMaterial : null; + for ( let i = 0, l = renderList.length; i < l; i ++ ) { - for ( let i = 0, l = renderList.length; i < l; i ++ ) { + const renderItem = renderList[ i ]; - const renderItem = renderList[ i ]; + const object = renderItem.object; + const geometry = renderItem.geometry; + const material = overrideMaterial === null ? renderItem.material : overrideMaterial; + const group = renderItem.group; - const object = renderItem.object; - const geometry = renderItem.geometry; - const material = overrideMaterial === null ? renderItem.material : overrideMaterial; - const group = renderItem.group; + if ( object.layers.test( camera.layers ) ) { - if ( object.layers.test( camera.layers ) ) { + renderObject( object, scene, camera, geometry, material, group ); - renderObject( object, scene, camera, geometry, material, group ); + } } } - } + function renderObject( object, scene, camera, geometry, material, group ) { - function renderObject( object, scene, camera, geometry, material, group ) { + object.onBeforeRender( _this, scene, camera, geometry, material, group ); - object.onBeforeRender( _this, scene, camera, geometry, material, group ); + object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); + object.normalMatrix.getNormalMatrix( object.modelViewMatrix ); - object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); - object.normalMatrix.getNormalMatrix( object.modelViewMatrix ); + material.onBeforeRender( _this, scene, camera, geometry, object, group ); - material.onBeforeRender( _this, scene, camera, geometry, object, group ); + if ( material.transparent === true && material.side === DoubleSide && material.forceSinglePass === false ) { + + material.side = BackSide; + material.needsUpdate = true; + _this.renderBufferDirect( camera, scene, geometry, material, object, group ); - if ( material.transparent === true && material.side === DoubleSide && material.forceSinglePass === false ) { + material.side = FrontSide; + material.needsUpdate = true; + _this.renderBufferDirect( camera, scene, geometry, material, object, group ); - material.side = BackSide; - material.needsUpdate = true; - _this.renderBufferDirect( camera, scene, geometry, material, object, group ); + material.side = DoubleSide; - material.side = FrontSide; - material.needsUpdate = true; - _this.renderBufferDirect( camera, scene, geometry, material, object, group ); + } else { - material.side = DoubleSide; + _this.renderBufferDirect( camera, scene, geometry, material, object, group ); - } else { + } - _this.renderBufferDirect( camera, scene, geometry, material, object, group ); + object.onAfterRender( _this, scene, camera, geometry, material, group ); } - object.onAfterRender( _this, scene, camera, geometry, material, group ); + function getProgram( material, scene, object ) { - } + if ( scene.isScene !== true ) scene = _emptyScene; // scene could be a Mesh, Line, Points, ... - function getProgram( material, scene, object ) { + const materialProperties = properties.get( material ); - if ( scene.isScene !== true ) scene = _emptyScene; // scene could be a Mesh, Line, Points, ... + const lights = currentRenderState.state.lights; + const shadowsArray = currentRenderState.state.shadowsArray; - const materialProperties = properties.get( material ); + const lightsStateVersion = lights.state.version; - const lights = currentRenderState.state.lights; - const shadowsArray = currentRenderState.state.shadowsArray; + const parameters = programCache.getParameters( material, lights.state, shadowsArray, scene, object ); + const programCacheKey = programCache.getProgramCacheKey( parameters ); - const lightsStateVersion = lights.state.version; + let programs = materialProperties.programs; - const parameters = programCache.getParameters( material, lights.state, shadowsArray, scene, object ); - const programCacheKey = programCache.getProgramCacheKey( parameters ); + // always update environment and fog - changing these trigger an getProgram call, but it's possible that the program doesn't change - let programs = materialProperties.programs; + materialProperties.environment = material.isMeshStandardMaterial ? scene.environment : null; + materialProperties.fog = scene.fog; + materialProperties.envMap = ( material.isMeshStandardMaterial ? cubeuvmaps : cubemaps ).get( material.envMap || materialProperties.environment ); - // always update environment and fog - changing these trigger an getProgram call, but it's possible that the program doesn't change + if ( programs === undefined ) { - materialProperties.environment = material.isMeshStandardMaterial ? scene.environment : null; - materialProperties.fog = scene.fog; - materialProperties.envMap = ( material.isMeshStandardMaterial ? cubeuvmaps : cubemaps ).get( material.envMap || materialProperties.environment ); + // new material - if ( programs === undefined ) { + material.addEventListener( 'dispose', onMaterialDispose ); - // new material + programs = new Map(); + materialProperties.programs = programs; - material.addEventListener( 'dispose', onMaterialDispose ); + } - programs = new Map(); - materialProperties.programs = programs; + let program = programs.get( programCacheKey ); - } + if ( program !== undefined ) { - let program = programs.get( programCacheKey ); + // early out if program and light state is identical - if ( program !== undefined ) { + if ( materialProperties.currentProgram === program && materialProperties.lightsStateVersion === lightsStateVersion ) { - // early out if program and light state is identical + updateCommonMaterialProperties( material, parameters ); - if ( materialProperties.currentProgram === program && materialProperties.lightsStateVersion === lightsStateVersion ) { + return program; - updateCommonMaterialProperties( material, parameters ); + } - return program; + } else { - } + parameters.uniforms = programCache.getUniforms( material ); - } else { + material.onBuild( object, parameters, _this ); - parameters.uniforms = programCache.getUniforms( material ); + material.onBeforeCompile( parameters, _this ); - material.onBuild( object, parameters, _this ); + program = programCache.acquireProgram( parameters, programCacheKey ); + programs.set( programCacheKey, program ); - material.onBeforeCompile( parameters, _this ); + materialProperties.uniforms = parameters.uniforms; - program = programCache.acquireProgram( parameters, programCacheKey ); - programs.set( programCacheKey, program ); + } - materialProperties.uniforms = parameters.uniforms; + const uniforms = materialProperties.uniforms; - } + if ( ( ! material.isShaderMaterial && ! material.isRawShaderMaterial ) || material.clipping === true ) { - const uniforms = materialProperties.uniforms; + uniforms.clippingPlanes = clipping.uniform; - if ( ( ! material.isShaderMaterial && ! material.isRawShaderMaterial ) || material.clipping === true ) { + } - uniforms.clippingPlanes = clipping.uniform; + updateCommonMaterialProperties( material, parameters ); - } + // store the light setup it was created for - updateCommonMaterialProperties( material, parameters ); + materialProperties.needsLights = materialNeedsLights( material ); + materialProperties.lightsStateVersion = lightsStateVersion; - // store the light setup it was created for + if ( materialProperties.needsLights ) { - materialProperties.needsLights = materialNeedsLights( material ); - materialProperties.lightsStateVersion = lightsStateVersion; + // wire up the material to this renderer's lighting state + + 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.ltc_1.value = lights.state.rectAreaLTC1; + uniforms.ltc_2.value = lights.state.rectAreaLTC2; + uniforms.pointLights.value = lights.state.point; + uniforms.pointLightShadows.value = lights.state.pointShadow; + uniforms.hemisphereLights.value = lights.state.hemi; + + uniforms.directionalShadowMap.value = lights.state.directionalShadowMap; + uniforms.directionalShadowMatrix.value = lights.state.directionalShadowMatrix; + uniforms.spotShadowMap.value = lights.state.spotShadowMap; + uniforms.spotLightMatrix.value = lights.state.spotLightMatrix; + uniforms.spotLightMap.value = lights.state.spotLightMap; + uniforms.pointShadowMap.value = lights.state.pointShadowMap; + uniforms.pointShadowMatrix.value = lights.state.pointShadowMatrix; + // TODO (abelnation): add area lights shadow info to uniforms - if ( materialProperties.needsLights ) { + } - // wire up the material to this renderer's lighting state + const progUniforms = program.getUniforms(); + const uniformsList = WebGLUniforms.seqWithValue( progUniforms.seq, uniforms ); - 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.ltc_1.value = lights.state.rectAreaLTC1; - uniforms.ltc_2.value = lights.state.rectAreaLTC2; - uniforms.pointLights.value = lights.state.point; - uniforms.pointLightShadows.value = lights.state.pointShadow; - uniforms.hemisphereLights.value = lights.state.hemi; + materialProperties.currentProgram = program; + materialProperties.uniformsList = uniformsList; - uniforms.directionalShadowMap.value = lights.state.directionalShadowMap; - uniforms.directionalShadowMatrix.value = lights.state.directionalShadowMatrix; - uniforms.spotShadowMap.value = lights.state.spotShadowMap; - uniforms.spotLightMatrix.value = lights.state.spotLightMatrix; - uniforms.spotLightMap.value = lights.state.spotLightMap; - uniforms.pointShadowMap.value = lights.state.pointShadowMap; - uniforms.pointShadowMatrix.value = lights.state.pointShadowMatrix; - // TODO (abelnation): add area lights shadow info to uniforms + return program; } - const progUniforms = program.getUniforms(); - const uniformsList = WebGLUniforms.seqWithValue( progUniforms.seq, uniforms ); + function updateCommonMaterialProperties( material, parameters ) { - materialProperties.currentProgram = program; - materialProperties.uniformsList = uniformsList; + const materialProperties = properties.get( material ); - return program; + materialProperties.outputEncoding = parameters.outputEncoding; + materialProperties.instancing = parameters.instancing; + materialProperties.skinning = parameters.skinning; + materialProperties.morphTargets = parameters.morphTargets; + materialProperties.morphNormals = parameters.morphNormals; + materialProperties.morphColors = parameters.morphColors; + materialProperties.morphTargetsCount = parameters.morphTargetsCount; + materialProperties.numClippingPlanes = parameters.numClippingPlanes; + materialProperties.numIntersection = parameters.numClipIntersection; + materialProperties.vertexAlphas = parameters.vertexAlphas; + materialProperties.vertexTangents = parameters.vertexTangents; + materialProperties.toneMapping = parameters.toneMapping; - } + } - function updateCommonMaterialProperties( material, parameters ) { + function setProgram( camera, scene, geometry, material, object ) { - const materialProperties = properties.get( material ); + if ( scene.isScene !== true ) scene = _emptyScene; // scene could be a Mesh, Line, Points, ... - materialProperties.outputEncoding = parameters.outputEncoding; - materialProperties.instancing = parameters.instancing; - materialProperties.skinning = parameters.skinning; - materialProperties.morphTargets = parameters.morphTargets; - materialProperties.morphNormals = parameters.morphNormals; - materialProperties.morphColors = parameters.morphColors; - materialProperties.morphTargetsCount = parameters.morphTargetsCount; - materialProperties.numClippingPlanes = parameters.numClippingPlanes; - materialProperties.numIntersection = parameters.numClipIntersection; - materialProperties.vertexAlphas = parameters.vertexAlphas; - materialProperties.vertexTangents = parameters.vertexTangents; - materialProperties.toneMapping = parameters.toneMapping; + textures.resetTextureUnits(); - } + const fog = scene.fog; + const environment = material.isMeshStandardMaterial ? scene.environment : null; + const encoding = ( _currentRenderTarget === null ) ? _this.outputEncoding : ( _currentRenderTarget.isXRRenderTarget === true ? _currentRenderTarget.texture.encoding : LinearEncoding ); + const envMap = ( material.isMeshStandardMaterial ? cubeuvmaps : cubemaps ).get( material.envMap || environment ); + const vertexAlphas = material.vertexColors === true && !! geometry.attributes.color && geometry.attributes.color.itemSize === 4; + const vertexTangents = !! material.normalMap && !! geometry.attributes.tangent; + const morphTargets = !! geometry.morphAttributes.position; + const morphNormals = !! geometry.morphAttributes.normal; + const morphColors = !! geometry.morphAttributes.color; + const toneMapping = material.toneMapped ? _this.toneMapping : NoToneMapping; - function setProgram( camera, scene, geometry, material, object ) { + const morphAttribute = geometry.morphAttributes.position || geometry.morphAttributes.normal || geometry.morphAttributes.color; + const morphTargetsCount = ( morphAttribute !== undefined ) ? morphAttribute.length : 0; - if ( scene.isScene !== true ) scene = _emptyScene; // scene could be a Mesh, Line, Points, ... + const materialProperties = properties.get( material ); + const lights = currentRenderState.state.lights; - textures.resetTextureUnits(); + if ( _clippingEnabled === true ) { - const fog = scene.fog; - const environment = material.isMeshStandardMaterial ? scene.environment : null; - const encoding = ( _currentRenderTarget === null ) ? _this.outputEncoding : ( _currentRenderTarget.isXRRenderTarget === true ? _currentRenderTarget.texture.encoding : LinearEncoding ); - const envMap = ( material.isMeshStandardMaterial ? cubeuvmaps : cubemaps ).get( material.envMap || environment ); - const vertexAlphas = material.vertexColors === true && !! geometry.attributes.color && geometry.attributes.color.itemSize === 4; - const vertexTangents = !! material.normalMap && !! geometry.attributes.tangent; - const morphTargets = !! geometry.morphAttributes.position; - const morphNormals = !! geometry.morphAttributes.normal; - const morphColors = !! geometry.morphAttributes.color; - const toneMapping = material.toneMapped ? _this.toneMapping : NoToneMapping; + if ( _localClippingEnabled === true || camera !== _currentCamera ) { - const morphAttribute = geometry.morphAttributes.position || geometry.morphAttributes.normal || geometry.morphAttributes.color; - const morphTargetsCount = ( morphAttribute !== undefined ) ? morphAttribute.length : 0; + const useCache = + camera === _currentCamera && + material.id === _currentMaterialId; - const materialProperties = properties.get( material ); - const lights = currentRenderState.state.lights; + // we might want to call this function with some ClippingGroup + // object instead of the material, once it becomes feasible + // (#8465, #8379) + clipping.setState( material, camera, useCache ); - if ( _clippingEnabled === true ) { + } - if ( _localClippingEnabled === true || camera !== _currentCamera ) { + } - const useCache = - camera === _currentCamera && - material.id === _currentMaterialId; + // - // we might want to call this function with some ClippingGroup - // object instead of the material, once it becomes feasible - // (#8465, #8379) - clipping.setState( material, camera, useCache ); + let needsProgramChange = false; - } + if ( material.version === materialProperties.__version ) { - } + if ( materialProperties.needsLights && ( materialProperties.lightsStateVersion !== lights.state.version ) ) { - // + needsProgramChange = true; - let needsProgramChange = false; + } else if ( materialProperties.outputEncoding !== encoding ) { - if ( material.version === materialProperties.__version ) { + needsProgramChange = true; - if ( materialProperties.needsLights && ( materialProperties.lightsStateVersion !== lights.state.version ) ) { + } else if ( object.isInstancedMesh && materialProperties.instancing === false ) { - needsProgramChange = true; + needsProgramChange = true; - } else if ( materialProperties.outputEncoding !== encoding ) { + } else if ( ! object.isInstancedMesh && materialProperties.instancing === true ) { - needsProgramChange = true; + needsProgramChange = true; - } else if ( object.isInstancedMesh && materialProperties.instancing === false ) { + } else if ( object.isSkinnedMesh && materialProperties.skinning === false ) { - needsProgramChange = true; + needsProgramChange = true; - } else if ( ! object.isInstancedMesh && materialProperties.instancing === true ) { + } else if ( ! object.isSkinnedMesh && materialProperties.skinning === true ) { - needsProgramChange = true; + needsProgramChange = true; - } else if ( object.isSkinnedMesh && materialProperties.skinning === false ) { + } else if ( materialProperties.envMap !== envMap ) { - needsProgramChange = true; + needsProgramChange = true; - } else if ( ! object.isSkinnedMesh && materialProperties.skinning === true ) { + } else if ( material.fog === true && materialProperties.fog !== fog ) { - needsProgramChange = true; + needsProgramChange = true; - } else if ( materialProperties.envMap !== envMap ) { + } else if ( materialProperties.numClippingPlanes !== undefined && + ( materialProperties.numClippingPlanes !== clipping.numPlanes || + materialProperties.numIntersection !== clipping.numIntersection ) ) { - needsProgramChange = true; + needsProgramChange = true; - } else if ( material.fog === true && materialProperties.fog !== fog ) { + } else if ( materialProperties.vertexAlphas !== vertexAlphas ) { - needsProgramChange = true; + needsProgramChange = true; - } else if ( materialProperties.numClippingPlanes !== undefined && - ( materialProperties.numClippingPlanes !== clipping.numPlanes || - materialProperties.numIntersection !== clipping.numIntersection ) ) { + } else if ( materialProperties.vertexTangents !== vertexTangents ) { - needsProgramChange = true; + needsProgramChange = true; - } else if ( materialProperties.vertexAlphas !== vertexAlphas ) { + } else if ( materialProperties.morphTargets !== morphTargets ) { - needsProgramChange = true; + needsProgramChange = true; - } else if ( materialProperties.vertexTangents !== vertexTangents ) { + } else if ( materialProperties.morphNormals !== morphNormals ) { - needsProgramChange = true; - - } else if ( materialProperties.morphTargets !== morphTargets ) { + needsProgramChange = true; - needsProgramChange = true; + } else if ( materialProperties.morphColors !== morphColors ) { - } else if ( materialProperties.morphNormals !== morphNormals ) { + needsProgramChange = true; - needsProgramChange = true; + } else if ( materialProperties.toneMapping !== toneMapping ) { - } else if ( materialProperties.morphColors !== morphColors ) { + needsProgramChange = true; - needsProgramChange = true; + } else if ( capabilities.isWebGL2 === true && materialProperties.morphTargetsCount !== morphTargetsCount ) { - } else if ( materialProperties.toneMapping !== toneMapping ) { + needsProgramChange = true; - needsProgramChange = true; + } - } else if ( capabilities.isWebGL2 === true && materialProperties.morphTargetsCount !== morphTargetsCount ) { + } else { needsProgramChange = true; + materialProperties.__version = material.version; } - } else { - - needsProgramChange = true; - materialProperties.__version = material.version; + // - } + let program = materialProperties.currentProgram; - // + if ( needsProgramChange === true ) { - let program = materialProperties.currentProgram; + program = getProgram( material, scene, object ); - if ( needsProgramChange === true ) { + } - program = getProgram( material, scene, object ); + let refreshProgram = false; + let refreshMaterial = false; + let refreshLights = false; - } + const p_uniforms = program.getUniforms(), + m_uniforms = materialProperties.uniforms; - let refreshProgram = false; - let refreshMaterial = false; - let refreshLights = false; + if ( state.useProgram( program.program ) ) { - const p_uniforms = program.getUniforms(), - m_uniforms = materialProperties.uniforms; + refreshProgram = true; + refreshMaterial = true; + refreshLights = true; - if ( state.useProgram( program.program ) ) { + } - refreshProgram = true; - refreshMaterial = true; - refreshLights = true; + if ( material.id !== _currentMaterialId ) { - } + _currentMaterialId = material.id; - if ( material.id !== _currentMaterialId ) { + refreshMaterial = true; - _currentMaterialId = material.id; + } - refreshMaterial = true; + if ( refreshProgram || _currentCamera !== camera ) { - } + p_uniforms.setValue( _gl, 'projectionMatrix', camera.projectionMatrix ); - if ( refreshProgram || _currentCamera !== camera ) { + if ( capabilities.logarithmicDepthBuffer ) { - p_uniforms.setValue( _gl, 'projectionMatrix', camera.projectionMatrix ); + p_uniforms.setValue( _gl, 'logDepthBufFC', + 2.0 / ( Math.log( camera.far + 1.0 ) / Math.LN2 ) ); - if ( capabilities.logarithmicDepthBuffer ) { - - p_uniforms.setValue( _gl, 'logDepthBufFC', - 2.0 / ( Math.log( camera.far + 1.0 ) / Math.LN2 ) ); + } - } + if ( _currentCamera !== camera ) { - if ( _currentCamera !== camera ) { + _currentCamera = camera; - _currentCamera = camera; + // 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: - // 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 - refreshMaterial = true; // set to true on material change - refreshLights = true; // remains set until update done + } - } + // load material specific uniforms + // (shader material also gets them for the sake of genericity) - // 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.isShaderMaterial || - material.isMeshPhongMaterial || - material.isMeshToonMaterial || - material.isMeshStandardMaterial || - material.envMap ) { + const uCamPos = p_uniforms.map.cameraPosition; - const uCamPos = p_uniforms.map.cameraPosition; + if ( uCamPos !== undefined ) { - if ( uCamPos !== undefined ) { + uCamPos.setValue( _gl, + _vector3.setFromMatrixPosition( camera.matrixWorld ) ); - uCamPos.setValue( _gl, - _vector3.setFromMatrixPosition( camera.matrixWorld ) ); + } } - } + if ( material.isMeshPhongMaterial || + material.isMeshToonMaterial || + material.isMeshLambertMaterial || + material.isMeshBasicMaterial || + material.isMeshStandardMaterial || + material.isShaderMaterial ) { - if ( material.isMeshPhongMaterial || - material.isMeshToonMaterial || - material.isMeshLambertMaterial || - material.isMeshBasicMaterial || - material.isMeshStandardMaterial || - material.isShaderMaterial ) { + p_uniforms.setValue( _gl, 'isOrthographic', camera.isOrthographicCamera === true ); - p_uniforms.setValue( _gl, 'isOrthographic', camera.isOrthographicCamera === true ); + } - } + if ( material.isMeshPhongMaterial || + material.isMeshToonMaterial || + material.isMeshLambertMaterial || + material.isMeshBasicMaterial || + material.isMeshStandardMaterial || + material.isShaderMaterial || + material.isShadowMaterial || + object.isSkinnedMesh ) { - if ( material.isMeshPhongMaterial || - material.isMeshToonMaterial || - material.isMeshLambertMaterial || - material.isMeshBasicMaterial || - material.isMeshStandardMaterial || - material.isShaderMaterial || - material.isShadowMaterial || - object.isSkinnedMesh ) { + p_uniforms.setValue( _gl, 'viewMatrix', camera.matrixWorldInverse ); - p_uniforms.setValue( _gl, 'viewMatrix', camera.matrixWorldInverse ); + } } - } + // skinning and morph target uniforms must be set even if material didn't change + // auto-setting of texture unit for bone and morph texture must go before other textures + // otherwise textures used for skinning and morphing can take over texture units reserved for other material textures - // skinning and morph target uniforms must be set even if material didn't change - // auto-setting of texture unit for bone and morph texture must go before other textures - // otherwise textures used for skinning and morphing can take over texture units reserved for other material textures + if ( object.isSkinnedMesh ) { - if ( object.isSkinnedMesh ) { + p_uniforms.setOptional( _gl, object, 'bindMatrix' ); + p_uniforms.setOptional( _gl, object, 'bindMatrixInverse' ); - p_uniforms.setOptional( _gl, object, 'bindMatrix' ); - p_uniforms.setOptional( _gl, object, 'bindMatrixInverse' ); + const skeleton = object.skeleton; - const skeleton = object.skeleton; + if ( skeleton ) { - if ( skeleton ) { + if ( capabilities.floatVertexTextures ) { - if ( capabilities.floatVertexTextures ) { + if ( skeleton.boneTexture === null ) skeleton.computeBoneTexture(); - if ( skeleton.boneTexture === null ) skeleton.computeBoneTexture(); + p_uniforms.setValue( _gl, 'boneTexture', skeleton.boneTexture, textures ); + p_uniforms.setValue( _gl, 'boneTextureSize', skeleton.boneTextureSize ); - p_uniforms.setValue( _gl, 'boneTexture', skeleton.boneTexture, textures ); - p_uniforms.setValue( _gl, 'boneTextureSize', skeleton.boneTextureSize ); + } else { - } else { + console.warn( 'THREE.WebGLRenderer: SkinnedMesh can only be used with WebGL 2. With WebGL 1 OES_texture_float and vertex textures support is required.' ); - console.warn( 'THREE.WebGLRenderer: SkinnedMesh can only be used with WebGL 2. With WebGL 1 OES_texture_float and vertex textures support is required.' ); + } } } - } + const morphAttributes = geometry.morphAttributes; - const morphAttributes = geometry.morphAttributes; + if ( morphAttributes.position !== undefined || morphAttributes.normal !== undefined || ( morphAttributes.color !== undefined && capabilities.isWebGL2 === true ) ) { - if ( morphAttributes.position !== undefined || morphAttributes.normal !== undefined || ( morphAttributes.color !== undefined && capabilities.isWebGL2 === true ) ) { + morphtargets.update( object, geometry, program ); - morphtargets.update( object, geometry, program ); + } - } + if ( refreshMaterial || materialProperties.receiveShadow !== object.receiveShadow ) { - if ( refreshMaterial || materialProperties.receiveShadow !== object.receiveShadow ) { + materialProperties.receiveShadow = object.receiveShadow; + p_uniforms.setValue( _gl, 'receiveShadow', object.receiveShadow ); - materialProperties.receiveShadow = object.receiveShadow; - p_uniforms.setValue( _gl, 'receiveShadow', object.receiveShadow ); + } - } + // https://github.com/mrdoob/three.js/pull/24467#issuecomment-1209031512 - // https://github.com/mrdoob/three.js/pull/24467#issuecomment-1209031512 + if ( material.isMeshGouraudMaterial && material.envMap !== null ) { - if ( material.isMeshGouraudMaterial && material.envMap !== null ) { + m_uniforms.envMap.value = envMap; - m_uniforms.envMap.value = envMap; + m_uniforms.flipEnvMap.value = ( envMap.isCubeTexture && envMap.isRenderTargetTexture === false ) ? - 1 : 1; - m_uniforms.flipEnvMap.value = ( envMap.isCubeTexture && envMap.isRenderTargetTexture === false ) ? - 1 : 1; + } - } + if ( refreshMaterial ) { - if ( refreshMaterial ) { + p_uniforms.setValue( _gl, 'toneMappingExposure', _this.toneMappingExposure ); - p_uniforms.setValue( _gl, 'toneMappingExposure', _this.toneMappingExposure ); + if ( materialProperties.needsLights ) { - if ( materialProperties.needsLights ) { + // the current material requires lighting info - // the current material requires lighting info + // 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 - // 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 ); - markUniformsLightsNeedsUpdate( m_uniforms, refreshLights ); + } - } + // refresh uniforms common to several materials - // refresh uniforms common to several materials + if ( fog && material.fog === true ) { - if ( fog && material.fog === true ) { + materials.refreshFogUniforms( m_uniforms, fog ); - materials.refreshFogUniforms( m_uniforms, fog ); + } - } + materials.refreshMaterialUniforms( m_uniforms, material, _pixelRatio, _height, _transmissionRenderTarget ); - materials.refreshMaterialUniforms( m_uniforms, material, _pixelRatio, _height, _transmissionRenderTarget ); + WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, textures ); - WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, textures ); + } - } + if ( material.isShaderMaterial && material.uniformsNeedUpdate === true ) { - if ( material.isShaderMaterial && material.uniformsNeedUpdate === true ) { + WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, textures ); + material.uniformsNeedUpdate = false; - WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, textures ); - material.uniformsNeedUpdate = false; + } - } + if ( material.isSpriteMaterial ) { - if ( material.isSpriteMaterial ) { + p_uniforms.setValue( _gl, 'center', object.center ); - p_uniforms.setValue( _gl, 'center', object.center ); + } - } + // common matrices - // common matrices + p_uniforms.setValue( _gl, 'modelViewMatrix', object.modelViewMatrix ); + p_uniforms.setValue( _gl, 'normalMatrix', object.normalMatrix ); + p_uniforms.setValue( _gl, 'modelMatrix', object.matrixWorld ); - p_uniforms.setValue( _gl, 'modelViewMatrix', object.modelViewMatrix ); - p_uniforms.setValue( _gl, 'normalMatrix', object.normalMatrix ); - p_uniforms.setValue( _gl, 'modelMatrix', object.matrixWorld ); + // UBOs - // UBOs + if ( material.isShaderMaterial || material.isRawShaderMaterial ) { - if ( material.isShaderMaterial || material.isRawShaderMaterial ) { + const groups = material.uniformsGroups; - const groups = material.uniformsGroups; + for ( let i = 0, l = groups.length; i < l; i ++ ) { - for ( let i = 0, l = groups.length; i < l; i ++ ) { + if ( capabilities.isWebGL2 ) { - if ( capabilities.isWebGL2 ) { + const group = groups[ i ]; - const group = groups[ i ]; + uniformsGroups.update( group, program ); + uniformsGroups.bind( group, program ); - uniformsGroups.update( group, program ); - uniformsGroups.bind( group, program ); + } else { - } else { + console.warn( 'THREE.WebGLRenderer: Uniform Buffer Objects can only be used with WebGL 2.' ); - console.warn( 'THREE.WebGLRenderer: Uniform Buffer Objects can only be used with WebGL 2.' ); + } } } + return program; + } - return program; + // If uniforms are marked as clean, they don't need to be loaded to the GPU. - } + function markUniformsLightsNeedsUpdate( uniforms, value ) { - // If uniforms are marked as clean, they don't need to be loaded to the GPU. + uniforms.ambientLightColor.needsUpdate = value; + uniforms.lightProbe.needsUpdate = value; - function markUniformsLightsNeedsUpdate( uniforms, value ) { + uniforms.directionalLights.needsUpdate = value; + uniforms.directionalLightShadows.needsUpdate = value; + uniforms.pointLights.needsUpdate = value; + uniforms.pointLightShadows.needsUpdate = value; + uniforms.spotLights.needsUpdate = value; + uniforms.spotLightShadows.needsUpdate = value; + uniforms.rectAreaLights.needsUpdate = value; + uniforms.hemisphereLights.needsUpdate = value; - uniforms.ambientLightColor.needsUpdate = value; - uniforms.lightProbe.needsUpdate = value; + } - uniforms.directionalLights.needsUpdate = value; - uniforms.directionalLightShadows.needsUpdate = value; - uniforms.pointLights.needsUpdate = value; - uniforms.pointLightShadows.needsUpdate = value; - uniforms.spotLights.needsUpdate = value; - uniforms.spotLightShadows.needsUpdate = value; - uniforms.rectAreaLights.needsUpdate = value; - uniforms.hemisphereLights.needsUpdate = value; + function materialNeedsLights( material ) { - } + return material.isMeshLambertMaterial || material.isMeshToonMaterial || material.isMeshPhongMaterial || + material.isMeshStandardMaterial || material.isShadowMaterial || + ( material.isShaderMaterial && material.lights === true ); - function materialNeedsLights( material ) { - - return material.isMeshLambertMaterial || material.isMeshToonMaterial || material.isMeshPhongMaterial || - material.isMeshStandardMaterial || material.isShadowMaterial || - ( material.isShaderMaterial && material.lights === true ); + } - } + this.getActiveCubeFace = function () { - this.getActiveCubeFace = function () { + return _currentActiveCubeFace; - return _currentActiveCubeFace; + }; - }; + this.getActiveMipmapLevel = function () { - this.getActiveMipmapLevel = function () { + return _currentActiveMipmapLevel; - return _currentActiveMipmapLevel; + }; - }; + this.getRenderTarget = function () { - this.getRenderTarget = function () { + return _currentRenderTarget; - return _currentRenderTarget; + }; - }; + this.setRenderTargetTextures = function ( renderTarget, colorTexture, depthTexture ) { - this.setRenderTargetTextures = function ( renderTarget, colorTexture, depthTexture ) { + properties.get( renderTarget.texture ).__webglTexture = colorTexture; + properties.get( renderTarget.depthTexture ).__webglTexture = depthTexture; - properties.get( renderTarget.texture ).__webglTexture = colorTexture; - properties.get( renderTarget.depthTexture ).__webglTexture = depthTexture; + const renderTargetProperties = properties.get( renderTarget ); + renderTargetProperties.__hasExternalTextures = true; - const renderTargetProperties = properties.get( renderTarget ); - renderTargetProperties.__hasExternalTextures = true; + if ( renderTargetProperties.__hasExternalTextures ) { - if ( renderTargetProperties.__hasExternalTextures ) { + renderTargetProperties.__autoAllocateDepthBuffer = depthTexture === undefined; - renderTargetProperties.__autoAllocateDepthBuffer = depthTexture === undefined; + if ( ! renderTargetProperties.__autoAllocateDepthBuffer ) { - if ( ! renderTargetProperties.__autoAllocateDepthBuffer ) { + // 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; + } } } - } + }; - }; + this.setRenderTargetFramebuffer = function ( renderTarget, defaultFramebuffer ) { - this.setRenderTargetFramebuffer = function ( renderTarget, defaultFramebuffer ) { + const renderTargetProperties = properties.get( renderTarget ); + renderTargetProperties.__webglFramebuffer = defaultFramebuffer; + renderTargetProperties.__useDefaultFramebuffer = defaultFramebuffer === undefined; - const renderTargetProperties = properties.get( renderTarget ); - renderTargetProperties.__webglFramebuffer = defaultFramebuffer; - renderTargetProperties.__useDefaultFramebuffer = defaultFramebuffer === undefined; + }; - }; + this.setRenderTarget = function ( renderTarget, activeCubeFace = 0, activeMipmapLevel = 0 ) { - this.setRenderTarget = function ( renderTarget, activeCubeFace = 0, activeMipmapLevel = 0 ) { + _currentRenderTarget = renderTarget; + _currentActiveCubeFace = activeCubeFace; + _currentActiveMipmapLevel = activeMipmapLevel; - _currentRenderTarget = renderTarget; - _currentActiveCubeFace = activeCubeFace; - _currentActiveMipmapLevel = activeMipmapLevel; + let useDefaultFramebuffer = true; + let framebuffer = null; + let isCube = false; + let isRenderTarget3D = false; - let useDefaultFramebuffer = true; - let framebuffer = null; - let isCube = false; - let isRenderTarget3D = false; + if ( renderTarget ) { - if ( renderTarget ) { + const renderTargetProperties = properties.get( renderTarget ); - const renderTargetProperties = properties.get( renderTarget ); + if ( renderTargetProperties.__useDefaultFramebuffer !== undefined ) { - if ( renderTargetProperties.__useDefaultFramebuffer !== undefined ) { + // We need to make sure to rebind the framebuffer. + state.bindFramebuffer( _gl.FRAMEBUFFER, null ); + useDefaultFramebuffer = false; - // We need to make sure to rebind the framebuffer. - state.bindFramebuffer( _gl.FRAMEBUFFER, null ); - useDefaultFramebuffer = false; + } else if ( renderTargetProperties.__webglFramebuffer === undefined ) { - } else if ( renderTargetProperties.__webglFramebuffer === undefined ) { + textures.setupRenderTarget( renderTarget ); - textures.setupRenderTarget( renderTarget ); + } else if ( renderTargetProperties.__hasExternalTextures ) { - } else if ( renderTargetProperties.__hasExternalTextures ) { + // Color and depth texture must be rebound in order for the swapchain to update. + textures.rebindTextures( renderTarget, properties.get( renderTarget.texture ).__webglTexture, properties.get( renderTarget.depthTexture ).__webglTexture ); - // Color and depth texture must be rebound in order for the swapchain to update. - textures.rebindTextures( renderTarget, properties.get( renderTarget.texture ).__webglTexture, properties.get( renderTarget.depthTexture ).__webglTexture ); + } - } + const texture = renderTarget.texture; - const texture = renderTarget.texture; + if ( texture.isData3DTexture || texture.isDataArrayTexture || texture.isCompressedArrayTexture ) { - if ( texture.isData3DTexture || texture.isDataArrayTexture || texture.isCompressedArrayTexture ) { + isRenderTarget3D = true; - isRenderTarget3D = true; + } - } + const __webglFramebuffer = properties.get( renderTarget ).__webglFramebuffer; - const __webglFramebuffer = properties.get( renderTarget ).__webglFramebuffer; + if ( renderTarget.isWebGLCubeRenderTarget ) { - if ( renderTarget.isWebGLCubeRenderTarget ) { + framebuffer = __webglFramebuffer[ activeCubeFace ]; + isCube = true; - framebuffer = __webglFramebuffer[ activeCubeFace ]; - isCube = true; + } else if ( ( capabilities.isWebGL2 && renderTarget.samples > 0 ) && textures.useMultisampledRTT( renderTarget ) === false ) { - } else if ( ( capabilities.isWebGL2 && renderTarget.samples > 0 ) && textures.useMultisampledRTT( renderTarget ) === false ) { + framebuffer = properties.get( renderTarget ).__webglMultisampledFramebuffer; - framebuffer = properties.get( renderTarget ).__webglMultisampledFramebuffer; + } else { - } else { + framebuffer = __webglFramebuffer; - framebuffer = __webglFramebuffer; + } - } + _currentViewport.copy( renderTarget.viewport ); + _currentScissor.copy( renderTarget.scissor ); + _currentScissorTest = renderTarget.scissorTest; - _currentViewport.copy( renderTarget.viewport ); - _currentScissor.copy( renderTarget.scissor ); - _currentScissorTest = renderTarget.scissorTest; + } else { - } else { + _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ).floor(); + _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ).floor(); + _currentScissorTest = _scissorTest; - _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ).floor(); - _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ).floor(); - _currentScissorTest = _scissorTest; + } - } + const framebufferBound = state.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); - const framebufferBound = state.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); + if ( framebufferBound && capabilities.drawBuffers && useDefaultFramebuffer ) { - if ( framebufferBound && capabilities.drawBuffers && useDefaultFramebuffer ) { + state.drawBuffers( renderTarget, framebuffer ); - state.drawBuffers( renderTarget, framebuffer ); + } - } + state.viewport( _currentViewport ); + state.scissor( _currentScissor ); + state.setScissorTest( _currentScissorTest ); - state.viewport( _currentViewport ); - state.scissor( _currentScissor ); - state.setScissorTest( _currentScissorTest ); + if ( isCube ) { - if ( isCube ) { + const textureProperties = properties.get( renderTarget.texture ); + _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + activeCubeFace, textureProperties.__webglTexture, activeMipmapLevel ); - const textureProperties = properties.get( renderTarget.texture ); - _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + activeCubeFace, textureProperties.__webglTexture, activeMipmapLevel ); + } else if ( isRenderTarget3D ) { - } else if ( isRenderTarget3D ) { + const textureProperties = properties.get( renderTarget.texture ); + const layer = activeCubeFace || 0; + _gl.framebufferTextureLayer( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, textureProperties.__webglTexture, activeMipmapLevel || 0, layer ); - const textureProperties = properties.get( renderTarget.texture ); - const layer = activeCubeFace || 0; - _gl.framebufferTextureLayer( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, textureProperties.__webglTexture, activeMipmapLevel || 0, layer ); + } - } + _currentMaterialId = - 1; // reset current material to ensure correct uniform bindings - _currentMaterialId = - 1; // reset current material to ensure correct uniform bindings + }; - }; + this.readRenderTargetPixels = function ( renderTarget, x, y, width, height, buffer, activeCubeFaceIndex ) { - this.readRenderTargetPixels = function ( renderTarget, x, y, width, height, buffer, activeCubeFaceIndex ) { + if ( ! ( renderTarget && renderTarget.isWebGLRenderTarget ) ) { - if ( ! ( renderTarget && renderTarget.isWebGLRenderTarget ) ) { + console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.' ); + return; - console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.' ); - return; + } - } + let framebuffer = properties.get( renderTarget ).__webglFramebuffer; - let framebuffer = properties.get( renderTarget ).__webglFramebuffer; + if ( renderTarget.isWebGLCubeRenderTarget && activeCubeFaceIndex !== undefined ) { - if ( renderTarget.isWebGLCubeRenderTarget && activeCubeFaceIndex !== undefined ) { + framebuffer = framebuffer[ activeCubeFaceIndex ]; - framebuffer = framebuffer[ activeCubeFaceIndex ]; + } - } + if ( framebuffer ) { - if ( framebuffer ) { + state.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); - state.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); + try { - try { + const texture = renderTarget.texture; + const textureFormat = texture.format; + const textureType = texture.type; - const texture = renderTarget.texture; - const textureFormat = texture.format; - const textureType = texture.type; + if ( textureFormat !== RGBAFormat && utils.convert( textureFormat ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_FORMAT ) ) { - if ( textureFormat !== RGBAFormat && utils.convert( textureFormat ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_FORMAT ) ) { + console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format.' ); + return; - console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format.' ); - return; + } - } + const halfFloatSupportedByExt = ( textureType === HalfFloatType ) && ( extensions.has( 'EXT_color_buffer_half_float' ) || ( capabilities.isWebGL2 && extensions.has( 'EXT_color_buffer_float' ) ) ); - const halfFloatSupportedByExt = ( textureType === HalfFloatType ) && ( extensions.has( 'EXT_color_buffer_half_float' ) || ( capabilities.isWebGL2 && extensions.has( 'EXT_color_buffer_float' ) ) ); + if ( textureType !== UnsignedByteType && utils.convert( textureType ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_TYPE ) && // Edge and Chrome Mac < 52 (#9513) + ! ( textureType === FloatType && ( capabilities.isWebGL2 || extensions.has( 'OES_texture_float' ) || extensions.has( 'WEBGL_color_buffer_float' ) ) ) && // Chrome Mac >= 52 and Firefox + ! halfFloatSupportedByExt ) { - if ( textureType !== UnsignedByteType && utils.convert( textureType ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_TYPE ) && // Edge and Chrome Mac < 52 (#9513) - ! ( textureType === FloatType && ( capabilities.isWebGL2 || extensions.has( 'OES_texture_float' ) || extensions.has( 'WEBGL_color_buffer_float' ) ) ) && // Chrome Mac >= 52 and Firefox - ! halfFloatSupportedByExt ) { + console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.' ); + return; - console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.' ); - return; + } - } + // the following if statement ensures valid read requests (no out-of-bounds pixels, see #8604) - // the following if statement ensures valid read requests (no out-of-bounds pixels, see #8604) + if ( ( x >= 0 && x <= ( renderTarget.width - width ) ) && ( y >= 0 && y <= ( renderTarget.height - height ) ) ) { - if ( ( x >= 0 && x <= ( renderTarget.width - width ) ) && ( y >= 0 && y <= ( renderTarget.height - height ) ) ) { + _gl.readPixels( x, y, width, height, utils.convert( textureFormat ), utils.convert( textureType ), buffer ); - _gl.readPixels( x, y, width, height, utils.convert( textureFormat ), utils.convert( textureType ), buffer ); + } - } + } finally { - } finally { + // restore framebuffer of current render target if necessary - // restore framebuffer of current render target if necessary + const framebuffer = ( _currentRenderTarget !== null ) ? properties.get( _currentRenderTarget ).__webglFramebuffer : null; + state.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); - const framebuffer = ( _currentRenderTarget !== null ) ? properties.get( _currentRenderTarget ).__webglFramebuffer : null; - state.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); + } } - } + }; - }; + this.copyFramebufferToTexture = function ( position, texture, level = 0 ) { - this.copyFramebufferToTexture = function ( position, texture, level = 0 ) { + const levelScale = Math.pow( 2, - level ); + const width = Math.floor( texture.image.width * levelScale ); + const height = Math.floor( texture.image.height * levelScale ); - const levelScale = Math.pow( 2, - level ); - const width = Math.floor( texture.image.width * levelScale ); - const height = Math.floor( texture.image.height * levelScale ); + textures.setTexture2D( texture, 0 ); - textures.setTexture2D( texture, 0 ); + _gl.copyTexSubImage2D( _gl.TEXTURE_2D, level, 0, 0, position.x, position.y, width, height ); - _gl.copyTexSubImage2D( _gl.TEXTURE_2D, level, 0, 0, position.x, position.y, width, height ); + state.unbindTexture(); - state.unbindTexture(); + }; - }; + this.copyTextureToTexture = function ( position, srcTexture, dstTexture, level = 0 ) { - this.copyTextureToTexture = function ( position, srcTexture, dstTexture, level = 0 ) { + const width = srcTexture.image.width; + const height = srcTexture.image.height; + const glFormat = utils.convert( dstTexture.format ); + const glType = utils.convert( dstTexture.type ); - const width = srcTexture.image.width; - const height = srcTexture.image.height; - const glFormat = utils.convert( dstTexture.format ); - const glType = utils.convert( dstTexture.type ); + textures.setTexture2D( dstTexture, 0 ); - textures.setTexture2D( dstTexture, 0 ); + // As another texture upload may have changed pixelStorei + // parameters, make sure they are correct for the dstTexture + _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, dstTexture.flipY ); + _gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, dstTexture.premultiplyAlpha ); + _gl.pixelStorei( _gl.UNPACK_ALIGNMENT, dstTexture.unpackAlignment ); - // As another texture upload may have changed pixelStorei - // parameters, make sure they are correct for the dstTexture - _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, dstTexture.flipY ); - _gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, dstTexture.premultiplyAlpha ); - _gl.pixelStorei( _gl.UNPACK_ALIGNMENT, dstTexture.unpackAlignment ); + if ( srcTexture.isDataTexture ) { - if ( srcTexture.isDataTexture ) { + _gl.texSubImage2D( _gl.TEXTURE_2D, level, position.x, position.y, width, height, glFormat, glType, srcTexture.image.data ); - _gl.texSubImage2D( _gl.TEXTURE_2D, level, position.x, position.y, width, height, glFormat, glType, srcTexture.image.data ); + } else { - } else { + if ( srcTexture.isCompressedTexture ) { - if ( srcTexture.isCompressedTexture ) { + _gl.compressedTexSubImage2D( _gl.TEXTURE_2D, level, position.x, position.y, srcTexture.mipmaps[ 0 ].width, srcTexture.mipmaps[ 0 ].height, glFormat, srcTexture.mipmaps[ 0 ].data ); - _gl.compressedTexSubImage2D( _gl.TEXTURE_2D, level, position.x, position.y, srcTexture.mipmaps[ 0 ].width, srcTexture.mipmaps[ 0 ].height, glFormat, srcTexture.mipmaps[ 0 ].data ); + } else { - } else { + _gl.texSubImage2D( _gl.TEXTURE_2D, level, position.x, position.y, glFormat, glType, srcTexture.image ); - _gl.texSubImage2D( _gl.TEXTURE_2D, level, position.x, position.y, glFormat, glType, srcTexture.image ); + } } - } + // Generate mipmaps only when copying level 0 + if ( level === 0 && dstTexture.generateMipmaps ) _gl.generateMipmap( _gl.TEXTURE_2D ); - // Generate mipmaps only when copying level 0 - if ( level === 0 && dstTexture.generateMipmaps ) _gl.generateMipmap( _gl.TEXTURE_2D ); + state.unbindTexture(); - state.unbindTexture(); + }; - }; + this.copyTextureToTexture3D = function ( sourceBox, position, srcTexture, dstTexture, level = 0 ) { - this.copyTextureToTexture3D = function ( sourceBox, position, srcTexture, dstTexture, level = 0 ) { + if ( _this.isWebGL1Renderer ) { - if ( _this.isWebGL1Renderer ) { + console.warn( 'THREE.WebGLRenderer.copyTextureToTexture3D: can only be used with WebGL2.' ); + return; - console.warn( 'THREE.WebGLRenderer.copyTextureToTexture3D: can only be used with WebGL2.' ); - return; + } - } + const width = sourceBox.max.x - sourceBox.min.x + 1; + const height = sourceBox.max.y - sourceBox.min.y + 1; + const depth = sourceBox.max.z - sourceBox.min.z + 1; + const glFormat = utils.convert( dstTexture.format ); + const glType = utils.convert( dstTexture.type ); + let glTarget; - const width = sourceBox.max.x - sourceBox.min.x + 1; - const height = sourceBox.max.y - sourceBox.min.y + 1; - const depth = sourceBox.max.z - sourceBox.min.z + 1; - const glFormat = utils.convert( dstTexture.format ); - const glType = utils.convert( dstTexture.type ); - let glTarget; + if ( dstTexture.isData3DTexture ) { - if ( dstTexture.isData3DTexture ) { + textures.setTexture3D( dstTexture, 0 ); + glTarget = _gl.TEXTURE_3D; - textures.setTexture3D( dstTexture, 0 ); - glTarget = _gl.TEXTURE_3D; + } else if ( dstTexture.isDataArrayTexture ) { - } else if ( dstTexture.isDataArrayTexture ) { + textures.setTexture2DArray( dstTexture, 0 ); + glTarget = _gl.TEXTURE_2D_ARRAY; - textures.setTexture2DArray( dstTexture, 0 ); - glTarget = _gl.TEXTURE_2D_ARRAY; + } else { - } else { + console.warn( 'THREE.WebGLRenderer.copyTextureToTexture3D: only supports THREE.DataTexture3D and THREE.DataTexture2DArray.' ); + return; - console.warn( 'THREE.WebGLRenderer.copyTextureToTexture3D: only supports THREE.DataTexture3D and THREE.DataTexture2DArray.' ); - return; + } - } + _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, dstTexture.flipY ); + _gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, dstTexture.premultiplyAlpha ); + _gl.pixelStorei( _gl.UNPACK_ALIGNMENT, dstTexture.unpackAlignment ); - _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, dstTexture.flipY ); - _gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, dstTexture.premultiplyAlpha ); - _gl.pixelStorei( _gl.UNPACK_ALIGNMENT, dstTexture.unpackAlignment ); + const unpackRowLen = _gl.getParameter( _gl.UNPACK_ROW_LENGTH ); + const unpackImageHeight = _gl.getParameter( _gl.UNPACK_IMAGE_HEIGHT ); + const unpackSkipPixels = _gl.getParameter( _gl.UNPACK_SKIP_PIXELS ); + const unpackSkipRows = _gl.getParameter( _gl.UNPACK_SKIP_ROWS ); + const unpackSkipImages = _gl.getParameter( _gl.UNPACK_SKIP_IMAGES ); - const unpackRowLen = _gl.getParameter( _gl.UNPACK_ROW_LENGTH ); - const unpackImageHeight = _gl.getParameter( _gl.UNPACK_IMAGE_HEIGHT ); - const unpackSkipPixels = _gl.getParameter( _gl.UNPACK_SKIP_PIXELS ); - const unpackSkipRows = _gl.getParameter( _gl.UNPACK_SKIP_ROWS ); - const unpackSkipImages = _gl.getParameter( _gl.UNPACK_SKIP_IMAGES ); + const image = srcTexture.isCompressedTexture ? srcTexture.mipmaps[ 0 ] : srcTexture.image; - const image = srcTexture.isCompressedTexture ? srcTexture.mipmaps[ 0 ] : srcTexture.image; + _gl.pixelStorei( _gl.UNPACK_ROW_LENGTH, image.width ); + _gl.pixelStorei( _gl.UNPACK_IMAGE_HEIGHT, image.height ); + _gl.pixelStorei( _gl.UNPACK_SKIP_PIXELS, sourceBox.min.x ); + _gl.pixelStorei( _gl.UNPACK_SKIP_ROWS, sourceBox.min.y ); + _gl.pixelStorei( _gl.UNPACK_SKIP_IMAGES, sourceBox.min.z ); - _gl.pixelStorei( _gl.UNPACK_ROW_LENGTH, image.width ); - _gl.pixelStorei( _gl.UNPACK_IMAGE_HEIGHT, image.height ); - _gl.pixelStorei( _gl.UNPACK_SKIP_PIXELS, sourceBox.min.x ); - _gl.pixelStorei( _gl.UNPACK_SKIP_ROWS, sourceBox.min.y ); - _gl.pixelStorei( _gl.UNPACK_SKIP_IMAGES, sourceBox.min.z ); + if ( srcTexture.isDataTexture || srcTexture.isData3DTexture ) { - if ( srcTexture.isDataTexture || srcTexture.isData3DTexture ) { + _gl.texSubImage3D( glTarget, level, position.x, position.y, position.z, width, height, depth, glFormat, glType, image.data ); - _gl.texSubImage3D( glTarget, level, position.x, position.y, position.z, width, height, depth, glFormat, glType, image.data ); + } else { - } else { + if ( srcTexture.isCompressedArrayTexture ) { - if ( srcTexture.isCompressedArrayTexture ) { + console.warn( 'THREE.WebGLRenderer.copyTextureToTexture3D: untested support for compressed srcTexture.' ); + _gl.compressedTexSubImage3D( glTarget, level, position.x, position.y, position.z, width, height, depth, glFormat, image.data ); - console.warn( 'THREE.WebGLRenderer.copyTextureToTexture3D: untested support for compressed srcTexture.' ); - _gl.compressedTexSubImage3D( glTarget, level, position.x, position.y, position.z, width, height, depth, glFormat, image.data ); + } else { - } else { + _gl.texSubImage3D( glTarget, level, position.x, position.y, position.z, width, height, depth, glFormat, glType, image ); - _gl.texSubImage3D( glTarget, level, position.x, position.y, position.z, width, height, depth, glFormat, glType, image ); + } } - } + _gl.pixelStorei( _gl.UNPACK_ROW_LENGTH, unpackRowLen ); + _gl.pixelStorei( _gl.UNPACK_IMAGE_HEIGHT, unpackImageHeight ); + _gl.pixelStorei( _gl.UNPACK_SKIP_PIXELS, unpackSkipPixels ); + _gl.pixelStorei( _gl.UNPACK_SKIP_ROWS, unpackSkipRows ); + _gl.pixelStorei( _gl.UNPACK_SKIP_IMAGES, unpackSkipImages ); - _gl.pixelStorei( _gl.UNPACK_ROW_LENGTH, unpackRowLen ); - _gl.pixelStorei( _gl.UNPACK_IMAGE_HEIGHT, unpackImageHeight ); - _gl.pixelStorei( _gl.UNPACK_SKIP_PIXELS, unpackSkipPixels ); - _gl.pixelStorei( _gl.UNPACK_SKIP_ROWS, unpackSkipRows ); - _gl.pixelStorei( _gl.UNPACK_SKIP_IMAGES, unpackSkipImages ); + // Generate mipmaps only when copying level 0 + if ( level === 0 && dstTexture.generateMipmaps ) _gl.generateMipmap( glTarget ); - // Generate mipmaps only when copying level 0 - if ( level === 0 && dstTexture.generateMipmaps ) _gl.generateMipmap( glTarget ); + state.unbindTexture(); - state.unbindTexture(); + }; - }; + this.initTexture = function ( texture ) { - this.initTexture = function ( texture ) { + if ( texture.isCubeTexture ) { - if ( texture.isCubeTexture ) { + textures.setTextureCube( texture, 0 ); - textures.setTextureCube( texture, 0 ); + } else if ( texture.isData3DTexture ) { - } else if ( texture.isData3DTexture ) { + textures.setTexture3D( texture, 0 ); - textures.setTexture3D( texture, 0 ); + } else if ( texture.isDataArrayTexture || texture.isCompressedArrayTexture ) { - } else if ( texture.isDataArrayTexture || texture.isCompressedArrayTexture ) { + textures.setTexture2DArray( texture, 0 ); - textures.setTexture2DArray( texture, 0 ); + } else { - } else { + textures.setTexture2D( texture, 0 ); - textures.setTexture2D( texture, 0 ); + } - } + state.unbindTexture(); - state.unbindTexture(); + }; - }; + this.resetState = function () { - this.resetState = function () { + _currentActiveCubeFace = 0; + _currentActiveMipmapLevel = 0; + _currentRenderTarget = null; - _currentActiveCubeFace = 0; - _currentActiveMipmapLevel = 0; - _currentRenderTarget = null; + state.reset(); + bindingStates.reset(); - state.reset(); - bindingStates.reset(); + }; - }; + if ( typeof __THREE_DEVTOOLS__ !== 'undefined' ) { - if ( typeof __THREE_DEVTOOLS__ !== 'undefined' ) { + __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( 'observe', { detail: this } ) ); - __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( 'observe', { detail: this } ) ); + } } -} - -Object.defineProperties( WebGLRenderer.prototype, { - // @deprecated since r150 - physicallyCorrectLights: { + get physicallyCorrectLights() { - get: function () { + console.warn( 'THREE.WebGLRenderer: the property .physicallyCorrectLights has been removed. Set renderer.useLegacyLights instead.' ); + return ! this.useLegacyLights; - console.warn( 'THREE.WebGLRenderer: the property .physicallyCorrectLights has been removed. Set renderer.useLegacyLights instead.' ); - return ! this.useLegacyLights; - - }, + } - set: function ( value ) { + // @deprecated since r150 - console.warn( 'THREE.WebGLRenderer: the property .physicallyCorrectLights has been removed. Set renderer.useLegacyLights instead.' ); - this.useLegacyLights = ! value; + set physicallyCorrectLights( value ) { - } + console.warn( 'THREE.WebGLRenderer: the property .physicallyCorrectLights has been removed. Set renderer.useLegacyLights instead.' ); + this.useLegacyLights = ! value; } -} ); +} + export { WebGLRenderer };