diff --git a/Apps/Sandcastle/gallery/Earth at Night.html b/Apps/Sandcastle/gallery/Earth at Night.html
index 50e92f081f8d..c882a10d24fd 100644
--- a/Apps/Sandcastle/gallery/Earth at Night.html
+++ b/Apps/Sandcastle/gallery/Earth at Night.html
@@ -39,6 +39,40 @@
var viewer = new Cesium.Viewer("cesiumContainer", {
imageryProvider: new Cesium.IonImageryProvider({ assetId: 3812 }),
});
+
+ // The rest of the code is for dynamic lighting
+ var dynamicLighting = false;
+
+ viewer.clock.multiplier = 4000;
+
+ var imageryLayers = viewer.imageryLayers;
+ var nightLayer = imageryLayers.get(0);
+ var dayLayer = imageryLayers.addImageryProvider(
+ new Cesium.IonImageryProvider({
+ assetId: 3845,
+ })
+ );
+ imageryLayers.lowerToBottom(dayLayer);
+
+ function updateLighting(dynamicLighting) {
+ dayLayer.show = dynamicLighting;
+ viewer.scene.globe.enableLighting = dynamicLighting;
+ viewer.clock.shouldAnimate = dynamicLighting;
+
+ // If dynamic lighting is enabled, make the night imagery invisible
+ // on the lit side of the globe.
+ nightLayer.dayAlpha = dynamicLighting ? 0.0 : 1.0;
+ }
+
+ updateLighting(dynamicLighting);
+
+ Sandcastle.addToggleButton(
+ "Dynamic lighting",
+ dynamicLighting,
+ function (checked) {
+ updateLighting(checked);
+ }
+ );
//Sandcastle_End
Sandcastle.finishedLoading();
}
diff --git a/CHANGES.md b/CHANGES.md
index 8e17396ae61e..abecf42c8e6b 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -21,6 +21,8 @@
- Added `Cesium3DTileset.extensions` to get the extensions property from the tileset JSON. [#8829](https://github.com/CesiumGS/cesium/pull/8829)
- Added `frustumSplits` option to `DebugCameraPrimitive`. [8849](https://github.com/CesiumGS/cesium/pull/8849)
- Added `SkyAtmosphere.perFragmentAtmosphere` to switch between per-vertex and per-fragment atmosphere shading. [#8866](https://github.com/CesiumGS/cesium/pull/8866)
+- Added `Globe.undergroundColor` and `Globe.undergroundColorAlphaByDistance` for controlling how the back side of the globe is rendered when the camera is underground or the globe is translucent. [#8867](https://github.com/CesiumGS/cesium/pull/8867)
+- Added `nightAlpha` and `dayAlpha` properties to `ImageryLayer` to control alpha separately for the night and day sides of the globe. [#8868](https://github.com/CesiumGS/cesium/pull/8868)
- Added a new sandcastle example to show how to add fog using a `PostProcessStage` [#8798](https://github.com/CesiumGS/cesium/pull/8798)
- Supported `#rgba` and `#rrggbbaa` formats in `Color.fromCssColorString`. [8873](https://github.com/CesiumGS/cesium/pull/8873)
- Added `Camera.completeFlight`, which causes the current camera flight to immediately jump to the final destination and call its complete callback. [#8788](https://github.com/CesiumGS/cesium/pull/8788)
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index 7aac9d733c78..4d66e4d786bb 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -256,4 +256,5 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to contribute to Cesiu
- [SungHo Lim](https://github.com/SambaLim)
- [Michael Fink](https://github.com/vividos)
- [Jakub Vrana](https://github.com/vrana)
+- [Edvinas Pranka](https://github.com/epranka)
- [James Bromwell](https://github.com/thw0rted)
diff --git a/Source/Scene/GlobeSurfaceShaderSet.js b/Source/Scene/GlobeSurfaceShaderSet.js
index c44a14ac2f19..df662a8f69c2 100644
--- a/Source/Scene/GlobeSurfaceShaderSet.js
+++ b/Source/Scene/GlobeSurfaceShaderSet.js
@@ -80,6 +80,7 @@ GlobeSurfaceShaderSet.prototype.getShaderProgram = function (options) {
var applySaturation = options.applySaturation;
var applyGamma = options.applyGamma;
var applyAlpha = options.applyAlpha;
+ var applyDayNightAlpha = options.applyDayNightAlpha;
var applySplit = options.applySplit;
var showReflectiveOcean = options.showReflectiveOcean;
var showOceanWaves = options.showOceanWaves;
@@ -155,7 +156,8 @@ GlobeSurfaceShaderSet.prototype.getShaderProgram = function (options) {
(highlightFillTile << 24) |
(colorToAlpha << 25) |
(showUndergroundColor << 26) |
- (translucent << 27);
+ (translucent << 27) |
+ (applyDayNightAlpha << 28);
var currentClippingShaderState = 0;
if (defined(clippingPlanes) && clippingPlanes.length > 0) {
@@ -221,6 +223,9 @@ GlobeSurfaceShaderSet.prototype.getShaderProgram = function (options) {
if (applyAlpha) {
fs.defines.push("APPLY_ALPHA");
}
+ if (applyDayNightAlpha) {
+ fs.defines.push("APPLY_DAY_NIGHT_ALPHA");
+ }
if (showReflectiveOcean) {
fs.defines.push("SHOW_REFLECTIVE_OCEAN");
vs.defines.push("SHOW_REFLECTIVE_OCEAN");
@@ -290,7 +295,7 @@ GlobeSurfaceShaderSet.prototype.getShaderProgram = function (options) {
var computeDayColor =
"\
- vec4 computeDayColor(vec4 initialColor, vec3 textureCoordinates)\n\
+ vec4 computeDayColor(vec4 initialColor, vec3 textureCoordinates, float nightBlend)\n\
{\n\
vec4 color = initialColor;\n";
@@ -333,6 +338,10 @@ GlobeSurfaceShaderSet.prototype.getShaderProgram = function (options) {
(applyAlpha ? "u_dayTextureAlpha[" + i + "]" : "1.0") +
",\n\
" +
+ (applyDayNightAlpha ? "u_dayTextureNightAlpha[" + i + "]" : "1.0") +
+ ",\n" +
+ (applyDayNightAlpha ? "u_dayTextureDayAlpha[" + i + "]" : "1.0") +
+ ",\n" +
(applyBrightness ? "u_dayTextureBrightness[" + i + "]" : "0.0") +
",\n\
" +
@@ -352,7 +361,8 @@ GlobeSurfaceShaderSet.prototype.getShaderProgram = function (options) {
",\n\
" +
(colorToAlpha ? "u_colorsToAlpha[" + i + "]" : "vec4(0.0)") +
- "\n\
+ ",\n\
+ nightBlend\
);\n";
if (hasImageryLayerCutout) {
computeDayColor +=
diff --git a/Source/Scene/GlobeSurfaceTileProvider.js b/Source/Scene/GlobeSurfaceTileProvider.js
index 3473491f5e11..fe9e2bc7eed9 100644
--- a/Source/Scene/GlobeSurfaceTileProvider.js
+++ b/Source/Scene/GlobeSurfaceTileProvider.js
@@ -1560,6 +1560,12 @@ function createTileUniformMap(frameState, globeSurfaceTileProvider) {
u_dayTextureAlpha: function () {
return this.properties.dayTextureAlpha;
},
+ u_dayTextureNightAlpha: function () {
+ return this.properties.dayTextureNightAlpha;
+ },
+ u_dayTextureDayAlpha: function () {
+ return this.properties.dayTextureDayAlpha;
+ },
u_dayTextureBrightness: function () {
return this.properties.dayTextureBrightness;
},
@@ -1674,6 +1680,8 @@ function createTileUniformMap(frameState, globeSurfaceTileProvider) {
dayTextureTexCoordsRectangle: [],
dayTextureUseWebMercatorT: [],
dayTextureAlpha: [],
+ dayTextureNightAlpha: [],
+ dayTextureDayAlpha: [],
dayTextureBrightness: [],
dayTextureContrast: [],
dayTextureHue: [],
@@ -1880,6 +1888,7 @@ var surfaceShaderSetOptionsScratch = {
applySaturation: undefined,
applyGamma: undefined,
applyAlpha: undefined,
+ applyDayNightAlpha: undefined,
applySplit: undefined,
showReflectiveOcean: undefined,
showOceanWaves: undefined,
@@ -2324,6 +2333,7 @@ function addDrawCommandsForTile(tileProvider, tile, frameState) {
var applySaturation = false;
var applyGamma = false;
var applyAlpha = false;
+ var applyDayNightAlpha = false;
var applySplit = false;
var applyCutout = false;
var applyColorToAlpha = false;
@@ -2380,6 +2390,18 @@ function addDrawCommandsForTile(tileProvider, tile, frameState) {
applyAlpha ||
uniformMapProperties.dayTextureAlpha[numberOfDayTextures] !== 1.0;
+ uniformMapProperties.dayTextureNightAlpha[numberOfDayTextures] =
+ imageryLayer.nightAlpha;
+ applyDayNightAlpha =
+ applyDayNightAlpha ||
+ uniformMapProperties.dayTextureNightAlpha[numberOfDayTextures] !== 1.0;
+
+ uniformMapProperties.dayTextureDayAlpha[numberOfDayTextures] =
+ imageryLayer.dayAlpha;
+ applyDayNightAlpha =
+ applyDayNightAlpha ||
+ uniformMapProperties.dayTextureDayAlpha[numberOfDayTextures] !== 1.0;
+
uniformMapProperties.dayTextureBrightness[numberOfDayTextures] =
imageryLayer.brightness;
applyBrightness =
@@ -2527,6 +2549,7 @@ function addDrawCommandsForTile(tileProvider, tile, frameState) {
surfaceShaderSetOptions.applySaturation = applySaturation;
surfaceShaderSetOptions.applyGamma = applyGamma;
surfaceShaderSetOptions.applyAlpha = applyAlpha;
+ surfaceShaderSetOptions.applyDayNightAlpha = applyDayNightAlpha;
surfaceShaderSetOptions.applySplit = applySplit;
surfaceShaderSetOptions.enableFog = applyFog;
surfaceShaderSetOptions.enableClippingPlanes = clippingPlanesEnabled;
diff --git a/Source/Scene/ImageryLayer.js b/Source/Scene/ImageryLayer.js
index b8938689eff2..4cd1cb84b750 100644
--- a/Source/Scene/ImageryLayer.js
+++ b/Source/Scene/ImageryLayer.js
@@ -54,6 +54,18 @@ import TileImagery from "./TileImagery.js";
* current frame state, this layer, and the x, y, and level coordinates of the
* imagery tile for which the alpha is required, and it is expected to return
* the alpha value to use for the tile.
+ * @param {Number|Function} [options.nightAlpha=1.0] The alpha blending value of this layer on the night side of the globe, from 0.0 to 1.0.
+ * This can either be a simple number or a function with the signature
+ * function(frameState, layer, x, y, level)
. The function is passed the
+ * current frame state, this layer, and the x, y, and level coordinates of the
+ * imagery tile for which the alpha is required, and it is expected to return
+ * the alpha value to use for the tile. This only takes effect when enableLighting
is true
.
+ * @param {Number|Function} [options.dayAlpha=1.0] The alpha blending value of this layer on the day side of the globe, from 0.0 to 1.0.
+ * This can either be a simple number or a function with the signature
+ * function(frameState, layer, x, y, level)
. The function is passed the
+ * current frame state, this layer, and the x, y, and level coordinates of the
+ * imagery tile for which the alpha is required, and it is expected to return
+ * the alpha value to use for the tile. This only takes effect when enableLighting
is true
.
* @param {Number|Function} [options.brightness=1.0] The brightness of this layer. 1.0 uses the unmodified imagery
* color. Less than 1.0 makes the imagery darker while greater than 1.0 makes it brighter.
* This can either be a simple number or a function with the signature
@@ -131,6 +143,30 @@ function ImageryLayer(imageryProvider, options) {
defaultValue(imageryProvider.defaultAlpha, 1.0)
);
+ /**
+ * The alpha blending value of this layer on the night side of the globe, with 0.0 representing fully transparent and
+ * 1.0 representing fully opaque. This only takes effect when {@link Globe#enableLighting} is true
.
+ *
+ * @type {Number}
+ * @default 1.0
+ */
+ this.nightAlpha = defaultValue(
+ options.nightAlpha,
+ defaultValue(imageryProvider.defaultNightAlpha, 1.0)
+ );
+
+ /**
+ * The alpha blending value of this layer on the day side of the globe, with 0.0 representing fully transparent and
+ * 1.0 representing fully opaque. This only takes effect when {@link Globe#enableLighting} is true
.
+ *
+ * @type {Number}
+ * @default 1.0
+ */
+ this.dayAlpha = defaultValue(
+ options.dayAlpha,
+ defaultValue(imageryProvider.defaultDayAlpha, 1.0)
+ );
+
/**
* The brightness of this layer. 1.0 uses the unmodified imagery color. Less than 1.0
* makes the imagery darker while greater than 1.0 makes it brighter.
diff --git a/Source/Scene/ImageryProvider.js b/Source/Scene/ImageryProvider.js
index 3e140c3f7ccf..616bcbcbe8f9 100644
--- a/Source/Scene/ImageryProvider.js
+++ b/Source/Scene/ImageryProvider.js
@@ -41,6 +41,24 @@ function ImageryProvider() {
*/
this.defaultAlpha = undefined;
+ /**
+ * The default alpha blending value on the night side of the globe of this provider, with 0.0 representing fully transparent and
+ * 1.0 representing fully opaque.
+ *
+ * @type {Number}
+ * @default undefined
+ */
+ this.defaultNightAlpha = undefined;
+
+ /**
+ * The default alpha blending value on the day side of the globe of this provider, with 0.0 representing fully transparent and
+ * 1.0 representing fully opaque.
+ *
+ * @type {Number}
+ * @default undefined
+ */
+ this.defaultDayAlpha = undefined;
+
/**
* The default brightness of this provider. 1.0 uses the unmodified imagery color. Less than 1.0
* makes the imagery darker while greater than 1.0 makes it brighter.
diff --git a/Source/Shaders/GlobeFS.glsl b/Source/Shaders/GlobeFS.glsl
index 475f137619a6..10a5c29fc3f4 100644
--- a/Source/Shaders/GlobeFS.glsl
+++ b/Source/Shaders/GlobeFS.glsl
@@ -9,6 +9,11 @@ uniform bool u_dayTextureUseWebMercatorT[TEXTURE_UNITS];
uniform float u_dayTextureAlpha[TEXTURE_UNITS];
#endif
+#ifdef APPLY_DAY_NIGHT_ALPHA
+uniform float u_dayTextureNightAlpha[TEXTURE_UNITS];
+uniform float u_dayTextureDayAlpha[TEXTURE_UNITS];
+#endif
+
#ifdef APPLY_SPLIT
uniform float u_dayTextureSplit[TEXTURE_UNITS];
#endif
@@ -158,13 +163,16 @@ vec4 sampleAndBlend(
vec4 textureCoordinateRectangle,
vec4 textureCoordinateTranslationAndScale,
float textureAlpha,
+ float textureNightAlpha,
+ float textureDayAlpha,
float textureBrightness,
float textureContrast,
float textureHue,
float textureSaturation,
float textureOneOverGamma,
float split,
- vec4 colorToAlpha)
+ vec4 colorToAlpha,
+ float nightBlend)
{
// This crazy step stuff sets the alpha to 0.0 if this following condition is true:
// tileTextureCoordinates.s < textureCoordinateRectangle.s ||
@@ -179,6 +187,10 @@ vec4 sampleAndBlend(
alphaMultiplier = step(vec2(0.0), textureCoordinateRectangle.pq - tileTextureCoordinates);
textureAlpha = textureAlpha * alphaMultiplier.x * alphaMultiplier.y;
+#if defined(APPLY_DAY_NIGHT_ALPHA) && defined(ENABLE_DAYNIGHT_SHADING)
+ textureAlpha *= mix(textureDayAlpha, textureNightAlpha, nightBlend);
+#endif
+
vec2 translation = textureCoordinateTranslationAndScale.xy;
vec2 scale = textureCoordinateTranslationAndScale.zw;
vec2 textureCoordinates = tileTextureCoordinates * scale + translation;
@@ -269,7 +281,7 @@ vec3 colorCorrect(vec3 rgb) {
return rgb;
}
-vec4 computeDayColor(vec4 initialColor, vec3 textureCoordinates);
+vec4 computeDayColor(vec4 initialColor, vec3 textureCoordinates, float nightBlend);
vec4 computeWaterColor(vec3 positionEyeCoordinates, vec2 textureCoordinates, mat3 enuToEye, vec4 imageryColor, float specularMapValue, float fade);
#ifdef GROUND_ATMOSPHERE
@@ -292,11 +304,22 @@ void main()
float clipDistance = clip(gl_FragCoord, u_clippingPlanes, u_clippingPlanesMatrix);
#endif
+#if defined(SHOW_REFLECTIVE_OCEAN) || defined(ENABLE_DAYNIGHT_SHADING) || defined(HDR)
+ vec3 normalMC = czm_geodeticSurfaceNormal(v_positionMC, vec3(0.0), vec3(1.0)); // normalized surface normal in model coordinates
+ vec3 normalEC = czm_normal3D * normalMC; // normalized surface normal in eye coordiantes
+#endif
+
+#if defined(APPLY_DAY_NIGHT_ALPHA) && defined(ENABLE_DAYNIGHT_SHADING)
+ float nightBlend = 1.0 - clamp(czm_getLambertDiffuse(czm_lightDirectionEC, normalEC) * 5.0, 0.0, 1.0);
+#else
+ float nightBlend = 0.0;
+#endif
+
// The clamp below works around an apparent bug in Chrome Canary v23.0.1241.0
// where the fragment shader sees textures coordinates < 0.0 and > 1.0 for the
// fragments on the edges of tiles even though the vertex shader is outputting
// coordinates strictly in the 0-1 range.
- vec4 color = computeDayColor(u_initialColor, clamp(v_textureCoordinates, 0.0, 1.0));
+ vec4 color = computeDayColor(u_initialColor, clamp(v_textureCoordinates, 0.0, 1.0), nightBlend);
#ifdef SHOW_TILE_BOUNDARIES
if (v_textureCoordinates.x < (1.0/256.0) || v_textureCoordinates.x > (255.0/256.0) ||
@@ -306,11 +329,6 @@ void main()
}
#endif
-#if defined(SHOW_REFLECTIVE_OCEAN) || defined(ENABLE_DAYNIGHT_SHADING) || defined(HDR)
- vec3 normalMC = czm_geodeticSurfaceNormal(v_positionMC, vec3(0.0), vec3(1.0)); // normalized surface normal in model coordinates
- vec3 normalEC = czm_normal3D * normalMC; // normalized surface normal in eye coordiantes
-#endif
-
#if defined(ENABLE_DAYNIGHT_SHADING) || defined(GROUND_ATMOSPHERE)
float cameraDist;
if (czm_sceneMode == czm_sceneMode2D)
diff --git a/Specs/Scene/GlobeSurfaceTileProviderSpec.js b/Specs/Scene/GlobeSurfaceTileProviderSpec.js
index d615b23fc2b7..d81a3f01b372 100644
--- a/Specs/Scene/GlobeSurfaceTileProviderSpec.js
+++ b/Specs/Scene/GlobeSurfaceTileProviderSpec.js
@@ -622,6 +622,8 @@ describe(
);
layer.alpha = 0.123;
+ layer.nightAlpha = 0.658;
+ layer.dayAlpha = 0.356;
layer.brightness = 0.456;
layer.contrast = 0.654;
layer.gamma = 0.321;
@@ -651,6 +653,8 @@ describe(
++tileCommandCount;
expect(uniforms.u_dayTextureAlpha()).toEqual([0.123]);
+ expect(uniforms.u_dayTextureNightAlpha()).toEqual([0.658]);
+ expect(uniforms.u_dayTextureDayAlpha()).toEqual([0.356]);
expect(uniforms.u_dayTextureBrightness()).toEqual([0.456]);
expect(uniforms.u_dayTextureContrast()).toEqual([0.654]);
expect(uniforms.u_dayTextureOneOverGamma()).toEqual([1.0 / 0.321]);