SkyAtmosphere.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  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 SkyAtmosphereCommon from "../Shaders/SkyAtmosphereCommon.js";
  18. import SkyAtmosphereFS from "../Shaders/SkyAtmosphereFS.js";
  19. import SkyAtmosphereVS from "../Shaders/SkyAtmosphereVS.js";
  20. import Axis from "./Axis.js";
  21. import BlendingState from "./BlendingState.js";
  22. import CullFace from "./CullFace.js";
  23. import SceneMode from "./SceneMode.js";
  24. /**
  25. * An atmosphere drawn around the limb of the provided ellipsoid. Based on
  26. * {@link https://developer.nvidia.com/gpugems/GPUGems2/gpugems2_chapter16.html|Accurate Atmospheric Scattering}
  27. * in GPU Gems 2.
  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. * @demo {@link https://sandcastle.cesium.com/index.html?src=Sky%20Atmosphere.html|Sky atmosphere demo in Sandcastle}
  41. *
  42. * @see Scene.skyAtmosphere
  43. */
  44. function SkyAtmosphere(ellipsoid) {
  45. ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84);
  46. /**
  47. * Determines if the atmosphere is shown.
  48. *
  49. * @type {Boolean}
  50. * @default true
  51. */
  52. this.show = true;
  53. /**
  54. * Compute atmosphere per-fragment instead of per-vertex.
  55. * This produces better looking atmosphere with a slight performance penalty.
  56. *
  57. * @type {Boolean}
  58. * @default false
  59. */
  60. this.perFragmentAtmosphere = false;
  61. this._ellipsoid = ellipsoid;
  62. var outerEllipsoidScale = 1.025;
  63. var scaleVector = Cartesian3.multiplyByScalar(
  64. ellipsoid.radii,
  65. outerEllipsoidScale,
  66. new Cartesian3()
  67. );
  68. this._scaleMatrix = Matrix4.fromScale(scaleVector);
  69. this._modelMatrix = new Matrix4();
  70. this._command = new DrawCommand({
  71. owner: this,
  72. modelMatrix: this._modelMatrix,
  73. });
  74. this._spSkyFromSpace = undefined;
  75. this._spSkyFromAtmosphere = undefined;
  76. this._flags = undefined;
  77. /**
  78. * The hue shift to apply to the atmosphere. Defaults to 0.0 (no shift).
  79. * A hue shift of 1.0 indicates a complete rotation of the hues available.
  80. * @type {Number}
  81. * @default 0.0
  82. */
  83. this.hueShift = 0.0;
  84. /**
  85. * The saturation shift to apply to the atmosphere. Defaults to 0.0 (no shift).
  86. * A saturation shift of -1.0 is monochrome.
  87. * @type {Number}
  88. * @default 0.0
  89. */
  90. this.saturationShift = 0.0;
  91. /**
  92. * The brightness shift to apply to the atmosphere. Defaults to 0.0 (no shift).
  93. * A brightness shift of -1.0 is complete darkness, which will let space show through.
  94. * @type {Number}
  95. * @default 0.0
  96. */
  97. this.brightnessShift = 0.0;
  98. this._hueSaturationBrightness = new Cartesian3();
  99. // outer radius, inner radius, dynamic atmosphere color flag
  100. var radiiAndDynamicAtmosphereColor = new Cartesian3();
  101. radiiAndDynamicAtmosphereColor.x =
  102. ellipsoid.maximumRadius * outerEllipsoidScale;
  103. radiiAndDynamicAtmosphereColor.y = ellipsoid.maximumRadius;
  104. // Toggles whether the sun position is used. 0 treats the sun as always directly overhead.
  105. radiiAndDynamicAtmosphereColor.z = 0;
  106. this._radiiAndDynamicAtmosphereColor = radiiAndDynamicAtmosphereColor;
  107. var that = this;
  108. this._command.uniformMap = {
  109. u_radiiAndDynamicAtmosphereColor: function () {
  110. return that._radiiAndDynamicAtmosphereColor;
  111. },
  112. u_hsbShift: function () {
  113. that._hueSaturationBrightness.x = that.hueShift;
  114. that._hueSaturationBrightness.y = that.saturationShift;
  115. that._hueSaturationBrightness.z = that.brightnessShift;
  116. return that._hueSaturationBrightness;
  117. },
  118. };
  119. }
  120. Object.defineProperties(SkyAtmosphere.prototype, {
  121. /**
  122. * Gets the ellipsoid the atmosphere is drawn around.
  123. * @memberof SkyAtmosphere.prototype
  124. *
  125. * @type {Ellipsoid}
  126. * @readonly
  127. */
  128. ellipsoid: {
  129. get: function () {
  130. return this._ellipsoid;
  131. },
  132. },
  133. });
  134. /**
  135. * @private
  136. */
  137. SkyAtmosphere.prototype.setDynamicAtmosphereColor = function (
  138. enableLighting,
  139. useSunDirection
  140. ) {
  141. var lightEnum = enableLighting ? (useSunDirection ? 2.0 : 1.0) : 0.0;
  142. this._radiiAndDynamicAtmosphereColor.z = lightEnum;
  143. };
  144. var scratchModelMatrix = new Matrix4();
  145. /**
  146. * @private
  147. */
  148. SkyAtmosphere.prototype.update = function (frameState, globe) {
  149. if (!this.show) {
  150. return undefined;
  151. }
  152. var mode = frameState.mode;
  153. if (mode !== SceneMode.SCENE3D && mode !== SceneMode.MORPHING) {
  154. return undefined;
  155. }
  156. // The atmosphere is only rendered during the render pass; it is not pickable, it doesn't cast shadows, etc.
  157. if (!frameState.passes.render) {
  158. return undefined;
  159. }
  160. // Align the ellipsoid geometry so it always faces the same direction as the
  161. // camera to reduce artifacts when rendering atmosphere per-vertex
  162. var rotationMatrix = Matrix4.fromRotationTranslation(
  163. frameState.context.uniformState.inverseViewRotation,
  164. Cartesian3.ZERO,
  165. scratchModelMatrix
  166. );
  167. var rotationOffsetMatrix = Matrix4.multiplyTransformation(
  168. rotationMatrix,
  169. Axis.Y_UP_TO_Z_UP,
  170. scratchModelMatrix
  171. );
  172. var modelMatrix = Matrix4.multiply(
  173. this._scaleMatrix,
  174. rotationOffsetMatrix,
  175. scratchModelMatrix
  176. );
  177. Matrix4.clone(modelMatrix, this._modelMatrix);
  178. var context = frameState.context;
  179. var colorCorrect = hasColorCorrection(this);
  180. var translucent = frameState.globeTranslucencyState.translucent;
  181. var perFragmentAtmosphere =
  182. this.perFragmentAtmosphere || translucent || !defined(globe) || !globe.show;
  183. var command = this._command;
  184. if (!defined(command.vertexArray)) {
  185. var geometry = EllipsoidGeometry.createGeometry(
  186. new EllipsoidGeometry({
  187. radii: new Cartesian3(1.0, 1.0, 1.0),
  188. slicePartitions: 256,
  189. stackPartitions: 256,
  190. vertexFormat: VertexFormat.POSITION_ONLY,
  191. })
  192. );
  193. command.vertexArray = VertexArray.fromGeometry({
  194. context: context,
  195. geometry: geometry,
  196. attributeLocations: GeometryPipeline.createAttributeLocations(geometry),
  197. bufferUsage: BufferUsage.STATIC_DRAW,
  198. });
  199. command.renderState = RenderState.fromCache({
  200. cull: {
  201. enabled: true,
  202. face: CullFace.FRONT,
  203. },
  204. blending: BlendingState.ALPHA_BLEND,
  205. depthMask: false,
  206. });
  207. }
  208. var flags = colorCorrect | (perFragmentAtmosphere << 2) | (translucent << 3);
  209. if (flags !== this._flags) {
  210. this._flags = flags;
  211. var defines = [];
  212. if (colorCorrect) {
  213. defines.push("COLOR_CORRECT");
  214. }
  215. if (perFragmentAtmosphere) {
  216. defines.push("PER_FRAGMENT_ATMOSPHERE");
  217. }
  218. if (translucent) {
  219. defines.push("GLOBE_TRANSLUCENT");
  220. }
  221. var vs = new ShaderSource({
  222. defines: defines.concat("SKY_FROM_SPACE"),
  223. sources: [SkyAtmosphereCommon, SkyAtmosphereVS],
  224. });
  225. var fs = new ShaderSource({
  226. defines: defines.concat("SKY_FROM_SPACE"),
  227. sources: [SkyAtmosphereCommon, SkyAtmosphereFS],
  228. });
  229. this._spSkyFromSpace = ShaderProgram.fromCache({
  230. context: context,
  231. vertexShaderSource: vs,
  232. fragmentShaderSource: fs,
  233. });
  234. vs = new ShaderSource({
  235. defines: defines.concat("SKY_FROM_ATMOSPHERE"),
  236. sources: [SkyAtmosphereCommon, SkyAtmosphereVS],
  237. });
  238. fs = new ShaderSource({
  239. defines: defines.concat("SKY_FROM_ATMOSPHERE"),
  240. sources: [SkyAtmosphereCommon, SkyAtmosphereFS],
  241. });
  242. this._spSkyFromAtmosphere = ShaderProgram.fromCache({
  243. context: context,
  244. vertexShaderSource: vs,
  245. fragmentShaderSource: fs,
  246. });
  247. }
  248. var cameraPosition = frameState.camera.positionWC;
  249. var cameraHeight = Cartesian3.magnitude(cameraPosition);
  250. if (cameraHeight > this._radiiAndDynamicAtmosphereColor.x) {
  251. // Camera in space
  252. command.shaderProgram = this._spSkyFromSpace;
  253. } else {
  254. // Camera in atmosphere
  255. command.shaderProgram = this._spSkyFromAtmosphere;
  256. }
  257. return command;
  258. };
  259. function hasColorCorrection(skyAtmosphere) {
  260. return !(
  261. CesiumMath.equalsEpsilon(
  262. skyAtmosphere.hueShift,
  263. 0.0,
  264. CesiumMath.EPSILON7
  265. ) &&
  266. CesiumMath.equalsEpsilon(
  267. skyAtmosphere.saturationShift,
  268. 0.0,
  269. CesiumMath.EPSILON7
  270. ) &&
  271. CesiumMath.equalsEpsilon(
  272. skyAtmosphere.brightnessShift,
  273. 0.0,
  274. CesiumMath.EPSILON7
  275. )
  276. );
  277. }
  278. /**
  279. * Returns true if this object was destroyed; otherwise, false.
  280. * <br /><br />
  281. * If this object was destroyed, it should not be used; calling any function other than
  282. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception.
  283. *
  284. * @returns {Boolean} <code>true</code> if this object was destroyed; otherwise, <code>false</code>.
  285. *
  286. * @see SkyAtmosphere#destroy
  287. */
  288. SkyAtmosphere.prototype.isDestroyed = function () {
  289. return false;
  290. };
  291. /**
  292. * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic
  293. * release of WebGL resources, instead of relying on the garbage collector to destroy this object.
  294. * <br /><br />
  295. * Once an object is destroyed, it should not be used; calling any function other than
  296. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception. Therefore,
  297. * assign the return value (<code>undefined</code>) to the object as done in the example.
  298. *
  299. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  300. *
  301. *
  302. * @example
  303. * skyAtmosphere = skyAtmosphere && skyAtmosphere.destroy();
  304. *
  305. * @see SkyAtmosphere#isDestroyed
  306. */
  307. SkyAtmosphere.prototype.destroy = function () {
  308. var command = this._command;
  309. command.vertexArray = command.vertexArray && command.vertexArray.destroy();
  310. this._spSkyFromSpace = this._spSkyFromSpace && this._spSkyFromSpace.destroy();
  311. this._spSkyFromAtmosphere =
  312. this._spSkyFromAtmosphere && this._spSkyFromAtmosphere.destroy();
  313. return destroyObject(this);
  314. };
  315. export default SkyAtmosphere;