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 }; | |