From 5ec99b2d6e5aa3b126c7b1b8511a5e0e2cd4eb9d Mon Sep 17 00:00:00 2001 From: jjspace <8007967+jjspace@users.noreply.github.com> Date: Thu, 10 Oct 2024 12:01:00 -0400 Subject: [PATCH] expose new scalar to change the visual density of fog --- Apps/Sandcastle/gallery/Atmosphere.html | 2 + Apps/Sandcastle/gallery/development/Fog.html | 196 ++++++++++++++---- packages/engine/Source/Core/Math.js | 2 + .../Source/Renderer/AutomaticUniforms.js | 14 ++ .../engine/Source/Renderer/UniformState.js | 13 ++ packages/engine/Source/Scene/Fog.js | 9 + packages/engine/Source/Scene/FrameState.js | 8 +- .../Scene/Model/AtmospherePipelineStage.js | 15 +- .../Source/Scene/Model/ModelSceneGraph.js | 1 + packages/engine/Source/Shaders/GlobeFS.glsl | 3 +- .../Shaders/Model/AtmosphereStageFS.glsl | 5 +- 11 files changed, 214 insertions(+), 54 deletions(-) diff --git a/Apps/Sandcastle/gallery/Atmosphere.html b/Apps/Sandcastle/gallery/Atmosphere.html index 1f2741655f2..301fc9119c1 100644 --- a/Apps/Sandcastle/gallery/Atmosphere.html +++ b/Apps/Sandcastle/gallery/Atmosphere.html @@ -25,6 +25,8 @@ background: rgba(42, 42, 42, 0.8); padding: 4px; border-radius: 4px; + max-height: 80%; + overflow-y: auto; } #toolbar input { vertical-align: middle; diff --git a/Apps/Sandcastle/gallery/development/Fog.html b/Apps/Sandcastle/gallery/development/Fog.html index dbd30f10b64..6b427b22a50 100644 --- a/Apps/Sandcastle/gallery/development/Fog.html +++ b/Apps/Sandcastle/gallery/development/Fog.html @@ -21,6 +21,22 @@

Loading...

@@ -33,7 +49,60 @@ density - + + + + + + + visual density scalar + + + + + + + Fog min brightness + + + + sse increase factor @@ -57,7 +126,9 @@ const viewModel = { enabled: true, density: 0, + visualDensityScalar: 0, sse: 0, + minimumBrightness: 0, }; // Convert the viewModel members into knockout observables. Cesium.knockout.track(viewModel); @@ -78,60 +149,105 @@ viewer.scene.fog.density = newValue; }); + Cesium.knockout + .getObservable(viewModel, "visualDensityScalar") + .subscribe(function (newValue) { + viewer.scene.fog.visualDensityScalar = newValue; + }); + + Cesium.knockout + .getObservable(viewModel, "minimumBrightness") + .subscribe(function (newValue) { + viewer.scene.fog.minimumBrightness = newValue; + }); Cesium.knockout.getObservable(viewModel, "sse").subscribe(function (newValue) { viewer.scene.fog.screenSpaceErrorFactor = newValue; }); viewModel.enabled = viewer.scene.fog.enabled; viewModel.density = viewer.scene.fog.density; + viewModel.visualDensityScalar = viewer.scene.fog.visualDensityScalar; viewModel.sse = viewer.scene.fog.screenSpaceErrorFactor; + viewModel.minimumBrightness = viewer.scene.fog.minimumBrightness; - Sandcastle.addToolbarButton("Horizon high altitude", function () { - viewer.camera.setView({ - destination: new Cesium.Cartesian3( - -2467730.5740817646, - -4390507.315824514, - 3906155.113316938, - ), - orientation: { - heading: 4.492211521856625, - pitch: -0.2687139437696304, - }, - }); + Sandcastle.addToolbarButton( + "Horizon high altitude", + function () { + viewer.camera.setView({ + destination: new Cesium.Cartesian3( + -2467730.5740817646, + -4390507.315824514, + 3906155.113316938, + ), + orientation: { + heading: 4.492211521856625, + pitch: -0.2687139437696304, + }, + }); + }, + "zoomButtons", + ); + // default to the high altitude camera + viewer.camera.setView({ + destination: new Cesium.Cartesian3( + -2467730.5740817646, + -4390507.315824514, + 3906155.113316938, + ), + orientation: { + heading: 4.492211521856625, + pitch: -0.2687139437696304, + }, }); - Sandcastle.addToolbarButton("Horizon low altitude", function () { - viewer.camera.setView({ - destination: new Cesium.Cartesian3( - -734001.9511656855, - -4214090.596769834, - 4715898.125886317, - ), - orientation: { - heading: 5.634257362559497, - pitch: -0.019548505785381032, - }, - }); - }); + Sandcastle.addToolbarButton( + "Horizon low altitude", + function () { + viewer.camera.setView({ + destination: new Cesium.Cartesian3( + -734001.9511656855, + -4214090.596769834, + 4715898.125886317, + ), + orientation: { + heading: 5.634257362559497, + pitch: -0.019548505785381032, + }, + }); + }, + "zoomButtons", + ); viewer.scene.globe._surface._debug.enableDebugOutput = true; - Sandcastle.addToolbarButton("Snap", function () { - const container = document.getElementById("cesiumContainer"); - const tmpH = container.style.height; - const tmpW = container.style.width; + Sandcastle.addToolbarButton( + "Snapshot", + function () { + const container = document.getElementById("cesiumContainer"); + const tmpH = container.style.height; + const tmpW = container.style.width; - container.style.height = "600px"; - container.style.width = "800px"; + // resize for screenshot + container.style.height = "600px"; + container.style.width = "800px"; + viewer.resize(); + viewer.render(); - viewer.resize(); - viewer.render(); - window.open(viewer.canvas.toDataURL("image/png")); - container.style.height = tmpH; - container.style.width = tmpW; - viewer.resize(); - viewer.render(); - }); + // chrome blocks opening data urls directly, add an image to a new window instead + // https://stackoverflow.com/questions/45778720/window-open-opens-a-blank-screen-in-chrome + const win = window.open(); + win.document.write(``); + // stop the browser from trying to load "nothing" forever + win.stop(); + + // reset viewer size + container.style.height = tmpH; + container.style.width = tmpW; + viewer.resize(); + viewer.render(); + }, + "zoomButtons", + ); //Sandcastle_End }; if (typeof Cesium !== "undefined") { diff --git a/packages/engine/Source/Core/Math.js b/packages/engine/Source/Core/Math.js index 150cd9aa30a..12755e4be7a 100644 --- a/packages/engine/Source/Core/Math.js +++ b/packages/engine/Source/Core/Math.js @@ -1048,6 +1048,8 @@ CesiumMath.log2 = defaultValue(Math.log2, function log2(number) { }); /** + * Calculate the fog impact at a given distance. useful for culling. + * Matches the equation in `fog.glsl` * @private */ CesiumMath.fog = function (distanceToCamera, density) { diff --git a/packages/engine/Source/Renderer/AutomaticUniforms.js b/packages/engine/Source/Renderer/AutomaticUniforms.js index 53e028aca28..4cb4987d36f 100644 --- a/packages/engine/Source/Renderer/AutomaticUniforms.js +++ b/packages/engine/Source/Renderer/AutomaticUniforms.js @@ -1577,6 +1577,20 @@ const AutomaticUniforms = { }, }), + /** + * An automatic GLSL uniform scalar used to mix a color with the fog color based on the distance to the camera. + * + * @see czm_fog + */ + czm_fogVisualDensityScalar: new AutomaticUniform({ + size: 1, + datatype: WebGLConstants.FLOAT, + getValue: function (uniformState) { + return uniformState.fogVisualDensityScalar; + // return 4.0; + }, + }), + /** * An automatic GLSL uniform scalar used to set a minimum brightness when dynamic lighting is applied to fog. * diff --git a/packages/engine/Source/Renderer/UniformState.js b/packages/engine/Source/Renderer/UniformState.js index 3c3fa7c4275..a1573dbd722 100644 --- a/packages/engine/Source/Renderer/UniformState.js +++ b/packages/engine/Source/Renderer/UniformState.js @@ -160,6 +160,7 @@ function UniformState() { this._specularEnvironmentMapsMaximumLOD = undefined; this._fogDensity = undefined; + this._fogVisualDensityScalar = undefined; this._fogMinimumBrightness = undefined; this._atmosphereHsbShift = undefined; @@ -924,6 +925,17 @@ Object.defineProperties(UniformState.prototype, { }, }, + /** + * A scalar used to mix a color with the fog color based on the distance to the camera. + * @memberof UniformState.prototype + * @type {number} + */ + fogVisualDensityScalar: { + get: function () { + return this._fogVisualDensityScalar; + }, + }, + /** * A scalar used as a minimum value when brightening fog * @memberof UniformState.prototype @@ -1495,6 +1507,7 @@ UniformState.prototype.update = function (frameState) { frameState.specularEnvironmentMapsMaximumLOD; this._fogDensity = frameState.fog.density; + this._fogVisualDensityScalar = frameState.fog.visualDensityScalar; this._fogMinimumBrightness = frameState.fog.minimumBrightness; const atmosphere = frameState.atmosphere; diff --git a/packages/engine/Source/Scene/Fog.js b/packages/engine/Source/Scene/Fog.js index 9dd4e4e6899..1fd4563b898 100644 --- a/packages/engine/Source/Scene/Fog.js +++ b/packages/engine/Source/Scene/Fog.js @@ -34,6 +34,11 @@ function Fog() { * @default 2.0e-4 */ this.density = 2.0e-4; + /** + * A scalar that impacts the visual density of fog. This value does _not_ impact the culling of terrain. + * Use in combination with the `density` to make fog appear more or less dense. + */ + this.visualDensityScalar = 0.4; /** * A factor used to increase the screen space error of terrain tiles when they are partially in fog. The effect is to reduce * the number of terrain tiles requested for rendering. If set to zero, the feature will be disabled. If the value is increased @@ -122,6 +127,9 @@ function findInterval(height) { const scratchPositionNormal = new Cartesian3(); +/** + * @param {FrameState} frameState + */ Fog.prototype.update = function (frameState) { const enabled = (frameState.fog.enabled = this.enabled); if (!enabled) { @@ -167,6 +175,7 @@ Fog.prototype.update = function (frameState) { density *= 1.0 - dot; frameState.fog.density = density; + frameState.fog.visualDensityScalar = this.visualDensityScalar; frameState.fog.sse = this.screenSpaceErrorFactor; frameState.fog.minimumBrightness = this.minimumBrightness; }; diff --git a/packages/engine/Source/Scene/FrameState.js b/packages/engine/Source/Scene/FrameState.js index f2b45b73c44..36313c10269 100644 --- a/packages/engine/Source/Scene/FrameState.js +++ b/packages/engine/Source/Scene/FrameState.js @@ -261,9 +261,10 @@ function FrameState(context, creditDisplay, jobScheduler) { * @type {object} * @property {boolean} enabled true if fog is enabled, false otherwise. This affects both fog culling and rendering. * @property {boolean} renderable true if fog should be rendered, false if not. This flag should be checked in combination with fog.enabled. - * @property {number} density A positive number used to mix the color and fog color based on camera distance. - * @property {number} sse A scalar used to modify the screen space error of geometry partially in fog. - * @property {number} minimumBrightness The minimum brightness of terrain with fog applied. + * @property {number | undefined} density A positive number used to mix the color and fog color based on camera distance. + * @property {number | undefined} visualDensityScalar A positive number to modify how impactful the fog is based off the density + * @property {number | undefined} sse A scalar used to modify the screen space error of geometry partially in fog. + * @property {number | undefined} minimumBrightness The minimum brightness of terrain with fog applied. */ /** @@ -277,6 +278,7 @@ function FrameState(context, creditDisplay, jobScheduler) { enabled: false, renderable: false, density: undefined, + visualDensityScalar: undefined, sse: undefined, minimumBrightness: undefined, }; diff --git a/packages/engine/Source/Scene/Model/AtmospherePipelineStage.js b/packages/engine/Source/Scene/Model/AtmospherePipelineStage.js index 11128da39bd..c16aceb98b7 100644 --- a/packages/engine/Source/Scene/Model/AtmospherePipelineStage.js +++ b/packages/engine/Source/Scene/Model/AtmospherePipelineStage.js @@ -16,12 +16,17 @@ const AtmospherePipelineStage = { name: "AtmospherePipelineStage", // Helps with debugging }; +/** + * @param {ModelRenderResources} modelRenderResources + * @param {Model} model + * @param {FrameState} frameState + */ AtmospherePipelineStage.process = function ( - renderResources, + modelRenderResources, model, frameState, ) { - const shaderBuilder = renderResources.shaderBuilder; + const shaderBuilder = modelRenderResources.shaderBuilder; shaderBuilder.addDefine("HAS_ATMOSPHERE", undefined, ShaderDestination.BOTH); shaderBuilder.addDefine( @@ -37,12 +42,12 @@ AtmospherePipelineStage.process = function ( shaderBuilder.addVertexLines([AtmosphereStageVS]); shaderBuilder.addFragmentLines([AtmosphereStageFS]); - // Add a uniform so fog is only calculated when the efcfect would - // be non-negligible For example when the camera is in space, fog density decreases + // Add a uniform so fog is only calculated when the effect would + // be non-negligible. For example when the camera is in space, fog density decreases // to 0 so fog shouldn't be rendered. Since this state may change rapidly if // the camera is moving, this is implemented as a uniform, not a define. shaderBuilder.addUniform("bool", "u_isInFog", ShaderDestination.FRAGMENT); - renderResources.uniformMap.u_isInFog = function () { + modelRenderResources.uniformMap.u_isInFog = function () { // We only need a rough measure of distance to the model, so measure // from the camera to the bounding sphere center. const distance = Cartesian3.distance( diff --git a/packages/engine/Source/Scene/Model/ModelSceneGraph.js b/packages/engine/Source/Scene/Model/ModelSceneGraph.js index 11e3918c058..248ee80f340 100644 --- a/packages/engine/Source/Scene/Model/ModelSceneGraph.js +++ b/packages/engine/Source/Scene/Model/ModelSceneGraph.js @@ -601,6 +601,7 @@ ModelSceneGraph.prototype.buildDrawCommands = function (frameState) { * this method again to ensure the correct sequence of pipeline stages are * used. * + * @param {FrameState} frameState * @private */ ModelSceneGraph.prototype.configurePipeline = function (frameState) { diff --git a/packages/engine/Source/Shaders/GlobeFS.glsl b/packages/engine/Source/Shaders/GlobeFS.glsl index 2c9df10caf0..be303326adb 100644 --- a/packages/engine/Source/Shaders/GlobeFS.glsl +++ b/packages/engine/Source/Shaders/GlobeFS.glsl @@ -499,8 +499,7 @@ void main() fogColor.rgb = czm_inverseGamma(fogColor.rgb); #endif - const float modifier = 0.15; - finalColor = vec4(czm_fog(v_distance, finalColor.rgb, fogColor.rgb, modifier), finalColor.a); + finalColor = vec4(czm_fog(v_distance, finalColor.rgb, fogColor.rgb, czm_fogVisualDensityScalar), finalColor.a); #else // Apply ground atmosphere. This happens when the camera is far away from the earth. diff --git a/packages/engine/Source/Shaders/Model/AtmosphereStageFS.glsl b/packages/engine/Source/Shaders/Model/AtmosphereStageFS.glsl index e46dc6e6878..4c586ab096f 100644 --- a/packages/engine/Source/Shaders/Model/AtmosphereStageFS.glsl +++ b/packages/engine/Source/Shaders/Model/AtmosphereStageFS.glsl @@ -56,10 +56,7 @@ void applyFog(inout vec4 color, vec4 groundAtmosphereColor, vec3 lightDirection, fogColor.rgb = czm_inverseGamma(fogColor.rgb); #endif - // Matches the constant in GlobeFS.glsl. This makes the fog falloff - // more gradual. - const float fogModifier = 0.15; - vec3 withFog = czm_fog(distanceToCamera, color.rgb, fogColor, fogModifier); + vec3 withFog = czm_fog(distanceToCamera, color.rgb, fogColor, czm_fogVisualDensityScalar); color = vec4(withFog, color.a); }