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 () {