SkyAtmosphere.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. import Cartesian3 from "../Core/Cartesian3.js";
  2. import defaultValue from "../Core/defaultValue.js";
  3. import defined from "../Core/defined.js";
  4. import destroyObject from "../Core/destroyObject.js";
  5. import Ellipsoid from "../Core/Ellipsoid.js";
  6. import EllipsoidGeometry from "../Core/EllipsoidGeometry.js";
  7. import GeometryPipeline from "../Core/GeometryPipeline.js";
  8. import CesiumMath from "../Core/Math.js";
  9. import Matrix4 from "../Core/Matrix4.js";
  10. import VertexFormat from "../Core/VertexFormat.js";
  11. import BufferUsage from "../Renderer/BufferUsage.js";
  12. import DrawCommand from "../Renderer/DrawCommand.js";
  13. import RenderState from "../Renderer/RenderState.js";
  14. import ShaderProgram from "../Renderer/ShaderProgram.js";
  15. import ShaderSource from "../Renderer/ShaderSource.js";
  16. import VertexArray from "../Renderer/VertexArray.js";
  17. import AtmosphereCommon from "../Shaders/AtmosphereCommon.js";
  18. import SkyAtmosphereCommon from "../Shaders/SkyAtmosphereCommon.js";
  19. import SkyAtmosphereFS from "../Shaders/SkyAtmosphereFS.js";
  20. import SkyAtmosphereVS from "../Shaders/SkyAtmosphereVS.js";
  21. import Axis from "./Axis.js";
  22. import BlendingState from "./BlendingState.js";
  23. import CullFace from "./CullFace.js";
  24. import SceneMode from "./SceneMode.js";
  25. /**
  26. * An atmosphere drawn around the limb of the provided ellipsoid. Based on
  27. * {@link http://nishitalab.org/user/nis/cdrom/sig93_nis.pdf|Display of The Earth Taking Into Account Atmospheric Scattering}.
  28. * <p>
  29. * This is only supported in 3D. Atmosphere is faded out when morphing to 2D or Columbus view.
  30. * </p>
  31. *
  32. * @alias SkyAtmosphere
  33. * @constructor
  34. *
  35. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid that the atmosphere is drawn around.
  36. *
  37. * @example
  38. * scene.skyAtmosphere = new Cesium.SkyAtmosphere();
  39. *
  40. * @see Scene.skyAtmosphere
  41. */
  42. function SkyAtmosphere(ellipsoid) {
  43. ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84);
  44. /**
  45. * Determines if the atmosphere is shown.
  46. *
  47. * @type {Boolean}
  48. * @default true
  49. */
  50. this.show = true;
  51. /**
  52. * Compute atmosphere per-fragment instead of per-vertex.
  53. * This produces better looking atmosphere with a slight performance penalty.
  54. *
  55. * @type {Boolean}
  56. * @default false
  57. */
  58. this.perFragmentAtmosphere = false;
  59. this._ellipsoid = ellipsoid;
  60. const outerEllipsoidScale = 1.025;
  61. const scaleVector = Cartesian3.multiplyByScalar(
  62. ellipsoid.radii,
  63. outerEllipsoidScale,
  64. new Cartesian3()
  65. );
  66. this._scaleMatrix = Matrix4.fromScale(scaleVector);
  67. this._modelMatrix = new Matrix4();
  68. this._command = new DrawCommand({
  69. owner: this,
  70. modelMatrix: this._modelMatrix,
  71. });
  72. this._spSkyFromSpace = undefined;
  73. this._spSkyFromAtmosphere = undefined;
  74. this._flags = undefined;
  75. /**
  76. * The intensity of the light that is used for computing the sky atmosphere color.
  77. *
  78. * @type {Number}
  79. * @default 50.0
  80. */
  81. this.atmosphereLightIntensity = 50.0;
  82. /**
  83. * The Rayleigh scattering coefficient used in the atmospheric scattering equations for the sky atmosphere.
  84. *
  85. * @type {Cartesian3}
  86. * @default Cartesian3(5.5e-6, 13.0e-6, 28.4e-6)
  87. */
  88. this.atmosphereRayleighCoefficient = new Cartesian3(5.5e-6, 13.0e-6, 28.4e-6);
  89. /**
  90. * The Mie scattering coefficient used in the atmospheric scattering equations for the sky atmosphere.
  91. *
  92. * @type {Cartesian3}
  93. * @default Cartesian3(21e-6, 21e-6, 21e-6)
  94. */
  95. this.atmosphereMieCoefficient = new Cartesian3(21e-6, 21e-6, 21e-6);
  96. /**
  97. * The Rayleigh scale height used in the atmospheric scattering equations for the sky atmosphere, in meters.
  98. *
  99. * @type {Number}
  100. * @default 10000.0
  101. */
  102. this.atmosphereRayleighScaleHeight = 10000.0;
  103. /**
  104. * The Mie scale height used in the atmospheric scattering equations for the sky atmosphere, in meters.
  105. *
  106. * @type {Number}
  107. * @default 3200.0
  108. */
  109. this.atmosphereMieScaleHeight = 3200.0;
  110. /**
  111. * The anisotropy of the medium to consider for Mie scattering.
  112. * <p>
  113. * Valid values are between -1.0 and 1.0.
  114. * </p>
  115. * @type {Number}
  116. * @default 0.9
  117. */
  118. this.atmosphereMieAnisotropy = 0.9;
  119. /**
  120. * The hue shift to apply to the atmosphere. Defaults to 0.0 (no shift).
  121. * A hue shift of 1.0 indicates a complete rotation of the hues available.
  122. * @type {Number}
  123. * @default 0.0
  124. */
  125. this.hueShift = 0.0;
  126. /**
  127. * The saturation shift to apply to the atmosphere. Defaults to 0.0 (no shift).
  128. * A saturation shift of -1.0 is monochrome.
  129. * @type {Number}
  130. * @default 0.0
  131. */
  132. this.saturationShift = 0.0;
  133. /**
  134. * The brightness shift to apply to the atmosphere. Defaults to 0.0 (no shift).
  135. * A brightness shift of -1.0 is complete darkness, which will let space show through.
  136. * @type {Number}
  137. * @default 0.0
  138. */
  139. this.brightnessShift = 0.0;
  140. this._hueSaturationBrightness = new Cartesian3();
  141. // outer radius, inner radius, dynamic atmosphere color flag
  142. const radiiAndDynamicAtmosphereColor = new Cartesian3();
  143. radiiAndDynamicAtmosphereColor.x =
  144. ellipsoid.maximumRadius * outerEllipsoidScale;
  145. radiiAndDynamicAtmosphereColor.y = ellipsoid.maximumRadius;
  146. // Toggles whether the sun position is used. 0 treats the sun as always directly overhead.
  147. radiiAndDynamicAtmosphereColor.z = 0;
  148. this._radiiAndDynamicAtmosphereColor = radiiAndDynamicAtmosphereColor;
  149. const that = this;
  150. this._command.uniformMap = {
  151. u_radiiAndDynamicAtmosphereColor: function () {
  152. return that._radiiAndDynamicAtmosphereColor;
  153. },
  154. u_hsbShift: function () {
  155. that._hueSaturationBrightness.x = that.hueShift;
  156. that._hueSaturationBrightness.y = that.saturationShift;
  157. that._hueSaturationBrightness.z = that.brightnessShift;
  158. return that._hueSaturationBrightness;
  159. },
  160. u_atmosphereLightIntensity: function () {
  161. return that.atmosphereLightIntensity;
  162. },
  163. u_atmosphereRayleighCoefficient: function () {
  164. return that.atmosphereRayleighCoefficient;
  165. },
  166. u_atmosphereMieCoefficient: function () {
  167. return that.atmosphereMieCoefficient;
  168. },
  169. u_atmosphereRayleighScaleHeight: function () {
  170. return that.atmosphereRayleighScaleHeight;
  171. },
  172. u_atmosphereMieScaleHeight: function () {
  173. return that.atmosphereMieScaleHeight;
  174. },
  175. u_atmosphereMieAnisotropy: function () {
  176. return that.atmosphereMieAnisotropy;
  177. },
  178. };
  179. }
  180. Object.defineProperties(SkyAtmosphere.prototype, {
  181. /**
  182. * Gets the ellipsoid the atmosphere is drawn around.
  183. * @memberof SkyAtmosphere.prototype
  184. *
  185. * @type {Ellipsoid}
  186. * @readonly
  187. */
  188. ellipsoid: {
  189. get: function () {
  190. return this._ellipsoid;
  191. },
  192. },
  193. });
  194. /**
  195. * @private
  196. */
  197. SkyAtmosphere.prototype.setDynamicAtmosphereColor = function (
  198. enableLighting,
  199. useSunDirection
  200. ) {
  201. const lightEnum = enableLighting ? (useSunDirection ? 2.0 : 1.0) : 0.0;
  202. this._radiiAndDynamicAtmosphereColor.z = lightEnum;
  203. };
  204. const scratchModelMatrix = new Matrix4();
  205. /**
  206. * @private
  207. */
  208. SkyAtmosphere.prototype.update = function (frameState, globe) {
  209. if (!this.show) {
  210. return undefined;
  211. }
  212. const mode = frameState.mode;
  213. if (mode !== SceneMode.SCENE3D && mode !== SceneMode.MORPHING) {
  214. return undefined;
  215. }
  216. // The atmosphere is only rendered during the render pass; it is not pickable, it doesn't cast shadows, etc.
  217. if (!frameState.passes.render) {
  218. return undefined;
  219. }
  220. // Align the ellipsoid geometry so it always faces the same direction as the
  221. // camera to reduce artifacts when rendering atmosphere per-vertex
  222. const rotationMatrix = Matrix4.fromRotationTranslation(
  223. frameState.context.uniformState.inverseViewRotation,
  224. Cartesian3.ZERO,
  225. scratchModelMatrix
  226. );
  227. const rotationOffsetMatrix = Matrix4.multiplyTransformation(
  228. rotationMatrix,
  229. Axis.Y_UP_TO_Z_UP,
  230. scratchModelMatrix
  231. );
  232. const modelMatrix = Matrix4.multiply(
  233. this._scaleMatrix,
  234. rotationOffsetMatrix,
  235. scratchModelMatrix
  236. );
  237. Matrix4.clone(modelMatrix, this._modelMatrix);
  238. const context = frameState.context;
  239. const colorCorrect = hasColorCorrection(this);
  240. const translucent = frameState.globeTranslucencyState.translucent;
  241. const perFragmentAtmosphere =
  242. this.perFragmentAtmosphere || translucent || !defined(globe) || !globe.show;
  243. const command = this._command;
  244. if (!defined(command.vertexArray)) {
  245. const geometry = EllipsoidGeometry.createGeometry(
  246. new EllipsoidGeometry({
  247. radii: new Cartesian3(1.0, 1.0, 1.0),
  248. slicePartitions: 256,
  249. stackPartitions: 256,
  250. vertexFormat: VertexFormat.POSITION_ONLY,
  251. })
  252. );
  253. command.vertexArray = VertexArray.fromGeometry({
  254. context: context,
  255. geometry: geometry,
  256. attributeLocations: GeometryPipeline.createAttributeLocations(geometry),
  257. bufferUsage: BufferUsage.STATIC_DRAW,
  258. });
  259. command.renderState = RenderState.fromCache({
  260. cull: {
  261. enabled: true,
  262. face: CullFace.FRONT,
  263. },
  264. blending: BlendingState.ALPHA_BLEND,
  265. depthMask: false,
  266. });
  267. }
  268. const flags =
  269. colorCorrect | (perFragmentAtmosphere << 2) | (translucent << 3);
  270. if (flags !== this._flags) {
  271. this._flags = flags;
  272. const defines = [];
  273. if (colorCorrect) {
  274. defines.push("COLOR_CORRECT");
  275. }
  276. if (perFragmentAtmosphere) {
  277. defines.push("PER_FRAGMENT_ATMOSPHERE");
  278. }
  279. if (translucent) {
  280. defines.push("GLOBE_TRANSLUCENT");
  281. }
  282. const vs = new ShaderSource({
  283. defines: defines,
  284. sources: [AtmosphereCommon, SkyAtmosphereCommon, SkyAtmosphereVS],
  285. });
  286. const fs = new ShaderSource({
  287. defines: defines,
  288. sources: [AtmosphereCommon, SkyAtmosphereCommon, SkyAtmosphereFS],
  289. });
  290. this._spSkyAtmosphere = ShaderProgram.fromCache({
  291. context: context,
  292. vertexShaderSource: vs,
  293. fragmentShaderSource: fs,
  294. });
  295. command.shaderProgram = this._spSkyAtmosphere;
  296. }
  297. return command;
  298. };
  299. function hasColorCorrection(skyAtmosphere) {
  300. return !(
  301. CesiumMath.equalsEpsilon(
  302. skyAtmosphere.hueShift,
  303. 0.0,
  304. CesiumMath.EPSILON7
  305. ) &&
  306. CesiumMath.equalsEpsilon(
  307. skyAtmosphere.saturationShift,
  308. 0.0,
  309. CesiumMath.EPSILON7
  310. ) &&
  311. CesiumMath.equalsEpsilon(
  312. skyAtmosphere.brightnessShift,
  313. 0.0,
  314. CesiumMath.EPSILON7
  315. )
  316. );
  317. }
  318. /**
  319. * Returns true if this object was destroyed; otherwise, false.
  320. * <br /><br />
  321. * If this object was destroyed, it should not be used; calling any function other than
  322. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception.
  323. *
  324. * @returns {Boolean} <code>true</code> if this object was destroyed; otherwise, <code>false</code>.
  325. *
  326. * @see SkyAtmosphere#destroy
  327. */
  328. SkyAtmosphere.prototype.isDestroyed = function () {
  329. return false;
  330. };
  331. /**
  332. * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic
  333. * release of WebGL resources, instead of relying on the garbage collector to destroy this object.
  334. * <br /><br />
  335. * Once an object is destroyed, it should not be used; calling any function other than
  336. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception. Therefore,
  337. * assign the return value (<code>undefined</code>) to the object as done in the example.
  338. *
  339. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  340. *
  341. *
  342. * @example
  343. * skyAtmosphere = skyAtmosphere && skyAtmosphere.destroy();
  344. *
  345. * @see SkyAtmosphere#isDestroyed
  346. */
  347. SkyAtmosphere.prototype.destroy = function () {
  348. const command = this._command;
  349. command.vertexArray = command.vertexArray && command.vertexArray.destroy();
  350. this._spSkyAtmosphere =
  351. this._spSkyAtmosphere && this._spSkyAtmosphere.destroy();
  352. return destroyObject(this);
  353. };
  354. export default SkyAtmosphere;