Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fog #3154

Merged
merged 47 commits into from
Nov 9, 2015
Merged

Fog #3154

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
e0efc3a
Initial fog. WIP.
bagnell Oct 22, 2015
7655844
Fog color WIP.
bagnell Oct 23, 2015
7e59d39
Clean up different fog equations and remove uniform fog color.
bagnell Oct 23, 2015
60e8bd5
More clean up.
bagnell Oct 23, 2015
b05a074
More fog color WIP.
bagnell Oct 23, 2015
9b690c7
Merge branch 'master' into fog
bagnell Oct 23, 2015
7894da3
Merge branch 'master' into fog
bagnell Oct 28, 2015
d397e32
Use atmosphere color for fog.
bagnell Oct 29, 2015
8fd263d
Merge branch 'master' into fog
bagnell Oct 29, 2015
41b9330
Minor shader clean up.
bagnell Oct 29, 2015
bebe65c
Add fog parameters to frame state. Cull globe tiles based on fog WIP.
bagnell Oct 29, 2015
01f6a6c
Update density from experimentation. Fog culling WIP.
bagnell Oct 29, 2015
9d3b07a
Interpolate fog density based on camera height.
bagnell Oct 30, 2015
ca742f9
Use preprocessor to enable fog.
bagnell Oct 30, 2015
9d6a8e6
Make scene.fog property. Factor out fog computations. When getting th…
bagnell Nov 2, 2015
80f3f28
Update Sandcastle example locations. Try new density function.
bagnell Nov 2, 2015
8a8c75d
Remove fog density function, use look up table.
bagnell Nov 3, 2015
d2df9a1
Remove fog after zooming out a certain distance.
bagnell Nov 3, 2015
d2c927b
Clean up unused code and Sandcastle example.
bagnell Nov 3, 2015
64a2be8
Increase SSE when tile is in fog.
bagnell Nov 3, 2015
95048fd
Swap shader when tile is in/out of fog.
bagnell Nov 3, 2015
582426a
Explicitly tag CesiumMath.fog private
pjcozzi Nov 4, 2015
dd54283
Doc tweak
pjcozzi Nov 4, 2015
1a194b7
Add comment
pjcozzi Nov 4, 2015
d84186f
Add comment
pjcozzi Nov 4, 2015
f07d538
Fix whitespace
pjcozzi Nov 4, 2015
c5d7341
Tweak density look up table.
bagnell Nov 4, 2015
325d393
Merge branch 'master' into fog
bagnell Nov 4, 2015
35295de
Exploit temporal coherence of zoom level in table look up.
bagnell Nov 4, 2015
ff61da5
Clean up searching for height interval.
bagnell Nov 4, 2015
d11c50b
Only compute distance to tile once. Tweak frame state.
bagnell Nov 4, 2015
85cc52b
Re-add ability to change the density by scaling the table.
bagnell Nov 5, 2015
7902c29
Fade fog in as the camera is tilted towards the horizon.
bagnell Nov 5, 2015
5420561
Fix tests.
bagnell Nov 5, 2015
64828bc
Add fog tests.
bagnell Nov 5, 2015
22de5d1
Add fog documentation.
bagnell Nov 5, 2015
6f8fb41
Merge branch 'master' into fog
bagnell Nov 5, 2015
403b049
Update fog Sandcastle example with positions that demonstrate perform…
bagnell Nov 5, 2015
10a3b97
Fix cracking between the globe and the atmosphere.
bagnell Nov 6, 2015
c6ce4c2
Fix fog for views with negative altitude.
bagnell Nov 6, 2015
333c8f0
Move fog code from scene to new Fog object.
bagnell Nov 6, 2015
186c63f
Enable fog in the tests.
bagnell Nov 6, 2015
7d7c3b2
Tweak CHANGES.md
pjcozzi Nov 7, 2015
ed30aa0
Merge branch 'master' into fog
bagnell Nov 9, 2015
28d15df
Fix shader compile in Firefox/IE.
bagnell Nov 9, 2015
4cccade
Only render atmosphere when terrain tiles are rendered.
bagnell Nov 9, 2015
3ba3717
Add checkbox to enable/disable fog in the terrain sandcastle example.
bagnell Nov 9, 2015
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions Apps/Sandcastle/gallery/Terrain.html
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@
<div id="zoomButtons"></div>
<div id="toggleLighting"></div>
<div id="sampleButtons"></div>
<table><tbody>
<tr>
<td>Enable fog</td>
<td><input type="checkbox" data-bind="checked: fogEnabled"/></td>
</tr>
</tbody></table>
</div>
<script id="cesium_sandcastle_script">
function startup(Cesium) {
Expand Down Expand Up @@ -177,6 +183,16 @@

Cesium.when(Cesium.sampleTerrain(viewer.terrainProvider, 9, terrainSamplePositions), sampleTerrainSuccess);
}, 'sampleButtons');

var viewModel = { fogEnabled : true };
Cesium.knockout.track(viewModel);

var toolbar = document.getElementById('toolbar');
Cesium.knockout.applyBindings(viewModel, toolbar);
Cesium.knockout.getObservable(viewModel, 'fogEnabled').subscribe(function(newValue) {
viewer.scene.fog.enabled = newValue;
});
viewModel.enabled = viewer.scene.fog.enabled;
//Sandcastle_End
Sandcastle.finishedLoading();
}
Expand Down
139 changes: 139 additions & 0 deletions Apps/Sandcastle/gallery/development/Fog.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
<!DOCTYPE html>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The globe is not visible in the default view. Should we update this?

<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
<meta name="description" content="Control fog parameters.">
<meta name="cesium-sandcastle-labels" content="Development">
<title>Cesium Demo</title>
<script type="text/javascript" src="../Sandcastle-header.js"></script>
<script type="text/javascript" src="../../../ThirdParty/requirejs-2.1.20/require.js"></script>
<script type="text/javascript">
require.config({
baseUrl : '../../../Source',
waitSeconds : 60
});
</script>
</head>
<body class="sandcastle-loading" data-sandcastle-bucket="bucket-requirejs.html">
<style>
@import url(../templates/bucket.css);
</style>
<div id="cesiumContainer" class="fullSize"></div>
<div id="loadingOverlay"><h1>Loading...</h1></div>
<div id="toolbar">
<table><tbody>
<tr>
<td>enabled</td>
<td><input type="checkbox" data-bind="checked: enabled"/></td>
</tr>
<tr>
<td>density</td>
<td><input type="text" size="5" data-bind="value: density"></td>
</tr>
<tr>
<td>sse increase factor</td>
<td><input type="text" size="5" data-bind="value: sse"></td>
</tr>
</tbody></table>
<div id="zoomButtons"></div>
</div>
<script id="cesium_sandcastle_script">
function startup(Cesium) {
"use strict";
//Sandcastle_Begin
var viewer = new Cesium.Viewer('cesiumContainer');
viewer.terrainProvider = new Cesium.CesiumTerrainProvider({
url : '//assets.agi.com/stk-terrain/world',
requestWaterMask : true,
requestVertexNormals : true
});

viewer.extend(Cesium.viewerCesiumInspectorMixin);

//The viewModel tracks the state of our mini application.
var viewModel = {
enabled : true,
density : 0,
sse : 0
};
// Convert the viewModel members into knockout observables.
Cesium.knockout.track(viewModel);

// Bind the viewModel to the DOM elements of the UI that call for it.
var toolbar = document.getElementById('toolbar');
Cesium.knockout.applyBindings(viewModel, toolbar);

Cesium.knockout.getObservable(viewModel, 'enabled').subscribe(
function(newValue) {
viewer.scene.fog.enabled = newValue;
}
);

Cesium.knockout.getObservable(viewModel, 'density').subscribe(
function(newValue) {
viewer.scene.fog.density = 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.sse = viewer.scene.fog.screenSpaceErrorFactor;

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 low altitude", function() {
viewer.camera.setView({
destination : new Cesium.Cartesian3(-734001.9511656855, -4214090.596769834, 4715898.125886317),
orientation : {
heading : 5.634257362559497,
pitch : -0.019548505785381032
}
});
});

viewer.scene.globe._surface._debug.enableDebugOutput = true;

Sandcastle.addToolbarButton("Snap", function() {
var container = document.getElementById("cesiumContainer");
var tmpH = container.style.height;
var tmpW = container.style.width;

container.style.height = "600px";
container.style.width = "800px";

viewer.resize();
viewer.render();
window.open(viewer.canvas.toDataURL("image/png"));
container.style.height = tmpH;
container.style.width = tmpW;
viewer.resize();
viewer.render();
});

//Sandcastle_End
Sandcastle.finishedLoading();
}
if (typeof Cesium !== "undefined") {
startup(Cesium);
} else if (typeof require === "function") {
require(["Cesium"], startup);
}
</script>
</body>
</html>
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Change Log
* `GeoJsonDataSource.load` now takes an optional `describeProperty` function for generating feature description properties. [#3140](https://github.com/AnalyticalGraphicsInc/cesium/pull/3140)
* Fixed a bug which caused `Entity` polyline graphics to be incorrect when a scene's ellipsoid was not WGS84. [#3174](https://github.com/AnalyticalGraphicsInc/cesium/pull/3174)
* Added `ImageryProvider.readyPromise` and `TerrainProvider.readyPromise` and implemented it in all terrain and imagery providers. This is a promise which resolves when `ready` becomes true and rejected if there is an error during initialization. [#3175](https://github.com/AnalyticalGraphicsInc/cesium/pull/3175)
* Added support for fog near the horizon, which improves performance by rendering less terrain tiles and reduces terrain tile requests. This is enabled by default. See `Scene.fog` for options. [#3154](https://github.com/AnalyticalGraphicsInc/cesium/pull/3154)

### 1.15 - 2015-11-02

Expand Down
8 changes: 8 additions & 0 deletions Source/Core/Math.js
Original file line number Diff line number Diff line change
Expand Up @@ -780,5 +780,13 @@ define([
return 2.0 * radius * Math.sin(angle * 0.5);
};

/**
* @private
*/
CesiumMath.fog = function(distanceToCamera, density) {
var scalar = distanceToCamera * density;
return 1.0 - Math.exp(-(scalar * scalar));
};

return CesiumMath;
});
16 changes: 16 additions & 0 deletions Source/Renderer/AutomaticUniforms.js
Original file line number Diff line number Diff line change
Expand Up @@ -1396,6 +1396,22 @@ define([
getValue : function(uniformState) {
return uniformState.resolutionScale;
}
}),

/**
* An automatic GLSL uniform scalar used to mix a color with the fog color based on the distance to the camera.
*
* @alias czm_fogDensity
* @glslUniform
*
* @see czm_fog
*/
czm_fogDensity : new AutomaticUniform({
size : 1,
datatype : WebGLConstants.FLOAT,
getValue : function(uniformState) {
return uniformState.fogDensity;
}
})
};

Expand Down
15 changes: 15 additions & 0 deletions Source/Renderer/UniformState.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,8 @@ define([
this._frustum2DWidth = 0.0;
this._eyeHeight2D = new Cartesian2();
this._resolutionScale = 1.0;

this._fogDensity = undefined;
};

defineProperties(UniformState.prototype, {
Expand Down Expand Up @@ -764,6 +766,17 @@ define([
get : function() {
return this._resolutionScale;
}
},

/**
* A scalar used to mix a color with the fog color based on the distance to the camera.
* @memberof UniformState.prototype
* @type {Number}
*/
fogDensity : {
get : function() {
return this._fogDensity;
}
}
});

Expand Down Expand Up @@ -904,6 +917,8 @@ define([
this._entireFrustum.y = camera.frustum.far;
this.updateFrustum(camera.frustum);

this._fogDensity = frameState.fog.density;

this._frameState = frameState;
this._temeToPseudoFixed = Transforms.computeTemeToPseudoFixedMatrix(frameState.time, this._temeToPseudoFixed);
};
Expand Down
142 changes: 142 additions & 0 deletions Source/Scene/Fog.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/*global define*/
define([
'../Core/Cartesian3',
'../Core/defined',
'../Core/Math',
'./SceneMode'
], function(
Cartesian3,
defined,
CesiumMath,
SceneMode) {
"use strict";

/**
* Blends the atmosphere to geometry far from the camera for horizon views. Allows for additional
* performance improvements by rendering less geometry and dispatching less terrain requests.
*
* @alias Fog
* @constructor
*/
var Fog = function() {
/**
* <code>true</code> if fog is enabled, <code>false</code> otherwise.
* @type {Boolean}
* @default true
*/
this.enabled = true;

/**
* A scalar that determines the density of the fog. Terrain that is in full fog are culled.
* The density of the fog increases as this number approaches 1.0 and becomes less dense as it approaches zero.
* The more dense the fog is, the more aggressively the terrain is culled. For example, if the camera is a height of
* 1000.0m above the ellipsoid, increasing the value to 3.0e-3 will cause many tiles close to the viewer be culled.
* Decreasing the value will push the fog further from the viewer, but decrease performance as more of the terrain is rendered.
* @type {Number}
* @default 2.0e-4
*/
this.density = 2.0e-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
* for mountainous regions, less tiles will need to be requested, but the terrain meshes near the horizon may be a noticeably
* lower resolution. If the value is increased in a relatively flat area, there will be little noticeable change on the horizon.
* @type {Number}
* @default 2.0
*/
this.screenSpaceErrorFactor = 2.0;
};

// These values were found by sampling the density at certain views and finding at what point culled tiles impacted the view at the horizon.
var heightsTable = [359.393, 800.749, 1275.6501, 2151.1192, 3141.7763, 4777.5198, 6281.2493, 12364.307, 15900.765, 49889.0549, 78026.8259, 99260.7344, 120036.3873, 151011.0158, 156091.1953, 203849.3112, 274866.9803, 319916.3149, 493552.0528, 628733.5874];
var densityTable = [2.0e-5, 2.0e-4, 1.0e-4, 7.0e-5, 5.0e-5, 4.0e-5, 3.0e-5, 1.9e-5, 1.0e-5, 8.5e-6, 6.2e-6, 5.8e-6, 5.3e-6, 5.2e-6, 5.1e-6, 4.2e-6, 4.0e-6, 3.4e-6, 2.6e-6, 2.2e-6];

// Scale densities by 1e6 to bring lowest value to ~1. Prevents divide by zero.
for (var i = 0; i < densityTable.length; ++i) {
densityTable[i] *= 1.0e6;
}
// Change range to [0, 1].
var tableStartDensity = densityTable[1];
var tableEndDensity = densityTable[densityTable.length - 1];
for (var j = 0; j < densityTable.length; ++j) {
densityTable[j] = (densityTable[j] - tableEndDensity) / (tableStartDensity - tableEndDensity);
}

var tableLastIndex = 0;

function findInterval(height) {
var heights = heightsTable;
var length = heights.length;

if (height < heights[0]) {
tableLastIndex = 0;
return tableLastIndex;
} else if (height > heights[length - 1]) {
tableLastIndex = length - 2;
return tableLastIndex;
}

// Take advantage of temporal coherence by checking current, next and previous intervals
// for containment of time.
if (height >= heights[tableLastIndex]) {
if (tableLastIndex + 1 < length && height < heights[tableLastIndex + 1]) {
return tableLastIndex;
} else if (tableLastIndex + 2 < length && height < heights[tableLastIndex + 2]) {
++tableLastIndex;
return tableLastIndex;
}
} else if (tableLastIndex - 1 >= 0 && height >= heights[tableLastIndex - 1]) {
--tableLastIndex;
return tableLastIndex;
}

// The above failed so do a linear search.
var i;
for (i = 0; i < length - 2; ++i) {
if (height >= heights[i] && height < heights[i + 1]) {
break;
}
}

tableLastIndex = i;
return tableLastIndex;
}

var scratchPositionNormal = new Cartesian3();

Fog.prototype.update = function(frameState) {
var enabled = frameState.fog.enabled = this.enabled;
if (!enabled) {
return;
}

var camera = frameState.camera;
var positionCartographic = camera.positionCartographic;

// Turn off fog in space.
if (!defined(positionCartographic) || positionCartographic.height > 800000.0 || frameState.mode !== SceneMode.SCENE3D) {
frameState.fog.enabled = false;
return;
}

var height = positionCartographic.height;
var i = findInterval(height);
var t = CesiumMath.clamp((height - heightsTable[i]) / (heightsTable[i + 1] - heightsTable[i]), 0.0, 1.0);
var density = CesiumMath.lerp(densityTable[i], densityTable[i + 1], t);

// Again, scale value to be in the range of densityTable (prevents divide by zero) and change to new range.
var startDensity = this.density * 1.0e6;
var endDensity = (startDensity / tableStartDensity) * tableEndDensity;
density = (density * (startDensity - endDensity)) * 1.0e-6;

// Fade fog in as the camera tilts toward the horizon.
var positionNormal = Cartesian3.normalize(camera.positionWC, scratchPositionNormal);
var dot = CesiumMath.clamp(Cartesian3.dot(camera.directionWC, positionNormal), 0.0, 1.0);
density *= 1.0 - dot;

frameState.fog.density = density;
frameState.fog.sse = this.screenSpaceErrorFactor;
};

return Fog;
});
Loading