Fog.js 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. import Cartesian3 from "../Core/Cartesian3.js";
  2. import defined from "../Core/defined.js";
  3. import CesiumMath from "../Core/Math.js";
  4. import SceneMode from "./SceneMode.js";
  5. /**
  6. * Blends the atmosphere to geometry far from the camera for horizon views. Allows for additional
  7. * performance improvements by rendering less geometry and dispatching less terrain requests.
  8. *
  9. * @alias Fog
  10. * @constructor
  11. */
  12. function Fog() {
  13. /**
  14. * <code>true</code> if fog is enabled, <code>false</code> otherwise.
  15. * @type {boolean}
  16. * @default true
  17. */
  18. this.enabled = true;
  19. /**
  20. * <code>true</code> if fog is renderable in shaders, <code>false</code> otherwise.
  21. * This allows to benefits from optimized tile loading strategy based on fog density without the actual visual rendering.
  22. * @type {boolean}
  23. * @default true
  24. */
  25. this.renderable = true;
  26. /**
  27. * A scalar that determines the density of the fog. Terrain that is in full fog are culled.
  28. * The density of the fog increases as this number approaches 1.0 and becomes less dense as it approaches zero.
  29. * The more dense the fog is, the more aggressively the terrain is culled. For example, if the camera is a height of
  30. * 1000.0m above the ellipsoid, increasing the value to 3.0e-3 will cause many tiles close to the viewer be culled.
  31. * Decreasing the value will push the fog further from the viewer, but decrease performance as more of the terrain is rendered.
  32. * @type {number}
  33. * @default 2.0e-4
  34. */
  35. this.density = 2.0e-4;
  36. /**
  37. * A factor used to increase the screen space error of terrain tiles when they are partially in fog. The effect is to reduce
  38. * the number of terrain tiles requested for rendering. If set to zero, the feature will be disabled. If the value is increased
  39. * for mountainous regions, less tiles will need to be requested, but the terrain meshes near the horizon may be a noticeably
  40. * lower resolution. If the value is increased in a relatively flat area, there will be little noticeable change on the horizon.
  41. * @type {number}
  42. * @default 2.0
  43. */
  44. this.screenSpaceErrorFactor = 2.0;
  45. /**
  46. * The minimum brightness of the fog color from lighting. A value of 0.0 can cause the fog to be completely black. A value of 1.0 will not affect
  47. * the brightness at all.
  48. * @type {number}
  49. * @default 0.03
  50. */
  51. this.minimumBrightness = 0.03;
  52. }
  53. // These values were found by sampling the density at certain views and finding at what point culled tiles impacted the view at the horizon.
  54. const heightsTable = [
  55. 359.393,
  56. 800.749,
  57. 1275.6501,
  58. 2151.1192,
  59. 3141.7763,
  60. 4777.5198,
  61. 6281.2493,
  62. 12364.307,
  63. 15900.765,
  64. 49889.0549,
  65. 78026.8259,
  66. 99260.7344,
  67. 120036.3873,
  68. 151011.0158,
  69. 156091.1953,
  70. 203849.3112,
  71. 274866.9803,
  72. 319916.3149,
  73. 493552.0528,
  74. 628733.5874,
  75. ];
  76. const densityTable = [
  77. 2.0e-5,
  78. 2.0e-4,
  79. 1.0e-4,
  80. 7.0e-5,
  81. 5.0e-5,
  82. 4.0e-5,
  83. 3.0e-5,
  84. 1.9e-5,
  85. 1.0e-5,
  86. 8.5e-6,
  87. 6.2e-6,
  88. 5.8e-6,
  89. 5.3e-6,
  90. 5.2e-6,
  91. 5.1e-6,
  92. 4.2e-6,
  93. 4.0e-6,
  94. 3.4e-6,
  95. 2.6e-6,
  96. 2.2e-6,
  97. ];
  98. // Scale densities by 1e6 to bring lowest value to ~1. Prevents divide by zero.
  99. for (let i = 0; i < densityTable.length; ++i) {
  100. densityTable[i] *= 1.0e6;
  101. }
  102. // Change range to [0, 1].
  103. const tableStartDensity = densityTable[1];
  104. const tableEndDensity = densityTable[densityTable.length - 1];
  105. for (let j = 0; j < densityTable.length; ++j) {
  106. densityTable[j] =
  107. (densityTable[j] - tableEndDensity) / (tableStartDensity - tableEndDensity);
  108. }
  109. let tableLastIndex = 0;
  110. function findInterval(height) {
  111. const heights = heightsTable;
  112. const length = heights.length;
  113. if (height < heights[0]) {
  114. tableLastIndex = 0;
  115. return tableLastIndex;
  116. } else if (height > heights[length - 1]) {
  117. tableLastIndex = length - 2;
  118. return tableLastIndex;
  119. }
  120. // Take advantage of temporal coherence by checking current, next and previous intervals
  121. // for containment of time.
  122. if (height >= heights[tableLastIndex]) {
  123. if (tableLastIndex + 1 < length && height < heights[tableLastIndex + 1]) {
  124. return tableLastIndex;
  125. } else if (
  126. tableLastIndex + 2 < length &&
  127. height < heights[tableLastIndex + 2]
  128. ) {
  129. ++tableLastIndex;
  130. return tableLastIndex;
  131. }
  132. } else if (tableLastIndex - 1 >= 0 && height >= heights[tableLastIndex - 1]) {
  133. --tableLastIndex;
  134. return tableLastIndex;
  135. }
  136. // The above failed so do a linear search.
  137. let i;
  138. for (i = 0; i < length - 2; ++i) {
  139. if (height >= heights[i] && height < heights[i + 1]) {
  140. break;
  141. }
  142. }
  143. tableLastIndex = i;
  144. return tableLastIndex;
  145. }
  146. const scratchPositionNormal = new Cartesian3();
  147. Fog.prototype.update = function (frameState) {
  148. const enabled = (frameState.fog.enabled = this.enabled);
  149. if (!enabled) {
  150. return;
  151. }
  152. frameState.fog.renderable = this.renderable;
  153. const camera = frameState.camera;
  154. const positionCartographic = camera.positionCartographic;
  155. // Turn off fog in space.
  156. if (
  157. !defined(positionCartographic) ||
  158. positionCartographic.height > 800000.0 ||
  159. frameState.mode !== SceneMode.SCENE3D
  160. ) {
  161. frameState.fog.enabled = false;
  162. return;
  163. }
  164. const height = positionCartographic.height;
  165. const i = findInterval(height);
  166. const t = CesiumMath.clamp(
  167. (height - heightsTable[i]) / (heightsTable[i + 1] - heightsTable[i]),
  168. 0.0,
  169. 1.0
  170. );
  171. let density = CesiumMath.lerp(densityTable[i], densityTable[i + 1], t);
  172. // Again, scale value to be in the range of densityTable (prevents divide by zero) and change to new range.
  173. const startDensity = this.density * 1.0e6;
  174. const endDensity = (startDensity / tableStartDensity) * tableEndDensity;
  175. density = density * (startDensity - endDensity) * 1.0e-6;
  176. // Fade fog in as the camera tilts toward the horizon.
  177. const positionNormal = Cartesian3.normalize(
  178. camera.positionWC,
  179. scratchPositionNormal
  180. );
  181. const dot = Math.abs(Cartesian3.dot(camera.directionWC, positionNormal));
  182. density *= 1.0 - dot;
  183. frameState.fog.density = density;
  184. frameState.fog.sse = this.screenSpaceErrorFactor;
  185. frameState.fog.minimumBrightness = this.minimumBrightness;
  186. };
  187. export default Fog;