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]);