From 875e0a839f0a0704e1b431eddbfe0de091c0eb1e Mon Sep 17 00:00:00 2001 From: Jeshurun Hembd Date: Tue, 10 Sep 2024 12:46:16 -0400 Subject: [PATCH 01/15] Update PostProcessStageCollection to follow Coding Guide --- .../Scene/PostProcessStageCollection.js | 33 +++++++------------ 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/packages/engine/Source/Scene/PostProcessStageCollection.js b/packages/engine/Source/Scene/PostProcessStageCollection.js index 10cfeb5be476..af65a360285d 100644 --- a/packages/engine/Source/Scene/PostProcessStageCollection.js +++ b/packages/engine/Source/Scene/PostProcessStageCollection.js @@ -423,8 +423,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++; @@ -591,23 +590,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; @@ -654,9 +650,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); } @@ -689,19 +685,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); @@ -752,12 +746,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, @@ -767,7 +758,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); } } From 52da7fdf986fc450b9f28613e75bb5b2889c1636 Mon Sep 17 00:00:00 2001 From: Jeshurun Hembd Date: Tue, 10 Sep 2024 12:53:17 -0400 Subject: [PATCH 02/15] Clean up DerivedCommand --- .../engine/Source/Scene/DerivedCommand.js | 407 ++++++++---------- packages/engine/Source/Scene/PickDepth.js | 3 +- .../Functions/windowToEyeCoordinates.glsl | 2 +- .../Builtin/Functions/writeLogDepth.glsl | 4 +- .../engine/Source/Shaders/DepthPlaneVS.glsl | 2 +- 5 files changed, 187 insertions(+), 231 deletions(-) diff --git a/packages/engine/Source/Scene/DerivedCommand.js b/packages/engine/Source/Scene/DerivedCommand.js index 431de308f79b..356d9d3f77f6 100644 --- a/packages/engine/Source/Scene/DerivedCommand.js +++ b/packages/engine/Source/Scene/DerivedCommand.js @@ -12,89 +12,77 @@ 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() \n" + "{ \n" + " out_FragColor = vec4(1.0); \n" + "} \n"; + fs = new ShaderSource({ + sources: [source], + }); + } else if (!writesDepthOrDiscards && usesLogDepth) { + const source = + "void main() \n" + + "{ \n" + + " out_FragColor = vec4(1.0); \n" + + " czm_writeLogDepth(); \n" + + "} \n"; + 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 +101,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 +138,87 @@ 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 = + "\n\n" + + "void main() \n" + + "{ \n" + + " czm_log_depth_main(); \n" + + " czm_vertexLogDepth(); \n" + + "} \n"; + 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 += + "\n" + + "void main() \n" + + "{ \n" + + " czm_log_depth_main(); \n" + + " czm_writeLogDepth(); \n" + + "} \n"; } - 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 +226,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,24 +244,20 @@ 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; + if (defined(cachedShader)) { + return cachedShader; + } - const sources = fs.sources; - const length = sources.length; + 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 () + 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) { @@ -293,47 +266,42 @@ function getPickShaderProgram(context, shaderProgram, pickId) { ${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 +315,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 +340,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 +369,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/PickDepth.js b/packages/engine/Source/Scene/PickDepth.js index f350e0a8a76e..35f7338fad0e 100644 --- a/packages/engine/Source/Scene/PickDepth.js +++ b/packages/engine/Source/Scene/PickDepth.js @@ -23,8 +23,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); } 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(); } From 4daa78e03c7742687a46d32768347017ae966b50 Mon Sep 17 00:00:00 2001 From: Jeshurun Hembd Date: Tue, 10 Sep 2024 12:57:26 -0400 Subject: [PATCH 03/15] More cleanup in Scene, View, PostProcessStage, GlobeDepth --- packages/engine/Source/Scene/GlobeDepth.js | 96 +++++++------ .../engine/Source/Scene/PostProcessStage.js | 73 +++++----- packages/engine/Source/Scene/Scene.js | 126 +++++++++--------- packages/engine/Source/Scene/View.js | 63 ++++----- 4 files changed, 162 insertions(+), 196 deletions(-) diff --git a/packages/engine/Source/Scene/GlobeDepth.js b/packages/engine/Source/Scene/GlobeDepth.js index 562ec8ad0ccc..d7f683e20b71 100644 --- a/packages/engine/Source/Scene/GlobeDepth.js +++ b/packages/engine/Source/Scene/GlobeDepth.js @@ -83,20 +83,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 +107,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 +128,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, @@ -244,8 +237,7 @@ GlobeDepth.prototype.update = function ( hdr, clearGlobeDepth ) { - const width = viewport.width; - const height = viewport.height; + const { width, height } = viewport; const pixelDatatype = hdr ? context.halfFloatingPointTexture @@ -296,44 +288,44 @@ GlobeDepth.prototype.executeUpdateDepth = function ( ? depthTexture : passState.framebuffer.depthStencilTexture; if ( - clearGlobeDepth || - depthTextureToCopy !== this.colorFramebufferManager.getDepthStencilTexture() + !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. + if ( + !defined(this._updateDepthFramebuffer.framebuffer) || + this._updateDepthFramebuffer.getDepthStencilTexture() !== + depthTextureToCopy || + this._updateDepthFramebuffer.getColorTexture() !== + this._copyDepthFramebuffer.getColorTexture() + ) { + const colorTexture = this._copyDepthFramebuffer.getColorTexture(); + const { width, height } = colorTexture; + this._tempCopyDepthFramebuffer.destroy(); + this._tempCopyDepthFramebuffer.update(context, width, height); + + 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); }; GlobeDepth.prototype.executeCopyColor = function (context, passState) { @@ -355,7 +347,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 = this._copyColorCommand.shaderProgram.destroy(); diff --git a/packages/engine/Source/Scene/PostProcessStage.js b/packages/engine/Source/Scene/PostProcessStage.js index ab05093b5c67..6e4f29962da0 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); @@ -640,24 +642,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 +669,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 @@ -736,27 +729,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; @@ -773,8 +766,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; } @@ -797,13 +789,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)) { @@ -811,7 +799,7 @@ 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; @@ -833,16 +821,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); @@ -850,7 +837,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/Scene.js b/packages/engine/Source/Scene/Scene.js index 2191d9f1ff93..dc91a71069ec 100644 --- a/packages/engine/Source/Scene/Scene.js +++ b/packages/engine/Source/Scene/Scene.js @@ -1787,26 +1787,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); @@ -1820,36 +1819,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); } }; @@ -2821,44 +2817,46 @@ function executeOverlayCommands(scene, passState) { } 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(command, shadowMapCullingVolume) ) { - 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(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; } } } diff --git a/packages/engine/Source/Scene/View.js b/packages/engine/Source/Scene/View.js index 9c167ab5bf0b..9e5e9be3177e 100644 --- a/packages/engine/Source/Scene/View.js +++ b/packages/engine/Source/Scene/View.js @@ -122,8 +122,8 @@ View.prototype.checkForCameraUpdates = function (scene) { function updateFrustums(view, scene, near, far) { const frameState = scene.frameState; - const camera = frameState.camera; - const farToNearRatio = frameState.useLogDepth + const { camera, useLogDepth } = frameState; + const farToNearRatio = useLogDepth ? scene.logarithmicDepthFarToNearRatio : scene.farToNearRatio; const is2D = scene.mode === SceneMode.SCENE2D; @@ -153,7 +153,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; @@ -187,10 +187,9 @@ function insertIntoBin(view, scene, command, commandNear, commandFar) { 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; @@ -234,13 +233,11 @@ 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 { camera, commandList } = 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 +247,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; } } @@ -284,10 +279,9 @@ View.prototype.createPotentiallyVisibleSet = function (scene) { } 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 +291,14 @@ View.prototype.createPotentiallyVisibleSet = function (scene) { let commandNear; let commandFar; - const boundingVolume = command.boundingVolume; if (defined(boundingVolume)) { if (!scene.isVisible(command, cullingVolume, occluder)) { continue; } const nearFarInterval = boundingVolume.computePlaneDistances( - position, - direction, + positionWC, + directionWC, scratchNearFarInterval ); commandNear = nearFarInterval.start; @@ -334,13 +327,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,11 +350,8 @@ 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); + 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 @@ -373,18 +363,15 @@ View.prototype.createPotentiallyVisibleSet = function (scene) { updateFrustums(this, scene, near, far); - let c; - let ce; - - for (c = 0; c < commandExtentCount; c++) { - ce = commandExtents[c]; + for (let c = 0; c < commandExtentCount; c++) { + const ce = commandExtents[c]; insertIntoBin(this, scene, ce.command, ce.near, ce.far); } // Dereference old commands if (commandExtentCount < commandExtentCapacity) { - for (c = commandExtentCount; c < commandExtentCapacity; c++) { - ce = commandExtents[c]; + for (let c = commandExtentCount; c < commandExtentCapacity; c++) { + const ce = commandExtents[c]; if (!defined(ce.command)) { // If the command is undefined, it's assumed that all // subsequent commmands were set to undefined as well, @@ -421,10 +408,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(); } }; From faa07f7575485284f982cf3fd1a235f54a7d6d2c Mon Sep 17 00:00:00 2001 From: Jeshurun Hembd Date: Tue, 10 Sep 2024 13:02:39 -0400 Subject: [PATCH 04/15] Cleanup PostProcessStage, Scene, add docs in View --- .../engine/Source/Scene/PostProcessStage.js | 118 +++++++++--------- packages/engine/Source/Scene/Scene.js | 106 +++++++--------- packages/engine/Source/Scene/View.js | 116 +++++++++++------ .../Builtin/Functions/unpackDepth.glsl | 6 +- 4 files changed, 181 insertions(+), 165 deletions(-) diff --git a/packages/engine/Source/Scene/PostProcessStage.js b/packages/engine/Source/Scene/PostProcessStage.js index 6e4f29962da0..1b93e702417a 100644 --- a/packages/engine/Source/Scene/PostProcessStage.js +++ b/packages/engine/Source/Scene/PostProcessStage.js @@ -486,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 + ); } } @@ -545,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) && @@ -554,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, @@ -801,12 +800,7 @@ function createSelectedTexture(stage, context) { 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, diff --git a/packages/engine/Source/Scene/Scene.js b/packages/engine/Source/Scene/Scene.js index dc91a71069ec..2d0aab073b85 100644 --- a/packages/engine/Source/Scene/Scene.js +++ b/packages/engine/Source/Scene/Scene.js @@ -1718,8 +1718,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; @@ -2430,7 +2429,6 @@ function executeCommands(scene, passState) { const height2D = camera.position.z; // 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]; @@ -2460,8 +2458,7 @@ function executeCommands(scene, passState) { } uniformState.updatePass(Pass.GLOBE); - let commands = frustumCommands.commands[Pass.GLOBE]; - let length = frustumCommands.indices[Pass.GLOBE]; + let length; if (globeTranslucent) { globeTranslucencyState.executeGlobeCommands( @@ -2472,7 +2469,9 @@ function executeCommands(scene, passState) { passState ); } else { - for (j = 0; j < length; ++j) { + const commands = frustumCommands.commands[Pass.GLOBE]; + length = frustumCommands.indices[Pass.GLOBE]; + for (let j = 0; j < length; ++j) { executeCommand(commands[j], scene, context, passState); } } @@ -2485,7 +2484,7 @@ function executeCommands(scene, passState) { // Draw terrain classification if (!environmentState.renderTranslucentDepthForPick) { uniformState.updatePass(Pass.TERRAIN_CLASSIFICATION); - commands = frustumCommands.commands[Pass.TERRAIN_CLASSIFICATION]; + const commands = frustumCommands.commands[Pass.TERRAIN_CLASSIFICATION]; length = frustumCommands.indices[Pass.TERRAIN_CLASSIFICATION]; if (globeTranslucent) { @@ -2497,7 +2496,7 @@ function executeCommands(scene, passState) { passState ); } else { - for (j = 0; j < length; ++j) { + for (let j = 0; j < length; ++j) { executeCommand(commands[j], scene, context, passState); } } @@ -2519,9 +2518,9 @@ function executeCommands(scene, passState) { // Draw 3D Tiles uniformState.updatePass(Pass.CESIUM_3D_TILE); - commands = frustumCommands.commands[Pass.CESIUM_3D_TILE]; + const commands = frustumCommands.commands[Pass.CESIUM_3D_TILE]; length = frustumCommands.indices[Pass.CESIUM_3D_TILE]; - for (j = 0; j < length; ++j) { + for (let j = 0; j < length; ++j) { executeCommand(commands[j], scene, context, passState); } @@ -2541,10 +2540,10 @@ function executeCommands(scene, passState) { // Draw classifications. Modifies 3D Tiles color. if (!environmentState.renderTranslucentDepthForPick) { uniformState.updatePass(Pass.CESIUM_3D_TILE_CLASSIFICATION); - commands = + const commands = frustumCommands.commands[Pass.CESIUM_3D_TILE_CLASSIFICATION]; length = frustumCommands.indices[Pass.CESIUM_3D_TILE_CLASSIFICATION]; - for (j = 0; j < length; ++j) { + for (let j = 0; j < length; ++j) { executeCommand(commands[j], scene, context, passState); } } @@ -2589,9 +2588,9 @@ function executeCommands(scene, passState) { // Draw normally uniformState.updatePass(Pass.CESIUM_3D_TILE); - commands = frustumCommands.commands[Pass.CESIUM_3D_TILE]; + let commands = frustumCommands.commands[Pass.CESIUM_3D_TILE]; length = frustumCommands.indices[Pass.CESIUM_3D_TILE]; - for (j = 0; j < length; ++j) { + for (let j = 0; j < length; ++j) { executeCommand(commands[j], scene, context, passState); } @@ -2613,7 +2612,7 @@ function executeCommands(scene, passState) { ]; length = frustumCommands.indices[Pass.CESIUM_3D_TILE_CLASSIFICATION_IGNORE_SHOW]; - for (j = 0; j < length; ++j) { + for (let j = 0; j < length; ++j) { executeCommand(commands[j], scene, context, passState); } @@ -2635,7 +2634,7 @@ function executeCommands(scene, passState) { 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) { + for (let j = 0; j < length; ++j) { executeCommand(commands[j], scene, context, passState); } } @@ -2645,7 +2644,7 @@ function executeCommands(scene, passState) { } uniformState.updatePass(Pass.VOXELS); - commands = frustumCommands.commands[Pass.VOXELS]; + let commands = frustumCommands.commands[Pass.VOXELS]; length = frustumCommands.indices[Pass.VOXELS]; commands.length = length; executeVoxelCommands(scene, executeCommand, passState, commands); @@ -2653,7 +2652,7 @@ function executeCommands(scene, passState) { uniformState.updatePass(Pass.OPAQUE); commands = frustumCommands.commands[Pass.OPAQUE]; length = frustumCommands.indices[Pass.OPAQUE]; - for (j = 0; j < length; ++j) { + for (let j = 0; j < length; ++j) { executeCommand(commands[j], scene, context, passState); } @@ -2748,7 +2747,7 @@ function executeCommands(scene, passState) { passState ); } else { - for (j = 0; j < length; ++j) { + for (let j = 0; j < length; ++j) { executeIdCommand(commands[j], scene, context, passState); } } @@ -2766,21 +2765,21 @@ function executeCommands(scene, 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) { + for (let j = 0; j < length; ++j) { executeIdCommand(commands[j], scene, context, passState); } uniformState.updatePass(Pass.OPAQUE); commands = frustumCommands.commands[Pass.OPAQUE]; length = frustumCommands.indices[Pass.OPAQUE]; - for (j = 0; j < length; ++j) { + for (let j = 0; j < length; ++j) { executeIdCommand(commands[j], scene, context, passState); } uniformState.updatePass(Pass.TRANSLUCENT); commands = frustumCommands.commands[Pass.TRANSLUCENT]; length = frustumCommands.indices[Pass.TRANSLUCENT]; - for (j = 0; j < length; ++j) { + for (let j = 0; j < length; ++j) { executeIdCommand(commands[j], scene, context, passState); } @@ -2789,8 +2788,7 @@ function executeCommands(scene, passState) { } 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)) { @@ -2798,20 +2796,17 @@ 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); } } 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); } } @@ -2866,7 +2861,6 @@ function insertShadowCastCommands(scene, commandList, shadowMap) { function executeShadowMapCastCommands(scene) { const frameState = scene.frameState; const shadowMaps = frameState.shadowState.shadowMaps; - const shadowMapLength = shadowMaps.length; if (!frameState.shadowState.shadowsEnabled) { return; @@ -2875,7 +2869,7 @@ function executeShadowMapCastCommands(scene) { const context = scene.context; const uniformState = context.uniformState; - for (let i = 0; i < shadowMapLength; ++i) { + for (let i = 0; i < shadowMaps.length; ++i) { const shadowMap = shadowMaps[i]; if (shadowMap.outOfView) { continue; @@ -2883,22 +2877,21 @@ function executeShadowMapCastCommands(scene) { // Reset the command lists const passes = shadowMap.passes; - const numberOfPasses = passes.length; - for (let j = 0; j < numberOfPasses; ++j) { + 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; + const sceneCommands = frameState.commandList; insertShadowCastCommands(scene, sceneCommands, 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); @@ -3009,9 +3002,7 @@ const scratch2DViewportWindowCoords = new Cartesian3(); const scratch2DViewport = new BoundingRectangle(); function execute2DViewportCommands(scene, passState) { - const context = scene.context; - const frameState = scene.frameState; - const camera = scene.camera; + const { context, frameState, camera } = scene; const originalViewport = passState.viewport; const viewport = BoundingRectangle.clone(originalViewport, scratch2DViewport); @@ -3434,7 +3425,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; @@ -3539,7 +3530,7 @@ function updateAndClearFramebuffers(scene, passState, clearColor) { !picking && defined(passState.framebuffer) && scene.invertClassification); if (useInvertClassification) { let depthFramebuffer; - if (scene.frameState.invertClassificationColor.alpha === 1.0) { + if (frameState.invertClassificationColor.alpha === 1.0) { if (environmentState.useGlobeDepthFramebuffer) { depthFramebuffer = view.globeDepth.framebuffer; } @@ -3554,7 +3545,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( @@ -3644,7 +3635,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(); @@ -3658,9 +3649,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); } @@ -3925,8 +3914,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); @@ -3937,10 +3925,7 @@ function prePassesUpdate(scene) { } function postPassesUpdate(scene) { - const frameState = scene._frameState; - const primitives = scene.primitives; - primitives.postPassesUpdate(frameState); - + scene.primitives.postPassesUpdate(scene._frameState); RequestScheduler.update(); } @@ -3950,7 +3935,7 @@ 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; @@ -3972,13 +3957,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); } diff --git a/packages/engine/Source/Scene/View.js b/packages/engine/Source/Scene/View.js index 9e5e9be3177e..41915a87626d 100644 --- a/packages/engine/Source/Scene/View.js +++ b/packages/engine/Source/Scene/View.js @@ -73,16 +73,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 +105,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,8 +138,20 @@ 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 { frameState } = scene; const { camera, useLogDepth } = frameState; const farToNearRatio = useLogDepth ? scene.logarithmicDepthFarToNearRatio @@ -182,7 +212,19 @@ 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; } @@ -191,14 +233,12 @@ function insertIntoBin(view, scene, command, commandNear, commandFar) { 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; } @@ -216,13 +256,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); @@ -232,8 +272,8 @@ const scratchCullingVolume = new CullingVolume(); const scratchNearFarInterval = new Interval(); View.prototype.createPotentiallyVisibleSet = function (scene) { - const frameState = scene.frameState; - const { camera, commandList } = frameState; + const { frameState } = scene; + const { camera, commandList, shadowState } = frameState; const { positionWC, directionWC, frustum } = camera; const computeList = scene._computeCommandList; @@ -263,16 +303,16 @@ 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]; @@ -352,45 +392,39 @@ View.prototype.createPotentiallyVisibleSet = function (scene) { if (shadowsEnabled) { 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 - if (shadowsEnabled) { - frameState.shadowState.nearPlane = shadowNear; - frameState.shadowState.farPlane = shadowFar; - frameState.shadowState.closestObjectSize = shadowClosestObjectSize; + // Use the computed near and far for shadows + shadowState.nearPlane = shadowNear; + shadowState.farPlane = shadowFar; + shadowState.closestObjectSize = shadowClosestObjectSize; } updateFrustums(this, scene, near, far); for (let c = 0; c < commandExtentCount; c++) { - const ce = commandExtents[c]; - insertIntoBin(this, scene, ce.command, ce.near, ce.far); + insertIntoBin(this, scene, commandExtents[c]); } // Dereference old commands if (commandExtentCount < commandExtentCapacity) { for (let c = commandExtentCount; c < commandExtentCapacity; c++) { - const ce = commandExtents[c]; - if (!defined(ce.command)) { + 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; - if (j === numFrustums - 1) { - frustumSplits[j + 1] = frustumCommandsList[j].far; - } } + frustumSplits[numFrustums] = frustumCommandsList[numFrustums - 1].far; }; View.prototype.destroy = function () { 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)); - } +} From 2d6ea50717b5266f6ee0a2dc0bc6c5fb06dc6d7e Mon Sep 17 00:00:00 2001 From: Jeshurun Hembd Date: Wed, 28 Aug 2024 14:40:36 -0400 Subject: [PATCH 05/15] Edit docs related to depth buffers --- packages/engine/Source/Scene/Camera.js | 6 +-- packages/engine/Source/Scene/GlobeDepth.js | 3 ++ packages/engine/Source/Scene/PickDepth.js | 13 +++++++ packages/engine/Source/Scene/Picking.js | 7 ++-- packages/engine/Source/Scene/Scene.js | 38 +++++++++---------- packages/engine/Source/Scene/View.js | 10 +++++ .../Shaders/Builtin/Functions/packDepth.glsl | 4 +- 7 files changed, 52 insertions(+), 29 deletions(-) diff --git a/packages/engine/Source/Scene/Camera.js b/packages/engine/Source/Scene/Camera.js index eb000c7befa9..6ed0e6a6cab2 100644 --- a/packages/engine/Source/Scene/Camera.js +++ b/packages/engine/Source/Scene/Camera.js @@ -2521,10 +2521,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/GlobeDepth.js b/packages/engine/Source/Scene/GlobeDepth.js index d7f683e20b71..1617bbc9997b 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() { diff --git a/packages/engine/Source/Scene/PickDepth.js b/packages/engine/Source/Scene/PickDepth.js index 35f7338fad0e..a29d9fe86c8b 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() { @@ -72,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 e248e9632c33..4b2ca4660e6a 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/Scene.js b/packages/engine/Source/Scene/Scene.js index 2d0aab073b85..e6f1f1e809da 100644 --- a/packages/engine/Source/Scene/Scene.js +++ b/packages/engine/Source/Scene/Scene.js @@ -2484,8 +2484,6 @@ function executeCommands(scene, passState) { // Draw terrain classification if (!environmentState.renderTranslucentDepthForPick) { uniformState.updatePass(Pass.TERRAIN_CLASSIFICATION); - const commands = frustumCommands.commands[Pass.TERRAIN_CLASSIFICATION]; - length = frustumCommands.indices[Pass.TERRAIN_CLASSIFICATION]; if (globeTranslucent) { globeTranslucencyState.executeGlobeClassificationCommands( @@ -2496,6 +2494,8 @@ function executeCommands(scene, passState) { passState ); } else { + const commands = frustumCommands.commands[Pass.TERRAIN_CLASSIFICATION]; + length = frustumCommands.indices[Pass.TERRAIN_CLASSIFICATION]; for (let j = 0; j < length; ++j) { executeCommand(commands[j], scene, context, passState); } @@ -3169,10 +3169,8 @@ function executeCommandsInViewport( passState, backgroundColor ) { - const environmentState = scene._environmentState; const view = scene._view; - const renderTranslucentDepthForPick = - environmentState.renderTranslucentDepthForPick; + const { renderTranslucentDepthForPick } = scene._environmentState; if (!firstViewport) { scene.frameState.commandList.length = 0; @@ -3355,21 +3353,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; @@ -3378,28 +3376,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; } } diff --git a/packages/engine/Source/Scene/View.js b/packages/engine/Source/Scene/View.js index 41915a87626d..315fd0ed48bf 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; 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) { From ad274f8a0f3b0f35d74ace17b8ee19ec56ae68e2 Mon Sep 17 00:00:00 2001 From: Jeshurun Hembd Date: Wed, 28 Aug 2024 15:36:10 -0400 Subject: [PATCH 06/15] Fix spec failure --- packages/engine/Source/Scene/View.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/engine/Source/Scene/View.js b/packages/engine/Source/Scene/View.js index 315fd0ed48bf..b7fd9e20813d 100644 --- a/packages/engine/Source/Scene/View.js +++ b/packages/engine/Source/Scene/View.js @@ -433,8 +433,10 @@ View.prototype.createPotentiallyVisibleSet = function (scene) { frustumSplits.length = numFrustums + 1; for (let j = 0; j < numFrustums; ++j) { frustumSplits[j] = frustumCommandsList[j].near; + if (j === numFrustums - 1) { + frustumSplits[j + 1] = frustumCommandsList[j].far; + } } - frustumSplits[numFrustums] = frustumCommandsList[numFrustums - 1].far; }; View.prototype.destroy = function () { From e36f1cf0de07566d7bd5352bbbd60d5ab50bab4f Mon Sep 17 00:00:00 2001 From: Jeshurun Hembd Date: Wed, 28 Aug 2024 18:36:09 -0400 Subject: [PATCH 07/15] Simplify executeCommand signature in Scene --- .../engine/Source/Renderer/DrawCommand.js | 3 + .../Source/Scene/GlobeTranslucencyState.js | 10 +-- packages/engine/Source/Scene/OIT.js | 83 +++++-------------- packages/engine/Source/Scene/Scene.js | 71 +++++----------- .../Scene/TranslucentTileClassification.js | 26 +++--- .../Specs/Scene/GlobeTranslucencyStateSpec.js | 4 - .../Scene/SpecularEnvironmentCubeMapSpec.js | 27 +++--- .../TranslucentTileClassificationSpec.js | 4 +- 8 files changed, 72 insertions(+), 156 deletions(-) diff --git a/packages/engine/Source/Renderer/DrawCommand.js b/packages/engine/Source/Renderer/DrawCommand.js index 14d75d5c7ee5..ebe5ecf88bf6 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/Scene/GlobeTranslucencyState.js b/packages/engine/Source/Scene/GlobeTranslucencyState.js index 8e3f13ab66e4..acc180197c2f 100644 --- a/packages/engine/Source/Scene/GlobeTranslucencyState.js +++ b/packages/engine/Source/Scene/GlobeTranslucencyState.js @@ -966,7 +966,6 @@ function executeCommandsMatchingType( commandsLength, executeCommandFunction, scene, - context, passState, types ) { @@ -974,7 +973,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); } } } @@ -984,11 +983,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); } } @@ -1026,7 +1024,6 @@ GlobeTranslucencyState.prototype.executeGlobeCommands = function ( globeCommandsLength, executeCommandFunction, scene, - context, passState, opaqueTypes ); @@ -1061,7 +1058,6 @@ GlobeTranslucencyState.prototype.executeGlobeClassificationCommands = function ( classificationCommandsLength, executeCommandFunction, scene, - context, passState ); } @@ -1085,7 +1081,6 @@ GlobeTranslucencyState.prototype.executeGlobeClassificationCommands = function ( globeCommandsLength, executeCommandFunction, scene, - context, passState, depthOnlyTypes ); @@ -1105,7 +1100,6 @@ GlobeTranslucencyState.prototype.executeGlobeClassificationCommands = function ( classificationCommandsLength, executeCommandFunction, scene, - context, passState ); diff --git a/packages/engine/Source/Scene/OIT.js b/packages/engine/Source/Scene/OIT.js index 4f479831b49e..051d33994c90 100644 --- a/packages/engine/Source/Scene/OIT.js +++ b/packages/engine/Source/Scene/OIT.js @@ -710,10 +710,6 @@ function executeTranslucentCommandsSortedMultipass( commands, invertClassification ) { - let command; - let derivedCommand; - let j; - const { context, frameState } = scene; const { useLogDepth, shadowState } = frameState; const useHdr = scene._hdr; @@ -729,70 +725,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; @@ -828,39 +800,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/Scene.js b/packages/engine/Source/Scene/Scene.js index e6f1f1e809da..b45832a25124 100644 --- a/packages/engine/Source/Scene/Scene.js +++ b/packages/engine/Source/Scene/Scene.js @@ -2136,8 +2136,9 @@ function debugShowBoundingVolume(command, scene, passState, debugFramebuffer) { frameState.commandList = savedCommandList; } -function executeCommand(command, scene, context, passState, debugFramebuffer) { +function executeCommand(command, scene, passState, debugFramebuffer) { const frameState = scene._frameState; + const context = scene._context; if (defined(scene.debugCommandFilter) && !scene.debugCommandFilter(command)) { return; @@ -2251,22 +2252,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); } } @@ -2277,33 +2270,22 @@ 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; - 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) { + executeFunction(commands[i], scene, passState); } } @@ -2349,16 +2331,11 @@ function executeCommands(scene, passState) { if (!picking) { const skyBoxCommand = environmentState.skyBoxCommand; if (defined(skyBoxCommand)) { - executeCommand(skyBoxCommand, scene, context, passState); + executeCommand(skyBoxCommand, scene, passState); } if (environmentState.isSkyAtmosphereVisible) { - executeCommand( - environmentState.skyAtmosphereCommand, - scene, - context, - passState - ); + executeCommand(environmentState.skyAtmosphereCommand, scene, passState); } if (environmentState.isSunVisible) { @@ -2472,7 +2449,7 @@ function executeCommands(scene, passState) { const commands = frustumCommands.commands[Pass.GLOBE]; length = frustumCommands.indices[Pass.GLOBE]; for (let j = 0; j < length; ++j) { - executeCommand(commands[j], scene, context, passState); + executeCommand(commands[j], scene, passState); } } @@ -2497,7 +2474,7 @@ function executeCommands(scene, passState) { const commands = frustumCommands.commands[Pass.TERRAIN_CLASSIFICATION]; length = frustumCommands.indices[Pass.TERRAIN_CLASSIFICATION]; for (let j = 0; j < length; ++j) { - executeCommand(commands[j], scene, context, passState); + executeCommand(commands[j], scene, passState); } } } @@ -2521,7 +2498,7 @@ function executeCommands(scene, passState) { const commands = frustumCommands.commands[Pass.CESIUM_3D_TILE]; length = frustumCommands.indices[Pass.CESIUM_3D_TILE]; for (let j = 0; j < length; ++j) { - executeCommand(commands[j], scene, context, passState); + executeCommand(commands[j], scene, passState); } if (length > 0) { @@ -2544,7 +2521,7 @@ function executeCommands(scene, passState) { frustumCommands.commands[Pass.CESIUM_3D_TILE_CLASSIFICATION]; length = frustumCommands.indices[Pass.CESIUM_3D_TILE_CLASSIFICATION]; for (let j = 0; j < length; ++j) { - executeCommand(commands[j], scene, context, passState); + executeCommand(commands[j], scene, passState); } } } @@ -2591,7 +2568,7 @@ function executeCommands(scene, passState) { let commands = frustumCommands.commands[Pass.CESIUM_3D_TILE]; length = frustumCommands.indices[Pass.CESIUM_3D_TILE]; for (let j = 0; j < length; ++j) { - executeCommand(commands[j], scene, context, passState); + executeCommand(commands[j], scene, passState); } if (defined(globeDepth) && environmentState.useGlobeDepthFramebuffer) { @@ -2613,7 +2590,7 @@ function executeCommands(scene, passState) { length = frustumCommands.indices[Pass.CESIUM_3D_TILE_CLASSIFICATION_IGNORE_SHOW]; for (let j = 0; j < length; ++j) { - executeCommand(commands[j], scene, context, passState); + executeCommand(commands[j], scene, passState); } passState.framebuffer = opaqueClassificationFramebuffer; @@ -2635,7 +2612,7 @@ function executeCommands(scene, passState) { commands = frustumCommands.commands[Pass.CESIUM_3D_TILE_CLASSIFICATION]; length = frustumCommands.indices[Pass.CESIUM_3D_TILE_CLASSIFICATION]; for (let j = 0; j < length; ++j) { - executeCommand(commands[j], scene, context, passState); + executeCommand(commands[j], scene, passState); } } @@ -2653,7 +2630,7 @@ function executeCommands(scene, passState) { commands = frustumCommands.commands[Pass.OPAQUE]; length = frustumCommands.indices[Pass.OPAQUE]; for (let j = 0; j < length; ++j) { - executeCommand(commands[j], scene, context, passState); + executeCommand(commands[j], scene, passState); } if (index !== 0 && scene.mode !== SceneMode.SCENE2D) { @@ -2895,12 +2872,8 @@ function executeShadowMapCastCommands(scene) { // 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); } } } diff --git a/packages/engine/Source/Scene/TranslucentTileClassification.js b/packages/engine/Source/Scene/TranslucentTileClassification.js index b171fe76adff..56176416727c 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; @@ -412,7 +408,7 @@ TranslucentTileClassification.prototype.executeClassificationCommands = function } const context = scene.context; - const us = context.uniformState; + const uniformState = context.uniformState; const framebuffer = passState.framebuffer; if (this._frustumsDrawn === 2) { @@ -426,16 +422,16 @@ TranslucentTileClassification.prototype.executeClassificationCommands = function 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/Specs/Scene/GlobeTranslucencyStateSpec.js b/packages/engine/Specs/Scene/GlobeTranslucencyStateSpec.js index 61a65cd7420f..c5a55ddcb988 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); @@ -703,13 +701,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 23d3fe118284..500386e89389 100644 --- a/packages/engine/Specs/Scene/SpecularEnvironmentCubeMapSpec.js +++ b/packages/engine/Specs/Scene/SpecularEnvironmentCubeMapSpec.js @@ -212,7 +212,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; } @@ -221,22 +221,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 c10b5048d032..6f606483eeb4 100644 --- a/packages/engine/Specs/Scene/TranslucentTileClassificationSpec.js +++ b/packages/engine/Specs/Scene/TranslucentTileClassificationSpec.js @@ -282,8 +282,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 () { From 22bf29be7737a48c5206c727169f74bc840cfcc0 Mon Sep 17 00:00:00 2001 From: Jeshurun Hembd Date: Fri, 30 Aug 2024 12:29:42 -0400 Subject: [PATCH 08/15] Clarify docs and calculations for projection matrices --- packages/engine/Source/Core/Matrix4.js | 4 +- .../engine/Source/Core/PerspectiveFrustum.js | 94 ++++++++++--------- .../Core/PerspectiveOffCenterFrustum.js | 87 +++++++++-------- .../engine/Source/Renderer/UniformState.js | 3 + packages/engine/Source/Scene/Scene.js | 3 +- 5 files changed, 99 insertions(+), 92 deletions(-) diff --git a/packages/engine/Source/Core/Matrix4.js b/packages/engine/Source/Core/Matrix4.js index ecdaa3c0d001..8babc307afeb 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/PerspectiveFrustum.js b/packages/engine/Source/Core/PerspectiveFrustum.js index 0442961c533f..f588f8c7441c 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); + 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; + + 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 00339b199d60..2e6d7bf6a8b4 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/UniformState.js b/packages/engine/Source/Renderer/UniformState.js index eee7abd96771..9cf580df9af6 100644 --- a/packages/engine/Source/Renderer/UniformState.js +++ b/packages/engine/Source/Renderer/UniformState.js @@ -1362,7 +1362,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/Scene.js b/packages/engine/Source/Scene/Scene.js index b45832a25124..1b71da73dbfa 100644 --- a/packages/engine/Source/Scene/Scene.js +++ b/packages/engine/Source/Scene/Scene.js @@ -2316,7 +2316,6 @@ function executeCommands(scene, passState) { // 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; @@ -2396,7 +2395,7 @@ function executeCommands(scene, passState) { const useDepthPlane = environmentState.useDepthPlane; const globeTranslucencyState = scene._globeTranslucencyState; const globeTranslucent = globeTranslucencyState.translucent; - const globeTranslucencyFramebuffer = scene._view.globeTranslucencyFramebuffer; + const globeTranslucencyFramebuffer = view.globeTranslucencyFramebuffer; const clearDepth = scene._depthClearCommand; const clearStencil = scene._stencilClearCommand; const clearClassificationStencil = scene._classificationStencilClearCommand; From 05d98644f47a6dbe103197f90de87860536d6258 Mon Sep 17 00:00:00 2001 From: Jeshurun Hembd Date: Tue, 10 Sep 2024 13:26:34 -0400 Subject: [PATCH 09/15] Document more private functions in Scene --- packages/engine/Source/Renderer/Context.js | 2 +- packages/engine/Source/Scene/Scene.js | 591 ++++++++++++--------- 2 files changed, 327 insertions(+), 266 deletions(-) diff --git a/packages/engine/Source/Renderer/Context.js b/packages/engine/Source/Renderer/Context.js index 97bc02b60868..81632eff7a3b 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/Scene/Scene.js b/packages/engine/Source/Scene/Scene.js index 1b71da73dbfa..1d565ca4ee13 100644 --- a/packages/engine/Source/Scene/Scene.js +++ b/packages/engine/Source/Scene/Scene.js @@ -1811,6 +1811,7 @@ Scene.prototype.updateDerivedCommands = function (command) { const needsLogDepthDerivedCommands = useLogDepth && !hasLogDepthDerivedCommands; const needsHdrCommands = useHdr && !hasHdrCommands; + // TODO: if (!useLogDepth && !useHdr), why do we need derived commands? const needsDerivedCommands = (!useLogDepth || !useHdr) && !hasDerivedCommands; command.dirty = command.dirty || @@ -1866,31 +1867,36 @@ const requestRenderModeDeferCheckPassState = new Cesium3DTilePassState({ const scratchOccluderBoundingSphere = new BoundingSphere(); let scratchOccluder; - +/** + * Get the central body occluder for the scene. + * + * TODO: The occluder is the top-level globe. When we add + * support for multiple central bodies, this should be the closest one. + * + * @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; } /** @@ -1993,18 +1999,31 @@ Scene.prototype.updateFrameState = function () { }; /** + * Check whether a draw command will render anything visible in the current Scene, + * based on its bounding volume. + * + * @param {DrawCommand} [command] The draw command + * @param {CullingVolume} cullingVolume The culling volume of the current Scene. + * @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) { + 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) ); }; @@ -2031,9 +2050,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; @@ -2042,8 +2070,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); @@ -2052,63 +2078,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, - }) - ) + 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, + }) ); - - 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, - }), - asynchronous: false, - }); + modelMatrix = Matrix4.fromTranslation(center); } 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, - }) - ) + geometry = BoxGeometry.createGeometry( + BoxGeometry.fromDimensions({ + dimensions: new Cartesian3(2.0, 2.0, 2.0), + vertexFormat: PerInstanceColorAppearance.FLAT_VERTEX_FORMAT, + }) + ); + modelMatrix = Matrix4.fromRotationTranslation( + boundingVolume.halfAxes, + center, + new Matrix4() ); - - 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, - }), - asynchronous: false, - }); } + 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 = []); @@ -2136,6 +2143,16 @@ function debugShowBoundingVolume(command, scene, passState, debugFramebuffer) { frameState.commandList = savedCommandList; } +/** + * 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; @@ -2208,23 +2225,34 @@ function executeCommand(command, scene, 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); } } @@ -2294,6 +2322,14 @@ const scratchPerspectiveOffCenterFrustum = new PerspectiveOffCenterFrustum(); const scratchOrthographicFrustum = new OrthographicFrustum(); const scratchOrthographicOffCenterFrustum = new OrthographicOffCenterFrustum(); +/** + * 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; @@ -2312,57 +2348,33 @@ function executeCommands(scene, passState) { frustum = camera.frustum.clone(scratchOrthographicOffCenterFrustum); } - // 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.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; + // 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) { - const skyBoxCommand = environmentState.skyBoxCommand; - if (defined(skyBoxCommand)) { - executeCommand(skyBoxCommand, scene, passState); - } - - if (environmentState.isSkyAtmosphereVisible) { - executeCommand(environmentState.skyAtmosphereCommand, scene, 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); - } + executeEnvironmentCommands(scene, passState); } + const view = scene._view; + const { + clearGlobeDepth, + renderTranslucentDepthForPick, + useDepthPlane, + useGlobeDepthFramebuffer, + useInvertClassification, + useOIT, + usePostProcessSelected, + } = scene._environmentState; + // Determine how translucent surfaces will be handled. let executeTranslucentCommands; - if (environmentState.useOIT) { + if (useOIT) { if (!defined(scene._executeOITFunction)) { scene._executeOITFunction = function ( scene, @@ -2388,22 +2400,40 @@ function executeCommands(scene, passState) { executeTranslucentCommands = executeTranslucentCommandsFrontToBack; } - const frustumCommandsList = view.frustumCommandsList; + const { + globeDepth, + globeTranslucencyFramebuffer, + frustumCommandsList, + } = view; const numFrustums = frustumCommandsList.length; - const clearGlobeDepth = environmentState.clearGlobeDepth; - const useDepthPlane = environmentState.useDepthPlane; const globeTranslucencyState = scene._globeTranslucencyState; - const globeTranslucent = globeTranslucencyState.translucent; - const globeTranslucencyFramebuffer = 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 executePass(frustumCommands, passId) { + uniformState.updatePass(passId); + const commands = frustumCommands.commands[passId]; + const length = frustumCommands.indices[passId]; + for (let j = 0; j < length; ++j) { + executeCommand(commands[j], scene, passState); + } + return length; + } + + function executeIdPass(frustumCommands, passId) { + uniformState.updatePass(passId); + const commands = frustumCommands.commands[passId]; + const length = frustumCommands.indices[passId]; + for (let j = 0; j < length; ++j) { + executeIdCommand(commands[j], scene, passState); + } + } + // Execute commands in each frustum in back to front order for (let i = 0; i < numFrustums; ++i) { const index = numFrustums - i - 1; @@ -2433,10 +2463,10 @@ function executeCommands(scene, passState) { clearStencil.execute(context, passState); } - uniformState.updatePass(Pass.GLOBE); let length; - if (globeTranslucent) { + if (globeTranslucencyState.translucent) { + uniformState.updatePass(Pass.GLOBE); globeTranslucencyState.executeGlobeCommands( frustumCommands, executeCommand, @@ -2445,23 +2475,17 @@ function executeCommands(scene, passState) { passState ); } else { - const commands = frustumCommands.commands[Pass.GLOBE]; - length = frustumCommands.indices[Pass.GLOBE]; - for (let j = 0; j < length; ++j) { - executeCommand(commands[j], scene, passState); - } + length = executePass(frustumCommands, Pass.GLOBE); } - const globeDepth = view.globeDepth; - if (defined(globeDepth) && environmentState.useGlobeDepthFramebuffer) { + if (defined(globeDepth) && useGlobeDepthFramebuffer) { globeDepth.executeCopyDepth(context, passState); } // Draw terrain classification - if (!environmentState.renderTranslucentDepthForPick) { - uniformState.updatePass(Pass.TERRAIN_CLASSIFICATION); - - if (globeTranslucent) { + if (!renderTranslucentDepthForPick) { + if (globeTranslucencyState.translucent) { + uniformState.updatePass(Pass.TERRAIN_CLASSIFICATION); globeTranslucencyState.executeGlobeClassificationCommands( frustumCommands, executeCommand, @@ -2470,11 +2494,7 @@ function executeCommands(scene, passState) { passState ); } else { - const commands = frustumCommands.commands[Pass.TERRAIN_CLASSIFICATION]; - length = frustumCommands.indices[Pass.TERRAIN_CLASSIFICATION]; - for (let j = 0; j < length; ++j) { - executeCommand(commands[j], scene, passState); - } + length = executePass(frustumCommands, Pass.TERRAIN_CLASSIFICATION); } } @@ -2485,23 +2505,14 @@ function executeCommands(scene, passState) { } } - if ( - !environmentState.useInvertClassification || - picking || - environmentState.renderTranslucentDepthForPick - ) { + if (!useInvertClassification || picking || renderTranslucentDepthForPick) { // Common/fastest path. Draw 3D Tiles and classification normally. // Draw 3D Tiles - uniformState.updatePass(Pass.CESIUM_3D_TILE); - const commands = frustumCommands.commands[Pass.CESIUM_3D_TILE]; - length = frustumCommands.indices[Pass.CESIUM_3D_TILE]; - for (let j = 0; j < length; ++j) { - executeCommand(commands[j], scene, passState); - } + length = executePass(frustumCommands, Pass.CESIUM_3D_TILE); if (length > 0) { - if (defined(globeDepth) && environmentState.useGlobeDepthFramebuffer) { + if (defined(globeDepth) && useGlobeDepthFramebuffer) { // When clearGlobeDepth is true, executeUpdateDepth needs // a globe depth texture with resolved stencil bits. globeDepth.prepareColorTextures(context, clearGlobeDepth); @@ -2514,14 +2525,11 @@ function executeCommands(scene, passState) { } // Draw classifications. Modifies 3D Tiles color. - if (!environmentState.renderTranslucentDepthForPick) { - uniformState.updatePass(Pass.CESIUM_3D_TILE_CLASSIFICATION); - const commands = - frustumCommands.commands[Pass.CESIUM_3D_TILE_CLASSIFICATION]; - length = frustumCommands.indices[Pass.CESIUM_3D_TILE_CLASSIFICATION]; - for (let j = 0; j < length; ++j) { - executeCommand(commands[j], scene, passState); - } + if (!renderTranslucentDepthForPick) { + length = executePass( + frustumCommands, + Pass.CESIUM_3D_TILE_CLASSIFICATION + ); } } } else { @@ -2563,14 +2571,9 @@ function executeCommands(scene, passState) { passState.framebuffer = scene._invertClassification._fbo.framebuffer; // Draw normally - uniformState.updatePass(Pass.CESIUM_3D_TILE); - let commands = frustumCommands.commands[Pass.CESIUM_3D_TILE]; - length = frustumCommands.indices[Pass.CESIUM_3D_TILE]; - for (let j = 0; j < length; ++j) { - executeCommand(commands[j], scene, passState); - } + length = executePass(frustumCommands, Pass.CESIUM_3D_TILE); - if (defined(globeDepth) && environmentState.useGlobeDepthFramebuffer) { + if (defined(globeDepth) && useGlobeDepthFramebuffer) { scene._invertClassification.prepareTextures(context); globeDepth.executeUpdateDepth( context, @@ -2581,16 +2584,10 @@ function executeCommands(scene, passState) { } // 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 (let j = 0; j < length; ++j) { - executeCommand(commands[j], scene, passState); - } + length = executePass( + frustumCommands, + Pass.CESIUM_3D_TILE_CLASSIFICATION_IGNORE_SHOW + ); passState.framebuffer = opaqueClassificationFramebuffer; @@ -2607,12 +2604,7 @@ function executeCommands(scene, 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 (let j = 0; j < length; ++j) { - executeCommand(commands[j], scene, passState); - } + length = executePass(frustumCommands, Pass.CESIUM_3D_TILE_CLASSIFICATION); } if (length > 0 && context.stencilBuffer) { @@ -2621,16 +2613,10 @@ function executeCommands(scene, passState) { uniformState.updatePass(Pass.VOXELS); let commands = frustumCommands.commands[Pass.VOXELS]; - length = frustumCommands.indices[Pass.VOXELS]; - commands.length = length; + commands.length = frustumCommands.indices[Pass.VOXELS]; executeVoxelCommands(scene, executeCommand, passState, commands); - uniformState.updatePass(Pass.OPAQUE); - commands = frustumCommands.commands[Pass.OPAQUE]; - length = frustumCommands.indices[Pass.OPAQUE]; - for (let j = 0; j < length; ++j) { - executeCommand(commands[j], scene, passState); - } + length = executePass(frustumCommands, Pass.OPAQUE); if (index !== 0 && scene.mode !== SceneMode.SCENE2D) { // Do not overlap frustums in the translucent pass to avoid blending artifacts @@ -2641,7 +2627,7 @@ function executeCommands(scene, passState) { let invertClassification; if ( !picking && - environmentState.useInvertClassification && + useInvertClassification && frameState.invertClassificationColor.alpha < 1.0 ) { // Fullscreen pass to copy unclassified fragments when alpha < 1.0. @@ -2685,8 +2671,7 @@ function executeCommands(scene, passState) { if ( context.depthTexture && scene.useDepthPicking && - (environmentState.useGlobeDepthFramebuffer || - renderTranslucentDepthForPick) + (useGlobeDepthFramebuffer || renderTranslucentDepthForPick) ) { // PERFORMANCE_IDEA: Use MRT to avoid the extra copy. const depthStencilTexture = globeDepth.depthStencilTexture; @@ -2710,11 +2695,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, @@ -2723,9 +2705,7 @@ function executeCommands(scene, passState) { passState ); } else { - for (let j = 0; j < length; ++j) { - executeIdCommand(commands[j], scene, context, passState); - } + executeIdPass(frustumCommands, Pass.GLOBE); } if (clearGlobeDepth) { @@ -2738,28 +2718,55 @@ 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 (let j = 0; j < length; ++j) { - executeIdCommand(commands[j], scene, context, passState); - } + executeIdPass(frustumCommands, Pass.CESIUM_3D_TILE); + executeIdPass(frustumCommands, Pass.OPAQUE); + executeIdPass(frustumCommands, Pass.TRANSLUCENT); - uniformState.updatePass(Pass.OPAQUE); - commands = frustumCommands.commands[Pass.OPAQUE]; - length = frustumCommands.indices[Pass.OPAQUE]; - for (let 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 (let j = 0; j < length; ++j) { - executeIdCommand(commands[j], scene, context, passState); +/** + * Execute draw commands for the environment primitives. + * + * @param {Scene} scene The scene. + * @param {PassState} passState The render state for the pass. + * + * @private + */ +function executeEnvironmentCommands(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); } } @@ -2787,6 +2794,15 @@ function executeOverlayCommands(scene, 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 { shadowMapCullingVolume, isPointLight, passes } = shadowMap; const numberOfPasses = passes.length; @@ -2834,16 +2850,23 @@ function insertShadowCastCommands(scene, commandList, shadowMap) { } } +/** + * 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 { 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 < shadowMaps.length; ++i) { const shadowMap = shadowMaps[i]; @@ -2852,14 +2875,13 @@ function executeShadowMapCastCommands(scene) { } // Reset the command lists - const passes = shadowMap.passes; + 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 = 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 < passes.length; ++j) { const pass = shadowMap.passes[j]; @@ -2881,6 +2903,11 @@ function executeShadowMapCastCommands(scene) { const scratchEyeTranslation = new Cartesian3(); /** + * Update framebuffers as needed, and execute draw commands. + * + * @param {PassState} passState State specific to each render pass. + * @param {Color} backgroundColor + * * @private */ Scene.prototype.updateAndExecuteCommands = function ( @@ -2904,6 +2931,15 @@ Scene.prototype.updateAndExecuteCommands = function ( } }; +/** + * Execute the draw commands to render the scene into the stereo viewports of a WebVR application. + * + * @param {Scene} scene + * @param {PassState} passState + * @param {Color} backgroundColor + * + * @private + */ function executeWebVRCommands(scene, passState, backgroundColor) { const view = scene._view; const camera = view.camera; @@ -2973,8 +3009,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, frameState, camera } = scene; + const { frameState, camera } = scene; + const { uniformState } = scene.context; const originalViewport = passState.viewport; const viewport = BoundingRectangle.clone(originalViewport, scratch2DViewport); @@ -3045,7 +3090,7 @@ function execute2DViewportCommands(scene, passState) { camera.directionWC, camera.upWC ); - context.uniformState.update(frameState); + uniformState.update(frameState); executeCommandsInViewport(true, scene, passState); @@ -3061,7 +3106,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) { @@ -3075,7 +3120,7 @@ function execute2DViewportCommands(scene, passState) { camera.directionWC, camera.upWC ); - context.uniformState.update(frameState); + uniformState.update(frameState); executeCommandsInViewport(true, scene, passState); @@ -3092,7 +3137,7 @@ function execute2DViewportCommands(scene, passState) { camera.directionWC, camera.upWC ); - context.uniformState.update(frameState); + uniformState.update(frameState); executeCommandsInViewport(false, scene, passState); } else { @@ -3107,7 +3152,7 @@ function execute2DViewportCommands(scene, passState) { camera.directionWC, camera.upWC ); - context.uniformState.update(frameState); + uniformState.update(frameState); executeCommandsInViewport(true, scene, passState); @@ -3124,7 +3169,7 @@ function execute2DViewportCommands(scene, passState) { camera.directionWC, camera.upWC ); - context.uniformState.update(frameState); + uniformState.update(frameState); executeCommandsInViewport(false, scene, passState); } @@ -3135,6 +3180,17 @@ function execute2DViewportCommands(scene, passState) { passState.viewport = originalViewport; } +/** + * 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 + * @param {Color} backgroundColor + * + * @private + */ function executeCommandsInViewport( firstViewport, scene, @@ -3501,7 +3557,7 @@ function updateAndClearFramebuffers(scene, passState, clearColor) { if (useInvertClassification) { let depthFramebuffer; if (frameState.invertClassificationColor.alpha === 1.0) { - if (environmentState.useGlobeDepthFramebuffer) { + if (useGlobeDepthFramebuffer) { depthFramebuffer = view.globeDepth.framebuffer; } } @@ -3551,11 +3607,13 @@ Scene.prototype.resolveFramebuffers = function (passState) { 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; @@ -3565,7 +3623,7 @@ Scene.prototype.resolveFramebuffers = function (passState) { if (useOIT) { passState.framebuffer = usePostProcess ? sceneFramebuffer.framebuffer - : defaultFramebuffer; + : originalFramebuffer; view.oit.execute(context, passState); } @@ -3592,11 +3650,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); } }; @@ -3901,6 +3959,12 @@ function postPassesUpdate(scene) { const scratchBackgroundColor = new Color(); +/** + * Render the scene + * + * @param {Scene} scene + * @private + */ function render(scene) { const frameState = scene._frameState; @@ -4058,9 +4122,7 @@ Scene.prototype.render = function (time) { tryAndCatchError(this, prePassesUpdate); /** - * * Passes update. Add any passes here - * */ if (this.primitives.show) { tryAndCatchError(this, updateMostDetailedRayPicks); @@ -4080,14 +4142,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); From e250d7281e5b94743ae7bf074e3a8258fbdd6050 Mon Sep 17 00:00:00 2001 From: Jeshurun Hembd Date: Thu, 5 Sep 2024 11:46:53 -0400 Subject: [PATCH 10/15] Update Framebuffer to follow current CodingGuide --- .../engine/Source/Renderer/Framebuffer.js | 43 ++++++++----------- packages/engine/Source/Scene/Scene.js | 3 +- 2 files changed, 18 insertions(+), 28 deletions(-) diff --git a/packages/engine/Source/Renderer/Framebuffer.js b/packages/engine/Source/Renderer/Framebuffer.js index ad0caf4688b0..f85ee38eab6e 100644 --- a/packages/engine/Source/Renderer/Framebuffer.js +++ b/packages/engine/Source/Renderer/Framebuffer.js @@ -161,16 +161,10 @@ function Framebuffer(options) { this._bind(); - let texture; - let renderbuffer; - let i; - let length; - let attachmentEnum; - if (defined(options.colorTextures)) { const textures = options.colorTextures; - length = this._colorTextures.length = this._activeColorAttachments.length = - textures.length; + const length = (this._colorTextures.length = this._activeColorAttachments.length = + textures.length); //>>includeStart('debug', pragmas.debug); if (length > maximumColorAttachments) { @@ -180,8 +174,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)) { @@ -207,7 +201,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; @@ -216,8 +210,8 @@ function Framebuffer(options) { if (defined(options.colorRenderbuffers)) { const renderbuffers = options.colorRenderbuffers; - length = this._colorRenderbuffers.length = this._activeColorAttachments.length = - renderbuffers.length; + const length = (this._colorRenderbuffers.length = this._activeColorAttachments.length = + renderbuffers.length); //>>includeStart('debug', pragmas.debug); if (length > maximumColorAttachments) { @@ -227,9 +221,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; @@ -237,7 +231,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) { @@ -252,19 +246,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) { @@ -279,7 +273,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; } @@ -411,10 +405,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(); @@ -422,8 +414,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/Scene/Scene.js b/packages/engine/Source/Scene/Scene.js index 1d565ca4ee13..f5b046631edf 100644 --- a/packages/engine/Source/Scene/Scene.js +++ b/packages/engine/Source/Scene/Scene.js @@ -2674,9 +2674,8 @@ function executeCommands(scene, passState) { (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); } From 854907babbc420d83d5676212ab317b33ea96154 Mon Sep 17 00:00:00 2001 From: Jeshurun Hembd Date: Tue, 10 Sep 2024 13:30:31 -0400 Subject: [PATCH 11/15] Add docs, simplify signatures for depth buffers --- .../engine/Source/Renderer/Framebuffer.js | 16 ++++--- .../Source/Renderer/FramebufferManager.js | 28 ++++++----- .../Source/Renderer/MultisampleFramebuffer.js | 41 ++++++++++++---- packages/engine/Source/Scene/GlobeDepth.js | 45 ++++++++++++++---- packages/engine/Source/Scene/Scene.js | 47 +++++++------------ 5 files changed, 111 insertions(+), 66 deletions(-) diff --git a/packages/engine/Source/Renderer/Framebuffer.js b/packages/engine/Source/Renderer/Framebuffer.js index f85ee38eab6e..36f67ee3d312 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,8 +163,6 @@ function Framebuffer(options) { } //>>includeEnd('debug'); - /////////////////////////////////////////////////////////////////// - this._bind(); if (defined(options.colorTextures)) { diff --git a/packages/engine/Source/Renderer/FramebufferManager.js b/packages/engine/Source/Renderer/FramebufferManager.js index 5594895fa3db..bd67073f5eb8 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 183e1bf20a74..93a2083f7635 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/Scene/GlobeDepth.js b/packages/engine/Source/Scene/GlobeDepth.js index 1617bbc9997b..5b2508f5832f 100644 --- a/packages/engine/Source/Scene/GlobeDepth.js +++ b/packages/engine/Source/Scene/GlobeDepth.js @@ -232,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, @@ -263,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); @@ -281,17 +300,23 @@ 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 && + !this._clearGlobeDepth && depthTextureToCopy === this.colorFramebufferManager.getDepthStencilTexture() ) { // Fast path - the depth texture can be copied normally. @@ -308,11 +333,11 @@ GlobeDepth.prototype.executeUpdateDepth = function ( // 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(this._updateDepthFramebuffer.framebuffer) || - this._updateDepthFramebuffer.getDepthStencilTexture() !== - depthTextureToCopy || - this._updateDepthFramebuffer.getColorTexture() !== + !defined(updateDepthFramebuffer.framebuffer) || + updateDepthFramebuffer.getDepthStencilTexture() !== depthTextureToCopy || + updateDepthFramebuffer.getColorTexture() !== this._copyDepthFramebuffer.getColorTexture() ) { const colorTexture = this._copyDepthFramebuffer.getColorTexture(); @@ -320,9 +345,9 @@ GlobeDepth.prototype.executeUpdateDepth = function ( this._tempCopyDepthFramebuffer.destroy(); this._tempCopyDepthFramebuffer.update(context, width, height); - this._updateDepthFramebuffer.setColorTexture(colorTexture, 0); - this._updateDepthFramebuffer.setDepthStencilTexture(depthTextureToCopy); - this._updateDepthFramebuffer.update(context, width, height); + updateDepthFramebuffer.setColorTexture(colorTexture, 0); + updateDepthFramebuffer.setDepthStencilTexture(depthTextureToCopy); + updateDepthFramebuffer.update(context, width, height); updateCopyCommands(this, context, width, height, passState); } diff --git a/packages/engine/Source/Scene/Scene.js b/packages/engine/Source/Scene/Scene.js index f5b046631edf..12960a4d1d49 100644 --- a/packages/engine/Source/Scene/Scene.js +++ b/packages/engine/Source/Scene/Scene.js @@ -2478,7 +2478,7 @@ function executeCommands(scene, passState) { length = executePass(frustumCommands, Pass.GLOBE); } - if (defined(globeDepth) && useGlobeDepthFramebuffer) { + if (useGlobeDepthFramebuffer) { globeDepth.executeCopyDepth(context, passState); } @@ -2512,14 +2512,11 @@ function executeCommands(scene, passState) { length = executePass(frustumCommands, Pass.CESIUM_3D_TILE); if (length > 0) { - if (defined(globeDepth) && useGlobeDepthFramebuffer) { - // When clearGlobeDepth is true, executeUpdateDepth needs - // a globe depth texture with resolved stencil bits. + if (useGlobeDepthFramebuffer) { globeDepth.prepareColorTextures(context, clearGlobeDepth); globeDepth.executeUpdateDepth( context, passState, - clearGlobeDepth, globeDepth.depthStencilTexture ); } @@ -2573,12 +2570,11 @@ function executeCommands(scene, passState) { // Draw normally length = executePass(frustumCommands, Pass.CESIUM_3D_TILE); - if (defined(globeDepth) && useGlobeDepthFramebuffer) { + if (useGlobeDepthFramebuffer) { scene._invertClassification.prepareTextures(context); globeDepth.executeUpdateDepth( context, passState, - clearGlobeDepth, scene._invertClassification._fbo.getDepthStencilTexture() ); } @@ -2783,6 +2779,14 @@ function executeComputeCommands(scene) { } } +/** + * Execute the draw commands for overlays + * + * @param {Scene} scene + * @param {PassState} passState + * + * @private + */ function executeOverlayCommands(scene, passState) { scene.context.uniformState.updatePass(Pass.OVERLAY); @@ -2902,7 +2906,7 @@ function executeShadowMapCastCommands(scene) { const scratchEyeTranslation = new Cartesian3(); /** - * Update framebuffers as needed, and execute draw commands. + * Update and clear framebuffers, and execute draw commands. * * @param {PassState} passState State specific to each render pass. * @param {Color} backgroundColor @@ -2913,19 +2917,16 @@ 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); } }; @@ -2935,19 +2936,16 @@ Scene.prototype.updateAndExecuteCommands = function ( * * @param {Scene} scene * @param {PassState} passState - * @param {Color} backgroundColor * * @private */ -function executeWebVRCommands(scene, passState, backgroundColor) { +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); @@ -3186,16 +3184,10 @@ function execute2DViewportCommands(scene, passState) { * @param {boolean} firstViewport true if this is the first viewport rendered. * @param {Scene} scene * @param {PassState} passState - * @param {Color} backgroundColor * * @private */ -function executeCommandsInViewport( - firstViewport, - scene, - passState, - backgroundColor -) { +function executeCommandsInViewport(firstViewport, scene, passState) { const view = scene._view; const { renderTranslucentDepthForPick } = scene._environmentState; @@ -3208,9 +3200,6 @@ function executeCommandsInViewport( view.createPotentiallyVisibleSet(scene); if (firstViewport) { - if (defined(backgroundColor)) { - updateAndClearFramebuffers(scene, passState, backgroundColor); - } executeComputeCommands(scene); if (!renderTranslucentDepthForPick) { executeShadowMapCastCommands(scene); From 611bcca4e20c00f0df7db942e9c7a6022b85e78a Mon Sep 17 00:00:00 2001 From: Jeshurun Hembd Date: Tue, 10 Sep 2024 20:01:21 -0400 Subject: [PATCH 12/15] Incorporate ideas from PR 12058 --- .../Source/Scene/GlobeTranslucencyState.js | 10 +- packages/engine/Source/Scene/Scene.js | 329 +++++++++++------- 2 files changed, 214 insertions(+), 125 deletions(-) diff --git a/packages/engine/Source/Scene/GlobeTranslucencyState.js b/packages/engine/Source/Scene/GlobeTranslucencyState.js index acc180197c2f..b855ae167092 100644 --- a/packages/engine/Source/Scene/GlobeTranslucencyState.js +++ b/packages/engine/Source/Scene/GlobeTranslucencyState.js @@ -1036,7 +1036,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 = @@ -1069,7 +1071,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 @@ -1091,7 +1093,7 @@ GlobeTranslucencyState.prototype.executeGlobeClassificationCommands = function ( context, passState ); - context.uniformState.globeDepthTexture = packedDepthTexture; + uniformState.globeDepthTexture = packedDepthTexture; } // Render classification on translucent faces @@ -1104,7 +1106,7 @@ GlobeTranslucencyState.prototype.executeGlobeClassificationCommands = function ( ); // Unset temporary state - context.uniformState.globeDepthTexture = originalGlobeDepthTexture; + uniformState.globeDepthTexture = originalGlobeDepthTexture; passState.framebuffer = originalFramebuffer; }; diff --git a/packages/engine/Source/Scene/Scene.js b/packages/engine/Source/Scene/Scene.js index 12960a4d1d49..7e3c71c95607 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); @@ -2309,11 +2315,25 @@ function executeTranslucentCommandsFrontToBack( } } -function executeVoxelCommands(scene, executeFunction, passState, commands) { +/** + * 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); for (let i = 0; i < commands.length; ++i) { - executeFunction(commands[i], scene, passState); + executeCommand(commands[i], scene, passState); } } @@ -2321,6 +2341,148 @@ const scratchPerspectiveFrustum = new PerspectiveFrustum(); const scratchPerspectiveOffCenterFrustum = new PerspectiveOffCenterFrustum(); const scratchOrthographicFrustum = new OrthographicFrustum(); const scratchOrthographicOffCenterFrustum = new OrthographicOffCenterFrustum(); +/** + * 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); + } + 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. + * + * 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, + passState, + commands, + invertClassification + ) { + view.globeDepth.prepareColorTextures(context); + view.oit.executeCommands( + scene, + executeFunction, + passState, + commands, + invertClassification + ); + }; + } + 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 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. @@ -2336,18 +2498,7 @@ function executeCommands(scene, passState) { 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); - } - + const frustum = createWorkingFrustum(camera); frustum.near = camera.frustum.near; frustum.far = camera.frustum.far; @@ -2358,53 +2509,24 @@ function executeCommands(scene, passState) { // 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) { - executeEnvironmentCommands(scene, passState); + renderEnvironment(scene, passState); } - const view = scene._view; const { clearGlobeDepth, renderTranslucentDepthForPick, useDepthPlane, useGlobeDepthFramebuffer, useInvertClassification, - useOIT, usePostProcessSelected, } = scene._environmentState; - // Determine how translucent surfaces will be handled. - let executeTranslucentCommands; - if (useOIT) { - if (!defined(scene._executeOITFunction)) { - scene._executeOITFunction = function ( - scene, - executeFunction, - passState, - commands, - invertClassification - ) { - view.globeDepth.prepareColorTextures(context); - view.oit.executeCommands( - scene, - executeFunction, - passState, - commands, - invertClassification - ); - }; - } - executeTranslucentCommands = scene._executeOITFunction; - } else if (passes.render) { - executeTranslucentCommands = executeTranslucentCommandsBackToFront; - } else { - executeTranslucentCommands = executeTranslucentCommandsFrontToBack; - } - const { globeDepth, globeTranslucencyFramebuffer, + sceneFramebuffer, frustumCommandsList, - } = view; + } = scene._view; const numFrustums = frustumCommandsList.length; const globeTranslucencyState = scene._globeTranslucencyState; @@ -2415,21 +2537,21 @@ function executeCommands(scene, passState) { const height2D = camera.position.z; - function executePass(frustumCommands, passId) { + function performPass(frustumCommands, passId) { uniformState.updatePass(passId); const commands = frustumCommands.commands[passId]; - const length = frustumCommands.indices[passId]; - for (let j = 0; j < length; ++j) { + const commandCount = frustumCommands.indices[passId]; + for (let j = 0; j < commandCount; ++j) { executeCommand(commands[j], scene, passState); } - return length; + return commandCount; } - function executeIdPass(frustumCommands, passId) { + function performIdPass(frustumCommands, passId) { uniformState.updatePass(passId); const commands = frustumCommands.commands[passId]; - const length = frustumCommands.indices[passId]; - for (let j = 0; j < length; ++j) { + const commandCount = frustumCommands.indices[passId]; + for (let j = 0; j < commandCount; ++j) { executeIdCommand(commands[j], scene, passState); } } @@ -2463,8 +2585,6 @@ function executeCommands(scene, passState) { clearStencil.execute(context, passState); } - let length; - if (globeTranslucencyState.translucent) { uniformState.updatePass(Pass.GLOBE); globeTranslucencyState.executeGlobeCommands( @@ -2475,7 +2595,7 @@ function executeCommands(scene, passState) { passState ); } else { - length = executePass(frustumCommands, Pass.GLOBE); + performPass(frustumCommands, Pass.GLOBE); } if (useGlobeDepthFramebuffer) { @@ -2494,7 +2614,7 @@ function executeCommands(scene, passState) { passState ); } else { - length = executePass(frustumCommands, Pass.TERRAIN_CLASSIFICATION); + performPass(frustumCommands, Pass.TERRAIN_CLASSIFICATION); } } @@ -2505,13 +2625,14 @@ function executeCommands(scene, passState) { } } + let commandCount; if (!useInvertClassification || picking || renderTranslucentDepthForPick) { // Common/fastest path. Draw 3D Tiles and classification normally. // Draw 3D Tiles - length = executePass(frustumCommands, Pass.CESIUM_3D_TILE); + commandCount = performPass(frustumCommands, Pass.CESIUM_3D_TILE); - if (length > 0) { + if (commandCount > 0) { if (useGlobeDepthFramebuffer) { globeDepth.prepareColorTextures(context, clearGlobeDepth); globeDepth.executeUpdateDepth( @@ -2523,7 +2644,7 @@ function executeCommands(scene, passState) { // Draw classifications. Modifies 3D Tiles color. if (!renderTranslucentDepthForPick) { - length = executePass( + commandCount = performPass( frustumCommands, Pass.CESIUM_3D_TILE_CLASSIFICATION ); @@ -2568,7 +2689,7 @@ function executeCommands(scene, passState) { passState.framebuffer = scene._invertClassification._fbo.framebuffer; // Draw normally - length = executePass(frustumCommands, Pass.CESIUM_3D_TILE); + commandCount = performPass(frustumCommands, Pass.CESIUM_3D_TILE); if (useGlobeDepthFramebuffer) { scene._invertClassification.prepareTextures(context); @@ -2580,7 +2701,7 @@ function executeCommands(scene, passState) { } // Set stencil - length = executePass( + commandCount = performPass( frustumCommands, Pass.CESIUM_3D_TILE_CLASSIFICATION_IGNORE_SHOW ); @@ -2595,24 +2716,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. - length = executePass(frustumCommands, Pass.CESIUM_3D_TILE_CLASSIFICATION); + 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); - let commands = frustumCommands.commands[Pass.VOXELS]; - commands.length = frustumCommands.indices[Pass.VOXELS]; - executeVoxelCommands(scene, executeCommand, passState, commands); + performVoxelsPass(scene, passState, frustumCommands); - length = executePass(frustumCommands, Pass.OPAQUE); + performPass(frustumCommands, Pass.OPAQUE); if (index !== 0 && scene.mode !== SceneMode.SCENE2D) { // Do not overlap frustums in the translucent pass to avoid blending artifacts @@ -2620,49 +2741,9 @@ function executeCommands(scene, passState) { uniformState.updateFrustum(frustum); } - let invertClassification; - if ( - !picking && - 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 && @@ -2680,7 +2761,7 @@ function executeCommands(scene, passState) { } const originalFramebuffer = passState.framebuffer; - passState.framebuffer = view.sceneFramebuffer.getIdFramebuffer(); + passState.framebuffer = sceneFramebuffer.getIdFramebuffer(); // reset frustum frustum.near = @@ -2700,7 +2781,7 @@ function executeCommands(scene, passState) { passState ); } else { - executeIdPass(frustumCommands, Pass.GLOBE); + performIdPass(frustumCommands, Pass.GLOBE); } if (clearGlobeDepth) { @@ -2713,23 +2794,23 @@ function executeCommands(scene, passState) { depthPlane.execute(context, passState); } - executeIdPass(frustumCommands, Pass.CESIUM_3D_TILE); - executeIdPass(frustumCommands, Pass.OPAQUE); - executeIdPass(frustumCommands, Pass.TRANSLUCENT); + performIdPass(frustumCommands, Pass.CESIUM_3D_TILE); + performIdPass(frustumCommands, Pass.OPAQUE); + performIdPass(frustumCommands, Pass.TRANSLUCENT); passState.framebuffer = originalFramebuffer; } } /** - * Execute draw commands for the environment primitives. + * Render the sky, atmosphere, sun, and moon * * @param {Scene} scene The scene. * @param {PassState} passState The render state for the pass. * * @private */ -function executeEnvironmentCommands(scene, passState) { +function renderEnvironment(scene, passState) { const { context, environmentState, view } = scene; context.uniformState.updatePass(Pass.ENVIRONMENT); @@ -2765,6 +2846,13 @@ function executeEnvironmentCommands(scene, passState) { } } +/** + * Execute compute commands from the scene's environment state and computeCommandList + * + * @param {Scene} scene + * + * @private + */ function executeComputeCommands(scene) { scene.context.uniformState.updatePass(Pass.COMPUTE); @@ -3590,7 +3678,7 @@ 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); } @@ -3615,7 +3703,6 @@ Scene.prototype.resolveFramebuffers = function (passState) { view.oit.execute(context, passState); } - const translucentTileClassification = view.translucentTileClassification; if ( translucentTileClassification.hasTranslucentDepth && translucentTileClassification.isSupported() From 03501526d7bbe1db6cf14d37238a7af99d0e715a Mon Sep 17 00:00:00 2001 From: Jeshurun Hembd Date: Thu, 12 Sep 2024 14:13:29 -0400 Subject: [PATCH 13/15] Address TODO comment --- packages/engine/Source/Scene/Scene.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/engine/Source/Scene/Scene.js b/packages/engine/Source/Scene/Scene.js index 7e3c71c95607..1d7c58cdd45a 100644 --- a/packages/engine/Source/Scene/Scene.js +++ b/packages/engine/Source/Scene/Scene.js @@ -1817,7 +1817,7 @@ Scene.prototype.updateDerivedCommands = function (command) { const needsLogDepthDerivedCommands = useLogDepth && !hasLogDepthDerivedCommands; const needsHdrCommands = useHdr && !hasHdrCommands; - // TODO: if (!useLogDepth && !useHdr), why do we need derived commands? + // PERFORMANCE_IDEA: if (!useLogDepth && !useHdr), we may be able to save a call to updateDerivedCommands. const needsDerivedCommands = (!useLogDepth || !useHdr) && !hasDerivedCommands; command.dirty = command.dirty || From 556c76769c7adfbb50ad2d29f0ff2b4a8ab9da02 Mon Sep 17 00:00:00 2001 From: Jeshurun Hembd Date: Thu, 26 Sep 2024 22:59:10 -0400 Subject: [PATCH 14/15] PR feedback --- packages/engine/Source/Core/Occluder.js | 2 - .../engine/Source/Core/PerspectiveFrustum.js | 20 ++++---- .../engine/Source/Scene/DerivedCommand.js | 48 ++++++++++--------- packages/engine/Source/Scene/Scene.js | 16 +++---- packages/engine/Source/Scene/View.js | 2 +- .../TranslucentTileClassificationSpec.js | 8 ++-- 6 files changed, 48 insertions(+), 48 deletions(-) diff --git a/packages/engine/Source/Core/Occluder.js b/packages/engine/Source/Core/Occluder.js index 991502086533..3ae77aa49649 100644 --- a/packages/engine/Source/Core/Occluder.js +++ b/packages/engine/Source/Core/Occluder.js @@ -301,8 +301,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 f588f8c7441c..5b55e25dac3c 100644 --- a/packages/engine/Source/Core/PerspectiveFrustum.js +++ b/packages/engine/Source/Core/PerspectiveFrustum.js @@ -180,18 +180,18 @@ function update(frustum) { } //>>includeStart('debug', pragmas.debug); - if (frustum.fov < 0 || frustum.fov >= Math.PI) { - throw new DeveloperError("fov must be in the range [0, PI)."); - } + Check.typeOf.number.greaterThanOrEquals("fov", frustum.fov, 0.0); + Check.typeOf.number.lessThan("fov", frustum.fov, Math.PI); - if (frustum.aspectRatio < 0) { - throw new DeveloperError("aspectRatio must be positive."); - } + Check.typeOf.number.greaterThanOrEquals( + "aspectRatio", + frustum.aspectRatio, + 0.0 + ); - if (frustum.near < 0 || frustum.near > frustum.far) { - throw new DeveloperError( - "near must be greater than zero and less than far." - ); + 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'); diff --git a/packages/engine/Source/Scene/DerivedCommand.js b/packages/engine/Source/Scene/DerivedCommand.js index 356d9d3f77f6..38ddc8582fb0 100644 --- a/packages/engine/Source/Scene/DerivedCommand.js +++ b/packages/engine/Source/Scene/DerivedCommand.js @@ -34,18 +34,21 @@ function getDepthOnlyShaderProgram(context, shaderProgram) { const usesLogDepth = fs.defines.indexOf("LOG_DEPTH") >= 0; if (!writesDepthOrDiscards && !usesLogDepth) { - const source = - "void main() \n" + "{ \n" + " out_FragColor = vec4(1.0); \n" + "} \n"; + const source = `void main() +{ + out_FragColor = vec4(1.0); +} +`; fs = new ShaderSource({ sources: [source], }); } else if (!writesDepthOrDiscards && usesLogDepth) { - const source = - "void main() \n" + - "{ \n" + - " out_FragColor = vec4(1.0); \n" + - " czm_writeLogDepth(); \n" + - "} \n"; + const source = `void main() +{ + out_FragColor = vec4(1.0); + czm_writeLogDepth(); +} +`; fs = new ShaderSource({ defines: ["LOG_DEPTH"], sources: [source], @@ -169,13 +172,14 @@ function getLogDepthShaderProgram(context, shaderProgram) { sources[i] = ShaderSource.replaceMain(sources[i], "czm_log_depth_main"); } - const logMain = - "\n\n" + - "void main() \n" + - "{ \n" + - " czm_log_depth_main(); \n" + - " czm_vertexLogDepth(); \n" + - "} \n"; + const logMain = ` + +void main() +{ + czm_log_depth_main(); + czm_vertexLogDepth(); +} +`; sources.push(logMain); } @@ -199,13 +203,13 @@ function getLogDepthShaderProgram(context, shaderProgram) { sources[i] = ShaderSource.replaceMain(sources[i], "czm_log_depth_main"); } - logSource += - "\n" + - "void main() \n" + - "{ \n" + - " czm_log_depth_main(); \n" + - " czm_writeLogDepth(); \n" + - "} \n"; + logSource = ` +void main() +{ + czm_log_depth_main(); + czm_writeLogDepth(); +} +`; } sources.push(logSource); diff --git a/packages/engine/Source/Scene/Scene.js b/packages/engine/Source/Scene/Scene.js index 1d7c58cdd45a..4539e90e1825 100644 --- a/packages/engine/Source/Scene/Scene.js +++ b/packages/engine/Source/Scene/Scene.js @@ -1875,9 +1875,7 @@ const scratchOccluderBoundingSphere = new BoundingSphere(); let scratchOccluder; /** * Get the central body occluder for the scene. - * - * TODO: The occluder is the top-level globe. When we add - * support for multiple central bodies, this should be the closest one. + * Assumes only one central body occluder, the top-level globe. * * @param {Scene} scene * @returns {Occluder|undefined} @@ -2008,14 +2006,14 @@ Scene.prototype.updateFrameState = function () { * Check whether a draw command will render anything visible in the current Scene, * based on its bounding volume. * - * @param {DrawCommand} [command] The draw command * @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; } @@ -2912,7 +2910,7 @@ function insertShadowCastCommands(scene, commandList, shadowMap) { if ( !command.castShadows || shadowedPasses.indexOf(command.pass) < 0 || - !scene.isVisible(command, shadowMapCullingVolume) + !scene.isVisible(shadowMapCullingVolume, command) ) { continue; } @@ -2928,7 +2926,7 @@ function insertShadowCastCommands(scene, commandList, shadowMap) { // 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)) { + if (scene.isVisible(cascadeVolume, command)) { passes[j].commandList.push(command); wasVisible = true; } else if (wasVisible) { @@ -3408,13 +3406,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 ); diff --git a/packages/engine/Source/Scene/View.js b/packages/engine/Source/Scene/View.js index b7fd9e20813d..bb7bcfbcbf7f 100644 --- a/packages/engine/Source/Scene/View.js +++ b/packages/engine/Source/Scene/View.js @@ -342,7 +342,7 @@ View.prototype.createPotentiallyVisibleSet = function (scene) { let commandFar; if (defined(boundingVolume)) { - if (!scene.isVisible(command, cullingVolume, occluder)) { + if (!scene.isVisible(cullingVolume, command, occluder)) { continue; } diff --git a/packages/engine/Specs/Scene/TranslucentTileClassificationSpec.js b/packages/engine/Specs/Scene/TranslucentTileClassificationSpec.js index 6f606483eeb4..36ea7dcf2459 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,7 +283,7 @@ describe( } function executeCommand(command, scene, passState) { - command.execute(scene._context, passState); + command.execute(scene.context, passState); } it("draws translucent commands into a buffer for depth", function () { From a5e00590d0c66196e0d4692b04b04563f090b592 Mon Sep 17 00:00:00 2001 From: Jeshurun Hembd Date: Thu, 26 Sep 2024 23:02:10 -0400 Subject: [PATCH 15/15] Remove PERFORMANCE_IDEA comment in Scene --- packages/engine/Source/Scene/Scene.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/engine/Source/Scene/Scene.js b/packages/engine/Source/Scene/Scene.js index 4539e90e1825..7dbc48eb1d07 100644 --- a/packages/engine/Source/Scene/Scene.js +++ b/packages/engine/Source/Scene/Scene.js @@ -1817,7 +1817,6 @@ Scene.prototype.updateDerivedCommands = function (command) { const needsLogDepthDerivedCommands = useLogDepth && !hasLogDepthDerivedCommands; const needsHdrCommands = useHdr && !hasHdrCommands; - // PERFORMANCE_IDEA: if (!useLogDepth && !useHdr), we may be able to save a call to updateDerivedCommands. const needsDerivedCommands = (!useLogDepth || !useHdr) && !hasDerivedCommands; command.dirty = command.dirty ||