@@ -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);
}