import { 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) { 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", _failIfMajorPerformanceCaveat = parameters.failIfMajorPerformanceCaveat !== undefined ? parameters.failIfMajorPerformanceCaveat : false; var currentRenderList = null; var currentRenderState = null; // public properties this.domElement = _canvas; // Debug configuration container this.debug = { /** * Enables error checking and reporting when shader programs are being compiled * @type {boolean} */ checkShaderErrors: true, }; // 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, _currentActiveCubeFace = 0, _currentActiveMipmapLevel = 0, _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, failIfMajorPerformanceCaveat: _failIfMajorPerformanceCaveat, xrCompatible: true, }; // 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).floor(), ); state.viewport( _currentViewport.copy(_viewport).multiplyScalar(_pixelRatio).floor(), ); 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); 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.capabilities = capabilities; _this.extensions = extensions; _this.properties = properties; _this.renderLists = renderLists; _this.state = state; _this.info = info; } initGLContext(); // vr var vr = typeof navigator !== "undefined" && "xr" in navigator && "supportsSession" in navigator.xr ? new WebXRManager(_this, _gl) : 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 = Math.floor(width * _pixelRatio); _canvas.height = Math.floor(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).floor(); }; this.setDrawingBufferSize = function (width, height, pixelRatio) { _width = width; _height = height; _pixelRatio = pixelRatio; _canvas.width = Math.floor(width * pixelRatio); _canvas.height = Math.floor(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).floor(), ); }; 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).floor(), ); }; 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.isLOD) { if (object.autoUpdate === true) object.update(camera); } 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)) { state.viewport(_currentViewport.copy(camera2.viewport)); 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 lightsStateVersion = lights.state.version; 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 (materialProperties.lightsStateVersion !== lightsStateVersion) { materialProperties.lightsStateVersion = lightsStateVersion; 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 materialProperties.lightsStateVersion = lightsStateVersion; if (material.lights) { // wire up the material to this renderer's lighting state uniforms.ambientLightColor.value = lights.state.ambient; uniforms.lightProbe.value = lights.state.probe; uniforms.directionalLights.value = lights.state.directional; uniforms.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; 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 && materialProperties.lightsStateVersion !== lights.state.version ) { 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.copy(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.copy(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.copy(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.copy(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.copy(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.copy(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.copy(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; if (material.sheen) uniforms.sheen.value.copy(material.sheen); if (material.clearcoatNormalMap) { uniforms.clearcoatNormalScale.value.copy(material.clearcoatNormalScale); uniforms.clearcoatNormalMap.value = material.clearcoatNormalMap; if (material.side === BackSide) { uniforms.clearcoatNormalScale.value.negate(); } } uniforms.transparency.value = material.transparency; } 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.lightProbe.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) { if (_framebuffer !== value) _gl.bindFramebuffer(_gl.FRAMEBUFFER, value); _framebuffer = value; }; this.getActiveCubeFace = function () { return _currentActiveCubeFace; }; this.getActiveMipmapLevel = function () { return _currentActiveMipmapLevel; }; this.getRenderTarget = function () { return _currentRenderTarget; }; this.setRenderTarget = function ( renderTarget, activeCubeFace, activeMipmapLevel, ) { _currentRenderTarget = renderTarget; _currentActiveCubeFace = activeCubeFace; _currentActiveMipmapLevel = activeMipmapLevel; 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).floor(); _currentScissor.copy(_scissor).multiplyScalar(_pixelRatio).floor(); _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, activeCubeFaceIndex, ) { if (!(renderTarget && renderTarget.isWebGLRenderTarget)) { console.error( "THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.", ); return; } var framebuffer = properties.get(renderTarget).__webglFramebuffer; if ( renderTarget.isWebGLRenderTargetCube && activeCubeFaceIndex !== undefined ) { framebuffer = framebuffer[activeCubeFaceIndex]; } 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, ); } }; if (typeof __THREE_DEVTOOLS__ !== "undefined") { __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent("observe", { detail: this }), ); // eslint-disable-line no-undef } } export { WebGLRenderer };