diff --git a/packages/engine/Source/Core/Matrix4.js b/packages/engine/Source/Core/Matrix4.js index df4d5dfc76d6..afd1949ad9f4 100644 --- a/packages/engine/Source/Core/Matrix4.js +++ b/packages/engine/Source/Core/Matrix4.js @@ -937,8 +937,8 @@ Matrix4.computeOrthographicOffCenter = function ( * * @param {number} left The number of meters to the left of the camera that will be in view. * @param {number} right The number of meters to the right of the camera that will be in view. - * @param {number} bottom The number of meters below of the camera that will be in view. - * @param {number} top The number of meters above of the camera that will be in view. + * @param {number} bottom The number of meters below the camera that will be in view. + * @param {number} top The number of meters above the camera that will be in view. * @param {number} near The distance to the near plane in meters. * @param {number} far The distance to the far plane in meters. * @param {Matrix4} result The object in which the result will be stored. diff --git a/packages/engine/Source/Core/Occluder.js b/packages/engine/Source/Core/Occluder.js index da62d2d44f59..ed24dde73bf9 100644 --- a/packages/engine/Source/Core/Occluder.js +++ b/packages/engine/Source/Core/Occluder.js @@ -300,8 +300,6 @@ const tempScratch = new Cartesian3(); * const cameraPosition = new Cesium.Cartesian3(0, 0, 0); * const occluder = new Cesium.Occluder(sphere1, cameraPosition); * occluder.computeVisibility(sphere2); //returns Visibility.NONE - * - * @see Occluder#isVisible */ Occluder.prototype.computeVisibility = function (occludeeBS) { //>>includeStart('debug', pragmas.debug); diff --git a/packages/engine/Source/Core/PerspectiveFrustum.js b/packages/engine/Source/Core/PerspectiveFrustum.js index 60b5e93d69d8..9c42b739ce58 100644 --- a/packages/engine/Source/Core/PerspectiveFrustum.js +++ b/packages/engine/Source/Core/PerspectiveFrustum.js @@ -167,65 +167,71 @@ function update(frustum) { } //>>includeEnd('debug'); - const f = frustum._offCenterFrustum; - - if ( + const changed = frustum.fov !== frustum._fov || frustum.aspectRatio !== frustum._aspectRatio || frustum.near !== frustum._near || frustum.far !== frustum._far || frustum.xOffset !== frustum._xOffset || - frustum.yOffset !== frustum._yOffset - ) { - //>>includeStart('debug', pragmas.debug); - if (frustum.fov < 0 || frustum.fov >= Math.PI) { - throw new DeveloperError("fov must be in the range [0, PI)."); - } - - if (frustum.aspectRatio < 0) { - throw new DeveloperError("aspectRatio must be positive."); - } - - if (frustum.near < 0 || frustum.near > frustum.far) { - throw new DeveloperError( - "near must be greater than zero and less than far.", - ); - } - //>>includeEnd('debug'); - - frustum._aspectRatio = frustum.aspectRatio; - frustum._fov = frustum.fov; - frustum._fovy = - frustum.aspectRatio <= 1 - ? frustum.fov - : Math.atan(Math.tan(frustum.fov * 0.5) / frustum.aspectRatio) * 2.0; - frustum._near = frustum.near; - frustum._far = frustum.far; - frustum._sseDenominator = 2.0 * Math.tan(0.5 * frustum._fovy); - frustum._xOffset = frustum.xOffset; - frustum._yOffset = frustum.yOffset; - - f.top = frustum.near * Math.tan(0.5 * frustum._fovy); - f.bottom = -f.top; - f.right = frustum.aspectRatio * f.top; - f.left = -f.right; - f.near = frustum.near; - f.far = frustum.far; - - f.right += frustum.xOffset; - f.left += frustum.xOffset; - f.top += frustum.yOffset; - f.bottom += frustum.yOffset; + frustum.yOffset !== frustum._yOffset; + + if (!changed) { + return; } + + //>>includeStart('debug', pragmas.debug); + Check.typeOf.number.greaterThanOrEquals("fov", frustum.fov, 0.0); + Check.typeOf.number.lessThan("fov", frustum.fov, Math.PI); + + Check.typeOf.number.greaterThanOrEquals( + "aspectRatio", + frustum.aspectRatio, + 0.0, + ); + + Check.typeOf.number.greaterThanOrEquals("near", frustum.near, 0.0); + if (frustum.near > frustum.far) { + throw new DeveloperError("near must be less than far."); + } + //>>includeEnd('debug'); + + frustum._aspectRatio = frustum.aspectRatio; + frustum._fov = frustum.fov; + frustum._fovy = + frustum.aspectRatio <= 1 + ? frustum.fov + : Math.atan(Math.tan(frustum.fov * 0.5) / frustum.aspectRatio) * 2.0; + frustum._near = frustum.near; + frustum._far = frustum.far; + frustum._sseDenominator = 2.0 * Math.tan(0.5 * frustum._fovy); + frustum._xOffset = frustum.xOffset; + frustum._yOffset = frustum.yOffset; + + const f = frustum._offCenterFrustum; + + f.top = frustum.near * Math.tan(0.5 * frustum._fovy); + f.bottom = -f.top; + f.right = frustum.aspectRatio * f.top; + f.left = -f.right; + f.near = frustum.near; + f.far = frustum.far; + + f.right += frustum.xOffset; + f.left += frustum.xOffset; + f.top += frustum.yOffset; + f.bottom += frustum.yOffset; } Object.defineProperties(PerspectiveFrustum.prototype, { /** * Gets the perspective projection matrix computed from the view frustum. + * If necessary, the projection matrix will be recomputed. + * * @memberof PerspectiveFrustum.prototype * @type {Matrix4} * @readonly * + * @see PerspectiveOffCenterFrustum#projectionMatrix. * @see PerspectiveFrustum#infiniteProjectionMatrix */ projectionMatrix: { diff --git a/packages/engine/Source/Core/PerspectiveOffCenterFrustum.js b/packages/engine/Source/Core/PerspectiveOffCenterFrustum.js index d5b716e191d6..fb4c4af981dd 100644 --- a/packages/engine/Source/Core/PerspectiveOffCenterFrustum.js +++ b/packages/engine/Source/Core/PerspectiveOffCenterFrustum.js @@ -108,58 +108,57 @@ function update(frustum) { } //>>includeEnd('debug'); - const t = frustum.top; - const b = frustum.bottom; - const r = frustum.right; - const l = frustum.left; - const n = frustum.near; - const f = frustum.far; + const { top, bottom, right, left, near, far } = frustum; + + const changed = + top !== frustum._top || + bottom !== frustum._bottom || + left !== frustum._left || + right !== frustum._right || + near !== frustum._near || + far !== frustum._far; + if (!changed) { + return; + } - if ( - t !== frustum._top || - b !== frustum._bottom || - l !== frustum._left || - r !== frustum._right || - n !== frustum._near || - f !== frustum._far - ) { - //>>includeStart('debug', pragmas.debug); - if (frustum.near <= 0 || frustum.near > frustum.far) { - throw new DeveloperError( - "near must be greater than zero and less than far.", - ); - } - //>>includeEnd('debug'); - - frustum._left = l; - frustum._right = r; - frustum._top = t; - frustum._bottom = b; - frustum._near = n; - frustum._far = f; - frustum._perspectiveMatrix = Matrix4.computePerspectiveOffCenter( - l, - r, - b, - t, - n, - f, - frustum._perspectiveMatrix, - ); - frustum._infinitePerspective = Matrix4.computeInfinitePerspectiveOffCenter( - l, - r, - b, - t, - n, - frustum._infinitePerspective, + //>>includeStart('debug', pragmas.debug); + if (frustum.near <= 0 || frustum.near > frustum.far) { + throw new DeveloperError( + "near must be greater than zero and less than far.", ); } + //>>includeEnd('debug'); + + frustum._left = left; + frustum._right = right; + frustum._top = top; + frustum._bottom = bottom; + frustum._near = near; + frustum._far = far; + frustum._perspectiveMatrix = Matrix4.computePerspectiveOffCenter( + left, + right, + bottom, + top, + near, + far, + frustum._perspectiveMatrix, + ); + frustum._infinitePerspective = Matrix4.computeInfinitePerspectiveOffCenter( + left, + right, + bottom, + top, + near, + frustum._infinitePerspective, + ); } Object.defineProperties(PerspectiveOffCenterFrustum.prototype, { /** * Gets the perspective projection matrix computed from the view frustum. + * The projection matrix will be recomputed if any frustum parameters have changed. + * * @memberof PerspectiveOffCenterFrustum.prototype * @type {Matrix4} * @readonly diff --git a/packages/engine/Source/Renderer/Context.js b/packages/engine/Source/Renderer/Context.js index b8e1aa42c76a..abfe2a5dd59a 100644 --- a/packages/engine/Source/Renderer/Context.js +++ b/packages/engine/Source/Renderer/Context.js @@ -1409,7 +1409,7 @@ Context.prototype.draw = function ( //>>includeEnd('debug'); passState = defaultValue(passState, this._defaultPassState); - // The command's framebuffer takes presidence over the pass' framebuffer, e.g., for off-screen rendering. + // The command's framebuffer takes precedence over the pass' framebuffer, e.g., for off-screen rendering. const framebuffer = defaultValue( drawCommand._framebuffer, passState.framebuffer, diff --git a/packages/engine/Source/Renderer/DrawCommand.js b/packages/engine/Source/Renderer/DrawCommand.js index e4388bdeed54..0b7335f05305 100644 --- a/packages/engine/Source/Renderer/DrawCommand.js +++ b/packages/engine/Source/Renderer/DrawCommand.js @@ -16,6 +16,9 @@ const Flags = { /** * Represents a command to the renderer for drawing. * + * @alias DrawCommand + * @constructor + * * @private */ function DrawCommand(options) { diff --git a/packages/engine/Source/Renderer/Framebuffer.js b/packages/engine/Source/Renderer/Framebuffer.js index 66b4c47eea44..36745f24cb5d 100644 --- a/packages/engine/Source/Renderer/Framebuffer.js +++ b/packages/engine/Source/Renderer/Framebuffer.js @@ -33,7 +33,16 @@ function attachRenderbuffer(framebuffer, attachment, renderbuffer) { * Framebuffers are used for render-to-texture effects; they allow us to render to * textures in one pass, and read from it in a later pass. * - * @param {object} options The initial framebuffer attachments as shown in the example below. context is required. The possible properties are colorTextures, colorRenderbuffers, depthTexture, depthRenderbuffer, stencilRenderbuffer, depthStencilTexture, depthStencilRenderbuffer, and destroyAttachments. + * @param {object} options Object with the following properties: + * @param {Context} options.context + * @param {Texture[]} [options.colorTextures] + * @param {Renderbuffer[]} [options.colorRenderbuffers] + * @param {Texture} [options.depthTexture] + * @param {Renderbuffer} [options.depthRenderbuffer] + * @param {Renderbuffer} [options.stencilRenderbuffer] + * @param {Texture} [options.depthStencilTexture] + * @param {Renderbuffer} [options.depthStencilRenderbuffer] + * @param {boolean} [options.destroyAttachments=true] When true, the framebuffer owns its attachments so they will be destroyed when {@link Framebuffer#destroy} is called or when a new attachment is assigned to an attachment point. * * @exception {DeveloperError} Cannot have both color texture and color renderbuffer attachments. * @exception {DeveloperError} Cannot have both a depth texture and depth renderbuffer attachment. @@ -130,7 +139,6 @@ function Framebuffer(options) { "Cannot have both a depth-stencil texture and depth-stencil renderbuffer attachment.", ); } - //>>includeEnd('debug'); // Avoid errors defined in Section 6.5 of the WebGL spec const depthAttachment = @@ -138,8 +146,6 @@ function Framebuffer(options) { const depthStencilAttachment = defined(options.depthStencilTexture) || defined(options.depthStencilRenderbuffer); - - //>>includeStart('debug', pragmas.debug); if (depthAttachment && depthStencilAttachment) { throw new DeveloperError( "Cannot have both a depth and depth-stencil attachment.", @@ -157,22 +163,14 @@ function Framebuffer(options) { } //>>includeEnd('debug'); - /////////////////////////////////////////////////////////////////// - this._bind(); - let texture; - let renderbuffer; - let i; - let length; - let attachmentEnum; - if (defined(options.colorTextures)) { const textures = options.colorTextures; - length = - this._colorTextures.length = + const length = + (this._colorTextures.length = this._activeColorAttachments.length = - textures.length; + textures.length); //>>includeStart('debug', pragmas.debug); if (length > maximumColorAttachments) { @@ -182,8 +180,8 @@ function Framebuffer(options) { } //>>includeEnd('debug'); - for (i = 0; i < length; ++i) { - texture = textures[i]; + for (let i = 0; i < length; ++i) { + const texture = textures[i]; //>>includeStart('debug', pragmas.debug); if (!PixelFormat.isColorFormat(texture.pixelFormat)) { @@ -209,7 +207,7 @@ function Framebuffer(options) { } //>>includeEnd('debug'); - attachmentEnum = this._gl.COLOR_ATTACHMENT0 + i; + const attachmentEnum = this._gl.COLOR_ATTACHMENT0 + i; attachTexture(this, attachmentEnum, texture); this._activeColorAttachments[i] = attachmentEnum; this._colorTextures[i] = texture; @@ -218,10 +216,10 @@ function Framebuffer(options) { if (defined(options.colorRenderbuffers)) { const renderbuffers = options.colorRenderbuffers; - length = - this._colorRenderbuffers.length = + const length = + (this._colorRenderbuffers.length = this._activeColorAttachments.length = - renderbuffers.length; + renderbuffers.length); //>>includeStart('debug', pragmas.debug); if (length > maximumColorAttachments) { @@ -231,9 +229,9 @@ function Framebuffer(options) { } //>>includeEnd('debug'); - for (i = 0; i < length; ++i) { - renderbuffer = renderbuffers[i]; - attachmentEnum = this._gl.COLOR_ATTACHMENT0 + i; + for (let i = 0; i < length; ++i) { + const renderbuffer = renderbuffers[i]; + const attachmentEnum = this._gl.COLOR_ATTACHMENT0 + i; attachRenderbuffer(this, attachmentEnum, renderbuffer); this._activeColorAttachments[i] = attachmentEnum; this._colorRenderbuffers[i] = renderbuffer; @@ -241,7 +239,7 @@ function Framebuffer(options) { } if (defined(options.depthTexture)) { - texture = options.depthTexture; + const texture = options.depthTexture; //>>includeStart('debug', pragmas.debug); if (texture.pixelFormat !== PixelFormat.DEPTH_COMPONENT) { @@ -256,19 +254,19 @@ function Framebuffer(options) { } if (defined(options.depthRenderbuffer)) { - renderbuffer = options.depthRenderbuffer; + const renderbuffer = options.depthRenderbuffer; attachRenderbuffer(this, this._gl.DEPTH_ATTACHMENT, renderbuffer); this._depthRenderbuffer = renderbuffer; } if (defined(options.stencilRenderbuffer)) { - renderbuffer = options.stencilRenderbuffer; + const renderbuffer = options.stencilRenderbuffer; attachRenderbuffer(this, this._gl.STENCIL_ATTACHMENT, renderbuffer); this._stencilRenderbuffer = renderbuffer; } if (defined(options.depthStencilTexture)) { - texture = options.depthStencilTexture; + const texture = options.depthStencilTexture; //>>includeStart('debug', pragmas.debug); if (texture.pixelFormat !== PixelFormat.DEPTH_STENCIL) { @@ -283,7 +281,7 @@ function Framebuffer(options) { } if (defined(options.depthStencilRenderbuffer)) { - renderbuffer = options.depthStencilRenderbuffer; + const renderbuffer = options.depthStencilRenderbuffer; attachRenderbuffer(this, this._gl.DEPTH_STENCIL_ATTACHMENT, renderbuffer); this._depthStencilRenderbuffer = renderbuffer; } @@ -415,10 +413,8 @@ Framebuffer.prototype.isDestroyed = function () { Framebuffer.prototype.destroy = function () { if (this.destroyAttachments) { // If the color texture is a cube map face, it is owned by the cube map, and will not be destroyed. - let i = 0; const textures = this._colorTextures; - let length = textures.length; - for (; i < length; ++i) { + for (let i = 0; i < textures.length; ++i) { const texture = textures[i]; if (defined(texture)) { texture.destroy(); @@ -426,8 +422,7 @@ Framebuffer.prototype.destroy = function () { } const renderbuffers = this._colorRenderbuffers; - length = renderbuffers.length; - for (i = 0; i < length; ++i) { + for (let i = 0; i < renderbuffers.length; ++i) { const renderbuffer = renderbuffers[i]; if (defined(renderbuffer)) { renderbuffer.destroy(); diff --git a/packages/engine/Source/Renderer/FramebufferManager.js b/packages/engine/Source/Renderer/FramebufferManager.js index 8b8324f9d82f..d9e5a8a64493 100644 --- a/packages/engine/Source/Renderer/FramebufferManager.js +++ b/packages/engine/Source/Renderer/FramebufferManager.js @@ -402,6 +402,14 @@ FramebufferManager.prototype.setDepthStencilTexture = function (texture) { this._depthStencilTexture = texture; }; +/** + * If using MSAA, resolve the stencil. + * + * @param {Context} context + * @param {boolean} blitStencil + * + * @private + */ FramebufferManager.prototype.prepareTextures = function (context, blitStencil) { if (this._numSamples > 1) { this._multisampleFramebuffer.blitFramebuffers(context, blitStencil); @@ -427,28 +435,26 @@ FramebufferManager.prototype.destroyFramebuffer = function () { FramebufferManager.prototype.destroy = function () { if (this._color) { - let i; - const length = this._colorTextures.length; - for (i = 0; i < length; ++i) { - const texture = this._colorTextures[i]; + const colorTextures = this._colorTextures; + const colorRenderbuffers = this._colorRenderbuffers; + for (let i = 0; i < colorTextures.length; ++i) { + const texture = colorTextures[i]; if (this._createColorAttachments) { if (defined(texture) && !texture.isDestroyed()) { - this._colorTextures[i].destroy(); - this._colorTextures[i] = undefined; + texture.destroy(); } } if (defined(texture) && texture.isDestroyed()) { - this._colorTextures[i] = undefined; + colorTextures[i] = undefined; } - const renderbuffer = this._colorRenderbuffers[i]; + const renderbuffer = colorRenderbuffers[i]; if (this._createColorAttachments) { if (defined(renderbuffer) && !renderbuffer.isDestroyed()) { - this._colorRenderbuffers[i].destroy(); - this._colorRenderbuffers[i] = undefined; + renderbuffer.destroy(); } } if (defined(renderbuffer) && renderbuffer.isDestroyed()) { - this._colorRenderbuffers[i] = undefined; + colorRenderbuffers[i] = undefined; } } } diff --git a/packages/engine/Source/Renderer/MultisampleFramebuffer.js b/packages/engine/Source/Renderer/MultisampleFramebuffer.js index 395f5a96c0b1..7990b5f1abfa 100644 --- a/packages/engine/Source/Renderer/MultisampleFramebuffer.js +++ b/packages/engine/Source/Renderer/MultisampleFramebuffer.js @@ -12,7 +12,15 @@ import Framebuffer from "./Framebuffer.js"; * second is bound to DRAW_FRAMEBUFFER during the blit, and has texture attachments * to store the copied pixels. * - * @param {object} options The initial framebuffer attachments. context, width, and height are required. The possible properties are colorTextures, colorRenderbuffers, depthStencilTexture, depthStencilRenderbuffer, and destroyAttachments. + * @param {object} options Object with the following properties: + * @param {Context} options.context + * @param {number} options.width + * @param {number} options.height + * @param {Texture[]} [options.colorTextures] + * @param {Renderbuffer[]} [options.colorRenderbuffers] + * @param {Texture} [options.depthStencilTexture] + * @param {Renderbuffer} [options.depthStencilRenderbuffer] + * @param {boolean} [options.destroyAttachments] * * @exception {DeveloperError} Both color renderbuffer and texture attachments must be provided. * @exception {DeveloperError} Both depth-stencil renderbuffer and texture attachments must be provided. @@ -23,27 +31,32 @@ import Framebuffer from "./Framebuffer.js"; function MultisampleFramebuffer(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); - const context = options.context; - const width = options.width; - const height = options.height; + const { + context, + width, + height, + colorRenderbuffers, + colorTextures, + depthStencilRenderbuffer, + depthStencilTexture, + destroyAttachments, + } = options; + //>>includeStart('debug', pragmas.debug); Check.defined("options.context", context); Check.defined("options.width", width); Check.defined("options.height", height); //>>includeEnd('debug'); + this._width = width; this._height = height; - const colorRenderbuffers = options.colorRenderbuffers; - const colorTextures = options.colorTextures; if (defined(colorRenderbuffers) !== defined(colorTextures)) { throw new DeveloperError( "Both color renderbuffer and texture attachments must be provided.", ); } - const depthStencilRenderbuffer = options.depthStencilRenderbuffer; - const depthStencilTexture = options.depthStencilTexture; if (defined(depthStencilRenderbuffer) !== defined(depthStencilTexture)) { throw new DeveloperError( "Both depth-stencil renderbuffer and texture attachments must be provided.", @@ -54,13 +67,13 @@ function MultisampleFramebuffer(options) { context: context, colorRenderbuffers: colorRenderbuffers, depthStencilRenderbuffer: depthStencilRenderbuffer, - destroyAttachments: options.destroyAttachments, + destroyAttachments: destroyAttachments, }); this._colorFramebuffer = new Framebuffer({ context: context, colorTextures: colorTextures, depthStencilTexture: depthStencilTexture, - destroyAttachments: options.destroyAttachments, + destroyAttachments: destroyAttachments, }); } @@ -72,6 +85,14 @@ MultisampleFramebuffer.prototype.getColorFramebuffer = function () { return this._colorFramebuffer; }; +/** + * Copy from the render framebuffer to the color framebuffer, resolving the stencil. + * + * @param {Context} context + * @param {boolean} blitStencil true if the stencil mask should be applied. + * + * @private + */ MultisampleFramebuffer.prototype.blitFramebuffers = function ( context, blitStencil, diff --git a/packages/engine/Source/Renderer/UniformState.js b/packages/engine/Source/Renderer/UniformState.js index 2e28f0585217..3c3fa7c42754 100644 --- a/packages/engine/Source/Renderer/UniformState.js +++ b/packages/engine/Source/Renderer/UniformState.js @@ -1365,7 +1365,10 @@ UniformState.prototype.updateCamera = function (camera) { * @param {object} frustum The frustum to synchronize with. */ UniformState.prototype.updateFrustum = function (frustum) { + // If any frustum parameters have changed, calling the frustum.projectionMatrix + // getter will recompute the projection before it is copied. setProjection(this, frustum.projectionMatrix); + if (defined(frustum.infiniteProjectionMatrix)) { setInfiniteProjection(this, frustum.infiniteProjectionMatrix); } diff --git a/packages/engine/Source/Scene/Camera.js b/packages/engine/Source/Scene/Camera.js index 42c0f50f52cd..f24e257cef63 100644 --- a/packages/engine/Source/Scene/Camera.js +++ b/packages/engine/Source/Scene/Camera.js @@ -2522,10 +2522,8 @@ function rectangleCameraPosition3D(camera, rectangle, result, updateCamera) { const ellipsoid = camera._projection.ellipsoid; const cameraRF = updateCamera ? camera : defaultRF; - const north = rectangle.north; - const south = rectangle.south; - let east = rectangle.east; - const west = rectangle.west; + const { north, south, west } = rectangle; + let { east } = rectangle; // If we go across the International Date Line if (west > east) { diff --git a/packages/engine/Source/Scene/DerivedCommand.js b/packages/engine/Source/Scene/DerivedCommand.js index d64958604815..e0d7d2cb3de5 100644 --- a/packages/engine/Source/Scene/DerivedCommand.js +++ b/packages/engine/Source/Scene/DerivedCommand.js @@ -12,89 +12,80 @@ const fragDepthRegex = /\bgl_FragDepth\b/; const discardRegex = /\bdiscard\b/; function getDepthOnlyShaderProgram(context, shaderProgram) { - let shader = context.shaderCache.getDerivedShaderProgram( + const cachedShader = context.shaderCache.getDerivedShaderProgram( shaderProgram, "depthOnly", ); - if (!defined(shader)) { - const attributeLocations = shaderProgram._attributeLocations; - let fs = shaderProgram.fragmentShaderSource; - - let i; - let writesDepthOrDiscards = false; - const sources = fs.sources; - let length = sources.length; - for (i = 0; i < length; ++i) { - if (fragDepthRegex.test(sources[i]) || discardRegex.test(sources[i])) { - writesDepthOrDiscards = true; - break; - } - } + if (defined(cachedShader)) { + return cachedShader; + } - let usesLogDepth = false; - const defines = fs.defines; - length = defines.length; - for (i = 0; i < length; ++i) { - if (defines[i] === "LOG_DEPTH") { - usesLogDepth = true; - break; - } - } + let fs = shaderProgram.fragmentShaderSource; - let source; - if (!writesDepthOrDiscards && !usesLogDepth) { - source = - "void main() \n" + - "{ \n" + - " out_FragColor = vec4(1.0); \n" + - "} \n"; - fs = new ShaderSource({ - sources: [source], - }); - } else if (!writesDepthOrDiscards && usesLogDepth) { - source = - "void main() \n" + - "{ \n" + - " out_FragColor = vec4(1.0); \n" + - " czm_writeLogDepth(); \n" + - "} \n"; - fs = new ShaderSource({ - defines: ["LOG_DEPTH"], - sources: [source], - }); + let writesDepthOrDiscards = false; + const sources = fs.sources; + for (let i = 0; i < sources.length; ++i) { + if (fragDepthRegex.test(sources[i]) || discardRegex.test(sources[i])) { + writesDepthOrDiscards = true; + break; } + } - shader = context.shaderCache.createDerivedShaderProgram( - shaderProgram, - "depthOnly", - { - vertexShaderSource: shaderProgram.vertexShaderSource, - fragmentShaderSource: fs, - attributeLocations: attributeLocations, - }, - ); + const usesLogDepth = fs.defines.indexOf("LOG_DEPTH") >= 0; + + if (!writesDepthOrDiscards && !usesLogDepth) { + const source = `void main() +{ + out_FragColor = vec4(1.0); +} +`; + fs = new ShaderSource({ + sources: [source], + }); + } else if (!writesDepthOrDiscards && usesLogDepth) { + const source = `void main() +{ + out_FragColor = vec4(1.0); + czm_writeLogDepth(); +} +`; + fs = new ShaderSource({ + defines: ["LOG_DEPTH"], + sources: [source], + }); } - return shader; + return context.shaderCache.createDerivedShaderProgram( + shaderProgram, + "depthOnly", + { + vertexShaderSource: shaderProgram.vertexShaderSource, + fragmentShaderSource: fs, + attributeLocations: shaderProgram._attributeLocations, + }, + ); } function getDepthOnlyRenderState(scene, renderState) { const cache = scene._depthOnlyRenderStateCache; - let depthOnlyState = cache[renderState.id]; - if (!defined(depthOnlyState)) { - const rs = RenderState.getState(renderState); - rs.depthMask = true; - rs.colorMask = { - red: false, - green: false, - blue: false, - alpha: false, - }; - - depthOnlyState = RenderState.fromCache(rs); - cache[renderState.id] = depthOnlyState; + + const cachedDepthOnlyState = cache[renderState.id]; + if (defined(cachedDepthOnlyState)) { + return cachedDepthOnlyState; } + const rs = RenderState.getState(renderState); + rs.depthMask = true; + rs.colorMask = { + red: false, + green: false, + blue: false, + alpha: false, + }; + + const depthOnlyState = RenderState.fromCache(rs); + cache[renderState.id] = depthOnlyState; + return depthOnlyState; } @@ -113,12 +104,8 @@ DerivedCommand.createDepthOnlyDerivedCommand = function ( result = {}; } - let shader; - let renderState; - if (defined(result.depthOnlyCommand)) { - shader = result.depthOnlyCommand.shaderProgram; - renderState = result.depthOnlyCommand.renderState; - } + const shader = result.depthOnlyCommand?.shaderProgram; + const renderState = result.depthOnlyCommand?.renderState; result.depthOnlyCommand = DrawCommand.shallowClone( command, @@ -154,91 +141,88 @@ function getLogDepthShaderProgram(context, shaderProgram) { return shaderProgram; } - let shader = context.shaderCache.getDerivedShaderProgram( + const cachedShader = context.shaderCache.getDerivedShaderProgram( shaderProgram, "logDepth", ); - if (!defined(shader)) { - const attributeLocations = shaderProgram._attributeLocations; - const vs = shaderProgram.vertexShaderSource.clone(); - const fs = shaderProgram.fragmentShaderSource.clone(); - - vs.defines = defined(vs.defines) ? vs.defines.slice(0) : []; - vs.defines.push("LOG_DEPTH"); - fs.defines = defined(fs.defines) ? fs.defines.slice(0) : []; - fs.defines.push("LOG_DEPTH"); - - let i; - let logMain; - let writesLogDepth = false; - let sources = vs.sources; - let length = sources.length; - for (i = 0; i < length; ++i) { - if (vertexlogDepthRegex.test(sources[i])) { - writesLogDepth = true; - break; - } - } + if (defined(cachedShader)) { + return cachedShader; + } - if (!writesLogDepth) { - for (i = 0; i < length; ++i) { - sources[i] = ShaderSource.replaceMain(sources[i], "czm_log_depth_main"); - } - - logMain = - "\n\n" + - "void main() \n" + - "{ \n" + - " czm_log_depth_main(); \n" + - " czm_vertexLogDepth(); \n" + - "} \n"; - sources.push(logMain); - } + const attributeLocations = shaderProgram._attributeLocations; + const vs = shaderProgram.vertexShaderSource.clone(); + const fs = shaderProgram.fragmentShaderSource.clone(); - sources = fs.sources; - length = sources.length; + vs.defines = defined(vs.defines) ? vs.defines.slice(0) : []; + vs.defines.push("LOG_DEPTH"); + fs.defines = defined(fs.defines) ? fs.defines.slice(0) : []; + fs.defines.push("LOG_DEPTH"); - writesLogDepth = false; - for (i = 0; i < length; ++i) { - if (writeLogDepthRegex.test(sources[i])) { - writesLogDepth = true; - } - } - // This define indicates that a log depth value is written by the shader but doesn't use czm_writeLogDepth. - if (fs.defines.indexOf("LOG_DEPTH_WRITE") !== -1) { + let writesLogDepth = false; + let sources = vs.sources; + for (let i = 0; i < sources.length; ++i) { + if (vertexlogDepthRegex.test(sources[i])) { writesLogDepth = true; + break; + } + } + + if (!writesLogDepth) { + for (let i = 0; i < sources.length; ++i) { + sources[i] = ShaderSource.replaceMain(sources[i], "czm_log_depth_main"); } - let logSource = ""; + const logMain = ` + +void main() +{ + czm_log_depth_main(); + czm_vertexLogDepth(); +} +`; + sources.push(logMain); + } - if (!writesLogDepth) { - for (i = 0; i < length; i++) { - sources[i] = ShaderSource.replaceMain(sources[i], "czm_log_depth_main"); - } + sources = fs.sources; - logSource += - "\n" + - "void main() \n" + - "{ \n" + - " czm_log_depth_main(); \n" + - " czm_writeLogDepth(); \n" + - "} \n"; + writesLogDepth = false; + for (let i = 0; i < sources.length; ++i) { + if (writeLogDepthRegex.test(sources[i])) { + writesLogDepth = true; } + } + // This define indicates that a log depth value is written by the shader but doesn't use czm_writeLogDepth. + if (fs.defines.indexOf("LOG_DEPTH_WRITE") !== -1) { + writesLogDepth = true; + } - sources.push(logSource); + let logSource = ""; - shader = context.shaderCache.createDerivedShaderProgram( - shaderProgram, - "logDepth", - { - vertexShaderSource: vs, - fragmentShaderSource: fs, - attributeLocations: attributeLocations, - }, - ); + if (!writesLogDepth) { + for (let i = 0; i < sources.length; i++) { + sources[i] = ShaderSource.replaceMain(sources[i], "czm_log_depth_main"); + } + + logSource = ` +void main() +{ + czm_log_depth_main(); + czm_writeLogDepth(); +} +`; } - return shader; + sources.push(logSource); + + return context.shaderCache.createDerivedShaderProgram( + shaderProgram, + "logDepth", + { + vertexShaderSource: vs, + fragmentShaderSource: fs, + attributeLocations: attributeLocations, + }, + ); } DerivedCommand.createLogDepthCommand = function (command, context, result) { @@ -246,10 +230,7 @@ DerivedCommand.createLogDepthCommand = function (command, context, result) { result = {}; } - let shader; - if (defined(result.command)) { - shader = result.command.shaderProgram; - } + const shader = result.command?.shaderProgram; result.command = DrawCommand.shallowClone(command, result.command); @@ -267,73 +248,64 @@ DerivedCommand.createLogDepthCommand = function (command, context, result) { }; function getPickShaderProgram(context, shaderProgram, pickId) { - let shader = context.shaderCache.getDerivedShaderProgram( + const cachedShader = context.shaderCache.getDerivedShaderProgram( shaderProgram, "pick", ); - if (!defined(shader)) { - const attributeLocations = shaderProgram._attributeLocations; - let fs = shaderProgram.fragmentShaderSource; - - const sources = fs.sources; - const length = sources.length; + if (defined(cachedShader)) { + return cachedShader; + } - const hasFragData = sources.some((source) => - source.includes("out_FragData"), - ); - const outputColorVariable = hasFragData - ? "out_FragData_0" - : "out_FragColor"; - const newMain = `void main () -{ - czm_non_pick_main(); - if (${outputColorVariable}.a == 0.0) { - discard; - } - ${outputColorVariable} = ${pickId}; + const attributeLocations = shaderProgram._attributeLocations; + const { sources, defines } = shaderProgram.fragmentShaderSource; + + const hasFragData = sources.some((source) => source.includes("out_FragData")); + const outputColorVariable = hasFragData ? "out_FragData_0" : "out_FragColor"; + const newMain = `void main () +{ + czm_non_pick_main(); + if (${outputColorVariable}.a == 0.0) { + discard; + } + ${outputColorVariable} = ${pickId}; } `; - const newSources = new Array(length + 1); - for (let i = 0; i < length; ++i) { - newSources[i] = ShaderSource.replaceMain(sources[i], "czm_non_pick_main"); - } - newSources[length] = newMain; - fs = new ShaderSource({ - sources: newSources, - defines: fs.defines, - }); - shader = context.shaderCache.createDerivedShaderProgram( - shaderProgram, - "pick", - { - vertexShaderSource: shaderProgram.vertexShaderSource, - fragmentShaderSource: fs, - attributeLocations: attributeLocations, - }, - ); + const length = sources.length; + const newSources = new Array(length + 1); + for (let i = 0; i < length; ++i) { + newSources[i] = ShaderSource.replaceMain(sources[i], "czm_non_pick_main"); } - - return shader; + newSources[length] = newMain; + const fragmentShaderSource = new ShaderSource({ + sources: newSources, + defines: defines, + }); + return context.shaderCache.createDerivedShaderProgram(shaderProgram, "pick", { + vertexShaderSource: shaderProgram.vertexShaderSource, + fragmentShaderSource: fragmentShaderSource, + attributeLocations: attributeLocations, + }); } function getPickRenderState(scene, renderState) { const cache = scene.picking.pickRenderStateCache; - let pickState = cache[renderState.id]; - if (!defined(pickState)) { - const rs = RenderState.getState(renderState); - rs.blending.enabled = false; - - // Turns on depth writing for opaque and translucent passes - // Overlapping translucent geometry on the globe surface may exhibit z-fighting - // during the pick pass which may not match the rendered scene. Once - // terrain is on by default and ground primitives are used instead - // this will become less of a problem. - rs.depthMask = true; - - pickState = RenderState.fromCache(rs); - cache[renderState.id] = pickState; + const cachedPickState = cache[renderState.id]; + if (defined(cachedPickState)) { + return cachedPickState; } + const rs = RenderState.getState(renderState); + rs.blending.enabled = false; + + // Turns on depth writing for opaque and translucent passes + // Overlapping translucent geometry on the globe surface may exhibit z-fighting + // during the pick pass which may not match the rendered scene. Once + // terrain is on by default and ground primitives are used instead + // this will become less of a problem. + rs.depthMask = true; + + const pickState = RenderState.fromCache(rs); + cache[renderState.id] = pickState; return pickState; } @@ -347,12 +319,8 @@ DerivedCommand.createPickDerivedCommand = function ( result = {}; } - let shader; - let renderState; - if (defined(result.pickCommand)) { - shader = result.pickCommand.shaderProgram; - renderState = result.pickCommand.renderState; - } + const shader = result.pickCommand?.shaderProgram; + const renderState = result.pickCommand?.renderState; result.pickCommand = DrawCommand.shallowClone(command, result.pickCommand); @@ -376,32 +344,28 @@ DerivedCommand.createPickDerivedCommand = function ( }; function getHdrShaderProgram(context, shaderProgram) { - let shader = context.shaderCache.getDerivedShaderProgram( + const cachedShader = context.shaderCache.getDerivedShaderProgram( shaderProgram, "HDR", ); - if (!defined(shader)) { - const attributeLocations = shaderProgram._attributeLocations; - const vs = shaderProgram.vertexShaderSource.clone(); - const fs = shaderProgram.fragmentShaderSource.clone(); - - vs.defines = defined(vs.defines) ? vs.defines.slice(0) : []; - vs.defines.push("HDR"); - fs.defines = defined(fs.defines) ? fs.defines.slice(0) : []; - fs.defines.push("HDR"); - - shader = context.shaderCache.createDerivedShaderProgram( - shaderProgram, - "HDR", - { - vertexShaderSource: vs, - fragmentShaderSource: fs, - attributeLocations: attributeLocations, - }, - ); + if (defined(cachedShader)) { + return cachedShader; } - return shader; + const attributeLocations = shaderProgram._attributeLocations; + const vs = shaderProgram.vertexShaderSource.clone(); + const fs = shaderProgram.fragmentShaderSource.clone(); + + vs.defines = defined(vs.defines) ? vs.defines.slice(0) : []; + vs.defines.push("HDR"); + fs.defines = defined(fs.defines) ? fs.defines.slice(0) : []; + fs.defines.push("HDR"); + + return context.shaderCache.createDerivedShaderProgram(shaderProgram, "HDR", { + vertexShaderSource: vs, + fragmentShaderSource: fs, + attributeLocations: attributeLocations, + }); } DerivedCommand.createHdrCommand = function (command, context, result) { @@ -409,10 +373,7 @@ DerivedCommand.createHdrCommand = function (command, context, result) { result = {}; } - let shader; - if (defined(result.command)) { - shader = result.command.shaderProgram; - } + const shader = result.command?.shaderProgram; result.command = DrawCommand.shallowClone(command, result.command); diff --git a/packages/engine/Source/Scene/GlobeDepth.js b/packages/engine/Source/Scene/GlobeDepth.js index 7f4f7cb649ba..6058642edb36 100644 --- a/packages/engine/Source/Scene/GlobeDepth.js +++ b/packages/engine/Source/Scene/GlobeDepth.js @@ -14,6 +14,9 @@ import StencilFunction from "./StencilFunction.js"; import StencilOperation from "./StencilOperation.js"; /** + * @alias GlobeDepth + * @constructor + * * @private */ function GlobeDepth() { @@ -83,20 +86,13 @@ Object.defineProperties(GlobeDepth.prototype, { }, }); -function destroyFramebuffers(globeDepth) { - globeDepth._pickColorFramebuffer.destroy(); - globeDepth._outputFramebuffer.destroy(); - globeDepth._copyDepthFramebuffer.destroy(); - globeDepth._tempCopyDepthFramebuffer.destroy(); - globeDepth._updateDepthFramebuffer.destroy(); -} - function updateCopyCommands(globeDepth, context, width, height, passState) { - globeDepth._viewport.width = width; - globeDepth._viewport.height = height; + const viewport = globeDepth._viewport; + viewport.width = width; + viewport.height = height; const useScissorTest = !BoundingRectangle.equals( - globeDepth._viewport, + viewport, passState.viewport, ); let updateScissor = useScissorTest !== globeDepth._useScissorTest; @@ -114,18 +110,18 @@ function updateCopyCommands(globeDepth, context, width, height, passState) { if ( !defined(globeDepth._rs) || - !BoundingRectangle.equals(globeDepth._viewport, globeDepth._rs.viewport) || + !BoundingRectangle.equals(viewport, globeDepth._rs.viewport) || updateScissor ) { globeDepth._rs = RenderState.fromCache({ - viewport: globeDepth._viewport, + viewport: viewport, scissorTest: { enabled: globeDepth._useScissorTest, rectangle: globeDepth._scissorRectangle, }, }); globeDepth._rsBlend = RenderState.fromCache({ - viewport: globeDepth._viewport, + viewport: viewport, scissorTest: { enabled: globeDepth._useScissorTest, rectangle: globeDepth._scissorRectangle, @@ -135,7 +131,7 @@ function updateCopyCommands(globeDepth, context, width, height, passState) { // Copy packed depth only if the 3D Tiles bit is set globeDepth._rsUpdate = RenderState.fromCache({ - viewport: globeDepth._viewport, + viewport: viewport, scissorTest: { enabled: globeDepth._useScissorTest, rectangle: globeDepth._scissorRectangle, @@ -236,6 +232,18 @@ function updateCopyCommands(globeDepth, context, width, height, passState) { globeDepth._clearGlobeColorCommand.framebuffer = globeDepth.framebuffer; } +/** + * Update framebuffers and render state. + * + * @param {Context} context The context used for rendering. + * @param {PassState} passState Rendering state for subsequent render passes. + * @param {BoundingRectangle} viewport The viewport for the rendering. + * @param {number} numSamples The number of samples for multi-sample anti-aliasing (MSAA). + * @param {boolean} hdr true if the color output needs to be floating point for HDR rendering. + * @param {boolean} clearGlobeDepth true if the depth buffer should be cleared before rendering 3D Tiles and opaque entities. + * + * @private + */ GlobeDepth.prototype.update = function ( context, passState, @@ -244,8 +252,7 @@ GlobeDepth.prototype.update = function ( hdr, clearGlobeDepth, ) { - const width = viewport.width; - const height = viewport.height; + const { width, height } = viewport; const pixelDatatype = hdr ? context.halfFloatingPointTexture @@ -268,10 +275,17 @@ GlobeDepth.prototype.update = function ( updateCopyCommands(this, context, width, height, passState); context.uniformState.globeDepthTexture = undefined; - this._useHdr = hdr; this._clearGlobeDepth = clearGlobeDepth; }; +/** + * If using MSAA, resolve the stencil. + * + * @param {Context} context + * @param {boolean} blitStencil true if the stencil has been set. + * + * @private + */ GlobeDepth.prototype.prepareColorTextures = function (context, blitStencil) { if (!this.picking && this._numSamples > 1) { this._outputFramebuffer.prepareTextures(context, blitStencil); @@ -287,54 +301,60 @@ GlobeDepth.prototype.executeCopyDepth = function (context, passState) { } }; +/** + * Update the existing depth texture using a stencil. + * + * @param {Context} context The context used for rendering. + * @param {PassState} passState Render state for subsequent rendering passes. + * @param {Texture} [depthTexture] The depth texture to copy. + */ GlobeDepth.prototype.executeUpdateDepth = function ( context, passState, - clearGlobeDepth, depthTexture, ) { const depthTextureToCopy = defined(depthTexture) ? depthTexture : passState.framebuffer.depthStencilTexture; if ( - clearGlobeDepth || - depthTextureToCopy !== this.colorFramebufferManager.getDepthStencilTexture() + !this._clearGlobeDepth && + depthTextureToCopy === this.colorFramebufferManager.getDepthStencilTexture() ) { - // First copy the depth to a temporary globe depth texture, then update the - // main globe depth texture where the stencil bit for 3D Tiles is set. - // This preserves the original globe depth except where 3D Tiles is rendered. - // The additional texture and framebuffer resources are created on demand. - if (defined(this._updateDepthCommand)) { - if ( - !defined(this._updateDepthFramebuffer.framebuffer) || - this._updateDepthFramebuffer.getDepthStencilTexture() !== - depthTextureToCopy || - this._updateDepthFramebuffer.getColorTexture() !== - this._copyDepthFramebuffer.getColorTexture() - ) { - const width = this._copyDepthFramebuffer.getColorTexture().width; - const height = this._copyDepthFramebuffer.getColorTexture().height; - this._tempCopyDepthFramebuffer.destroy(); - this._tempCopyDepthFramebuffer.update(context, width, height); - - const colorTexture = this._copyDepthFramebuffer.getColorTexture(); - this._updateDepthFramebuffer.setColorTexture(colorTexture, 0); - this._updateDepthFramebuffer.setDepthStencilTexture(depthTextureToCopy); - this._updateDepthFramebuffer.update(context, width, height); - - updateCopyCommands(this, context, width, height, passState); - } - this._tempCopyDepthTexture = depthTextureToCopy; - this._tempCopyDepthCommand.execute(context, passState); - this._updateDepthCommand.execute(context, passState); + // Fast path - the depth texture can be copied normally. + if (defined(this._copyDepthCommand)) { + this._copyDepthCommand.execute(context, passState); } return; } + if (!defined(this._updateDepthCommand)) { + return; + } - // Fast path - the depth texture can be copied normally. - if (defined(this._copyDepthCommand)) { - this._copyDepthCommand.execute(context, passState); + // First copy the depth to a temporary globe depth texture, then update the + // main globe depth texture where the stencil bit for 3D Tiles is set. + // This preserves the original globe depth except where 3D Tiles is rendered. + // The additional texture and framebuffer resources are created on demand. + const updateDepthFramebuffer = this._updateDepthFramebuffer; + if ( + !defined(updateDepthFramebuffer.framebuffer) || + updateDepthFramebuffer.getDepthStencilTexture() !== depthTextureToCopy || + updateDepthFramebuffer.getColorTexture() !== + this._copyDepthFramebuffer.getColorTexture() + ) { + const colorTexture = this._copyDepthFramebuffer.getColorTexture(); + const { width, height } = colorTexture; + this._tempCopyDepthFramebuffer.destroy(); + this._tempCopyDepthFramebuffer.update(context, width, height); + + updateDepthFramebuffer.setColorTexture(colorTexture, 0); + updateDepthFramebuffer.setDepthStencilTexture(depthTextureToCopy); + updateDepthFramebuffer.update(context, width, height); + + updateCopyCommands(this, context, width, height, passState); } + this._tempCopyDepthTexture = depthTextureToCopy; + this._tempCopyDepthCommand.execute(context, passState); + this._updateDepthCommand.execute(context, passState); }; GlobeDepth.prototype.executeCopyColor = function (context, passState) { @@ -356,7 +376,11 @@ GlobeDepth.prototype.isDestroyed = function () { }; GlobeDepth.prototype.destroy = function () { - destroyFramebuffers(this); + this._pickColorFramebuffer.destroy(); + this._outputFramebuffer.destroy(); + this._copyDepthFramebuffer.destroy(); + this._tempCopyDepthFramebuffer.destroy(); + this._updateDepthFramebuffer.destroy(); if (defined(this._copyColorCommand)) { this._copyColorCommand.shaderProgram = diff --git a/packages/engine/Source/Scene/GlobeTranslucencyState.js b/packages/engine/Source/Scene/GlobeTranslucencyState.js index c1f2f693f327..c26d7da41d68 100644 --- a/packages/engine/Source/Scene/GlobeTranslucencyState.js +++ b/packages/engine/Source/Scene/GlobeTranslucencyState.js @@ -968,7 +968,6 @@ function executeCommandsMatchingType( commandsLength, executeCommandFunction, scene, - context, passState, types, ) { @@ -976,7 +975,7 @@ function executeCommandsMatchingType( const command = commands[i]; const type = command.derivedCommands.type; if (!defined(types) || types.indexOf(type) > -1) { - executeCommandFunction(command, scene, context, passState); + executeCommandFunction(command, scene, passState); } } } @@ -986,11 +985,10 @@ function executeCommands( commandsLength, executeCommandFunction, scene, - context, passState, ) { for (let i = 0; i < commandsLength; ++i) { - executeCommandFunction(commands[i], scene, context, passState); + executeCommandFunction(commands[i], scene, passState); } } @@ -1028,7 +1026,6 @@ GlobeTranslucencyState.prototype.executeGlobeCommands = function ( globeCommandsLength, executeCommandFunction, scene, - context, passState, opaqueTypes, ); @@ -1041,7 +1038,9 @@ GlobeTranslucencyState.prototype.executeGlobeClassificationCommands = function ( scene, passState, ) { - const context = scene.context; + const { context } = scene; + const { uniformState } = context; + const globeCommands = frustumCommands.commands[Pass.GLOBE]; const globeCommandsLength = frustumCommands.indices[Pass.GLOBE]; const classificationCommands = @@ -1063,7 +1062,6 @@ GlobeTranslucencyState.prototype.executeGlobeClassificationCommands = function ( classificationCommandsLength, executeCommandFunction, scene, - context, passState, ); } @@ -1075,7 +1073,7 @@ GlobeTranslucencyState.prototype.executeGlobeClassificationCommands = function ( this._globeTranslucencyFramebuffer = globeTranslucencyFramebuffer; - const originalGlobeDepthTexture = context.uniformState.globeDepthTexture; + const originalGlobeDepthTexture = uniformState.globeDepthTexture; const originalFramebuffer = passState.framebuffer; // Render to internal framebuffer and get the first depth peel @@ -1087,7 +1085,6 @@ GlobeTranslucencyState.prototype.executeGlobeClassificationCommands = function ( globeCommandsLength, executeCommandFunction, scene, - context, passState, depthOnlyTypes, ); @@ -1098,7 +1095,7 @@ GlobeTranslucencyState.prototype.executeGlobeClassificationCommands = function ( context, passState, ); - context.uniformState.globeDepthTexture = packedDepthTexture; + uniformState.globeDepthTexture = packedDepthTexture; } // Render classification on translucent faces @@ -1107,12 +1104,11 @@ GlobeTranslucencyState.prototype.executeGlobeClassificationCommands = function ( classificationCommandsLength, executeCommandFunction, scene, - context, passState, ); // Unset temporary state - context.uniformState.globeDepthTexture = originalGlobeDepthTexture; + uniformState.globeDepthTexture = originalGlobeDepthTexture; passState.framebuffer = originalFramebuffer; }; diff --git a/packages/engine/Source/Scene/OIT.js b/packages/engine/Source/Scene/OIT.js index e2fbffd43f2e..572120d40510 100644 --- a/packages/engine/Source/Scene/OIT.js +++ b/packages/engine/Source/Scene/OIT.js @@ -704,10 +704,6 @@ function executeTranslucentCommandsSortedMultipass( commands, invertClassification, ) { - let command; - let derivedCommand; - let j; - const { context, frameState } = scene; const { useLogDepth, shadowState } = frameState; const useHdr = scene._hdr; @@ -723,70 +719,46 @@ function executeTranslucentCommandsSortedMultipass( const debugFramebuffer = oit._opaqueFBO.framebuffer; passState.framebuffer = oit._translucentFBO.framebuffer; - for (j = 0; j < commands.length; ++j) { - command = commands[j]; + for (let j = 0; j < commands.length; ++j) { + let command = commands[j]; command = useLogDepth ? command.derivedCommands.logDepth.command : command; command = useHdr ? command.derivedCommands.hdr.command : command; - derivedCommand = + const derivedCommand = lightShadowsEnabled && command.receiveShadows ? command.derivedCommands.oit.shadows.translucentCommand : command.derivedCommands.oit.translucentCommand; - executeFunction( - derivedCommand, - scene, - context, - passState, - debugFramebuffer, - ); + executeFunction(derivedCommand, scene, passState, debugFramebuffer); } if (defined(invertClassification)) { - command = invertClassification.unclassifiedCommand; - derivedCommand = + const command = invertClassification.unclassifiedCommand; + const derivedCommand = lightShadowsEnabled && command.receiveShadows ? command.derivedCommands.oit.shadows.translucentCommand : command.derivedCommands.oit.translucentCommand; - executeFunction( - derivedCommand, - scene, - context, - passState, - debugFramebuffer, - ); + executeFunction(derivedCommand, scene, passState, debugFramebuffer); } passState.framebuffer = oit._alphaFBO.framebuffer; - for (j = 0; j < commands.length; ++j) { - command = commands[j]; + for (let j = 0; j < commands.length; ++j) { + let command = commands[j]; command = useLogDepth ? command.derivedCommands.logDepth.command : command; command = useHdr ? command.derivedCommands.hdr.command : command; - derivedCommand = + const derivedCommand = lightShadowsEnabled && command.receiveShadows ? command.derivedCommands.oit.shadows.alphaCommand : command.derivedCommands.oit.alphaCommand; - executeFunction( - derivedCommand, - scene, - context, - passState, - debugFramebuffer, - ); + executeFunction(derivedCommand, scene, passState, debugFramebuffer); } if (defined(invertClassification)) { - command = invertClassification.unclassifiedCommand; - derivedCommand = + const command = invertClassification.unclassifiedCommand; + const derivedCommand = lightShadowsEnabled && command.receiveShadows ? command.derivedCommands.oit.shadows.alphaCommand : command.derivedCommands.oit.alphaCommand; - executeFunction( - derivedCommand, - scene, - context, - passState, - debugFramebuffer, - ); + executeFunction(derivedCommand, scene, passState, debugFramebuffer); } passState.framebuffer = framebuffer; @@ -822,39 +794,24 @@ function executeTranslucentCommandsSortedMRT( const debugFramebuffer = oit._opaqueFBO.framebuffer; passState.framebuffer = oit._translucentFBO.framebuffer; - let command; - let derivedCommand; - for (let j = 0; j < commands.length; ++j) { - command = commands[j]; + let command = commands[j]; command = useLogDepth ? command.derivedCommands.logDepth.command : command; command = useHdr ? command.derivedCommands.hdr.command : command; - derivedCommand = + const derivedCommand = lightShadowsEnabled && command.receiveShadows ? command.derivedCommands.oit.shadows.translucentCommand : command.derivedCommands.oit.translucentCommand; - executeFunction( - derivedCommand, - scene, - context, - passState, - debugFramebuffer, - ); + executeFunction(derivedCommand, scene, passState, debugFramebuffer); } if (defined(invertClassification)) { - command = invertClassification.unclassifiedCommand; - derivedCommand = + const command = invertClassification.unclassifiedCommand; + const derivedCommand = lightShadowsEnabled && command.receiveShadows ? command.derivedCommands.oit.shadows.translucentCommand : command.derivedCommands.oit.translucentCommand; - executeFunction( - derivedCommand, - scene, - context, - passState, - debugFramebuffer, - ); + executeFunction(derivedCommand, scene, passState, debugFramebuffer); } passState.framebuffer = framebuffer; diff --git a/packages/engine/Source/Scene/PickDepth.js b/packages/engine/Source/Scene/PickDepth.js index d687c286b883..726ccbc8a6f8 100644 --- a/packages/engine/Source/Scene/PickDepth.js +++ b/packages/engine/Source/Scene/PickDepth.js @@ -5,6 +5,9 @@ import FramebufferManager from "../Renderer/FramebufferManager.js"; import RenderState from "../Renderer/RenderState.js"; /** + * @alias PickDepth + * @constructor + * * @private */ function PickDepth() { @@ -23,8 +26,7 @@ Object.defineProperties(PickDepth.prototype, { }); function updateFramebuffers(pickDepth, context, depthTexture) { - const width = depthTexture.width; - const height = depthTexture.height; + const { width, height } = depthTexture; pickDepth._framebuffer.update(context, width, height); } @@ -73,6 +75,16 @@ const packedDepthScale = new Cartesian4( 1.0 / 16581375.0, ); +/** + * Read the depth from the framebuffer at the given coordinate. + * + * @param {Context} context + * @param {number} x The x-coordinate at which to read the depth. + * @param {number} y The y-coordinate at which to read the depth. + * @returns {number} The depth read from the framebuffer. + * + * @private + */ PickDepth.prototype.getDepth = function (context, x, y) { // If this function is called before the framebuffer is created, the depth is undefined. if (!defined(this.framebuffer)) { diff --git a/packages/engine/Source/Scene/Picking.js b/packages/engine/Source/Scene/Picking.js index bc1fd926ae0b..3d742f0db973 100644 --- a/packages/engine/Source/Scene/Picking.js +++ b/packages/engine/Source/Scene/Picking.js @@ -495,7 +495,7 @@ Picking.prototype.pickPositionWorldCoordinates = function ( frustum = camera.frustum.clone(scratchOrthographicOffCenterFrustum); } - const frustumCommandsList = defaultView.frustumCommandsList; + const { frustumCommandsList } = defaultView; const numFrustums = frustumCommandsList.length; for (let i = 0; i < numFrustums; ++i) { const pickDepth = this.getPickDepth(scene, i); @@ -855,7 +855,8 @@ function getRayIntersection( const object = view.pickFramebuffer.end(scratchRectangle); if (scene.context.depthTexture) { - const numFrustums = view.frustumCommandsList.length; + const { frustumCommandsList } = view; + const numFrustums = frustumCommandsList.length; for (let i = 0; i < numFrustums; ++i) { const pickDepth = picking.getPickDepth(scene, i); const depth = pickDepth.getDepth(context, 0, 0); @@ -863,7 +864,7 @@ function getRayIntersection( continue; } if (depth > 0.0 && depth < 1.0) { - const renderedFrustum = view.frustumCommandsList[i]; + const renderedFrustum = frustumCommandsList[i]; const near = renderedFrustum.near * (i !== 0 ? scene.opaqueFrustumNearOffset : 1.0); diff --git a/packages/engine/Source/Scene/PostProcessStage.js b/packages/engine/Source/Scene/PostProcessStage.js index 925d908c731e..44012ca77b56 100644 --- a/packages/engine/Source/Scene/PostProcessStage.js +++ b/packages/engine/Source/Scene/PostProcessStage.js @@ -93,9 +93,11 @@ import PostProcessStageSampleMode from "./PostProcessStageSampleMode.js"; */ function PostProcessStage(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); - const fragmentShader = options.fragmentShader; - const textureScale = defaultValue(options.textureScale, 1.0); - const pixelFormat = defaultValue(options.pixelFormat, PixelFormat.RGBA); + const { + fragmentShader, + textureScale = 1.0, + pixelFormat = PixelFormat.RGBA, + } = options; //>>includeStart('debug', pragmas.debug); Check.typeOf.string("options.fragmentShader", fragmentShader); @@ -484,34 +486,31 @@ function createUniformMap(stage) { const uniforms = stage._uniforms; const actualUniforms = stage._actualUniforms; for (const name in uniforms) { - if (uniforms.hasOwnProperty(name)) { - if (typeof uniforms[name] !== "function") { - uniformMap[name] = getUniformMapFunction(stage, name); - newUniforms[name] = getUniformValueGetterAndSetter( - stage, - uniforms, - name, - ); - } else { - uniformMap[name] = uniforms[name]; - newUniforms[name] = uniforms[name]; - } + if (!uniforms.hasOwnProperty(name)) { + continue; + } + if (typeof uniforms[name] !== "function") { + uniformMap[name] = getUniformMapFunction(stage, name); + newUniforms[name] = getUniformValueGetterAndSetter(stage, uniforms, name); + } else { + uniformMap[name] = uniforms[name]; + newUniforms[name] = uniforms[name]; + } - actualUniforms[name] = uniforms[name]; + actualUniforms[name] = uniforms[name]; - const value = uniformMap[name](); - if ( - typeof value === "string" || - value instanceof Texture || - value instanceof HTMLImageElement || - value instanceof HTMLCanvasElement || - value instanceof HTMLVideoElement - ) { - uniformMap[`${name}Dimensions`] = getUniformMapDimensionsFunction( - uniformMap, - name, - ); - } + const value = uniformMap[name](); + if ( + typeof value === "string" || + value instanceof Texture || + value instanceof HTMLImageElement || + value instanceof HTMLCanvasElement || + value instanceof HTMLVideoElement + ) { + uniformMap[`${name}Dimensions`] = getUniformMapDimensionsFunction( + uniformMap, + name, + ); } } @@ -543,6 +542,35 @@ function createUniformMap(stage) { }); } +function addSelectedIdToShader(shaderSource, idTextureWidth) { + shaderSource = shaderSource.replace(/in\s+vec2\s+v_textureCoordinates;/g, ""); + return `#define CZM_SELECTED_FEATURE +uniform sampler2D czm_idTexture; +uniform sampler2D czm_selectedIdTexture; +uniform float czm_selectedIdTextureStep; +in vec2 v_textureCoordinates; +bool czm_selected(vec2 offset) +{ + bool selected = false; + vec4 id = texture(czm_idTexture, v_textureCoordinates + offset); + for (int i = 0; i < ${idTextureWidth}; ++i) + { + vec4 selectedId = texture(czm_selectedIdTexture, vec2((float(i) + 0.5) * czm_selectedIdTextureStep, 0.5)); + if (all(equal(id, selectedId))) + { + return true; + } + } + return false; +} +bool czm_selected() +{ + return czm_selected(vec2(0.0)); +} + +${shaderSource}`; +} + function createDrawCommand(stage, context) { if ( defined(stage._command) && @@ -552,42 +580,15 @@ function createDrawCommand(stage, context) { return; } - let fs = stage._fragmentShader; + let fragmentShaderSource = stage._fragmentShader; if (defined(stage._selectedIdTexture)) { const width = stage._selectedIdTexture.width; - - fs = fs.replace(/in\s+vec2\s+v_textureCoordinates;/g, ""); - fs = - `${ - "#define CZM_SELECTED_FEATURE \n" + - "uniform sampler2D czm_idTexture; \n" + - "uniform sampler2D czm_selectedIdTexture; \n" + - "uniform float czm_selectedIdTextureStep; \n" + - "in vec2 v_textureCoordinates; \n" + - "bool czm_selected(vec2 offset) \n" + - "{ \n" + - " bool selected = false;\n" + - " vec4 id = texture(czm_idTexture, v_textureCoordinates + offset); \n" + - " for (int i = 0; i < " - }${width}; ++i) \n` + - ` { \n` + - ` vec4 selectedId = texture(czm_selectedIdTexture, vec2((float(i) + 0.5) * czm_selectedIdTextureStep, 0.5)); \n` + - ` if (all(equal(id, selectedId))) \n` + - ` { \n` + - ` return true; \n` + - ` } \n` + - ` } \n` + - ` return false; \n` + - `} \n\n` + - `bool czm_selected() \n` + - `{ \n` + - ` return czm_selected(vec2(0.0)); \n` + - `} \n\n${fs}`; + fragmentShaderSource = addSelectedIdToShader(fragmentShaderSource, width); } const fragmentShader = new ShaderSource({ defines: [stage._useLogDepth ? "LOG_DEPTH" : ""], - sources: [fs], + sources: [fragmentShaderSource], }); stage._command = context.createViewportQuadCommand(fragmentShader, { uniformMap: stage._uniformMap, @@ -640,24 +641,16 @@ function createStageOutputTextureFunction(stage, name) { } function updateUniformTextures(stage, context) { - let i; - let texture; - let name; - const texturesToRelease = stage._texturesToRelease; - let length = texturesToRelease.length; - for (i = 0; i < length; ++i) { - texture = texturesToRelease[i]; + for (let i = 0; i < texturesToRelease.length; ++i) { + let texture = texturesToRelease[i]; texture = texture && texture.destroy(); } texturesToRelease.length = 0; const texturesToCreate = stage._texturesToCreate; - length = texturesToCreate.length; - for (i = 0; i < length; ++i) { - const textureToCreate = texturesToCreate[i]; - name = textureToCreate.name; - const source = textureToCreate.source; + for (let i = 0; i < texturesToCreate.length; ++i) { + const { name, source } = texturesToCreate[i]; stage._actualUniforms[name] = new Texture({ context: context, source: source, @@ -675,11 +668,10 @@ function updateUniformTextures(stage, context) { return; } - length = dirtyUniforms.length; const uniforms = stage._uniforms; const promises = []; - for (i = 0; i < length; ++i) { - name = dirtyUniforms[i]; + for (let i = 0; i < dirtyUniforms.length; ++i) { + const name = dirtyUniforms[i]; const stageNameUrlOrImage = uniforms[name]; const stageWithName = stage._textureCache.getStageByName(stageNameUrlOrImage); @@ -735,27 +727,27 @@ function releaseResources(stage) { const uniforms = stage._uniforms; const actualUniforms = stage._actualUniforms; for (const name in actualUniforms) { - if (actualUniforms.hasOwnProperty(name)) { - if (actualUniforms[name] instanceof Texture) { - if (!defined(textureCache.getStageByName(uniforms[name]))) { - actualUniforms[name].destroy(); - } - stage._dirtyUniforms.push(name); + if (!actualUniforms.hasOwnProperty(name)) { + continue; + } + const actualUniform = actualUniforms[name]; + if (actualUniform instanceof Texture) { + if (!defined(textureCache.getStageByName(uniforms[name]))) { + actualUniform.destroy(); } + stage._dirtyUniforms.push(name); } } } function isSelectedTextureDirty(stage) { - let length = defined(stage._selected) ? stage._selected.length : 0; + const length = defined(stage._selected) ? stage._selected.length : 0; const parentLength = defined(stage._parentSelected) ? stage._parentSelected : 0; - let dirty = + const dirty = stage._selected !== stage._selectedShadow || - length !== stage._selectedLength; - dirty = - dirty || + length !== stage._selectedLength || stage._parentSelected !== stage._parentSelectedShadow || parentLength !== stage._parentSelectedLength; @@ -772,8 +764,7 @@ function isSelectedTextureDirty(stage) { return true; } - length = stage._combinedSelected.length; - for (let i = 0; i < length; ++i) { + for (let i = 0; i < stage._combinedSelected.length; ++i) { if (stage._combinedSelected[i] !== stage._combinedSelectedShadow[i]) { return true; } @@ -796,13 +787,9 @@ function createSelectedTexture(stage, context) { return; } - let i; - let feature; - let textureLength = 0; - const length = features.length; - for (i = 0; i < length; ++i) { - feature = features[i]; + for (let i = 0; i < features.length; ++i) { + const feature = features[i]; if (defined(feature.pickIds)) { textureLength += feature.pickIds.length; } else if (defined(feature.pickId)) { @@ -810,14 +797,9 @@ function createSelectedTexture(stage, context) { } } - if (length === 0 || textureLength === 0) { + if (features.length === 0 || textureLength === 0) { // max pick id is reserved - const empty = new Uint8Array(4); - empty[0] = 255; - empty[1] = 255; - empty[2] = 255; - empty[3] = 255; - + const empty = new Uint8Array([255, 255, 255, 255]); stage._selectedIdTexture = new Texture({ context: context, pixelFormat: PixelFormat.RGBA, @@ -832,16 +814,15 @@ function createSelectedTexture(stage, context) { return; } - let pickColor; let offset = 0; const ids = new Uint8Array(textureLength * 4); - for (i = 0; i < length; ++i) { - feature = features[i]; + for (let i = 0; i < features.length; ++i) { + const feature = features[i]; if (defined(feature.pickIds)) { const pickIds = feature.pickIds; const pickIdsLength = pickIds.length; for (let j = 0; j < pickIdsLength; ++j) { - pickColor = pickIds[j].color; + const pickColor = pickIds[j].color; ids[offset] = Color.floatToByte(pickColor.red); ids[offset + 1] = Color.floatToByte(pickColor.green); ids[offset + 2] = Color.floatToByte(pickColor.blue); @@ -849,7 +830,7 @@ function createSelectedTexture(stage, context) { offset += 4; } } else if (defined(feature.pickId)) { - pickColor = feature.pickId.color; + const pickColor = feature.pickId.color; ids[offset] = Color.floatToByte(pickColor.red); ids[offset + 1] = Color.floatToByte(pickColor.green); ids[offset + 2] = Color.floatToByte(pickColor.blue); diff --git a/packages/engine/Source/Scene/PostProcessStageCollection.js b/packages/engine/Source/Scene/PostProcessStageCollection.js index 505213addec8..59ca0e293923 100644 --- a/packages/engine/Source/Scene/PostProcessStageCollection.js +++ b/packages/engine/Source/Scene/PostProcessStageCollection.js @@ -426,8 +426,7 @@ function removeStages(collection) { const newStages = []; const stages = collection._stages; - const length = stages.length; - for (let i = 0, j = 0; i < length; ++i) { + for (let i = 0, j = 0; i < stages.length; ++i) { const stage = stages[i]; if (stage) { stage._index = j++; @@ -594,23 +593,20 @@ PostProcessStageCollection.prototype.update = function ( this._previousActiveStages = previousActiveStages; const stages = this._stages; - let length = (activeStages.length = stages.length); + activeStages.length = stages.length; - let i; - let stage; let count = 0; - for (i = 0; i < length; ++i) { - stage = stages[i]; + for (let i = 0; i < stages.length; ++i) { + const stage = stages[i]; if (stage.ready && stage.enabled && stage._isSupported(context)) { activeStages[count++] = stage; } } - activeStages.length = count; let activeStagesChanged = count !== previousActiveStages.length; if (!activeStagesChanged) { - for (i = 0; i < count; ++i) { + for (let i = 0; i < count; ++i) { if (activeStages[i] !== previousActiveStages[i]) { activeStagesChanged = true; break; @@ -657,9 +653,9 @@ PostProcessStageCollection.prototype.update = function ( } if (!defined(this._randomTexture) && aoEnabled) { - length = 256 * 256 * 3; + const length = 256 * 256 * 3; const random = new Uint8Array(length); - for (i = 0; i < length; i += 3) { + for (let i = 0; i < length; i += 3) { random[i] = Math.floor(Math.random() * 255.0); } @@ -692,19 +688,17 @@ PostProcessStageCollection.prototype.update = function ( autoexposure.update(context, useLogDepth); } - length = stages.length; - for (i = 0; i < length; ++i) { + for (let i = 0; i < stages.length; ++i) { stages[i].update(context, useLogDepth); } count = 0; - for (i = 0; i < length; ++i) { - stage = stages[i]; + for (let i = 0; i < stages.length; ++i) { + const stage = stages[i]; if (stage.ready && stage.enabled && stage._isSupported(context)) { count++; } } - activeStagesChanged = count !== activeStages.length; if (activeStagesChanged) { this.update(context, useLogDepth, useHdr); @@ -755,12 +749,9 @@ function execute(stage, context, colorTexture, depthTexture, idTexture) { return; } - const length = stage.length; - let i; - if (stage.inputPreviousStageTexture) { execute(stage.get(0), context, colorTexture, depthTexture, idTexture); - for (i = 1; i < length; ++i) { + for (let i = 1; i < stage.length; ++i) { execute( stage.get(i), context, @@ -770,7 +761,7 @@ function execute(stage, context, colorTexture, depthTexture, idTexture) { ); } } else { - for (i = 0; i < length; ++i) { + for (let i = 0; i < stage.length; ++i) { execute(stage.get(i), context, colorTexture, depthTexture, idTexture); } } diff --git a/packages/engine/Source/Scene/Scene.js b/packages/engine/Source/Scene/Scene.js index b90cfd4be8a9..7fc05b1b3dd1 100644 --- a/packages/engine/Source/Scene/Scene.js +++ b/packages/engine/Source/Scene/Scene.js @@ -191,6 +191,12 @@ function Scene(options) { this._overlayCommandList = []; this._useOIT = defaultValue(options.orderIndependentTranslucency, true); + /** + * The function that will be used for executing translucent commands when + * useOIT is true. This is created once in + * obtainTranslucentCommandExecutionFunction, then cached here. + * @private + */ this._executeOITFunction = undefined; this._depthPlane = new DepthPlane(options.depthPlaneEllipsoidOffset); @@ -1719,8 +1725,7 @@ function updateDerivedCommands(scene, command, shadowsDirty) { const frameState = scene._frameState; const context = scene._context; const oit = scene._view.oit; - const lightShadowMaps = frameState.shadowState.lightShadowMaps; - const lightShadowsEnabled = frameState.shadowState.lightShadowsEnabled; + const { lightShadowMaps, lightShadowsEnabled } = frameState.shadowState; let derivedCommands = command.derivedCommands; @@ -1788,26 +1793,25 @@ function updateDerivedCommands(scene, command, shadowsDirty) { * @private */ Scene.prototype.updateDerivedCommands = function (command) { - if (!defined(command.derivedCommands)) { + const { derivedCommands } = command; + if (!defined(derivedCommands)) { // Is not a DrawCommand return; } - const frameState = this._frameState; + const { shadowState, useLogDepth } = this._frameState; const context = this._context; // Update derived commands when any shadow maps become dirty let shadowsDirty = false; - const lastDirtyTime = frameState.shadowState.lastDirtyTime; + const lastDirtyTime = shadowState.lastDirtyTime; if (command.lastDirtyTime !== lastDirtyTime) { command.lastDirtyTime = lastDirtyTime; command.dirty = true; shadowsDirty = true; } - const useLogDepth = frameState.useLogDepth; const useHdr = this._hdr; - const derivedCommands = command.derivedCommands; const hasLogDepthDerivedCommands = defined(derivedCommands.logDepth); const hasHdrCommands = defined(derivedCommands.hdr); const hasDerivedCommands = defined(derivedCommands.originalCommand); @@ -1821,36 +1825,33 @@ Scene.prototype.updateDerivedCommands = function (command) { needsHdrCommands || needsDerivedCommands; - if (command.dirty) { - command.dirty = false; + if (!command.dirty) { + return; + } - const shadowMaps = frameState.shadowState.shadowMaps; - const shadowsEnabled = frameState.shadowState.shadowsEnabled; - if (shadowsEnabled && command.castShadows) { - derivedCommands.shadows = ShadowMap.createCastDerivedCommand( - shadowMaps, - command, - shadowsDirty, - context, - derivedCommands.shadows, - ); - } + command.dirty = false; - if (hasLogDepthDerivedCommands || needsLogDepthDerivedCommands) { - derivedCommands.logDepth = DerivedCommand.createLogDepthCommand( - command, - context, - derivedCommands.logDepth, - ); - updateDerivedCommands( - this, - derivedCommands.logDepth.command, - shadowsDirty, - ); - } - if (hasDerivedCommands || needsDerivedCommands) { - updateDerivedCommands(this, command, shadowsDirty); - } + const { shadowsEnabled, shadowMaps } = shadowState; + if (shadowsEnabled && command.castShadows) { + derivedCommands.shadows = ShadowMap.createCastDerivedCommand( + shadowMaps, + command, + shadowsDirty, + context, + derivedCommands.shadows, + ); + } + + if (hasLogDepthDerivedCommands || needsLogDepthDerivedCommands) { + derivedCommands.logDepth = DerivedCommand.createLogDepthCommand( + command, + context, + derivedCommands.logDepth, + ); + updateDerivedCommands(this, derivedCommands.logDepth.command, shadowsDirty); + } + if (hasDerivedCommands || needsDerivedCommands) { + updateDerivedCommands(this, command, shadowsDirty); } }; @@ -1872,31 +1873,34 @@ const requestRenderModeDeferCheckPassState = new Cesium3DTilePassState({ const scratchOccluderBoundingSphere = new BoundingSphere(); let scratchOccluder; - +/** + * Get the central body occluder for the scene. + * Assumes only one central body occluder, the top-level globe. + * + * @param {Scene} scene + * @returns {Occluder|undefined} + * + * @private + */ function getOccluder(scene) { - // TODO: The occluder is the top-level globe. When we add - // support for multiple central bodies, this should be the closest one. - const globe = scene.globe; if ( - scene._mode === SceneMode.SCENE3D && - defined(globe) && - globe.show && - !scene._cameraUnderground && - !scene._globeTranslucencyState.translucent + scene._mode !== SceneMode.SCENE3D || + !scene.globe?.show || + scene._cameraUnderground || + scene._globeTranslucencyState.translucent ) { - const ellipsoid = scene.ellipsoid; - const minimumTerrainHeight = scene.frameState.minimumTerrainHeight; - scratchOccluderBoundingSphere.radius = - ellipsoid.minimumRadius + minimumTerrainHeight; - scratchOccluder = Occluder.fromBoundingSphere( - scratchOccluderBoundingSphere, - scene.camera.positionWC, - scratchOccluder, - ); - return scratchOccluder; + return undefined; } - return undefined; + scratchOccluderBoundingSphere.radius = + scene.ellipsoid.minimumRadius + scene.frameState.minimumTerrainHeight; + scratchOccluder = Occluder.fromBoundingSphere( + scratchOccluderBoundingSphere, + scene.camera.positionWC, + scratchOccluder, + ); + + return scratchOccluder; } /** @@ -2003,18 +2007,31 @@ Scene.prototype.updateFrameState = function () { }; /** + * Check whether a draw command will render anything visible in the current Scene, + * based on its bounding volume. + * + * @param {CullingVolume} cullingVolume The culling volume of the current Scene. + * @param {DrawCommand} [command] The draw command + * @param {Occluder} [occluder] An occluder that may be in front of the command's bounding volume. + * @returns {boolean} true if the command's bounding volume is visible in the scene. + * * @private */ -Scene.prototype.isVisible = function (command, cullingVolume, occluder) { +Scene.prototype.isVisible = function (cullingVolume, command, occluder) { + if (!defined(command)) { + return false; + } + const { boundingVolume } = command; + if (!defined(boundingVolume) || !command.cull) { + return true; + } + if (cullingVolume.computeVisibility(boundingVolume) === Intersect.OUTSIDE) { + return false; + } return ( - defined(command) && - (!defined(command.boundingVolume) || - !command.cull || - (cullingVolume.computeVisibility(command.boundingVolume) !== - Intersect.OUTSIDE && - (!defined(occluder) || - !command.occlude || - !command.boundingVolume.isOccluded(occluder)))) + !defined(occluder) || + !command.occlude || + !boundingVolume.isOccluded(occluder) ); }; @@ -2041,9 +2058,18 @@ transformFrom2D = Matrix4.inverseTransformation( transformFrom2D, ); +/** + * Debug code to draw bounding volume for command. Not optimized! + * Assumes bounding volume is a bounding sphere or box. + * + * @param {DrawCommand} command The draw command for which to render the bounding volume. + * @param {Scene} scene The scene. + * @param {PassState} passState The state for the current render pass. + * @param {Framebuffer} debugFramebuffer The framebuffer where the bounding volume will be rendered. + * + * @private + */ function debugShowBoundingVolume(command, scene, passState, debugFramebuffer) { - // Debug code to draw bounding volume for command. Not optimized! - // Assumes bounding volume is a bounding sphere or box const frameState = scene._frameState; const context = frameState.context; const boundingVolume = command.boundingVolume; @@ -2052,8 +2078,6 @@ function debugShowBoundingVolume(command, scene, passState, debugFramebuffer) { scene._debugVolume.destroy(); } - let geometry; - let center = Cartesian3.clone(boundingVolume.center); if (frameState.mode !== SceneMode.SCENE3D) { center = Matrix4.multiplyByPoint(transformFrom2D, center, center); @@ -2062,63 +2086,44 @@ function debugShowBoundingVolume(command, scene, passState, debugFramebuffer) { center = projection.ellipsoid.cartographicToCartesian(centerCartographic); } - if (defined(boundingVolume.radius)) { - const radius = boundingVolume.radius; - - geometry = GeometryPipeline.toWireframe( - EllipsoidGeometry.createGeometry( - new EllipsoidGeometry({ - radii: new Cartesian3(radius, radius, radius), - vertexFormat: PerInstanceColorAppearance.FLAT_VERTEX_FORMAT, - }), - ), - ); - - scene._debugVolume = new Primitive({ - geometryInstances: new GeometryInstance({ - geometry: geometry, - modelMatrix: Matrix4.fromTranslation(center), - attributes: { - color: new ColorGeometryInstanceAttribute(1.0, 0.0, 0.0, 1.0), - }, - }), - appearance: new PerInstanceColorAppearance({ - flat: true, - translucent: false, + let geometry; + let modelMatrix; + const { radius } = boundingVolume; + if (defined(radius)) { + geometry = EllipsoidGeometry.createGeometry( + new EllipsoidGeometry({ + radii: new Cartesian3(radius, radius, radius), + vertexFormat: PerInstanceColorAppearance.FLAT_VERTEX_FORMAT, }), - asynchronous: false, - }); - } else { - const halfAxes = boundingVolume.halfAxes; - - geometry = GeometryPipeline.toWireframe( - BoxGeometry.createGeometry( - BoxGeometry.fromDimensions({ - dimensions: new Cartesian3(2.0, 2.0, 2.0), - vertexFormat: PerInstanceColorAppearance.FLAT_VERTEX_FORMAT, - }), - ), ); - - scene._debugVolume = new Primitive({ - geometryInstances: new GeometryInstance({ - geometry: geometry, - modelMatrix: Matrix4.fromRotationTranslation( - halfAxes, - center, - new Matrix4(), - ), - attributes: { - color: new ColorGeometryInstanceAttribute(1.0, 0.0, 0.0, 1.0), - }, - }), - appearance: new PerInstanceColorAppearance({ - flat: true, - translucent: false, + modelMatrix = Matrix4.fromTranslation(center); + } else { + geometry = BoxGeometry.createGeometry( + BoxGeometry.fromDimensions({ + dimensions: new Cartesian3(2.0, 2.0, 2.0), + vertexFormat: PerInstanceColorAppearance.FLAT_VERTEX_FORMAT, }), - asynchronous: false, - }); + ); + modelMatrix = Matrix4.fromRotationTranslation( + boundingVolume.halfAxes, + center, + new Matrix4(), + ); } + scene._debugVolume = new Primitive({ + geometryInstances: new GeometryInstance({ + geometry: GeometryPipeline.toWireframe(geometry), + modelMatrix: modelMatrix, + attributes: { + color: new ColorGeometryInstanceAttribute(1.0, 0.0, 0.0, 1.0), + }, + }), + appearance: new PerInstanceColorAppearance({ + flat: true, + translucent: false, + }), + asynchronous: false, + }); const savedCommandList = frameState.commandList; const commandList = (frameState.commandList = []); @@ -2146,8 +2151,19 @@ function debugShowBoundingVolume(command, scene, passState, debugFramebuffer) { frameState.commandList = savedCommandList; } -function executeCommand(command, scene, context, passState, debugFramebuffer) { +/** + * Execute a single draw command, or one of its derived commands if appropriate for the current render state. + * + * @param {DrawCommand} command The command to execute. + * @param {Scene} scene The scene. + * @param {PassState} passState The state for the current render pass. + * @param {Framebuffer} debugFramebuffer The framebuffer where debug QCs will be rendered. + * + * @private + */ +function executeCommand(command, scene, passState, debugFramebuffer) { const frameState = scene._frameState; + const context = scene._context; if (defined(scene.debugCommandFilter) && !scene.debugCommandFilter(command)) { return; @@ -2217,23 +2233,34 @@ function executeCommand(command, scene, context, passState, debugFramebuffer) { } } -function executeIdCommand(command, scene, context, passState) { - const frameState = scene._frameState; - let derivedCommands = command.derivedCommands; +/** + * Execute a single ID draw command, used to render information for picking. + * + * @param {DrawCommand} command The command to execute. + * @param {Scene} scene The scene. + * @param {PassState} passState The state for the current render pass. + * + * @private + */ +function executeIdCommand(command, scene, passState) { + const { derivedCommands } = command; if (!defined(derivedCommands)) { return; } + const frameState = scene._frameState; + const context = scene._context; + if (frameState.useLogDepth && defined(derivedCommands.logDepth)) { command = derivedCommands.logDepth.command; } - derivedCommands = command.derivedCommands; - if (defined(derivedCommands.picking)) { - command = derivedCommands.picking.pickCommand; + const { picking, depth } = command.derivedCommands; + if (defined(picking)) { + command = picking.pickCommand; command.execute(context, passState); - } else if (defined(derivedCommands.depth)) { - command = derivedCommands.depth.depthOnlyCommand; + } else if (defined(depth)) { + command = depth.depthOnlyCommand; command.execute(context, passState); } } @@ -2261,22 +2288,14 @@ function executeTranslucentCommandsBackToFront( commands, invertClassification, ) { - const context = scene.context; - mergeSort(commands, backToFront, scene.camera.positionWC); if (defined(invertClassification)) { - executeFunction( - invertClassification.unclassifiedCommand, - scene, - context, - passState, - ); + executeFunction(invertClassification.unclassifiedCommand, scene, passState); } - const length = commands.length; - for (let i = 0; i < length; ++i) { - executeFunction(commands[i], scene, context, passState); + for (let i = 0; i < commands.length; ++i) { + executeFunction(commands[i], scene, passState); } } @@ -2287,33 +2306,36 @@ function executeTranslucentCommandsFrontToBack( commands, invertClassification, ) { - const context = scene.context; - mergeSort(commands, frontToBack, scene.camera.positionWC); if (defined(invertClassification)) { - executeFunction( - invertClassification.unclassifiedCommand, - scene, - context, - passState, - ); + executeFunction(invertClassification.unclassifiedCommand, scene, passState); } - const length = commands.length; - for (let i = 0; i < length; ++i) { - executeFunction(commands[i], scene, context, passState); + for (let i = 0; i < commands.length; ++i) { + executeFunction(commands[i], scene, passState); } } -function executeVoxelCommands(scene, executeFunction, passState, commands) { - const context = scene.context; +/** + * Execute commands to render voxels in the scene. + * + * @param {Scene} scene The scene. + * @param {PassState} passState The state for the current render pass. + * @param {FrustumCommands} frustumCommands The draw commands for the current frustum. + * + * @private + */ +function performVoxelsPass(scene, passState, frustumCommands) { + scene.context.uniformState.updatePass(Pass.VOXELS); + + const commands = frustumCommands.commands[Pass.VOXELS]; + commands.length = frustumCommands.indices[Pass.VOXELS]; mergeSort(commands, backToFront, scene.camera.positionWC); - const length = commands.length; - for (let i = 0; i < length; ++i) { - executeFunction(commands[i], scene, context, passState); + for (let i = 0; i < commands.length; ++i) { + executeCommand(commands[i], scene, passState); } } @@ -2321,83 +2343,43 @@ const scratchPerspectiveFrustum = new PerspectiveFrustum(); const scratchPerspectiveOffCenterFrustum = new PerspectiveOffCenterFrustum(); const scratchOrthographicFrustum = new OrthographicFrustum(); const scratchOrthographicOffCenterFrustum = new OrthographicOffCenterFrustum(); - -function executeCommands(scene, passState) { - const { camera, context, frameState } = scene; - const { uniformState } = context; - - uniformState.updateCamera(camera); - - // Create a working frustum from the original camera frustum. - let frustum; - if (defined(camera.frustum.fov)) { - frustum = camera.frustum.clone(scratchPerspectiveFrustum); - } else if (defined(camera.frustum.infiniteProjectionMatrix)) { - frustum = camera.frustum.clone(scratchPerspectiveOffCenterFrustum); - } else if (defined(camera.frustum.width)) { - frustum = camera.frustum.clone(scratchOrthographicFrustum); - } else { - frustum = camera.frustum.clone(scratchOrthographicOffCenterFrustum); +/** + * Create a working frustum from the original camera frustum. + * + * @param {Camera} camera The camera + * @returns {PerspectiveFrustum|PerspectiveOffCenterFrustum|OrthographicFrustum|OrthographicOffCenterFrustum} The working frustum + * + * @private + */ +function createWorkingFrustum(camera) { + const { frustum } = camera; + if (defined(frustum.fov)) { + return frustum.clone(scratchPerspectiveFrustum); } - - // Ideally, we would render the sky box and atmosphere last for - // early-z, but we would have to draw it in each frustum - frustum.near = camera.frustum.near; - frustum.far = camera.frustum.far; - uniformState.updateFrustum(frustum); - uniformState.updatePass(Pass.ENVIRONMENT); - - const passes = frameState.passes; - const picking = passes.pick || passes.pickVoxel; - const environmentState = scene._environmentState; - const view = scene._view; - const renderTranslucentDepthForPick = - environmentState.renderTranslucentDepthForPick; - const useWebVR = environmentState.useWebVR; - - // Do not render environment primitives during a pick pass since they do not generate picking commands. - if (!picking) { - const skyBoxCommand = environmentState.skyBoxCommand; - if (defined(skyBoxCommand)) { - executeCommand(skyBoxCommand, scene, context, passState); - } - - if (environmentState.isSkyAtmosphereVisible) { - executeCommand( - environmentState.skyAtmosphereCommand, - scene, - context, - passState, - ); - } - - if (environmentState.isSunVisible) { - environmentState.sunDrawCommand.execute(context, passState); - if (scene.sunBloom && !useWebVR) { - let framebuffer; - if (environmentState.useGlobeDepthFramebuffer) { - framebuffer = view.globeDepth.framebuffer; - } else if (environmentState.usePostProcess) { - framebuffer = view.sceneFramebuffer.framebuffer; - } else { - framebuffer = environmentState.originalFramebuffer; - } - scene._sunPostProcess.execute(context); - scene._sunPostProcess.copy(context, framebuffer); - passState.framebuffer = framebuffer; - } - } - - // Moon can be seen through the atmosphere, since the sun is rendered after the atmosphere. - if (environmentState.isMoonVisible) { - environmentState.moonCommand.execute(context, passState); - } + if (defined(frustum.infiniteProjectionMatrix)) { + return frustum.clone(scratchPerspectiveOffCenterFrustum); + } + if (defined(frustum.width)) { + return frustum.clone(scratchOrthographicFrustum); } + return frustum.clone(scratchOrthographicOffCenterFrustum); +} - // Determine how translucent surfaces will be handled. - let executeTranslucentCommands; - if (environmentState.useOIT) { +/** + * Determine how translucent surfaces will be handled. + * + * When OIT is enabled, then this will delegate to OIT.executeCommands. + * Otherwise, it will just be executeTranslucentCommandsBackToFront + * for render passes, or executeTranslucentCommandsFrontToBack for + * other passes. + * + * @param {Scene} scene The scene. + * @returns {Function} A function to execute translucent commands. + */ +function obtainTranslucentCommandExecutionFunction(scene) { + if (scene._environmentState.useOIT) { if (!defined(scene._executeOITFunction)) { + const { view, context } = scene; scene._executeOITFunction = function ( scene, executeFunction, @@ -2415,31 +2397,167 @@ function executeCommands(scene, passState) { ); }; } - executeTranslucentCommands = scene._executeOITFunction; - } else if (passes.render) { - executeTranslucentCommands = executeTranslucentCommandsBackToFront; - } else { - executeTranslucentCommands = executeTranslucentCommandsFrontToBack; + return scene._executeOITFunction; + } + if (scene.frameState.passes.render) { + return executeTranslucentCommandsBackToFront; + } + return executeTranslucentCommandsFrontToBack; +} + +/** + * Execute draw commands to render translucent objects in the scene. + * + * @param {Scene} scene The scene. + * @param {PassState} passState The state for the current render pass. + * @param {FrustumCommands} frustumCommands The draw commands for the current frustum. + * + * @private + */ +function performTranslucentPass(scene, passState, frustumCommands) { + const { frameState, context } = scene; + const { pick, pickVoxel } = frameState.passes; + const picking = pick || pickVoxel; + + let invertClassification; + if ( + !picking && + scene._environmentState.useInvertClassification && + frameState.invertClassificationColor.alpha < 1.0 + ) { + // Fullscreen pass to copy unclassified fragments when alpha < 1.0. + // Not executed when undefined. + invertClassification = scene._invertClassification; } - const frustumCommandsList = view.frustumCommandsList; + const executeTranslucentCommands = + obtainTranslucentCommandExecutionFunction(scene); + + context.uniformState.updatePass(Pass.TRANSLUCENT); + const commands = frustumCommands.commands[Pass.TRANSLUCENT]; + commands.length = frustumCommands.indices[Pass.TRANSLUCENT]; + executeTranslucentCommands( + scene, + executeCommand, + passState, + commands, + invertClassification, + ); +} + +/** + * Execute commands for classification of translucent 3D Tiles. + * + * @param {Scene} scene The scene. + * @param {PassState} passState The state for the current render pass. + * @param {FrustumCommands} frustumCommands The draw commands for the current frustum. + * + * @private + */ +function performTranslucent3DTilesClassification( + scene, + passState, + frustumCommands, +) { + const { translucentTileClassification, globeDepth } = scene._view; + const has3DTilesClassificationCommands = + frustumCommands.indices[Pass.CESIUM_3D_TILE_CLASSIFICATION] > 0; + if ( + !has3DTilesClassificationCommands || + !translucentTileClassification.isSupported() + ) { + return; + } + + const commands = frustumCommands.commands[Pass.TRANSLUCENT]; + translucentTileClassification.executeTranslucentCommands( + scene, + executeCommand, + passState, + commands, + globeDepth.depthStencilTexture, + ); + translucentTileClassification.executeClassificationCommands( + scene, + executeCommand, + passState, + frustumCommands, + ); +} + +/** + * Execute the draw commands for all the render passes. + * + * @param {Scene} scene + * @param {PassState} passState + * + * @private + */ +function executeCommands(scene, passState) { + const { camera, context, frameState } = scene; + const { uniformState } = context; + + uniformState.updateCamera(camera); + + const frustum = createWorkingFrustum(camera); + frustum.near = camera.frustum.near; + frustum.far = camera.frustum.far; + + const passes = frameState.passes; + const picking = passes.pick || passes.pickVoxel; + + // Ideally, we would render the sky box and atmosphere last for + // early-z, but we would have to draw it in each frustum. + // Do not render environment primitives during a pick pass since they do not generate picking commands. + if (!picking) { + renderEnvironment(scene, passState); + } + + const { + clearGlobeDepth, + renderTranslucentDepthForPick, + useDepthPlane, + useGlobeDepthFramebuffer, + useInvertClassification, + usePostProcessSelected, + } = scene._environmentState; + + const { + globeDepth, + globeTranslucencyFramebuffer, + sceneFramebuffer, + frustumCommandsList, + } = scene._view; const numFrustums = frustumCommandsList.length; - const clearGlobeDepth = environmentState.clearGlobeDepth; - const useDepthPlane = environmentState.useDepthPlane; const globeTranslucencyState = scene._globeTranslucencyState; - const globeTranslucent = globeTranslucencyState.translucent; - const globeTranslucencyFramebuffer = scene._view.globeTranslucencyFramebuffer; const clearDepth = scene._depthClearCommand; const clearStencil = scene._stencilClearCommand; const clearClassificationStencil = scene._classificationStencilClearCommand; const depthPlane = scene._depthPlane; - const usePostProcessSelected = environmentState.usePostProcessSelected; const height2D = camera.position.z; + function performPass(frustumCommands, passId) { + uniformState.updatePass(passId); + const commands = frustumCommands.commands[passId]; + const commandCount = frustumCommands.indices[passId]; + for (let j = 0; j < commandCount; ++j) { + executeCommand(commands[j], scene, passState); + } + return commandCount; + } + + function performIdPass(frustumCommands, passId) { + uniformState.updatePass(passId); + const commands = frustumCommands.commands[passId]; + const commandCount = frustumCommands.indices[passId]; + for (let j = 0; j < commandCount; ++j) { + executeIdCommand(commands[j], scene, passState); + } + } + // Execute commands in each frustum in back to front order - let j; for (let i = 0; i < numFrustums; ++i) { const index = numFrustums - i - 1; const frustumCommands = frustumCommandsList[index]; @@ -2468,11 +2586,8 @@ function executeCommands(scene, passState) { clearStencil.execute(context, passState); } - uniformState.updatePass(Pass.GLOBE); - let commands = frustumCommands.commands[Pass.GLOBE]; - let length = frustumCommands.indices[Pass.GLOBE]; - - if (globeTranslucent) { + if (globeTranslucencyState.translucent) { + uniformState.updatePass(Pass.GLOBE); globeTranslucencyState.executeGlobeCommands( frustumCommands, executeCommand, @@ -2481,23 +2596,17 @@ function executeCommands(scene, passState) { passState, ); } else { - for (j = 0; j < length; ++j) { - executeCommand(commands[j], scene, context, passState); - } + performPass(frustumCommands, Pass.GLOBE); } - const globeDepth = view.globeDepth; - if (defined(globeDepth) && environmentState.useGlobeDepthFramebuffer) { + if (useGlobeDepthFramebuffer) { globeDepth.executeCopyDepth(context, passState); } // Draw terrain classification - if (!environmentState.renderTranslucentDepthForPick) { - uniformState.updatePass(Pass.TERRAIN_CLASSIFICATION); - commands = frustumCommands.commands[Pass.TERRAIN_CLASSIFICATION]; - length = frustumCommands.indices[Pass.TERRAIN_CLASSIFICATION]; - - if (globeTranslucent) { + if (!renderTranslucentDepthForPick) { + if (globeTranslucencyState.translucent) { + uniformState.updatePass(Pass.TERRAIN_CLASSIFICATION); globeTranslucencyState.executeGlobeClassificationCommands( frustumCommands, executeCommand, @@ -2506,9 +2615,7 @@ function executeCommands(scene, passState) { passState, ); } else { - for (j = 0; j < length; ++j) { - executeCommand(commands[j], scene, context, passState); - } + performPass(frustumCommands, Pass.TERRAIN_CLASSIFICATION); } } @@ -2519,43 +2626,29 @@ function executeCommands(scene, passState) { } } - if ( - !environmentState.useInvertClassification || - picking || - environmentState.renderTranslucentDepthForPick - ) { + let commandCount; + if (!useInvertClassification || picking || renderTranslucentDepthForPick) { // Common/fastest path. Draw 3D Tiles and classification normally. // Draw 3D Tiles - uniformState.updatePass(Pass.CESIUM_3D_TILE); - commands = frustumCommands.commands[Pass.CESIUM_3D_TILE]; - length = frustumCommands.indices[Pass.CESIUM_3D_TILE]; - for (j = 0; j < length; ++j) { - executeCommand(commands[j], scene, context, passState); - } + commandCount = performPass(frustumCommands, Pass.CESIUM_3D_TILE); - if (length > 0) { - if (defined(globeDepth) && environmentState.useGlobeDepthFramebuffer) { - // When clearGlobeDepth is true, executeUpdateDepth needs - // a globe depth texture with resolved stencil bits. + if (commandCount > 0) { + if (useGlobeDepthFramebuffer) { globeDepth.prepareColorTextures(context, clearGlobeDepth); globeDepth.executeUpdateDepth( context, passState, - clearGlobeDepth, globeDepth.depthStencilTexture, ); } // Draw classifications. Modifies 3D Tiles color. - if (!environmentState.renderTranslucentDepthForPick) { - uniformState.updatePass(Pass.CESIUM_3D_TILE_CLASSIFICATION); - commands = - frustumCommands.commands[Pass.CESIUM_3D_TILE_CLASSIFICATION]; - length = frustumCommands.indices[Pass.CESIUM_3D_TILE_CLASSIFICATION]; - for (j = 0; j < length; ++j) { - executeCommand(commands[j], scene, context, passState); - } + if (!renderTranslucentDepthForPick) { + commandCount = performPass( + frustumCommands, + Pass.CESIUM_3D_TILE_CLASSIFICATION, + ); } } } else { @@ -2597,34 +2690,22 @@ function executeCommands(scene, passState) { passState.framebuffer = scene._invertClassification._fbo.framebuffer; // Draw normally - uniformState.updatePass(Pass.CESIUM_3D_TILE); - commands = frustumCommands.commands[Pass.CESIUM_3D_TILE]; - length = frustumCommands.indices[Pass.CESIUM_3D_TILE]; - for (j = 0; j < length; ++j) { - executeCommand(commands[j], scene, context, passState); - } + commandCount = performPass(frustumCommands, Pass.CESIUM_3D_TILE); - if (defined(globeDepth) && environmentState.useGlobeDepthFramebuffer) { + if (useGlobeDepthFramebuffer) { scene._invertClassification.prepareTextures(context); globeDepth.executeUpdateDepth( context, passState, - clearGlobeDepth, scene._invertClassification._fbo.getDepthStencilTexture(), ); } // Set stencil - uniformState.updatePass(Pass.CESIUM_3D_TILE_CLASSIFICATION_IGNORE_SHOW); - commands = - frustumCommands.commands[ - Pass.CESIUM_3D_TILE_CLASSIFICATION_IGNORE_SHOW - ]; - length = - frustumCommands.indices[Pass.CESIUM_3D_TILE_CLASSIFICATION_IGNORE_SHOW]; - for (j = 0; j < length; ++j) { - executeCommand(commands[j], scene, context, passState); - } + commandCount = performPass( + frustumCommands, + Pass.CESIUM_3D_TILE_CLASSIFICATION_IGNORE_SHOW, + ); passState.framebuffer = opaqueClassificationFramebuffer; @@ -2636,35 +2717,24 @@ function executeCommands(scene, passState) { } // Clear stencil set by the classification for the next classification pass - if (length > 0 && context.stencilBuffer) { + if (commandCount > 0 && context.stencilBuffer) { clearClassificationStencil.execute(context, passState); } // Draw style over classification. - uniformState.updatePass(Pass.CESIUM_3D_TILE_CLASSIFICATION); - commands = frustumCommands.commands[Pass.CESIUM_3D_TILE_CLASSIFICATION]; - length = frustumCommands.indices[Pass.CESIUM_3D_TILE_CLASSIFICATION]; - for (j = 0; j < length; ++j) { - executeCommand(commands[j], scene, context, passState); - } + commandCount = performPass( + frustumCommands, + Pass.CESIUM_3D_TILE_CLASSIFICATION, + ); } - if (length > 0 && context.stencilBuffer) { + if (commandCount > 0 && context.stencilBuffer) { clearStencil.execute(context, passState); } - uniformState.updatePass(Pass.VOXELS); - commands = frustumCommands.commands[Pass.VOXELS]; - length = frustumCommands.indices[Pass.VOXELS]; - commands.length = length; - executeVoxelCommands(scene, executeCommand, passState, commands); - - uniformState.updatePass(Pass.OPAQUE); - commands = frustumCommands.commands[Pass.OPAQUE]; - length = frustumCommands.indices[Pass.OPAQUE]; - for (j = 0; j < length; ++j) { - executeCommand(commands[j], scene, context, passState); - } + performVoxelsPass(scene, passState, frustumCommands); + + performPass(frustumCommands, Pass.OPAQUE); if (index !== 0 && scene.mode !== SceneMode.SCENE2D) { // Do not overlap frustums in the translucent pass to avoid blending artifacts @@ -2672,60 +2742,18 @@ function executeCommands(scene, passState) { uniformState.updateFrustum(frustum); } - let invertClassification; - if ( - !picking && - environmentState.useInvertClassification && - frameState.invertClassificationColor.alpha < 1.0 - ) { - // Fullscreen pass to copy unclassified fragments when alpha < 1.0. - // Not executed when undefined. - invertClassification = scene._invertClassification; - } + performTranslucentPass(scene, passState, frustumCommands); - uniformState.updatePass(Pass.TRANSLUCENT); - commands = frustumCommands.commands[Pass.TRANSLUCENT]; - commands.length = frustumCommands.indices[Pass.TRANSLUCENT]; - executeTranslucentCommands( - scene, - executeCommand, - passState, - commands, - invertClassification, - ); - - // Classification for translucent 3D Tiles - const has3DTilesClassificationCommands = - frustumCommands.indices[Pass.CESIUM_3D_TILE_CLASSIFICATION] > 0; - if ( - has3DTilesClassificationCommands && - view.translucentTileClassification.isSupported() - ) { - view.translucentTileClassification.executeTranslucentCommands( - scene, - executeCommand, - passState, - commands, - globeDepth.depthStencilTexture, - ); - view.translucentTileClassification.executeClassificationCommands( - scene, - executeCommand, - passState, - frustumCommands, - ); - } + performTranslucent3DTilesClassification(scene, passState, frustumCommands); if ( context.depthTexture && scene.useDepthPicking && - (environmentState.useGlobeDepthFramebuffer || - renderTranslucentDepthForPick) + (useGlobeDepthFramebuffer || renderTranslucentDepthForPick) ) { // PERFORMANCE_IDEA: Use MRT to avoid the extra copy. - const depthStencilTexture = globeDepth.depthStencilTexture; const pickDepth = scene._picking.getPickDepth(scene, index); - pickDepth.update(context, depthStencilTexture); + pickDepth.update(context, globeDepth.depthStencilTexture); pickDepth.executeCopyDepth(context, passState); } @@ -2734,7 +2762,7 @@ function executeCommands(scene, passState) { } const originalFramebuffer = passState.framebuffer; - passState.framebuffer = view.sceneFramebuffer.getIdFramebuffer(); + passState.framebuffer = sceneFramebuffer.getIdFramebuffer(); // reset frustum frustum.near = @@ -2744,11 +2772,8 @@ function executeCommands(scene, passState) { frustum.far = frustumCommands.far; uniformState.updateFrustum(frustum); - uniformState.updatePass(Pass.GLOBE); - commands = frustumCommands.commands[Pass.GLOBE]; - length = frustumCommands.indices[Pass.GLOBE]; - - if (globeTranslucent) { + if (globeTranslucencyState.translucent) { + uniformState.updatePass(Pass.GLOBE); globeTranslucencyState.executeGlobeCommands( frustumCommands, executeIdCommand, @@ -2757,9 +2782,7 @@ function executeCommands(scene, passState) { passState, ); } else { - for (j = 0; j < length; ++j) { - executeIdCommand(commands[j], scene, context, passState); - } + performIdPass(frustumCommands, Pass.GLOBE); } if (clearGlobeDepth) { @@ -2772,34 +2795,67 @@ function executeCommands(scene, passState) { depthPlane.execute(context, passState); } - uniformState.updatePass(Pass.CESIUM_3D_TILE); - commands = frustumCommands.commands[Pass.CESIUM_3D_TILE]; - length = frustumCommands.indices[Pass.CESIUM_3D_TILE]; - for (j = 0; j < length; ++j) { - executeIdCommand(commands[j], scene, context, passState); - } + performIdPass(frustumCommands, Pass.CESIUM_3D_TILE); + performIdPass(frustumCommands, Pass.OPAQUE); + performIdPass(frustumCommands, Pass.TRANSLUCENT); - uniformState.updatePass(Pass.OPAQUE); - commands = frustumCommands.commands[Pass.OPAQUE]; - length = frustumCommands.indices[Pass.OPAQUE]; - for (j = 0; j < length; ++j) { - executeIdCommand(commands[j], scene, context, passState); - } + passState.framebuffer = originalFramebuffer; + } +} - uniformState.updatePass(Pass.TRANSLUCENT); - commands = frustumCommands.commands[Pass.TRANSLUCENT]; - length = frustumCommands.indices[Pass.TRANSLUCENT]; - for (j = 0; j < length; ++j) { - executeIdCommand(commands[j], scene, context, passState); +/** + * Render the sky, atmosphere, sun, and moon + * + * @param {Scene} scene The scene. + * @param {PassState} passState The render state for the pass. + * + * @private + */ +function renderEnvironment(scene, passState) { + const { context, environmentState, view } = scene; + + context.uniformState.updatePass(Pass.ENVIRONMENT); + + if (defined(environmentState.skyBoxCommand)) { + executeCommand(environmentState.skyBoxCommand, scene, passState); + } + + if (environmentState.isSkyAtmosphereVisible) { + executeCommand(environmentState.skyAtmosphereCommand, scene, passState); + } + + if (environmentState.isSunVisible) { + environmentState.sunDrawCommand.execute(context, passState); + if (scene.sunBloom && !environmentState.useWebVR) { + let framebuffer; + if (environmentState.useGlobeDepthFramebuffer) { + framebuffer = view.globeDepth.framebuffer; + } else if (environmentState.usePostProcess) { + framebuffer = view.sceneFramebuffer.framebuffer; + } else { + framebuffer = environmentState.originalFramebuffer; + } + scene._sunPostProcess.execute(context); + scene._sunPostProcess.copy(context, framebuffer); + passState.framebuffer = framebuffer; } + } - passState.framebuffer = originalFramebuffer; + // Moon can be seen through the atmosphere, since the sun is rendered after the atmosphere. + if (environmentState.isMoonVisible) { + environmentState.moonCommand.execute(context, passState); } } +/** + * Execute compute commands from the scene's environment state and computeCommandList + * + * @param {Scene} scene + * + * @private + */ function executeComputeCommands(scene) { - const us = scene.context.uniformState; - us.updatePass(Pass.COMPUTE); + scene.context.uniformState.updatePass(Pass.COMPUTE); const sunComputeCommand = scene._environmentState.sunComputeCommand; if (defined(sunComputeCommand)) { @@ -2807,114 +2863,130 @@ function executeComputeCommands(scene) { } const commandList = scene._computeCommandList; - const length = commandList.length; - for (let i = 0; i < length; ++i) { + for (let i = 0; i < commandList.length; ++i) { commandList[i].execute(scene._computeEngine); } } +/** + * Execute the draw commands for overlays + * + * @param {Scene} scene + * @param {PassState} passState + * + * @private + */ function executeOverlayCommands(scene, passState) { - const us = scene.context.uniformState; - us.updatePass(Pass.OVERLAY); + scene.context.uniformState.updatePass(Pass.OVERLAY); const context = scene.context; const commandList = scene._overlayCommandList; - const length = commandList.length; - for (let i = 0; i < length; ++i) { + for (let i = 0; i < commandList.length; ++i) { commandList[i].execute(context, passState); } } +/** + * Add the scene's draw commands into the shadow map passes. + * + * @param {Scene} scene + * @param {DrawCommand[]} commandList + * @param {ShadowMap} shadowMap + * + * @private + */ function insertShadowCastCommands(scene, commandList, shadowMap) { - const shadowVolume = shadowMap.shadowMapCullingVolume; - const isPointLight = shadowMap.isPointLight; - const passes = shadowMap.passes; + const { shadowMapCullingVolume, isPointLight, passes } = shadowMap; const numberOfPasses = passes.length; - const length = commandList.length; - for (let i = 0; i < length; ++i) { + const shadowedPasses = [ + Pass.GLOBE, + Pass.CESIUM_3D_TILE, + Pass.OPAQUE, + Pass.TRANSLUCENT, + ]; + + for (let i = 0; i < commandList.length; ++i) { const command = commandList[i]; scene.updateDerivedCommands(command); if ( - command.castShadows && - (command.pass === Pass.GLOBE || - command.pass === Pass.CESIUM_3D_TILE || - command.pass === Pass.OPAQUE || - command.pass === Pass.TRANSLUCENT) + !command.castShadows || + shadowedPasses.indexOf(command.pass) < 0 || + !scene.isVisible(shadowMapCullingVolume, command) ) { - if (scene.isVisible(command, shadowVolume)) { - if (isPointLight) { - for (let k = 0; k < numberOfPasses; ++k) { - passes[k].commandList.push(command); - } - } else if (numberOfPasses === 1) { - passes[0].commandList.push(command); - } else { - let wasVisible = false; - // Loop over cascades from largest to smallest - for (let j = numberOfPasses - 1; j >= 0; --j) { - const cascadeVolume = passes[j].cullingVolume; - if (scene.isVisible(command, cascadeVolume)) { - passes[j].commandList.push(command); - wasVisible = true; - } else if (wasVisible) { - // If it was visible in the previous cascade but now isn't - // then there is no need to check any more cascades - break; - } - } + continue; + } + + if (isPointLight) { + for (let k = 0; k < numberOfPasses; ++k) { + passes[k].commandList.push(command); + } + } else if (numberOfPasses === 1) { + passes[0].commandList.push(command); + } else { + let wasVisible = false; + // Loop over cascades from largest to smallest + for (let j = numberOfPasses - 1; j >= 0; --j) { + const cascadeVolume = passes[j].cullingVolume; + if (scene.isVisible(cascadeVolume, command)) { + passes[j].commandList.push(command); + wasVisible = true; + } else if (wasVisible) { + // If it was visible in the previous cascade but now isn't + // then there is no need to check any more cascades + break; } } } } } +/** + * Execute the draw commands to cast shadows into the shadow maps. + * + * @param {Scene} scene + * + * @private + */ function executeShadowMapCastCommands(scene) { - const frameState = scene.frameState; - const shadowMaps = frameState.shadowState.shadowMaps; - const shadowMapLength = shadowMaps.length; + const { shadowState, commandList } = scene.frameState; + const { shadowsEnabled, shadowMaps } = shadowState; - if (!frameState.shadowState.shadowsEnabled) { + if (!shadowsEnabled) { return; } - const context = scene.context; - const uniformState = context.uniformState; + const { context } = scene; + const { uniformState } = context; - for (let i = 0; i < shadowMapLength; ++i) { + for (let i = 0; i < shadowMaps.length; ++i) { const shadowMap = shadowMaps[i]; if (shadowMap.outOfView) { continue; } // Reset the command lists - const passes = shadowMap.passes; - const numberOfPasses = passes.length; - for (let j = 0; j < numberOfPasses; ++j) { + const { passes } = shadowMap; + for (let j = 0; j < passes.length; ++j) { passes[j].commandList.length = 0; } - // Insert the primitive/model commands into the command lists - const sceneCommands = scene.frameState.commandList; - insertShadowCastCommands(scene, sceneCommands, shadowMap); + // Insert the primitive/model commands into the shadow map command lists + insertShadowCastCommands(scene, commandList, shadowMap); - for (let j = 0; j < numberOfPasses; ++j) { + for (let j = 0; j < passes.length; ++j) { const pass = shadowMap.passes[j]; - uniformState.updateCamera(pass.camera); + const { camera, commandList } = pass; + uniformState.updateCamera(camera); shadowMap.updatePass(context, j); - const numberOfCommands = pass.commandList.length; - for (let k = 0; k < numberOfCommands; ++k) { - const command = pass.commandList[k]; + for (let k = 0; k < commandList.length; ++k) { + const command = commandList[k]; // Set the correct pass before rendering into the shadow map because some shaders // conditionally render based on whether the pass is translucent or opaque. uniformState.updatePass(command.pass); - executeCommand( - command.derivedCommands.shadows.castCommands[i], - scene, - context, - pass.passState, - ); + const castCommand = command.derivedCommands.shadows.castCommands[i]; + executeCommand(castCommand, scene, pass.passState); } } } @@ -2923,38 +2995,46 @@ function executeShadowMapCastCommands(scene) { const scratchEyeTranslation = new Cartesian3(); /** + * Update and clear framebuffers, and execute draw commands. + * + * @param {PassState} passState State specific to each render pass. + * @param {Color} backgroundColor + * * @private */ Scene.prototype.updateAndExecuteCommands = function ( passState, backgroundColor, ) { - const frameState = this._frameState; - const mode = frameState.mode; - const useWebVR = this._environmentState.useWebVR; + updateAndClearFramebuffers(this, passState, backgroundColor); - if (useWebVR) { + if (this._environmentState.useWebVR) { executeWebVRCommands(this, passState, backgroundColor); } else if ( - mode !== SceneMode.SCENE2D || + this._frameState.mode !== SceneMode.SCENE2D || this._mapMode2D === MapMode2D.ROTATE ) { - executeCommandsInViewport(true, this, passState, backgroundColor); + executeCommandsInViewport(true, this, passState); } else { - updateAndClearFramebuffers(this, passState, backgroundColor); execute2DViewportCommands(this, passState); } }; -function executeWebVRCommands(scene, passState, backgroundColor) { +/** + * Execute the draw commands to render the scene into the stereo viewports of a WebVR application. + * + * @param {Scene} scene + * @param {PassState} passState + * + * @private + */ +function executeWebVRCommands(scene, passState) { const view = scene._view; const camera = view.camera; const environmentState = scene._environmentState; const renderTranslucentDepthForPick = environmentState.renderTranslucentDepthForPick; - updateAndClearFramebuffers(scene, passState, backgroundColor); - updateAndRenderPrimitives(scene); view.createPotentiallyVisibleSet(scene); @@ -3015,10 +3095,17 @@ const scratch2DViewportEyePoint = new Cartesian3(); const scratch2DViewportWindowCoords = new Cartesian3(); const scratch2DViewport = new BoundingRectangle(); +/** + * Execute the draw commands to render into a 2D viewport. + * + * @param {Scene} scene + * @param {PassState} passState + * + * @private + */ function execute2DViewportCommands(scene, passState) { - const context = scene.context; - const frameState = scene.frameState; - const camera = scene.camera; + const { frameState, camera } = scene; + const { uniformState } = scene.context; const originalViewport = passState.viewport; const viewport = BoundingRectangle.clone(originalViewport, scratch2DViewport); @@ -3089,7 +3176,7 @@ function execute2DViewportCommands(scene, passState) { camera.directionWC, camera.upWC, ); - context.uniformState.update(frameState); + uniformState.update(frameState); executeCommandsInViewport(true, scene, passState); @@ -3105,7 +3192,7 @@ function execute2DViewportCommands(scene, passState) { camera.directionWC, camera.upWC, ); - context.uniformState.update(frameState); + uniformState.update(frameState); executeCommandsInViewport(false, scene, passState); } else if (windowCoordinates.x > viewportX + viewportWidth * 0.5) { @@ -3119,7 +3206,7 @@ function execute2DViewportCommands(scene, passState) { camera.directionWC, camera.upWC, ); - context.uniformState.update(frameState); + uniformState.update(frameState); executeCommandsInViewport(true, scene, passState); @@ -3136,7 +3223,7 @@ function execute2DViewportCommands(scene, passState) { camera.directionWC, camera.upWC, ); - context.uniformState.update(frameState); + uniformState.update(frameState); executeCommandsInViewport(false, scene, passState); } else { @@ -3151,7 +3238,7 @@ function execute2DViewportCommands(scene, passState) { camera.directionWC, camera.upWC, ); - context.uniformState.update(frameState); + uniformState.update(frameState); executeCommandsInViewport(true, scene, passState); @@ -3168,7 +3255,7 @@ function execute2DViewportCommands(scene, passState) { camera.directionWC, camera.upWC, ); - context.uniformState.update(frameState); + uniformState.update(frameState); executeCommandsInViewport(false, scene, passState); } @@ -3179,16 +3266,19 @@ function execute2DViewportCommands(scene, passState) { passState.viewport = originalViewport; } -function executeCommandsInViewport( - firstViewport, - scene, - passState, - backgroundColor, -) { - const environmentState = scene._environmentState; +/** + * Execute the draw commands to render the scene into the viewport. + * If this is the first viewport rendered, the framebuffers will be cleared to the background color. + * + * @param {boolean} firstViewport true if this is the first viewport rendered. + * @param {Scene} scene + * @param {PassState} passState + * + * @private + */ +function executeCommandsInViewport(firstViewport, scene, passState) { const view = scene._view; - const renderTranslucentDepthForPick = - environmentState.renderTranslucentDepthForPick; + const { renderTranslucentDepthForPick } = scene._environmentState; if (!firstViewport) { scene.frameState.commandList.length = 0; @@ -3199,9 +3289,6 @@ function executeCommandsInViewport( view.createPotentiallyVisibleSet(scene); if (firstViewport) { - if (defined(backgroundColor)) { - updateAndClearFramebuffers(scene, passState, backgroundColor); - } executeComputeCommands(scene); if (!renderTranslucentDepthForPick) { executeShadowMapCastCommands(scene); @@ -3322,13 +3409,13 @@ Scene.prototype.updateEnvironment = function () { defined(environmentState.skyAtmosphereCommand) && environmentState.isReadyForAtmosphere; environmentState.isSunVisible = this.isVisible( - environmentState.sunDrawCommand, cullingVolume, + environmentState.sunDrawCommand, occluder, ); environmentState.isMoonVisible = this.isVisible( - environmentState.moonCommand, cullingVolume, + environmentState.moonCommand, occluder, ); @@ -3371,21 +3458,21 @@ function updateDebugFrustumPlanes(scene) { function updateShadowMaps(scene) { const frameState = scene._frameState; - const shadowMaps = frameState.shadowMaps; + const { passes, shadowState, shadowMaps } = frameState; const length = shadowMaps.length; const shadowsEnabled = length > 0 && - !frameState.passes.pick && - !frameState.passes.pickVoxel && + !passes.pick && + !passes.pickVoxel && scene.mode === SceneMode.SCENE3D; - if (shadowsEnabled !== frameState.shadowState.shadowsEnabled) { + if (shadowsEnabled !== shadowState.shadowsEnabled) { // Update derived commands when shadowsEnabled changes - ++frameState.shadowState.lastDirtyTime; - frameState.shadowState.shadowsEnabled = shadowsEnabled; + ++shadowState.lastDirtyTime; + shadowState.shadowsEnabled = shadowsEnabled; } - frameState.shadowState.lightShadowsEnabled = false; + shadowState.lightShadowsEnabled = false; if (!shadowsEnabled) { return; @@ -3394,28 +3481,28 @@ function updateShadowMaps(scene) { // Check if the shadow maps are different than the shadow maps last frame. // If so, the derived commands need to be updated. for (let j = 0; j < length; ++j) { - if (shadowMaps[j] !== frameState.shadowState.shadowMaps[j]) { - ++frameState.shadowState.lastDirtyTime; + if (shadowMaps[j] !== shadowState.shadowMaps[j]) { + ++shadowState.lastDirtyTime; break; } } - frameState.shadowState.shadowMaps.length = 0; - frameState.shadowState.lightShadowMaps.length = 0; + shadowState.shadowMaps.length = 0; + shadowState.lightShadowMaps.length = 0; for (let i = 0; i < length; ++i) { const shadowMap = shadowMaps[i]; shadowMap.update(frameState); - frameState.shadowState.shadowMaps.push(shadowMap); + shadowState.shadowMaps.push(shadowMap); if (shadowMap.fromLightSource) { - frameState.shadowState.lightShadowMaps.push(shadowMap); - frameState.shadowState.lightShadowsEnabled = true; + shadowState.lightShadowMaps.push(shadowMap); + shadowState.lightShadowsEnabled = true; } if (shadowMap.dirty) { - ++frameState.shadowState.lastDirtyTime; + ++shadowState.lastDirtyTime; shadowMap.dirty = false; } } @@ -3441,7 +3528,7 @@ function updateAndClearFramebuffers(scene, passState, clearColor) { const environmentState = scene._environmentState; const view = scene._view; - const passes = scene._frameState.passes; + const passes = frameState.passes; const picking = passes.pick || passes.pickVoxel; if (defined(view.globeDepth)) { view.globeDepth.picking = picking; @@ -3545,8 +3632,8 @@ function updateAndClearFramebuffers(scene, passState, clearColor) { !picking && defined(passState.framebuffer) && scene.invertClassification); if (useInvertClassification) { let depthFramebuffer; - if (scene.frameState.invertClassificationColor.alpha === 1.0) { - if (environmentState.useGlobeDepthFramebuffer) { + if (frameState.invertClassificationColor.alpha === 1.0) { + if (useGlobeDepthFramebuffer) { depthFramebuffer = view.globeDepth.framebuffer; } } @@ -3560,7 +3647,7 @@ function updateAndClearFramebuffers(scene, passState, clearColor) { ); scene._invertClassification.clear(context, passState); - if (scene.frameState.invertClassificationColor.alpha < 1.0 && useOIT) { + if (frameState.invertClassificationColor.alpha < 1.0 && useOIT) { const command = scene._invertClassification.unclassifiedCommand; const derivedCommands = command.derivedCommands; derivedCommands.oit = oit.createDerivedCommands( @@ -3591,16 +3678,18 @@ Scene.prototype.resolveFramebuffers = function (passState) { const context = this._context; const environmentState = this._environmentState; const view = this._view; - const globeDepth = view.globeDepth; + const { globeDepth, translucentTileClassification } = view; if (defined(globeDepth)) { globeDepth.prepareColorTextures(context); } - const useOIT = environmentState.useOIT; - const useGlobeDepthFramebuffer = environmentState.useGlobeDepthFramebuffer; - const usePostProcess = environmentState.usePostProcess; + const { + useOIT, + useGlobeDepthFramebuffer, + usePostProcess, + originalFramebuffer, + } = environmentState; - const defaultFramebuffer = environmentState.originalFramebuffer; const globeFramebuffer = useGlobeDepthFramebuffer ? globeDepth.colorFramebufferManager : undefined; @@ -3610,11 +3699,10 @@ Scene.prototype.resolveFramebuffers = function (passState) { if (useOIT) { passState.framebuffer = usePostProcess ? sceneFramebuffer.framebuffer - : defaultFramebuffer; + : originalFramebuffer; view.oit.execute(context, passState); } - const translucentTileClassification = view.translucentTileClassification; if ( translucentTileClassification.hasTranslucentDepth && translucentTileClassification.isSupported() @@ -3637,11 +3725,11 @@ Scene.prototype.resolveFramebuffers = function (passState) { sceneFramebuffer, ).getDepthStencilTexture(); postProcess.execute(context, colorTexture, depthTexture, idTexture); - postProcess.copy(context, defaultFramebuffer); + postProcess.copy(context, originalFramebuffer); } if (!useOIT && !usePostProcess && useGlobeDepthFramebuffer) { - passState.framebuffer = defaultFramebuffer; + passState.framebuffer = originalFramebuffer; globeDepth.executeCopyColor(context, passState); } }; @@ -3650,7 +3738,7 @@ function callAfterRenderFunctions(scene) { // Functions are queued up during primitive update and executed here in case // the function modifies scene state that should remain constant over the frame. const functions = scene._frameState.afterRender; - for (let i = 0, length = functions.length; i < length; ++i) { + for (let i = 0; i < functions.length; ++i) { const shouldRequestRender = functions[i](); if (shouldRequestRender) { scene.requestRender(); @@ -3664,9 +3752,7 @@ function getGlobeHeight(scene) { if (scene.mode === SceneMode.MORPHING) { return; } - - const camera = scene.camera; - const cartographic = camera.positionCartographic; + const cartographic = scene.camera.positionCartographic; return scene.getHeight(cartographic); } @@ -3930,8 +4016,7 @@ function prePassesUpdate(scene) { scene._jobScheduler.resetBudgets(); const frameState = scene._frameState; - const primitives = scene.primitives; - primitives.prePassesUpdate(frameState); + scene.primitives.prePassesUpdate(frameState); if (defined(scene.globe)) { scene.globe.update(frameState); @@ -3942,20 +4027,23 @@ function prePassesUpdate(scene) { } function postPassesUpdate(scene) { - const frameState = scene._frameState; - const primitives = scene.primitives; - primitives.postPassesUpdate(frameState); - + scene.primitives.postPassesUpdate(scene._frameState); RequestScheduler.update(); } const scratchBackgroundColor = new Color(); +/** + * Render the scene + * + * @param {Scene} scene + * @private + */ function render(scene) { const frameState = scene._frameState; const context = scene.context; - const us = context.uniformState; + const { uniformState } = context; const view = scene._defaultView; scene._view = view; @@ -3977,13 +4065,16 @@ function render(scene) { frameState.atmosphere = scene.atmosphere; scene.fog.update(frameState); - us.update(frameState); + uniformState.update(frameState); const shadowMap = scene.shadowMap; if (defined(shadowMap) && shadowMap.enabled) { if (!defined(scene.light) || scene.light instanceof SunLight) { // Negate the sun direction so that it is from the Sun, not to the Sun - Cartesian3.negate(us.sunDirectionWC, scene._shadowMapCamera.direction); + Cartesian3.negate( + uniformState.sunDirectionWC, + scene._shadowMapCamera.direction, + ); } else { Cartesian3.clone(scene.light.direction, scene._shadowMapCamera.direction); } @@ -4105,9 +4196,7 @@ Scene.prototype.render = function (time) { tryAndCatchError(this, prePassesUpdate); /** - * * Passes update. Add any passes here - * */ if (this.primitives.show) { tryAndCatchError(this, updateMostDetailedRayPicks); @@ -4127,14 +4216,13 @@ Scene.prototype.render = function (time) { } /** - * * Post passes update. Execute any pass invariant code that should run after the passes here. - * */ updateDebugShowFramesPerSecond(this, shouldRender); tryAndCatchError(this, postPassesUpdate); - // Often used to trigger events (so don't want in trycatch) that the user might be subscribed to. Things like the tile load events, promises, etc. + // Often used to trigger events (so don't want in trycatch) that the user + // might be subscribed to. Things like the tile load events, promises, etc. // We don't want those events to resolve during the render loop because the events might add new primitives callAfterRenderFunctions(this); diff --git a/packages/engine/Source/Scene/TranslucentTileClassification.js b/packages/engine/Source/Scene/TranslucentTileClassification.js index 3dab18bcfc23..c73d3ba732c9 100644 --- a/packages/engine/Source/Scene/TranslucentTileClassification.js +++ b/packages/engine/Source/Scene/TranslucentTileClassification.js @@ -347,16 +347,12 @@ TranslucentTileClassification.prototype.executeTranslucentCommands = function ( globeDepthStencilTexture, ) { // Check for translucent commands that should be classified - const length = commands.length; - let command; - let i; - const useLogDepth = scene.frameState.useLogDepth; const context = scene.context; const framebuffer = passState.framebuffer; - for (i = 0; i < length; ++i) { - command = commands[i]; + for (let i = 0; i < commands.length; ++i) { + let command = commands[i]; command = useLogDepth ? command.derivedCommands.logDepth.command : command; if (command.depthForTranslucentClassification) { @@ -377,8 +373,8 @@ TranslucentTileClassification.prototype.executeTranslucentCommands = function ( // Clear depth for multifrustum this._clearDepthStencilCommand.execute(context, passState); - for (i = 0; i < length; ++i) { - command = commands[i]; + for (let i = 0; i < commands.length; ++i) { + let command = commands[i]; command = useLogDepth ? command.derivedCommands.logDepth.command : command; if (!command.depthForTranslucentClassification) { @@ -387,7 +383,7 @@ TranslucentTileClassification.prototype.executeTranslucentCommands = function ( // Depth-only commands are created for all translucent 3D Tiles commands const depthOnlyCommand = command.derivedCommands.depth.depthOnlyCommand; - executeCommand(depthOnlyCommand, scene, context, passState); + executeCommand(depthOnlyCommand, scene, passState); } this._frustumsDrawn += this._hasTranslucentDepth ? 1 : 0; @@ -408,31 +404,28 @@ TranslucentTileClassification.prototype.executeClassificationCommands = } const context = scene.context; - const us = context.uniformState; + const uniformState = context.uniformState; const framebuffer = passState.framebuffer; - if (this._frustumsDrawn === 2) { - // copy classification from first frustum - passState.framebuffer = this._accumulationFBO.framebuffer; - this._copyCommand.execute(context, passState); - } + passState.framebuffer = this._accumulationFBO.framebuffer; + this._accumulateCommand.execute(context, passState); passState.framebuffer = this._drawClassificationFBO.framebuffer; if (this._frustumsDrawn > 1) { this._clearColorCommand.execute(context, passState); } - us.updatePass(Pass.CESIUM_3D_TILE_CLASSIFICATION); - const swapGlobeDepth = us.globeDepthTexture; - us.globeDepthTexture = this._packFBO.getColorTexture(); + uniformState.updatePass(Pass.CESIUM_3D_TILE_CLASSIFICATION); + const swapGlobeDepth = uniformState.globeDepthTexture; + uniformState.globeDepthTexture = this._packFBO.getColorTexture(); const commands = frustumCommands.commands[Pass.CESIUM_3D_TILE_CLASSIFICATION]; const length = frustumCommands.indices[Pass.CESIUM_3D_TILE_CLASSIFICATION]; for (let i = 0; i < length; ++i) { - executeCommand(commands[i], scene, context, passState); + executeCommand(commands[i], scene, passState); } - us.globeDepthTexture = swapGlobeDepth; + uniformState.globeDepthTexture = swapGlobeDepth; passState.framebuffer = framebuffer; if (this._frustumsDrawn === 1) { diff --git a/packages/engine/Source/Scene/View.js b/packages/engine/Source/Scene/View.js index d63df7b39854..ec8cb870f681 100644 --- a/packages/engine/Source/Scene/View.js +++ b/packages/engine/Source/Scene/View.js @@ -28,6 +28,13 @@ function CommandExtent() { } /** + * @alias View + * @constructor + * + * @param {Scene} scene + * @param {Camera} camera + * @param {BoundingRectangle} viewport + * * @private */ function View(scene, camera, viewport) { @@ -62,6 +69,9 @@ function View(scene, camera, viewport) { this.translucentTileClassification = new TranslucentTileClassification( context, ); + /** + * @type {PickDepth[]} + */ this.pickDepths = []; this.frustumCommandsList = []; this.debugFrustumStatistics = undefined; @@ -73,16 +83,26 @@ function View(scene, camera, viewport) { const scratchPosition0 = new Cartesian3(); const scratchPosition1 = new Cartesian3(); -function maxComponent(a, b) { - const x = Math.max(Math.abs(a.x), Math.abs(b.x)); - const y = Math.max(Math.abs(a.y), Math.abs(b.y)); - const z = Math.max(Math.abs(a.z), Math.abs(b.z)); - return Math.max(Math.max(x, y), z); -} - +/** + * Check if two cameras have the same view. + * + * @param {Camera} camera0 The first camera for comparison. + * @param {Camera} camera1 The second camera for comparison. + * @param {number} epsilon The epsilon tolerance to use for equality testing. + * @returns {boolean} true if the cameras are equal. + * + * @private + */ function cameraEqual(camera0, camera1, epsilon) { - const scalar = - 1 / Math.max(1, maxComponent(camera0.position, camera1.position)); + const maximumPositionComponent = Math.max( + Cartesian3.maximumComponent( + Cartesian3.abs(camera0.position, scratchPosition0), + ), + Cartesian3.maximumComponent( + Cartesian3.abs(camera1.position, scratchPosition1), + ), + ); + const scalar = 1 / Math.max(1, maximumPositionComponent); Cartesian3.multiplyByScalar(camera0.position, scalar, scratchPosition0); Cartesian3.multiplyByScalar(camera1.position, scalar, scratchPosition1); return ( @@ -95,6 +115,14 @@ function cameraEqual(camera0, camera1, epsilon) { ); } +/** + * Check if the camera position or direction has changed. + * + * @param {Scene} scene + * @returns {boolean} true if the camera has been updated + * + * @private + */ View.prototype.checkForCameraUpdates = function (scene) { const camera = this.camera; const cameraClone = this._cameraClone; @@ -120,10 +148,22 @@ View.prototype.checkForCameraUpdates = function (scene) { return false; }; +/** + * Split the depth range of the scene into multiple frustums, and initialize + * a list of {@link FrustumCommands} with the distances to the near and far + * planes for each frustum. + * + * @param {View} view The view to which the frustum commands list is attached. + * @param {Scene} scene The scene to be rendered. + * @param {number} near The distance to the nearest object in the scene. + * @param {number} far The distance to the farthest object in the scene. + * + * @private + */ function updateFrustums(view, scene, near, far) { - const frameState = scene.frameState; - const camera = frameState.camera; - const farToNearRatio = frameState.useLogDepth + const { frameState } = scene; + const { camera, useLogDepth } = frameState; + const farToNearRatio = useLogDepth ? scene.logarithmicDepthFarToNearRatio : scene.farToNearRatio; const is2D = scene.mode === SceneMode.SCENE2D; @@ -153,7 +193,7 @@ function updateFrustums(view, scene, near, far) { numFrustums = Math.ceil(Math.log(far / near) / Math.log(farToNearRatio)); } - const frustumCommandsList = view.frustumCommandsList; + const { frustumCommandsList } = view; frustumCommandsList.length = numFrustums; for (let m = 0; m < numFrustums; ++m) { let curNear; @@ -182,24 +222,33 @@ function updateFrustums(view, scene, near, far) { } } -function insertIntoBin(view, scene, command, commandNear, commandFar) { +/** + * Insert a command into the appropriate {@link FrustumCommands} based on the + * range of depths covered by its bounding volume. + * + * @param {View} view + * @param {Scene} scene + * @param {CommandExtent} commandExtent + * + * @private + */ +function insertIntoBin(view, scene, commandExtent) { + const { command, near, far } = commandExtent; + if (scene.debugShowFrustums) { command.debugOverlappingFrustums = 0; } - const frustumCommandsList = view.frustumCommandsList; - const length = frustumCommandsList.length; + const { frustumCommandsList } = view; - for (let i = 0; i < length; ++i) { + for (let i = 0; i < frustumCommandsList.length; ++i) { const frustumCommands = frustumCommandsList[i]; - const curNear = frustumCommands.near; - const curFar = frustumCommands.far; - if (commandNear > curFar) { + if (near > frustumCommands.far) { continue; } - if (commandFar < curNear) { + if (far < frustumCommands.near) { break; } @@ -217,13 +266,13 @@ function insertIntoBin(view, scene, command, commandNear, commandFar) { } if (scene.debugShowFrustums) { - const cf = view.debugFrustumStatistics.commandsInFrustums; - cf[command.debugOverlappingFrustums] = defined( - cf[command.debugOverlappingFrustums], - ) - ? cf[command.debugOverlappingFrustums] + 1 + const { debugFrustumStatistics } = view; + const { debugOverlappingFrustums } = command; + const cf = debugFrustumStatistics.commandsInFrustums; + cf[debugOverlappingFrustums] = defined(cf[debugOverlappingFrustums]) + ? cf[debugOverlappingFrustums] + 1 : 1; - ++view.debugFrustumStatistics.totalCommands; + ++debugFrustumStatistics.totalCommands; } scene.updateDerivedCommands(command); @@ -233,14 +282,12 @@ const scratchCullingVolume = new CullingVolume(); const scratchNearFarInterval = new Interval(); View.prototype.createPotentiallyVisibleSet = function (scene) { - const frameState = scene.frameState; - const camera = frameState.camera; - const direction = camera.directionWC; - const position = camera.positionWC; + const { frameState } = scene; + const { camera, commandList, shadowState } = frameState; + const { positionWC, directionWC, frustum } = camera; const computeList = scene._computeCommandList; const overlayList = scene._overlayCommandList; - const commandList = frameState.commandList; if (scene.debugShowFrustums) { this.debugFrustumStatistics = { @@ -250,10 +297,8 @@ View.prototype.createPotentiallyVisibleSet = function (scene) { } const frustumCommandsList = this.frustumCommandsList; - const numberOfFrustums = frustumCommandsList.length; - const numberOfPasses = Pass.NUMBER_OF_PASSES; - for (let n = 0; n < numberOfFrustums; ++n) { - for (let p = 0; p < numberOfPasses; ++p) { + for (let n = 0; n < frustumCommandsList.length; ++n) { + for (let p = 0; p < Pass.NUMBER_OF_PASSES; ++p) { frustumCommandsList[n].indices[p] = 0; } } @@ -268,26 +313,25 @@ View.prototype.createPotentiallyVisibleSet = function (scene) { let near = +Number.MAX_VALUE; let far = -Number.MAX_VALUE; - const shadowsEnabled = frameState.shadowState.shadowsEnabled; + const { shadowsEnabled } = shadowState; let shadowNear = +Number.MAX_VALUE; let shadowFar = -Number.MAX_VALUE; let shadowClosestObjectSize = Number.MAX_VALUE; const occluder = frameState.mode === SceneMode.SCENE3D ? frameState.occluder : undefined; - let cullingVolume = frameState.cullingVolume; // get user culling volume minus the far plane. + let { cullingVolume } = frameState; const planes = scratchCullingVolume.planes; for (let k = 0; k < 5; ++k) { planes[k] = cullingVolume.planes[k]; } cullingVolume = scratchCullingVolume; - const length = commandList.length; - for (let i = 0; i < length; ++i) { + for (let i = 0; i < commandList.length; ++i) { const command = commandList[i]; - const pass = command.pass; + const { pass, boundingVolume } = command; if (pass === Pass.COMPUTE) { computeList.push(command); @@ -297,15 +341,14 @@ View.prototype.createPotentiallyVisibleSet = function (scene) { let commandNear; let commandFar; - const boundingVolume = command.boundingVolume; if (defined(boundingVolume)) { - if (!scene.isVisible(command, cullingVolume, occluder)) { + if (!scene.isVisible(cullingVolume, command, occluder)) { continue; } const nearFarInterval = boundingVolume.computePlaneDistances( - position, - direction, + positionWC, + directionWC, scratchNearFarInterval, ); commandNear = nearFarInterval.start; @@ -334,13 +377,13 @@ View.prototype.createPotentiallyVisibleSet = function (scene) { } } else if (command instanceof ClearCommand) { // Clear commands don't need a bounding volume - just add the clear to all frustums. - commandNear = camera.frustum.near; - commandFar = camera.frustum.far; + commandNear = frustum.near; + commandFar = frustum.far; } else { // If command has no bounding volume we need to use the camera's // worst-case near and far planes to avoid clipping something important. - commandNear = camera.frustum.near; - commandFar = camera.frustum.far; + commandNear = frustum.near; + commandFar = frustum.far; near = Math.min(near, commandNear); far = Math.max(far, commandFar); } @@ -357,46 +400,36 @@ View.prototype.createPotentiallyVisibleSet = function (scene) { } if (shadowsEnabled) { - shadowNear = Math.min( - Math.max(shadowNear, camera.frustum.near), - camera.frustum.far, - ); - shadowFar = Math.max(Math.min(shadowFar, camera.frustum.far), shadowNear); - } - - // Use the computed near and far for shadows - if (shadowsEnabled) { - frameState.shadowState.nearPlane = shadowNear; - frameState.shadowState.farPlane = shadowFar; - frameState.shadowState.closestObjectSize = shadowClosestObjectSize; + shadowNear = Math.min(Math.max(shadowNear, frustum.near), frustum.far); + shadowFar = Math.max(Math.min(shadowFar, frustum.far), shadowNear); + // Use the computed near and far for shadows + shadowState.nearPlane = shadowNear; + shadowState.farPlane = shadowFar; + shadowState.closestObjectSize = shadowClosestObjectSize; } updateFrustums(this, scene, near, far); - let c; - let ce; - - for (c = 0; c < commandExtentCount; c++) { - ce = commandExtents[c]; - insertIntoBin(this, scene, ce.command, ce.near, ce.far); + for (let c = 0; c < commandExtentCount; c++) { + insertIntoBin(this, scene, commandExtents[c]); } // Dereference old commands if (commandExtentCount < commandExtentCapacity) { - for (c = commandExtentCount; c < commandExtentCapacity; c++) { - ce = commandExtents[c]; - if (!defined(ce.command)) { + for (let c = commandExtentCount; c < commandExtentCapacity; c++) { + const commandExtent = commandExtents[c]; + if (!defined(commandExtent.command)) { // If the command is undefined, it's assumed that all // subsequent commmands were set to undefined as well, // so no need to loop over them all break; } - ce.command = undefined; + commandExtent.command = undefined; } } const numFrustums = frustumCommandsList.length; - const frustumSplits = frameState.frustumSplits; + const { frustumSplits } = frameState; frustumSplits.length = numFrustums + 1; for (let j = 0; j < numFrustums; ++j) { frustumSplits[j] = frustumCommandsList[j].near; @@ -421,10 +454,8 @@ View.prototype.destroy = function () { this.globeTranslucencyFramebuffer && this.globeTranslucencyFramebuffer.destroy(); - let i; const pickDepths = this.pickDepths; - const length = pickDepths.length; - for (i = 0; i < length; ++i) { + for (let i = 0; i < pickDepths.length; ++i) { pickDepths[i].destroy(); } }; diff --git a/packages/engine/Source/Shaders/Builtin/Functions/packDepth.glsl b/packages/engine/Source/Shaders/Builtin/Functions/packDepth.glsl index b7c55255abf9..4bbe39d84b2d 100644 --- a/packages/engine/Source/Shaders/Builtin/Functions/packDepth.glsl +++ b/packages/engine/Source/Shaders/Builtin/Functions/packDepth.glsl @@ -1,11 +1,11 @@ /** - * Packs a depth value into a vec3 that can be represented by unsigned bytes. + * Packs a depth value into a vec4 that can be represented by unsigned bytes. * * @name czm_packDepth * @glslFunction * * @param {float} depth The floating-point depth. - * @returns {vec3} The packed depth. + * @returns {vec4} The packed depth. */ vec4 czm_packDepth(float depth) { diff --git a/packages/engine/Source/Shaders/Builtin/Functions/unpackDepth.glsl b/packages/engine/Source/Shaders/Builtin/Functions/unpackDepth.glsl index b6a7ef75cc9f..dae332855135 100644 --- a/packages/engine/Source/Shaders/Builtin/Functions/unpackDepth.glsl +++ b/packages/engine/Source/Shaders/Builtin/Functions/unpackDepth.glsl @@ -8,9 +8,9 @@ * * @returns {float} The floating-point depth in [0, 1) range. */ - float czm_unpackDepth(vec4 packedDepth) - { +float czm_unpackDepth(vec4 packedDepth) +{ // See Aras Pranckevičius' post Encoding Floats to RGBA // http://aras-p.info/blog/2009/07/30/encoding-floats-to-rgba-the-final/ return dot(packedDepth, vec4(1.0, 1.0 / 255.0, 1.0 / 65025.0, 1.0 / 16581375.0)); - } +} diff --git a/packages/engine/Source/Shaders/Builtin/Functions/windowToEyeCoordinates.glsl b/packages/engine/Source/Shaders/Builtin/Functions/windowToEyeCoordinates.glsl index fb902ad40fef..0e26ae8f8860 100644 --- a/packages/engine/Source/Shaders/Builtin/Functions/windowToEyeCoordinates.glsl +++ b/packages/engine/Source/Shaders/Builtin/Functions/windowToEyeCoordinates.glsl @@ -71,7 +71,7 @@ vec4 czm_screenToEyeCoordinates(vec2 screenCoordinateXY, float depthOrLogDepth) float near = czm_currentFrustum.x; float far = czm_currentFrustum.y; float log2Depth = depthOrLogDepth * czm_log2FarDepthFromNearPlusOne; - float depthFromNear = pow(2.0, log2Depth) - 1.0; + float depthFromNear = exp2(log2Depth) - 1.0; float depthFromCamera = depthFromNear + near; vec4 screenCoord = vec4(screenCoordinateXY, far * (1.0 - near / depthFromCamera) / (far - near), 1.0); vec4 eyeCoordinate = czm_screenToEyeCoordinates(screenCoord); diff --git a/packages/engine/Source/Shaders/Builtin/Functions/writeLogDepth.glsl b/packages/engine/Source/Shaders/Builtin/Functions/writeLogDepth.glsl index 36a745469d37..d70f47617439 100644 --- a/packages/engine/Source/Shaders/Builtin/Functions/writeLogDepth.glsl +++ b/packages/engine/Source/Shaders/Builtin/Functions/writeLogDepth.glsl @@ -10,7 +10,7 @@ uniform vec2 u_polygonOffset; /** * Writes the fragment depth to the logarithmic depth buffer. *

- * Use this when the vertex shader does not call {@link czm_vertexlogDepth}, for example, when + * Use this when the vertex shader does not call {@link czm_vertexLogDepth}, for example, when * ray-casting geometry using a full screen quad. *

* @name czm_writeLogDepth @@ -66,7 +66,7 @@ void czm_writeLogDepth(float depth) /** * Writes the fragment depth to the logarithmic depth buffer. *

- * Use this when the vertex shader calls {@link czm_vertexlogDepth}. + * Use this when the vertex shader calls {@link czm_vertexLogDepth}. *

* * @name czm_writeLogDepth diff --git a/packages/engine/Source/Shaders/DepthPlaneVS.glsl b/packages/engine/Source/Shaders/DepthPlaneVS.glsl index 4141585366a6..b5204ec2f53d 100644 --- a/packages/engine/Source/Shaders/DepthPlaneVS.glsl +++ b/packages/engine/Source/Shaders/DepthPlaneVS.glsl @@ -5,7 +5,7 @@ out vec4 positionEC; void main() { positionEC = czm_modelView * position; - gl_Position = czm_projection * positionEC; + gl_Position = czm_modelViewProjection * position; czm_vertexLogDepth(); } diff --git a/packages/engine/Specs/Scene/GlobeTranslucencyStateSpec.js b/packages/engine/Specs/Scene/GlobeTranslucencyStateSpec.js index 55982f57bd44..540d2507e52d 100644 --- a/packages/engine/Specs/Scene/GlobeTranslucencyStateSpec.js +++ b/packages/engine/Specs/Scene/GlobeTranslucencyStateSpec.js @@ -636,7 +636,6 @@ describe("Scene/GlobeTranslucencyState", function () { expect(executeCommand).toHaveBeenCalledWith( command.derivedCommands.globeTranslucency.opaqueBackFaceCommand, scene, - context, passState, ); expect( @@ -645,7 +644,6 @@ describe("Scene/GlobeTranslucencyState", function () { }); it("does not execute globe commands if there are no commands", function () { - const frameState = scene.frameState; const context = frameState.context; const passState = new PassState(context); @@ -702,13 +700,11 @@ describe("Scene/GlobeTranslucencyState", function () { expect(executeCommand).toHaveBeenCalledWith( classificationCommand, scene, - context, passState, ); expect(executeCommand).toHaveBeenCalledWith( command.derivedCommands.globeTranslucency.depthOnlyFrontFaceCommand, scene, - context, passState, ); diff --git a/packages/engine/Specs/Scene/SpecularEnvironmentCubeMapSpec.js b/packages/engine/Specs/Scene/SpecularEnvironmentCubeMapSpec.js index 1d23d2acc5fa..e264444a9fd0 100644 --- a/packages/engine/Specs/Scene/SpecularEnvironmentCubeMapSpec.js +++ b/packages/engine/Specs/Scene/SpecularEnvironmentCubeMapSpec.js @@ -215,7 +215,7 @@ describe( }); }); - it("raises error event when environment map fails to load.", async function () { + it("raises error event when environment map fails to load.", function () { if (!SpecularEnvironmentCubeMap.isSupported(context)) { return; } @@ -224,22 +224,19 @@ describe( const frameState = createFrameState(context); let error; - const promise = new Promise((resolve, reject) => { - const removeListener = cubeMap.errorEvent.addEventListener((e) => { - error = e; - expect(error).toBeDefined(); - expect(cubeMap.ready).toEqual(false); - removeListener(); - resolve(); - }); - }); - - await pollToPromise(function () { - cubeMap.update(frameState); - return defined(error); + const removeListener = cubeMap.errorEvent.addEventListener((e) => { + error = e; + expect(cubeMap.ready).toEqual(false); + removeListener(); }); - return promise; + return pollToPromise( + function () { + cubeMap.update(frameState); + return defined(error); + }, + { timeout: 10000 }, + ); }); }, "WebGL", diff --git a/packages/engine/Specs/Scene/TranslucentTileClassificationSpec.js b/packages/engine/Specs/Scene/TranslucentTileClassificationSpec.js index ea6e62ce4e4a..410234e577c4 100644 --- a/packages/engine/Specs/Scene/TranslucentTileClassificationSpec.js +++ b/packages/engine/Specs/Scene/TranslucentTileClassificationSpec.js @@ -49,9 +49,9 @@ describe( scene.render(); // generate globeDepth.framebuffer context = scene.context; - passState = scene._defaultView.passState; - if (defined(scene._defaultView.globeDepth)) { - globeDepthFramebuffer = scene._defaultView.globeDepth.framebuffer; + passState = scene.defaultView.passState; + if (defined(scene.defaultView.globeDepth)) { + globeDepthFramebuffer = scene.defaultView.globeDepth.framebuffer; } ellipsoid = Ellipsoid.WGS84; @@ -283,8 +283,8 @@ describe( }); } - function executeCommand(command, scene, context, passState) { - command.execute(context, passState); + function executeCommand(command, scene, passState) { + command.execute(scene.context, passState); } it("draws translucent commands into a buffer for depth", function () {