Spaces:
Running
Running
| import { | |
| REVISION, | |
| RGBAFormat, | |
| HalfFloatType, | |
| FloatType, | |
| UnsignedByteType, | |
| TriangleFanDrawMode, | |
| TriangleStripDrawMode, | |
| TrianglesDrawMode, | |
| LinearToneMapping, | |
| BackSide | |
| } from '../constants.js'; | |
| import { _Math } from '../math/Math.js'; | |
| import { DataTexture } from '../textures/DataTexture.js'; | |
| import { Frustum } from '../math/Frustum.js'; | |
| import { Matrix4 } from '../math/Matrix4.js'; | |
| import { ShaderLib } from './shaders/ShaderLib.js'; | |
| import { UniformsLib } from './shaders/UniformsLib.js'; | |
| import { cloneUniforms } from './shaders/UniformsUtils.js'; | |
| import { Vector2 } from '../math/Vector2.js'; | |
| import { Vector3 } from '../math/Vector3.js'; | |
| import { Vector4 } from '../math/Vector4.js'; | |
| import { WebGLAnimation } from './webgl/WebGLAnimation.js'; | |
| import { WebGLAttributes } from './webgl/WebGLAttributes.js'; | |
| import { WebGLBackground } from './webgl/WebGLBackground.js'; | |
| import { WebGLBufferRenderer } from './webgl/WebGLBufferRenderer.js'; | |
| import { WebGLCapabilities } from './webgl/WebGLCapabilities.js'; | |
| import { WebGLClipping } from './webgl/WebGLClipping.js'; | |
| import { WebGLExtensions } from './webgl/WebGLExtensions.js'; | |
| import { WebGLGeometries } from './webgl/WebGLGeometries.js'; | |
| import { WebGLIndexedBufferRenderer } from './webgl/WebGLIndexedBufferRenderer.js'; | |
| import { WebGLInfo } from './webgl/WebGLInfo.js'; | |
| import { WebGLMorphtargets } from './webgl/WebGLMorphtargets.js'; | |
| import { WebGLObjects } from './webgl/WebGLObjects.js'; | |
| import { WebGLPrograms } from './webgl/WebGLPrograms.js'; | |
| import { WebGLProperties } from './webgl/WebGLProperties.js'; | |
| import { WebGLRenderLists } from './webgl/WebGLRenderLists.js'; | |
| import { WebGLRenderStates } from './webgl/WebGLRenderStates.js'; | |
| import { WebGLShadowMap } from './webgl/WebGLShadowMap.js'; | |
| import { WebGLState } from './webgl/WebGLState.js'; | |
| import { WebGLTextures } from './webgl/WebGLTextures.js'; | |
| import { WebGLUniforms } from './webgl/WebGLUniforms.js'; | |
| import { WebGLUtils } from './webgl/WebGLUtils.js'; | |
| import { WebVRManager } from './webvr/WebVRManager.js'; | |
| import { WebXRManager } from './webvr/WebXRManager.js'; | |
| /** | |
| * @author supereggbert / http://www.paulbrunt.co.uk/ | |
| * @author mrdoob / http://mrdoob.com/ | |
| * @author alteredq / http://alteredqualia.com/ | |
| * @author szimek / https://github.com/szimek/ | |
| * @author tschw | |
| */ | |
| function WebGLRenderer( parameters ) { | |
| console.log( 'THREE.WebGLRenderer', REVISION ); | |
| parameters = parameters || {}; | |
| var _canvas = parameters.canvas !== undefined ? parameters.canvas : document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ), | |
| _context = parameters.context !== undefined ? parameters.context : null, | |
| _alpha = parameters.alpha !== undefined ? parameters.alpha : false, | |
| _depth = parameters.depth !== undefined ? parameters.depth : true, | |
| _stencil = parameters.stencil !== undefined ? parameters.stencil : true, | |
| _antialias = parameters.antialias !== undefined ? parameters.antialias : false, | |
| _premultipliedAlpha = parameters.premultipliedAlpha !== undefined ? parameters.premultipliedAlpha : true, | |
| _preserveDrawingBuffer = parameters.preserveDrawingBuffer !== undefined ? parameters.preserveDrawingBuffer : false, | |
| _powerPreference = parameters.powerPreference !== undefined ? parameters.powerPreference : 'default'; | |
| var currentRenderList = null; | |
| var currentRenderState = null; | |
| // public properties | |
| this.domElement = _canvas; | |
| this.context = null; | |
| // clearing | |
| this.autoClear = true; | |
| this.autoClearColor = true; | |
| this.autoClearDepth = true; | |
| this.autoClearStencil = true; | |
| // scene graph | |
| this.sortObjects = true; | |
| // user-defined clipping | |
| this.clippingPlanes = []; | |
| this.localClippingEnabled = false; | |
| // physically based shading | |
| this.gammaFactor = 2.0; // for backwards compatibility | |
| this.gammaInput = false; | |
| this.gammaOutput = false; | |
| // physical lights | |
| this.physicallyCorrectLights = false; | |
| // tone mapping | |
| this.toneMapping = LinearToneMapping; | |
| this.toneMappingExposure = 1.0; | |
| this.toneMappingWhitePoint = 1.0; | |
| // morphs | |
| this.maxMorphTargets = 8; | |
| this.maxMorphNormals = 4; | |
| // internal properties | |
| var _this = this, | |
| _isContextLost = false, | |
| // internal state cache | |
| _framebuffer = null, | |
| _currentRenderTarget = null, | |
| _currentFramebuffer = null, | |
| _currentMaterialId = - 1, | |
| // geometry and program caching | |
| _currentGeometryProgram = { | |
| geometry: null, | |
| program: null, | |
| wireframe: false | |
| }, | |
| _currentCamera = null, | |
| _currentArrayCamera = null, | |
| _currentViewport = new Vector4(), | |
| _currentScissor = new Vector4(), | |
| _currentScissorTest = null, | |
| // | |
| _width = _canvas.width, | |
| _height = _canvas.height, | |
| _pixelRatio = 1, | |
| _viewport = new Vector4( 0, 0, _width, _height ), | |
| _scissor = new Vector4( 0, 0, _width, _height ), | |
| _scissorTest = false, | |
| // frustum | |
| _frustum = new Frustum(), | |
| // clipping | |
| _clipping = new WebGLClipping(), | |
| _clippingEnabled = false, | |
| _localClippingEnabled = false, | |
| // camera matrices cache | |
| _projScreenMatrix = new Matrix4(), | |
| _vector3 = new Vector3(); | |
| function getTargetPixelRatio() { | |
| return _currentRenderTarget === null ? _pixelRatio : 1; | |
| } | |
| // initialize | |
| var _gl; | |
| try { | |
| var contextAttributes = { | |
| alpha: _alpha, | |
| depth: _depth, | |
| stencil: _stencil, | |
| antialias: _antialias, | |
| premultipliedAlpha: _premultipliedAlpha, | |
| preserveDrawingBuffer: _preserveDrawingBuffer, | |
| powerPreference: _powerPreference | |
| }; | |
| // event listeners must be registered before WebGL context is created, see #12753 | |
| _canvas.addEventListener( 'webglcontextlost', onContextLost, false ); | |
| _canvas.addEventListener( 'webglcontextrestored', onContextRestore, false ); | |
| _gl = _context || _canvas.getContext( 'webgl', contextAttributes ) || _canvas.getContext( 'experimental-webgl', contextAttributes ); | |
| if ( _gl === null ) { | |
| if ( _canvas.getContext( 'webgl' ) !== null ) { | |
| throw new Error( 'Error creating WebGL context with your selected attributes.' ); | |
| } else { | |
| throw new Error( 'Error creating WebGL context.' ); | |
| } | |
| } | |
| // Some experimental-webgl implementations do not have getShaderPrecisionFormat | |
| if ( _gl.getShaderPrecisionFormat === undefined ) { | |
| _gl.getShaderPrecisionFormat = function () { | |
| return { 'rangeMin': 1, 'rangeMax': 1, 'precision': 1 }; | |
| }; | |
| } | |
| } catch ( error ) { | |
| console.error( 'THREE.WebGLRenderer: ' + error.message ); | |
| throw error; | |
| } | |
| var extensions, capabilities, state, info; | |
| var properties, textures, attributes, geometries, objects; | |
| var programCache, renderLists, renderStates; | |
| var background, morphtargets, bufferRenderer, indexedBufferRenderer; | |
| var utils; | |
| function initGLContext() { | |
| extensions = new WebGLExtensions( _gl ); | |
| capabilities = new WebGLCapabilities( _gl, extensions, parameters ); | |
| if ( ! capabilities.isWebGL2 ) { | |
| extensions.get( 'WEBGL_depth_texture' ); | |
| extensions.get( 'OES_texture_float' ); | |
| extensions.get( 'OES_texture_half_float' ); | |
| extensions.get( 'OES_texture_half_float_linear' ); | |
| extensions.get( 'OES_standard_derivatives' ); | |
| extensions.get( 'OES_element_index_uint' ); | |
| extensions.get( 'ANGLE_instanced_arrays' ); | |
| } | |
| extensions.get( 'OES_texture_float_linear' ); | |
| utils = new WebGLUtils( _gl, extensions, capabilities ); | |
| state = new WebGLState( _gl, extensions, utils, capabilities ); | |
| state.scissor( _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ) ); | |
| state.viewport( _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ) ); | |
| info = new WebGLInfo( _gl ); | |
| properties = new WebGLProperties(); | |
| textures = new WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info ); | |
| attributes = new WebGLAttributes( _gl ); | |
| geometries = new WebGLGeometries( _gl, attributes, info ); | |
| objects = new WebGLObjects( geometries, info ); | |
| morphtargets = new WebGLMorphtargets( _gl ); | |
| programCache = new WebGLPrograms( _this, extensions, capabilities, textures ); | |
| renderLists = new WebGLRenderLists(); | |
| renderStates = new WebGLRenderStates(); | |
| background = new WebGLBackground( _this, state, objects, _premultipliedAlpha ); | |
| bufferRenderer = new WebGLBufferRenderer( _gl, extensions, info, capabilities ); | |
| indexedBufferRenderer = new WebGLIndexedBufferRenderer( _gl, extensions, info, capabilities ); | |
| info.programs = programCache.programs; | |
| _this.context = _gl; | |
| _this.capabilities = capabilities; | |
| _this.extensions = extensions; | |
| _this.properties = properties; | |
| _this.renderLists = renderLists; | |
| _this.state = state; | |
| _this.info = info; | |
| } | |
| initGLContext(); | |
| // vr | |
| var vr = null; | |
| if ( typeof navigator !== 'undefined' ) { | |
| vr = ( 'xr' in navigator ) ? new WebXRManager( _this ) : new WebVRManager( _this ); | |
| } | |
| this.vr = vr; | |
| // shadow map | |
| var shadowMap = new WebGLShadowMap( _this, objects, capabilities.maxTextureSize ); | |
| this.shadowMap = shadowMap; | |
| // API | |
| this.getContext = function () { | |
| return _gl; | |
| }; | |
| this.getContextAttributes = function () { | |
| return _gl.getContextAttributes(); | |
| }; | |
| this.forceContextLoss = function () { | |
| var extension = extensions.get( 'WEBGL_lose_context' ); | |
| if ( extension ) extension.loseContext(); | |
| }; | |
| this.forceContextRestore = function () { | |
| var extension = extensions.get( 'WEBGL_lose_context' ); | |
| if ( extension ) extension.restoreContext(); | |
| }; | |
| this.getPixelRatio = function () { | |
| return _pixelRatio; | |
| }; | |
| this.setPixelRatio = function ( value ) { | |
| if ( value === undefined ) return; | |
| _pixelRatio = value; | |
| this.setSize( _width, _height, false ); | |
| }; | |
| this.getSize = function ( target ) { | |
| if ( target === undefined ) { | |
| console.warn( 'WebGLRenderer: .getsize() now requires a Vector2 as an argument' ); | |
| target = new Vector2(); | |
| } | |
| return target.set( _width, _height ); | |
| }; | |
| this.setSize = function ( width, height, updateStyle ) { | |
| if ( vr.isPresenting() ) { | |
| console.warn( 'THREE.WebGLRenderer: Can\'t change size while VR device is presenting.' ); | |
| return; | |
| } | |
| _width = width; | |
| _height = height; | |
| _canvas.width = width * _pixelRatio; | |
| _canvas.height = height * _pixelRatio; | |
| if ( updateStyle !== false ) { | |
| _canvas.style.width = width + 'px'; | |
| _canvas.style.height = height + 'px'; | |
| } | |
| this.setViewport( 0, 0, width, height ); | |
| }; | |
| this.getDrawingBufferSize = function ( target ) { | |
| if ( target === undefined ) { | |
| console.warn( 'WebGLRenderer: .getdrawingBufferSize() now requires a Vector2 as an argument' ); | |
| target = new Vector2(); | |
| } | |
| return target.set( _width * _pixelRatio, _height * _pixelRatio ); | |
| }; | |
| this.setDrawingBufferSize = function ( width, height, pixelRatio ) { | |
| _width = width; | |
| _height = height; | |
| _pixelRatio = pixelRatio; | |
| _canvas.width = width * pixelRatio; | |
| _canvas.height = height * pixelRatio; | |
| this.setViewport( 0, 0, width, height ); | |
| }; | |
| this.getCurrentViewport = function ( target ) { | |
| if ( target === undefined ) { | |
| console.warn( 'WebGLRenderer: .getCurrentViewport() now requires a Vector4 as an argument' ); | |
| target = new Vector4(); | |
| } | |
| return target.copy( _currentViewport ); | |
| }; | |
| this.getViewport = function ( target ) { | |
| return target.copy( _viewport ); | |
| }; | |
| this.setViewport = function ( x, y, width, height ) { | |
| if ( x.isVector4 ) { | |
| _viewport.set( x.x, x.y, x.z, x.w ); | |
| } else { | |
| _viewport.set( x, y, width, height ); | |
| } | |
| state.viewport( _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ) ); | |
| }; | |
| this.getScissor = function ( target ) { | |
| return target.copy( _scissor ); | |
| }; | |
| this.setScissor = function ( x, y, width, height ) { | |
| if ( x.isVector4 ) { | |
| _scissor.set( x.x, x.y, x.z, x.w ); | |
| } else { | |
| _scissor.set( x, y, width, height ); | |
| } | |
| state.scissor( _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ) ); | |
| }; | |
| this.getScissorTest = function () { | |
| return _scissorTest; | |
| }; | |
| this.setScissorTest = function ( boolean ) { | |
| state.setScissorTest( _scissorTest = boolean ); | |
| }; | |
| // Clearing | |
| this.getClearColor = function () { | |
| return background.getClearColor(); | |
| }; | |
| this.setClearColor = function () { | |
| background.setClearColor.apply( background, arguments ); | |
| }; | |
| this.getClearAlpha = function () { | |
| return background.getClearAlpha(); | |
| }; | |
| this.setClearAlpha = function () { | |
| background.setClearAlpha.apply( background, arguments ); | |
| }; | |
| this.clear = function ( color, depth, stencil ) { | |
| var bits = 0; | |
| if ( color === undefined || color ) bits |= _gl.COLOR_BUFFER_BIT; | |
| if ( depth === undefined || depth ) bits |= _gl.DEPTH_BUFFER_BIT; | |
| if ( stencil === undefined || stencil ) bits |= _gl.STENCIL_BUFFER_BIT; | |
| _gl.clear( bits ); | |
| }; | |
| this.clearColor = function () { | |
| this.clear( true, false, false ); | |
| }; | |
| this.clearDepth = function () { | |
| this.clear( false, true, false ); | |
| }; | |
| this.clearStencil = function () { | |
| this.clear( false, false, true ); | |
| }; | |
| // | |
| this.dispose = function () { | |
| _canvas.removeEventListener( 'webglcontextlost', onContextLost, false ); | |
| _canvas.removeEventListener( 'webglcontextrestored', onContextRestore, false ); | |
| renderLists.dispose(); | |
| renderStates.dispose(); | |
| properties.dispose(); | |
| objects.dispose(); | |
| vr.dispose(); | |
| animation.stop(); | |
| }; | |
| // Events | |
| function onContextLost( event ) { | |
| event.preventDefault(); | |
| console.log( 'THREE.WebGLRenderer: Context Lost.' ); | |
| _isContextLost = true; | |
| } | |
| function onContextRestore( /* event */ ) { | |
| console.log( 'THREE.WebGLRenderer: Context Restored.' ); | |
| _isContextLost = false; | |
| initGLContext(); | |
| } | |
| function onMaterialDispose( event ) { | |
| var material = event.target; | |
| material.removeEventListener( 'dispose', onMaterialDispose ); | |
| deallocateMaterial( material ); | |
| } | |
| // Buffer deallocation | |
| function deallocateMaterial( material ) { | |
| releaseMaterialProgramReference( material ); | |
| properties.remove( material ); | |
| } | |
| function releaseMaterialProgramReference( material ) { | |
| var programInfo = properties.get( material ).program; | |
| material.program = undefined; | |
| if ( programInfo !== undefined ) { | |
| programCache.releaseProgram( programInfo ); | |
| } | |
| } | |
| // Buffer rendering | |
| function renderObjectImmediate( object, program ) { | |
| object.render( function ( object ) { | |
| _this.renderBufferImmediate( object, program ); | |
| } ); | |
| } | |
| this.renderBufferImmediate = function ( object, program ) { | |
| state.initAttributes(); | |
| var buffers = properties.get( object ); | |
| if ( object.hasPositions && ! buffers.position ) buffers.position = _gl.createBuffer(); | |
| if ( object.hasNormals && ! buffers.normal ) buffers.normal = _gl.createBuffer(); | |
| if ( object.hasUvs && ! buffers.uv ) buffers.uv = _gl.createBuffer(); | |
| if ( object.hasColors && ! buffers.color ) buffers.color = _gl.createBuffer(); | |
| var programAttributes = program.getAttributes(); | |
| if ( object.hasPositions ) { | |
| _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.position ); | |
| _gl.bufferData( _gl.ARRAY_BUFFER, object.positionArray, _gl.DYNAMIC_DRAW ); | |
| state.enableAttribute( programAttributes.position ); | |
| _gl.vertexAttribPointer( programAttributes.position, 3, _gl.FLOAT, false, 0, 0 ); | |
| } | |
| if ( object.hasNormals ) { | |
| _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.normal ); | |
| _gl.bufferData( _gl.ARRAY_BUFFER, object.normalArray, _gl.DYNAMIC_DRAW ); | |
| state.enableAttribute( programAttributes.normal ); | |
| _gl.vertexAttribPointer( programAttributes.normal, 3, _gl.FLOAT, false, 0, 0 ); | |
| } | |
| if ( object.hasUvs ) { | |
| _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.uv ); | |
| _gl.bufferData( _gl.ARRAY_BUFFER, object.uvArray, _gl.DYNAMIC_DRAW ); | |
| state.enableAttribute( programAttributes.uv ); | |
| _gl.vertexAttribPointer( programAttributes.uv, 2, _gl.FLOAT, false, 0, 0 ); | |
| } | |
| if ( object.hasColors ) { | |
| _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.color ); | |
| _gl.bufferData( _gl.ARRAY_BUFFER, object.colorArray, _gl.DYNAMIC_DRAW ); | |
| state.enableAttribute( programAttributes.color ); | |
| _gl.vertexAttribPointer( programAttributes.color, 3, _gl.FLOAT, false, 0, 0 ); | |
| } | |
| state.disableUnusedAttributes(); | |
| _gl.drawArrays( _gl.TRIANGLES, 0, object.count ); | |
| object.count = 0; | |
| }; | |
| this.renderBufferDirect = function ( camera, fog, geometry, material, object, group ) { | |
| var frontFaceCW = ( object.isMesh && object.matrixWorld.determinant() < 0 ); | |
| state.setMaterial( material, frontFaceCW ); | |
| var program = setProgram( camera, fog, material, object ); | |
| var updateBuffers = false; | |
| if ( _currentGeometryProgram.geometry !== geometry.id || | |
| _currentGeometryProgram.program !== program.id || | |
| _currentGeometryProgram.wireframe !== ( material.wireframe === true ) ) { | |
| _currentGeometryProgram.geometry = geometry.id; | |
| _currentGeometryProgram.program = program.id; | |
| _currentGeometryProgram.wireframe = material.wireframe === true; | |
| updateBuffers = true; | |
| } | |
| if ( object.morphTargetInfluences ) { | |
| morphtargets.update( object, geometry, material, program ); | |
| updateBuffers = true; | |
| } | |
| // | |
| var index = geometry.index; | |
| var position = geometry.attributes.position; | |
| var rangeFactor = 1; | |
| if ( material.wireframe === true ) { | |
| index = geometries.getWireframeAttribute( geometry ); | |
| rangeFactor = 2; | |
| } | |
| var attribute; | |
| var renderer = bufferRenderer; | |
| if ( index !== null ) { | |
| attribute = attributes.get( index ); | |
| renderer = indexedBufferRenderer; | |
| renderer.setIndex( attribute ); | |
| } | |
| if ( updateBuffers ) { | |
| setupVertexAttributes( material, program, geometry ); | |
| if ( index !== null ) { | |
| _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, attribute.buffer ); | |
| } | |
| } | |
| // | |
| var dataCount = Infinity; | |
| if ( index !== null ) { | |
| dataCount = index.count; | |
| } else if ( position !== undefined ) { | |
| dataCount = position.count; | |
| } | |
| var rangeStart = geometry.drawRange.start * rangeFactor; | |
| var rangeCount = geometry.drawRange.count * rangeFactor; | |
| var groupStart = group !== null ? group.start * rangeFactor : 0; | |
| var groupCount = group !== null ? group.count * rangeFactor : Infinity; | |
| var drawStart = Math.max( rangeStart, groupStart ); | |
| var drawEnd = Math.min( dataCount, rangeStart + rangeCount, groupStart + groupCount ) - 1; | |
| var drawCount = Math.max( 0, drawEnd - drawStart + 1 ); | |
| if ( drawCount === 0 ) return; | |
| // | |
| if ( object.isMesh ) { | |
| if ( material.wireframe === true ) { | |
| state.setLineWidth( material.wireframeLinewidth * getTargetPixelRatio() ); | |
| renderer.setMode( _gl.LINES ); | |
| } else { | |
| switch ( object.drawMode ) { | |
| case TrianglesDrawMode: | |
| renderer.setMode( _gl.TRIANGLES ); | |
| break; | |
| case TriangleStripDrawMode: | |
| renderer.setMode( _gl.TRIANGLE_STRIP ); | |
| break; | |
| case TriangleFanDrawMode: | |
| renderer.setMode( _gl.TRIANGLE_FAN ); | |
| break; | |
| } | |
| } | |
| } else if ( object.isLine ) { | |
| var lineWidth = material.linewidth; | |
| if ( lineWidth === undefined ) lineWidth = 1; // Not using Line*Material | |
| state.setLineWidth( lineWidth * getTargetPixelRatio() ); | |
| if ( object.isLineSegments ) { | |
| renderer.setMode( _gl.LINES ); | |
| } else if ( object.isLineLoop ) { | |
| renderer.setMode( _gl.LINE_LOOP ); | |
| } else { | |
| renderer.setMode( _gl.LINE_STRIP ); | |
| } | |
| } else if ( object.isPoints ) { | |
| renderer.setMode( _gl.POINTS ); | |
| } else if ( object.isSprite ) { | |
| renderer.setMode( _gl.TRIANGLES ); | |
| } | |
| if ( geometry && geometry.isInstancedBufferGeometry ) { | |
| if ( geometry.maxInstancedCount > 0 ) { | |
| renderer.renderInstances( geometry, drawStart, drawCount ); | |
| } | |
| } else { | |
| renderer.render( drawStart, drawCount ); | |
| } | |
| }; | |
| function setupVertexAttributes( material, program, geometry ) { | |
| if ( geometry && geometry.isInstancedBufferGeometry && ! capabilities.isWebGL2 ) { | |
| if ( extensions.get( 'ANGLE_instanced_arrays' ) === null ) { | |
| console.error( 'THREE.WebGLRenderer.setupVertexAttributes: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' ); | |
| return; | |
| } | |
| } | |
| state.initAttributes(); | |
| var geometryAttributes = geometry.attributes; | |
| var programAttributes = program.getAttributes(); | |
| var materialDefaultAttributeValues = material.defaultAttributeValues; | |
| for ( var name in programAttributes ) { | |
| var programAttribute = programAttributes[ name ]; | |
| if ( programAttribute >= 0 ) { | |
| var geometryAttribute = geometryAttributes[ name ]; | |
| if ( geometryAttribute !== undefined ) { | |
| var normalized = geometryAttribute.normalized; | |
| var size = geometryAttribute.itemSize; | |
| var attribute = attributes.get( geometryAttribute ); | |
| // TODO Attribute may not be available on context restore | |
| if ( attribute === undefined ) continue; | |
| var buffer = attribute.buffer; | |
| var type = attribute.type; | |
| var bytesPerElement = attribute.bytesPerElement; | |
| if ( geometryAttribute.isInterleavedBufferAttribute ) { | |
| var data = geometryAttribute.data; | |
| var stride = data.stride; | |
| var offset = geometryAttribute.offset; | |
| if ( data && data.isInstancedInterleavedBuffer ) { | |
| state.enableAttributeAndDivisor( programAttribute, data.meshPerAttribute ); | |
| if ( geometry.maxInstancedCount === undefined ) { | |
| geometry.maxInstancedCount = data.meshPerAttribute * data.count; | |
| } | |
| } else { | |
| state.enableAttribute( programAttribute ); | |
| } | |
| _gl.bindBuffer( _gl.ARRAY_BUFFER, buffer ); | |
| _gl.vertexAttribPointer( programAttribute, size, type, normalized, stride * bytesPerElement, offset * bytesPerElement ); | |
| } else { | |
| if ( geometryAttribute.isInstancedBufferAttribute ) { | |
| state.enableAttributeAndDivisor( programAttribute, geometryAttribute.meshPerAttribute ); | |
| if ( geometry.maxInstancedCount === undefined ) { | |
| geometry.maxInstancedCount = geometryAttribute.meshPerAttribute * geometryAttribute.count; | |
| } | |
| } else { | |
| state.enableAttribute( programAttribute ); | |
| } | |
| _gl.bindBuffer( _gl.ARRAY_BUFFER, buffer ); | |
| _gl.vertexAttribPointer( programAttribute, size, type, normalized, 0, 0 ); | |
| } | |
| } else if ( materialDefaultAttributeValues !== undefined ) { | |
| var value = materialDefaultAttributeValues[ name ]; | |
| if ( value !== undefined ) { | |
| switch ( value.length ) { | |
| case 2: | |
| _gl.vertexAttrib2fv( programAttribute, value ); | |
| break; | |
| case 3: | |
| _gl.vertexAttrib3fv( programAttribute, value ); | |
| break; | |
| case 4: | |
| _gl.vertexAttrib4fv( programAttribute, value ); | |
| break; | |
| default: | |
| _gl.vertexAttrib1fv( programAttribute, value ); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| state.disableUnusedAttributes(); | |
| } | |
| // Compile | |
| this.compile = function ( scene, camera ) { | |
| currentRenderState = renderStates.get( scene, camera ); | |
| currentRenderState.init(); | |
| scene.traverse( function ( object ) { | |
| if ( object.isLight ) { | |
| currentRenderState.pushLight( object ); | |
| if ( object.castShadow ) { | |
| currentRenderState.pushShadow( object ); | |
| } | |
| } | |
| } ); | |
| currentRenderState.setupLights( camera ); | |
| scene.traverse( function ( object ) { | |
| if ( object.material ) { | |
| if ( Array.isArray( object.material ) ) { | |
| for ( var i = 0; i < object.material.length; i ++ ) { | |
| initMaterial( object.material[ i ], scene.fog, object ); | |
| } | |
| } else { | |
| initMaterial( object.material, scene.fog, object ); | |
| } | |
| } | |
| } ); | |
| }; | |
| // Animation Loop | |
| var onAnimationFrameCallback = null; | |
| function onAnimationFrame( time ) { | |
| if ( vr.isPresenting() ) return; | |
| if ( onAnimationFrameCallback ) onAnimationFrameCallback( time ); | |
| } | |
| var animation = new WebGLAnimation(); | |
| animation.setAnimationLoop( onAnimationFrame ); | |
| if ( typeof window !== 'undefined' ) animation.setContext( window ); | |
| this.setAnimationLoop = function ( callback ) { | |
| onAnimationFrameCallback = callback; | |
| vr.setAnimationLoop( callback ); | |
| animation.start(); | |
| }; | |
| // Rendering | |
| this.render = function ( scene, camera ) { | |
| var renderTarget, forceClear; | |
| if ( arguments[ 2 ] !== undefined ) { | |
| console.warn( 'THREE.WebGLRenderer.render(): the renderTarget argument has been removed. Use .setRenderTarget() instead.' ); | |
| renderTarget = arguments[ 2 ]; | |
| } | |
| if ( arguments[ 3 ] !== undefined ) { | |
| console.warn( 'THREE.WebGLRenderer.render(): the forceClear argument has been removed. Use .clear() instead.' ); | |
| forceClear = arguments[ 3 ]; | |
| } | |
| if ( ! ( camera && camera.isCamera ) ) { | |
| console.error( 'THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.' ); | |
| return; | |
| } | |
| if ( _isContextLost ) return; | |
| // reset caching for this frame | |
| _currentGeometryProgram.geometry = null; | |
| _currentGeometryProgram.program = null; | |
| _currentGeometryProgram.wireframe = false; | |
| _currentMaterialId = - 1; | |
| _currentCamera = null; | |
| // update scene graph | |
| if ( scene.autoUpdate === true ) scene.updateMatrixWorld(); | |
| // update camera matrices and frustum | |
| if ( camera.parent === null ) camera.updateMatrixWorld(); | |
| if ( vr.enabled ) { | |
| camera = vr.getCamera( camera ); | |
| } | |
| // | |
| currentRenderState = renderStates.get( scene, camera ); | |
| currentRenderState.init(); | |
| scene.onBeforeRender( _this, scene, camera, renderTarget || _currentRenderTarget ); | |
| _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); | |
| _frustum.setFromMatrix( _projScreenMatrix ); | |
| _localClippingEnabled = this.localClippingEnabled; | |
| _clippingEnabled = _clipping.init( this.clippingPlanes, _localClippingEnabled, camera ); | |
| currentRenderList = renderLists.get( scene, camera ); | |
| currentRenderList.init(); | |
| projectObject( scene, camera, 0, _this.sortObjects ); | |
| if ( _this.sortObjects === true ) { | |
| currentRenderList.sort(); | |
| } | |
| // | |
| if ( _clippingEnabled ) _clipping.beginShadows(); | |
| var shadowsArray = currentRenderState.state.shadowsArray; | |
| shadowMap.render( shadowsArray, scene, camera ); | |
| currentRenderState.setupLights( camera ); | |
| if ( _clippingEnabled ) _clipping.endShadows(); | |
| // | |
| if ( this.info.autoReset ) this.info.reset(); | |
| if ( renderTarget !== undefined ) { | |
| this.setRenderTarget( renderTarget ); | |
| } | |
| // | |
| background.render( currentRenderList, scene, camera, forceClear ); | |
| // render scene | |
| var opaqueObjects = currentRenderList.opaque; | |
| var transparentObjects = currentRenderList.transparent; | |
| if ( scene.overrideMaterial ) { | |
| var overrideMaterial = scene.overrideMaterial; | |
| if ( opaqueObjects.length ) renderObjects( opaqueObjects, scene, camera, overrideMaterial ); | |
| if ( transparentObjects.length ) renderObjects( transparentObjects, scene, camera, overrideMaterial ); | |
| } else { | |
| // opaque pass (front-to-back order) | |
| if ( opaqueObjects.length ) renderObjects( opaqueObjects, scene, camera ); | |
| // transparent pass (back-to-front order) | |
| if ( transparentObjects.length ) renderObjects( transparentObjects, scene, camera ); | |
| } | |
| // | |
| scene.onAfterRender( _this, scene, camera ); | |
| // | |
| if ( _currentRenderTarget !== null ) { | |
| // Generate mipmap if we're using any kind of mipmap filtering | |
| textures.updateRenderTargetMipmap( _currentRenderTarget ); | |
| // resolve multisample renderbuffers to a single-sample texture if necessary | |
| textures.updateMultisampleRenderTarget( _currentRenderTarget ); | |
| } | |
| // 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.setPolygonOffset( false ); | |
| if ( vr.enabled ) { | |
| vr.submitFrame(); | |
| } | |
| // _gl.finish(); | |
| currentRenderList = null; | |
| currentRenderState = null; | |
| }; | |
| function projectObject( object, camera, groupOrder, sortObjects ) { | |
| if ( object.visible === false ) return; | |
| var visible = object.layers.test( camera.layers ); | |
| if ( visible ) { | |
| if ( object.isGroup ) { | |
| groupOrder = object.renderOrder; | |
| } else if ( object.isLight ) { | |
| currentRenderState.pushLight( object ); | |
| if ( object.castShadow ) { | |
| currentRenderState.pushShadow( object ); | |
| } | |
| } else if ( object.isSprite ) { | |
| if ( ! object.frustumCulled || _frustum.intersectsSprite( object ) ) { | |
| if ( sortObjects ) { | |
| _vector3.setFromMatrixPosition( object.matrixWorld ) | |
| .applyMatrix4( _projScreenMatrix ); | |
| } | |
| var geometry = objects.update( object ); | |
| var material = object.material; | |
| if ( material.visible ) { | |
| currentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null ); | |
| } | |
| } | |
| } else if ( object.isImmediateRenderObject ) { | |
| if ( sortObjects ) { | |
| _vector3.setFromMatrixPosition( object.matrixWorld ) | |
| .applyMatrix4( _projScreenMatrix ); | |
| } | |
| currentRenderList.push( object, null, object.material, groupOrder, _vector3.z, null ); | |
| } else if ( object.isMesh || object.isLine || object.isPoints ) { | |
| if ( object.isSkinnedMesh ) { | |
| object.skeleton.update(); | |
| } | |
| if ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) { | |
| if ( sortObjects ) { | |
| _vector3.setFromMatrixPosition( object.matrixWorld ) | |
| .applyMatrix4( _projScreenMatrix ); | |
| } | |
| var geometry = objects.update( object ); | |
| var material = object.material; | |
| if ( Array.isArray( material ) ) { | |
| var groups = geometry.groups; | |
| for ( var i = 0, l = groups.length; i < l; i ++ ) { | |
| var group = groups[ i ]; | |
| var groupMaterial = material[ group.materialIndex ]; | |
| if ( groupMaterial && groupMaterial.visible ) { | |
| currentRenderList.push( object, geometry, groupMaterial, groupOrder, _vector3.z, group ); | |
| } | |
| } | |
| } else if ( material.visible ) { | |
| currentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null ); | |
| } | |
| } | |
| } | |
| } | |
| var children = object.children; | |
| for ( var i = 0, l = children.length; i < l; i ++ ) { | |
| projectObject( children[ i ], camera, groupOrder, sortObjects ); | |
| } | |
| } | |
| function renderObjects( renderList, scene, camera, overrideMaterial ) { | |
| for ( var i = 0, l = renderList.length; i < l; i ++ ) { | |
| var renderItem = renderList[ i ]; | |
| var object = renderItem.object; | |
| var geometry = renderItem.geometry; | |
| var material = overrideMaterial === undefined ? renderItem.material : overrideMaterial; | |
| var group = renderItem.group; | |
| if ( camera.isArrayCamera ) { | |
| _currentArrayCamera = camera; | |
| var cameras = camera.cameras; | |
| for ( var j = 0, jl = cameras.length; j < jl; j ++ ) { | |
| var camera2 = cameras[ j ]; | |
| if ( object.layers.test( camera2.layers ) ) { | |
| if ( 'viewport' in camera2 ) { // XR | |
| state.viewport( _currentViewport.copy( camera2.viewport ) ); | |
| } else { | |
| var bounds = camera2.bounds; | |
| var x = bounds.x * _width; | |
| var y = bounds.y * _height; | |
| var width = bounds.z * _width; | |
| var height = bounds.w * _height; | |
| state.viewport( _currentViewport.set( x, y, width, height ).multiplyScalar( _pixelRatio ) ); | |
| } | |
| currentRenderState.setupLights( camera2 ); | |
| renderObject( object, scene, camera2, geometry, material, group ); | |
| } | |
| } | |
| } else { | |
| _currentArrayCamera = null; | |
| renderObject( object, scene, camera, geometry, material, group ); | |
| } | |
| } | |
| } | |
| function renderObject( object, scene, camera, geometry, material, group ) { | |
| object.onBeforeRender( _this, scene, camera, geometry, material, group ); | |
| currentRenderState = renderStates.get( scene, _currentArrayCamera || camera ); | |
| object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); | |
| object.normalMatrix.getNormalMatrix( object.modelViewMatrix ); | |
| if ( object.isImmediateRenderObject ) { | |
| state.setMaterial( material ); | |
| var program = setProgram( camera, scene.fog, material, object ); | |
| _currentGeometryProgram.geometry = null; | |
| _currentGeometryProgram.program = null; | |
| _currentGeometryProgram.wireframe = false; | |
| renderObjectImmediate( object, program ); | |
| } else { | |
| _this.renderBufferDirect( camera, scene.fog, geometry, material, object, group ); | |
| } | |
| object.onAfterRender( _this, scene, camera, geometry, material, group ); | |
| currentRenderState = renderStates.get( scene, _currentArrayCamera || camera ); | |
| } | |
| function initMaterial( material, fog, object ) { | |
| var materialProperties = properties.get( material ); | |
| var lights = currentRenderState.state.lights; | |
| var shadowsArray = currentRenderState.state.shadowsArray; | |
| var lightsHash = materialProperties.lightsHash; | |
| var lightsStateHash = lights.state.hash; | |
| var parameters = programCache.getParameters( | |
| material, lights.state, shadowsArray, fog, _clipping.numPlanes, _clipping.numIntersection, object ); | |
| var code = programCache.getProgramCode( material, parameters ); | |
| var program = materialProperties.program; | |
| var programChange = true; | |
| if ( program === undefined ) { | |
| // new material | |
| material.addEventListener( 'dispose', onMaterialDispose ); | |
| } else if ( program.code !== code ) { | |
| // changed glsl or parameters | |
| releaseMaterialProgramReference( material ); | |
| } else if ( lightsHash.stateID !== lightsStateHash.stateID || | |
| lightsHash.directionalLength !== lightsStateHash.directionalLength || | |
| lightsHash.pointLength !== lightsStateHash.pointLength || | |
| lightsHash.spotLength !== lightsStateHash.spotLength || | |
| lightsHash.rectAreaLength !== lightsStateHash.rectAreaLength || | |
| lightsHash.hemiLength !== lightsStateHash.hemiLength || | |
| lightsHash.shadowsLength !== lightsStateHash.shadowsLength ) { | |
| lightsHash.stateID = lightsStateHash.stateID; | |
| lightsHash.directionalLength = lightsStateHash.directionalLength; | |
| lightsHash.pointLength = lightsStateHash.pointLength; | |
| lightsHash.spotLength = lightsStateHash.spotLength; | |
| lightsHash.rectAreaLength = lightsStateHash.rectAreaLength; | |
| lightsHash.hemiLength = lightsStateHash.hemiLength; | |
| lightsHash.shadowsLength = lightsStateHash.shadowsLength; | |
| programChange = false; | |
| } else if ( parameters.shaderID !== undefined ) { | |
| // same glsl and uniform list | |
| return; | |
| } else { | |
| // only rebuild uniform list | |
| programChange = false; | |
| } | |
| if ( programChange ) { | |
| if ( parameters.shaderID ) { | |
| var shader = ShaderLib[ parameters.shaderID ]; | |
| materialProperties.shader = { | |
| name: material.type, | |
| uniforms: cloneUniforms( shader.uniforms ), | |
| vertexShader: shader.vertexShader, | |
| fragmentShader: shader.fragmentShader | |
| }; | |
| } else { | |
| materialProperties.shader = { | |
| name: material.type, | |
| uniforms: material.uniforms, | |
| vertexShader: material.vertexShader, | |
| fragmentShader: material.fragmentShader | |
| }; | |
| } | |
| material.onBeforeCompile( materialProperties.shader, _this ); | |
| // Computing code again as onBeforeCompile may have changed the shaders | |
| code = programCache.getProgramCode( material, parameters ); | |
| program = programCache.acquireProgram( material, materialProperties.shader, parameters, code ); | |
| materialProperties.program = program; | |
| material.program = program; | |
| } | |
| var programAttributes = program.getAttributes(); | |
| if ( material.morphTargets ) { | |
| material.numSupportedMorphTargets = 0; | |
| for ( var i = 0; i < _this.maxMorphTargets; i ++ ) { | |
| if ( programAttributes[ 'morphTarget' + i ] >= 0 ) { | |
| material.numSupportedMorphTargets ++; | |
| } | |
| } | |
| } | |
| if ( material.morphNormals ) { | |
| material.numSupportedMorphNormals = 0; | |
| for ( var i = 0; i < _this.maxMorphNormals; i ++ ) { | |
| if ( programAttributes[ 'morphNormal' + i ] >= 0 ) { | |
| material.numSupportedMorphNormals ++; | |
| } | |
| } | |
| } | |
| var uniforms = materialProperties.shader.uniforms; | |
| if ( ! material.isShaderMaterial && | |
| ! material.isRawShaderMaterial || | |
| material.clipping === true ) { | |
| materialProperties.numClippingPlanes = _clipping.numPlanes; | |
| materialProperties.numIntersection = _clipping.numIntersection; | |
| uniforms.clippingPlanes = _clipping.uniform; | |
| } | |
| materialProperties.fog = fog; | |
| // store the light setup it was created for | |
| if ( lightsHash === undefined ) { | |
| materialProperties.lightsHash = lightsHash = {}; | |
| } | |
| lightsHash.stateID = lightsStateHash.stateID; | |
| lightsHash.directionalLength = lightsStateHash.directionalLength; | |
| lightsHash.pointLength = lightsStateHash.pointLength; | |
| lightsHash.spotLength = lightsStateHash.spotLength; | |
| lightsHash.rectAreaLength = lightsStateHash.rectAreaLength; | |
| lightsHash.hemiLength = lightsStateHash.hemiLength; | |
| lightsHash.shadowsLength = lightsStateHash.shadowsLength; | |
| if ( material.lights ) { | |
| // wire up the material to this renderer's lighting state | |
| uniforms.ambientLightColor.value = lights.state.ambient; | |
| uniforms.directionalLights.value = lights.state.directional; | |
| uniforms.spotLights.value = lights.state.spot; | |
| uniforms.rectAreaLights.value = lights.state.rectArea; | |
| uniforms.pointLights.value = lights.state.point; | |
| 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.spotShadowMatrix.value = lights.state.spotShadowMatrix; | |
| uniforms.pointShadowMap.value = lights.state.pointShadowMap; | |
| uniforms.pointShadowMatrix.value = lights.state.pointShadowMatrix; | |
| // TODO (abelnation): add area lights shadow info to uniforms | |
| } | |
| var progUniforms = materialProperties.program.getUniforms(), | |
| uniformsList = | |
| WebGLUniforms.seqWithValue( progUniforms.seq, uniforms ); | |
| materialProperties.uniformsList = uniformsList; | |
| } | |
| function setProgram( camera, fog, material, object ) { | |
| textures.resetTextureUnits(); | |
| var materialProperties = properties.get( material ); | |
| var lights = currentRenderState.state.lights; | |
| var lightsHash = materialProperties.lightsHash; | |
| var lightsStateHash = lights.state.hash; | |
| if ( _clippingEnabled ) { | |
| if ( _localClippingEnabled || camera !== _currentCamera ) { | |
| var 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.clippingPlanes, material.clipIntersection, material.clipShadows, | |
| camera, materialProperties, useCache ); | |
| } | |
| } | |
| if ( material.needsUpdate === false ) { | |
| if ( materialProperties.program === undefined ) { | |
| material.needsUpdate = true; | |
| } else if ( material.fog && materialProperties.fog !== fog ) { | |
| material.needsUpdate = true; | |
| } else if ( material.lights && ( lightsHash.stateID !== lightsStateHash.stateID || | |
| lightsHash.directionalLength !== lightsStateHash.directionalLength || | |
| lightsHash.pointLength !== lightsStateHash.pointLength || | |
| lightsHash.spotLength !== lightsStateHash.spotLength || | |
| lightsHash.rectAreaLength !== lightsStateHash.rectAreaLength || | |
| lightsHash.hemiLength !== lightsStateHash.hemiLength || | |
| lightsHash.shadowsLength !== lightsStateHash.shadowsLength ) ) { | |
| material.needsUpdate = true; | |
| } else if ( materialProperties.numClippingPlanes !== undefined && | |
| ( materialProperties.numClippingPlanes !== _clipping.numPlanes || | |
| materialProperties.numIntersection !== _clipping.numIntersection ) ) { | |
| material.needsUpdate = true; | |
| } | |
| } | |
| if ( material.needsUpdate ) { | |
| initMaterial( material, fog, object ); | |
| material.needsUpdate = false; | |
| } | |
| var refreshProgram = false; | |
| var refreshMaterial = false; | |
| var refreshLights = false; | |
| var program = materialProperties.program, | |
| p_uniforms = program.getUniforms(), | |
| m_uniforms = materialProperties.shader.uniforms; | |
| if ( state.useProgram( program.program ) ) { | |
| refreshProgram = true; | |
| refreshMaterial = true; | |
| refreshLights = true; | |
| } | |
| if ( material.id !== _currentMaterialId ) { | |
| _currentMaterialId = material.id; | |
| refreshMaterial = true; | |
| } | |
| if ( refreshProgram || _currentCamera !== camera ) { | |
| p_uniforms.setValue( _gl, 'projectionMatrix', camera.projectionMatrix ); | |
| if ( capabilities.logarithmicDepthBuffer ) { | |
| p_uniforms.setValue( _gl, 'logDepthBufFC', | |
| 2.0 / ( Math.log( camera.far + 1.0 ) / Math.LN2 ) ); | |
| } | |
| if ( _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: | |
| 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) | |
| if ( material.isShaderMaterial || | |
| material.isMeshPhongMaterial || | |
| material.isMeshStandardMaterial || | |
| material.envMap ) { | |
| var uCamPos = p_uniforms.map.cameraPosition; | |
| if ( uCamPos !== undefined ) { | |
| uCamPos.setValue( _gl, | |
| _vector3.setFromMatrixPosition( camera.matrixWorld ) ); | |
| } | |
| } | |
| if ( material.isMeshPhongMaterial || | |
| material.isMeshLambertMaterial || | |
| material.isMeshBasicMaterial || | |
| material.isMeshStandardMaterial || | |
| material.isShaderMaterial || | |
| material.skinning ) { | |
| p_uniforms.setValue( _gl, 'viewMatrix', camera.matrixWorldInverse ); | |
| } | |
| } | |
| // skinning uniforms must be set even if material didn't change | |
| // auto-setting of texture unit for bone texture must go before other textures | |
| // not sure why, but otherwise weird things happen | |
| if ( material.skinning ) { | |
| p_uniforms.setOptional( _gl, object, 'bindMatrix' ); | |
| p_uniforms.setOptional( _gl, object, 'bindMatrixInverse' ); | |
| var skeleton = object.skeleton; | |
| if ( skeleton ) { | |
| var bones = skeleton.bones; | |
| if ( capabilities.floatVertexTextures ) { | |
| if ( skeleton.boneTexture === undefined ) { | |
| // layout (1 matrix = 4 pixels) | |
| // RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4) | |
| // with 8x8 pixel texture max 16 bones * 4 pixels = (8 * 8) | |
| // 16x16 pixel texture max 64 bones * 4 pixels = (16 * 16) | |
| // 32x32 pixel texture max 256 bones * 4 pixels = (32 * 32) | |
| // 64x64 pixel texture max 1024 bones * 4 pixels = (64 * 64) | |
| var size = Math.sqrt( bones.length * 4 ); // 4 pixels needed for 1 matrix | |
| size = _Math.ceilPowerOfTwo( size ); | |
| size = Math.max( size, 4 ); | |
| var boneMatrices = new Float32Array( size * size * 4 ); // 4 floats per RGBA pixel | |
| boneMatrices.set( skeleton.boneMatrices ); // copy current values | |
| var boneTexture = new DataTexture( boneMatrices, size, size, RGBAFormat, FloatType ); | |
| boneTexture.needsUpdate = true; | |
| skeleton.boneMatrices = boneMatrices; | |
| skeleton.boneTexture = boneTexture; | |
| skeleton.boneTextureSize = size; | |
| } | |
| p_uniforms.setValue( _gl, 'boneTexture', skeleton.boneTexture, textures ); | |
| p_uniforms.setValue( _gl, 'boneTextureSize', skeleton.boneTextureSize ); | |
| } else { | |
| p_uniforms.setOptional( _gl, skeleton, 'boneMatrices' ); | |
| } | |
| } | |
| } | |
| if ( refreshMaterial ) { | |
| p_uniforms.setValue( _gl, 'toneMappingExposure', _this.toneMappingExposure ); | |
| p_uniforms.setValue( _gl, 'toneMappingWhitePoint', _this.toneMappingWhitePoint ); | |
| if ( material.lights ) { | |
| // 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 | |
| markUniformsLightsNeedsUpdate( m_uniforms, refreshLights ); | |
| } | |
| // refresh uniforms common to several materials | |
| if ( fog && material.fog ) { | |
| refreshUniformsFog( m_uniforms, fog ); | |
| } | |
| if ( material.isMeshBasicMaterial ) { | |
| refreshUniformsCommon( m_uniforms, material ); | |
| } else if ( material.isMeshLambertMaterial ) { | |
| refreshUniformsCommon( m_uniforms, material ); | |
| refreshUniformsLambert( m_uniforms, material ); | |
| } else if ( material.isMeshPhongMaterial ) { | |
| refreshUniformsCommon( m_uniforms, material ); | |
| if ( material.isMeshToonMaterial ) { | |
| refreshUniformsToon( m_uniforms, material ); | |
| } else { | |
| refreshUniformsPhong( m_uniforms, material ); | |
| } | |
| } else if ( material.isMeshStandardMaterial ) { | |
| refreshUniformsCommon( m_uniforms, material ); | |
| if ( material.isMeshPhysicalMaterial ) { | |
| refreshUniformsPhysical( m_uniforms, material ); | |
| } else { | |
| refreshUniformsStandard( m_uniforms, material ); | |
| } | |
| } else if ( material.isMeshMatcapMaterial ) { | |
| refreshUniformsCommon( m_uniforms, material ); | |
| refreshUniformsMatcap( m_uniforms, material ); | |
| } else if ( material.isMeshDepthMaterial ) { | |
| refreshUniformsCommon( m_uniforms, material ); | |
| refreshUniformsDepth( m_uniforms, material ); | |
| } else if ( material.isMeshDistanceMaterial ) { | |
| refreshUniformsCommon( m_uniforms, material ); | |
| refreshUniformsDistance( m_uniforms, material ); | |
| } else if ( material.isMeshNormalMaterial ) { | |
| refreshUniformsCommon( m_uniforms, material ); | |
| refreshUniformsNormal( m_uniforms, material ); | |
| } else if ( material.isLineBasicMaterial ) { | |
| refreshUniformsLine( m_uniforms, material ); | |
| if ( material.isLineDashedMaterial ) { | |
| refreshUniformsDash( m_uniforms, material ); | |
| } | |
| } else if ( material.isPointsMaterial ) { | |
| refreshUniformsPoints( m_uniforms, material ); | |
| } else if ( material.isSpriteMaterial ) { | |
| refreshUniformsSprites( m_uniforms, material ); | |
| } else if ( material.isShadowMaterial ) { | |
| m_uniforms.color.value = material.color; | |
| m_uniforms.opacity.value = material.opacity; | |
| } | |
| // RectAreaLight Texture | |
| // TODO (mrdoob): Find a nicer implementation | |
| if ( m_uniforms.ltc_1 !== undefined ) m_uniforms.ltc_1.value = UniformsLib.LTC_1; | |
| if ( m_uniforms.ltc_2 !== undefined ) m_uniforms.ltc_2.value = UniformsLib.LTC_2; | |
| WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, textures ); | |
| } | |
| if ( material.isShaderMaterial && material.uniformsNeedUpdate === true ) { | |
| WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, textures ); | |
| material.uniformsNeedUpdate = false; | |
| } | |
| if ( material.isSpriteMaterial ) { | |
| p_uniforms.setValue( _gl, 'center', object.center ); | |
| } | |
| // common matrices | |
| p_uniforms.setValue( _gl, 'modelViewMatrix', object.modelViewMatrix ); | |
| p_uniforms.setValue( _gl, 'normalMatrix', object.normalMatrix ); | |
| p_uniforms.setValue( _gl, 'modelMatrix', object.matrixWorld ); | |
| return program; | |
| } | |
| // Uniforms (refresh uniforms objects) | |
| function refreshUniformsCommon( uniforms, material ) { | |
| uniforms.opacity.value = material.opacity; | |
| if ( material.color ) { | |
| uniforms.diffuse.value = material.color; | |
| } | |
| if ( material.emissive ) { | |
| uniforms.emissive.value.copy( material.emissive ).multiplyScalar( material.emissiveIntensity ); | |
| } | |
| if ( material.map ) { | |
| uniforms.map.value = material.map; | |
| } | |
| if ( material.alphaMap ) { | |
| uniforms.alphaMap.value = material.alphaMap; | |
| } | |
| if ( material.specularMap ) { | |
| uniforms.specularMap.value = material.specularMap; | |
| } | |
| if ( material.envMap ) { | |
| uniforms.envMap.value = material.envMap; | |
| // don't flip CubeTexture envMaps, flip everything else: | |
| // WebGLRenderTargetCube will be flipped for backwards compatibility | |
| // WebGLRenderTargetCube.texture will be flipped because it's a Texture and NOT a CubeTexture | |
| // this check must be handled differently, or removed entirely, if WebGLRenderTargetCube uses a CubeTexture in the future | |
| uniforms.flipEnvMap.value = material.envMap.isCubeTexture ? - 1 : 1; | |
| uniforms.reflectivity.value = material.reflectivity; | |
| uniforms.refractionRatio.value = material.refractionRatio; | |
| uniforms.maxMipLevel.value = properties.get( material.envMap ).__maxMipLevel; | |
| } | |
| if ( material.lightMap ) { | |
| uniforms.lightMap.value = material.lightMap; | |
| uniforms.lightMapIntensity.value = material.lightMapIntensity; | |
| } | |
| if ( material.aoMap ) { | |
| uniforms.aoMap.value = material.aoMap; | |
| uniforms.aoMapIntensity.value = material.aoMapIntensity; | |
| } | |
| // uv repeat and offset setting priorities | |
| // 1. color map | |
| // 2. specular map | |
| // 3. normal map | |
| // 4. bump map | |
| // 5. alpha map | |
| // 6. emissive map | |
| var uvScaleMap; | |
| if ( material.map ) { | |
| uvScaleMap = material.map; | |
| } else if ( material.specularMap ) { | |
| uvScaleMap = material.specularMap; | |
| } else if ( material.displacementMap ) { | |
| uvScaleMap = material.displacementMap; | |
| } else if ( material.normalMap ) { | |
| uvScaleMap = material.normalMap; | |
| } else if ( material.bumpMap ) { | |
| uvScaleMap = material.bumpMap; | |
| } else if ( material.roughnessMap ) { | |
| uvScaleMap = material.roughnessMap; | |
| } else if ( material.metalnessMap ) { | |
| uvScaleMap = material.metalnessMap; | |
| } else if ( material.alphaMap ) { | |
| uvScaleMap = material.alphaMap; | |
| } else if ( material.emissiveMap ) { | |
| uvScaleMap = material.emissiveMap; | |
| } | |
| if ( uvScaleMap !== undefined ) { | |
| // backwards compatibility | |
| if ( uvScaleMap.isWebGLRenderTarget ) { | |
| uvScaleMap = uvScaleMap.texture; | |
| } | |
| if ( uvScaleMap.matrixAutoUpdate === true ) { | |
| uvScaleMap.updateMatrix(); | |
| } | |
| uniforms.uvTransform.value.copy( uvScaleMap.matrix ); | |
| } | |
| } | |
| function refreshUniformsLine( uniforms, material ) { | |
| uniforms.diffuse.value = material.color; | |
| uniforms.opacity.value = material.opacity; | |
| } | |
| function refreshUniformsDash( uniforms, material ) { | |
| uniforms.dashSize.value = material.dashSize; | |
| uniforms.totalSize.value = material.dashSize + material.gapSize; | |
| uniforms.scale.value = material.scale; | |
| } | |
| function refreshUniformsPoints( uniforms, material ) { | |
| uniforms.diffuse.value = material.color; | |
| uniforms.opacity.value = material.opacity; | |
| uniforms.size.value = material.size * _pixelRatio; | |
| uniforms.scale.value = _height * 0.5; | |
| uniforms.map.value = material.map; | |
| if ( material.map !== null ) { | |
| if ( material.map.matrixAutoUpdate === true ) { | |
| material.map.updateMatrix(); | |
| } | |
| uniforms.uvTransform.value.copy( material.map.matrix ); | |
| } | |
| } | |
| function refreshUniformsSprites( uniforms, material ) { | |
| uniforms.diffuse.value = material.color; | |
| uniforms.opacity.value = material.opacity; | |
| uniforms.rotation.value = material.rotation; | |
| uniforms.map.value = material.map; | |
| if ( material.map !== null ) { | |
| if ( material.map.matrixAutoUpdate === true ) { | |
| material.map.updateMatrix(); | |
| } | |
| uniforms.uvTransform.value.copy( material.map.matrix ); | |
| } | |
| } | |
| function refreshUniformsFog( uniforms, fog ) { | |
| uniforms.fogColor.value = fog.color; | |
| if ( fog.isFog ) { | |
| uniforms.fogNear.value = fog.near; | |
| uniforms.fogFar.value = fog.far; | |
| } else if ( fog.isFogExp2 ) { | |
| uniforms.fogDensity.value = fog.density; | |
| } | |
| } | |
| function refreshUniformsLambert( uniforms, material ) { | |
| if ( material.emissiveMap ) { | |
| uniforms.emissiveMap.value = material.emissiveMap; | |
| } | |
| } | |
| function refreshUniformsPhong( uniforms, material ) { | |
| uniforms.specular.value = material.specular; | |
| uniforms.shininess.value = Math.max( material.shininess, 1e-4 ); // to prevent pow( 0.0, 0.0 ) | |
| if ( material.emissiveMap ) { | |
| uniforms.emissiveMap.value = material.emissiveMap; | |
| } | |
| if ( material.bumpMap ) { | |
| uniforms.bumpMap.value = material.bumpMap; | |
| uniforms.bumpScale.value = material.bumpScale; | |
| if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1; | |
| } | |
| if ( material.normalMap ) { | |
| uniforms.normalMap.value = material.normalMap; | |
| uniforms.normalScale.value.copy( material.normalScale ); | |
| if ( material.side === BackSide ) uniforms.normalScale.value.negate(); | |
| } | |
| if ( material.displacementMap ) { | |
| uniforms.displacementMap.value = material.displacementMap; | |
| uniforms.displacementScale.value = material.displacementScale; | |
| uniforms.displacementBias.value = material.displacementBias; | |
| } | |
| } | |
| function refreshUniformsToon( uniforms, material ) { | |
| refreshUniformsPhong( uniforms, material ); | |
| if ( material.gradientMap ) { | |
| uniforms.gradientMap.value = material.gradientMap; | |
| } | |
| } | |
| function refreshUniformsStandard( uniforms, material ) { | |
| uniforms.roughness.value = material.roughness; | |
| uniforms.metalness.value = material.metalness; | |
| if ( material.roughnessMap ) { | |
| uniforms.roughnessMap.value = material.roughnessMap; | |
| } | |
| if ( material.metalnessMap ) { | |
| uniforms.metalnessMap.value = material.metalnessMap; | |
| } | |
| if ( material.emissiveMap ) { | |
| uniforms.emissiveMap.value = material.emissiveMap; | |
| } | |
| if ( material.bumpMap ) { | |
| uniforms.bumpMap.value = material.bumpMap; | |
| uniforms.bumpScale.value = material.bumpScale; | |
| if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1; | |
| } | |
| if ( material.normalMap ) { | |
| uniforms.normalMap.value = material.normalMap; | |
| uniforms.normalScale.value.copy( material.normalScale ); | |
| if ( material.side === BackSide ) uniforms.normalScale.value.negate(); | |
| } | |
| if ( material.displacementMap ) { | |
| uniforms.displacementMap.value = material.displacementMap; | |
| uniforms.displacementScale.value = material.displacementScale; | |
| uniforms.displacementBias.value = material.displacementBias; | |
| } | |
| if ( material.envMap ) { | |
| //uniforms.envMap.value = material.envMap; // part of uniforms common | |
| uniforms.envMapIntensity.value = material.envMapIntensity; | |
| } | |
| } | |
| function refreshUniformsPhysical( uniforms, material ) { | |
| refreshUniformsStandard( uniforms, material ); | |
| uniforms.reflectivity.value = material.reflectivity; // also part of uniforms common | |
| uniforms.clearCoat.value = material.clearCoat; | |
| uniforms.clearCoatRoughness.value = material.clearCoatRoughness; | |
| } | |
| function refreshUniformsMatcap( uniforms, material ) { | |
| if ( material.matcap ) { | |
| uniforms.matcap.value = material.matcap; | |
| } | |
| if ( material.bumpMap ) { | |
| uniforms.bumpMap.value = material.bumpMap; | |
| uniforms.bumpScale.value = material.bumpScale; | |
| if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1; | |
| } | |
| if ( material.normalMap ) { | |
| uniforms.normalMap.value = material.normalMap; | |
| uniforms.normalScale.value.copy( material.normalScale ); | |
| if ( material.side === BackSide ) uniforms.normalScale.value.negate(); | |
| } | |
| if ( material.displacementMap ) { | |
| uniforms.displacementMap.value = material.displacementMap; | |
| uniforms.displacementScale.value = material.displacementScale; | |
| uniforms.displacementBias.value = material.displacementBias; | |
| } | |
| } | |
| function refreshUniformsDepth( uniforms, material ) { | |
| if ( material.displacementMap ) { | |
| uniforms.displacementMap.value = material.displacementMap; | |
| uniforms.displacementScale.value = material.displacementScale; | |
| uniforms.displacementBias.value = material.displacementBias; | |
| } | |
| } | |
| function refreshUniformsDistance( uniforms, material ) { | |
| if ( material.displacementMap ) { | |
| uniforms.displacementMap.value = material.displacementMap; | |
| uniforms.displacementScale.value = material.displacementScale; | |
| uniforms.displacementBias.value = material.displacementBias; | |
| } | |
| uniforms.referencePosition.value.copy( material.referencePosition ); | |
| uniforms.nearDistance.value = material.nearDistance; | |
| uniforms.farDistance.value = material.farDistance; | |
| } | |
| function refreshUniformsNormal( uniforms, material ) { | |
| if ( material.bumpMap ) { | |
| uniforms.bumpMap.value = material.bumpMap; | |
| uniforms.bumpScale.value = material.bumpScale; | |
| if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1; | |
| } | |
| if ( material.normalMap ) { | |
| uniforms.normalMap.value = material.normalMap; | |
| uniforms.normalScale.value.copy( material.normalScale ); | |
| if ( material.side === BackSide ) uniforms.normalScale.value.negate(); | |
| } | |
| if ( material.displacementMap ) { | |
| uniforms.displacementMap.value = material.displacementMap; | |
| uniforms.displacementScale.value = material.displacementScale; | |
| uniforms.displacementBias.value = material.displacementBias; | |
| } | |
| } | |
| // If uniforms are marked as clean, they don't need to be loaded to the GPU. | |
| function markUniformsLightsNeedsUpdate( uniforms, value ) { | |
| uniforms.ambientLightColor.needsUpdate = value; | |
| uniforms.directionalLights.needsUpdate = value; | |
| uniforms.pointLights.needsUpdate = value; | |
| uniforms.spotLights.needsUpdate = value; | |
| uniforms.rectAreaLights.needsUpdate = value; | |
| uniforms.hemisphereLights.needsUpdate = value; | |
| } | |
| // | |
| this.setFramebuffer = function ( value ) { | |
| _framebuffer = value; | |
| }; | |
| this.getRenderTarget = function () { | |
| return _currentRenderTarget; | |
| }; | |
| this.setRenderTarget = function ( renderTarget, activeCubeFace, activeMipMapLevel ) { | |
| _currentRenderTarget = renderTarget; | |
| if ( renderTarget && properties.get( renderTarget ).__webglFramebuffer === undefined ) { | |
| textures.setupRenderTarget( renderTarget ); | |
| } | |
| var framebuffer = _framebuffer; | |
| var isCube = false; | |
| if ( renderTarget ) { | |
| var __webglFramebuffer = properties.get( renderTarget ).__webglFramebuffer; | |
| if ( renderTarget.isWebGLRenderTargetCube ) { | |
| framebuffer = __webglFramebuffer[ activeCubeFace || 0 ]; | |
| isCube = true; | |
| } else if ( renderTarget.isWebGLMultisampleRenderTarget ) { | |
| framebuffer = properties.get( renderTarget ).__webglMultisampledFramebuffer; | |
| } else { | |
| framebuffer = __webglFramebuffer; | |
| } | |
| _currentViewport.copy( renderTarget.viewport ); | |
| _currentScissor.copy( renderTarget.scissor ); | |
| _currentScissorTest = renderTarget.scissorTest; | |
| } else { | |
| _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ); | |
| _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ); | |
| _currentScissorTest = _scissorTest; | |
| } | |
| if ( _currentFramebuffer !== framebuffer ) { | |
| _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); | |
| _currentFramebuffer = framebuffer; | |
| } | |
| state.viewport( _currentViewport ); | |
| state.scissor( _currentScissor ); | |
| state.setScissorTest( _currentScissorTest ); | |
| if ( isCube ) { | |
| var textureProperties = properties.get( renderTarget.texture ); | |
| _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + ( activeCubeFace || 0 ), textureProperties.__webglTexture, activeMipMapLevel || 0 ); | |
| } | |
| }; | |
| this.readRenderTargetPixels = function ( renderTarget, x, y, width, height, buffer ) { | |
| if ( ! ( renderTarget && renderTarget.isWebGLRenderTarget ) ) { | |
| console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.' ); | |
| return; | |
| } | |
| var framebuffer = properties.get( renderTarget ).__webglFramebuffer; | |
| if ( framebuffer ) { | |
| var restore = false; | |
| if ( framebuffer !== _currentFramebuffer ) { | |
| _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); | |
| restore = true; | |
| } | |
| try { | |
| var texture = renderTarget.texture; | |
| var textureFormat = texture.format; | |
| var textureType = texture.type; | |
| 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; | |
| } | |
| if ( textureType !== UnsignedByteType && utils.convert( textureType ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_TYPE ) && // IE11, Edge and Chrome Mac < 52 (#9513) | |
| ! ( textureType === FloatType && ( capabilities.isWebGL2 || extensions.get( 'OES_texture_float' ) || extensions.get( 'WEBGL_color_buffer_float' ) ) ) && // Chrome Mac >= 52 and Firefox | |
| ! ( textureType === HalfFloatType && ( capabilities.isWebGL2 ? extensions.get( 'EXT_color_buffer_float' ) : extensions.get( 'EXT_color_buffer_half_float' ) ) ) ) { | |
| console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.' ); | |
| return; | |
| } | |
| if ( _gl.checkFramebufferStatus( _gl.FRAMEBUFFER ) === _gl.FRAMEBUFFER_COMPLETE ) { | |
| // 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 ) ) ) { | |
| _gl.readPixels( x, y, width, height, utils.convert( textureFormat ), utils.convert( textureType ), buffer ); | |
| } | |
| } else { | |
| console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: readPixels from renderTarget failed. Framebuffer not complete.' ); | |
| } | |
| } finally { | |
| if ( restore ) { | |
| _gl.bindFramebuffer( _gl.FRAMEBUFFER, _currentFramebuffer ); | |
| } | |
| } | |
| } | |
| }; | |
| this.copyFramebufferToTexture = function ( position, texture, level ) { | |
| var width = texture.image.width; | |
| var height = texture.image.height; | |
| var glFormat = utils.convert( texture.format ); | |
| textures.setTexture2D( texture, 0 ); | |
| _gl.copyTexImage2D( _gl.TEXTURE_2D, level || 0, glFormat, position.x, position.y, width, height, 0 ); | |
| }; | |
| this.copyTextureToTexture = function ( position, srcTexture, dstTexture, level ) { | |
| var width = srcTexture.image.width; | |
| var height = srcTexture.image.height; | |
| var glFormat = utils.convert( dstTexture.format ); | |
| var glType = utils.convert( dstTexture.type ); | |
| textures.setTexture2D( dstTexture, 0 ); | |
| if ( srcTexture.isDataTexture ) { | |
| _gl.texSubImage2D( _gl.TEXTURE_2D, level || 0, position.x, position.y, width, height, glFormat, glType, srcTexture.image.data ); | |
| } else { | |
| _gl.texSubImage2D( _gl.TEXTURE_2D, level || 0, position.x, position.y, glFormat, glType, srcTexture.image ); | |
| } | |
| }; | |
| } | |
| export { WebGLRenderer }; | |