Skip to content

Commit

Permalink
Merge pull request #12149 from CesiumGS/water-mask-globe-material
Browse files Browse the repository at this point in the history
Add water mask material
  • Loading branch information
jjspace authored Aug 30, 2024
2 parents 2568dfe + 00dc961 commit 349364c
Show file tree
Hide file tree
Showing 12 changed files with 276 additions and 33 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no"
/>
<meta name="description" content="Apply materials to the globe." />
<meta name="cesium-sandcastle-labels" content="Showcases" />
<title>Cesium Demo</title>
<script type="text/javascript" src="../Sandcastle-header.js"></script>
<script
type="text/javascript"
src="../../../Build/CesiumUnminified/Cesium.js"
nomodule
></script>
<script type="module" src="../load-cesium-es6.js"></script>
</head>
<body
class="sandcastle-loading"
data-sandcastle-bucket="bucket-requirejs.html"
>
<style>
@import url(../templates/bucket.css);
</style>
<div id="cesiumContainer" class="fullSize"></div>
<div id="loadingOverlay"><h1>Loading...</h1></div>
<div id="toolbar">
<div id="zoomButtons"></div>
</div>
<script id="cesium_sandcastle_script">
window.startup = async function (Cesium) {
"use strict";
//Sandcastle_Begin
const viewer = new Cesium.Viewer("cesiumContainer", {
terrain: Cesium.Terrain.fromWorldTerrain({
requestVertexNormals: true, // Needed for hillshading
requestWaterMask: true, // Needed to distinguish land from water
}),
timeline: false,
animation: false,
});

// Create a globe material for shading elevation only on land
const customElevationMaterial = new Cesium.Material({
fabric: {
type: "ElevationLand",
materials: {
waterMaskMaterial: {
type: "WaterMask",
},
elevationRampMaterial: {
type: "ElevationRamp",
},
},
components: {
diffuse: "elevationRampMaterial.diffuse",
alpha: "1.0 - waterMaskMaterial.alpha", // We'll need the inverse of the watermask to shade land
},
},
translucent: false,
});

const minHeight = -414.0; // approximate dead sea elevation
const maxHeight = 8777.0; // approximate everest elevation
const elevationRamp = [0.0, 0.045, 0.45, 0.5, 0.55, 1.0];
function getColorRamp() {
const ramp = document.createElement("canvas");
ramp.width = 100;
ramp.height = 1;
const ctx = ramp.getContext("2d");

const values = elevationRamp;

const grd = ctx.createLinearGradient(0, 0, 100, 0);

// See https://gis.stackexchange.com/questions/25099/choosing-colour-ramp-to-use-for-elevation
grd.addColorStop(values[0], "#344f31");
grd.addColorStop(values[1], "#5b8742");
grd.addColorStop(values[2], "#e6daa5");
grd.addColorStop(values[3], "#fdc771");
grd.addColorStop(values[4], "#b99d89");
grd.addColorStop(values[5], "#f0f0f0");

ctx.fillStyle = grd;
ctx.fillRect(0, 0, 100, 1);

return ramp;
}

const globe = viewer.scene.globe;
const material = customElevationMaterial;
const shadingUniforms =
material.materials.elevationRampMaterial.uniforms;
shadingUniforms.minimumHeight = minHeight;
shadingUniforms.maximumHeight = maxHeight;
shadingUniforms.image = getColorRamp();

globe.material = material;
globe.showWaterEffect = false;
globe.enableLighting = true;
globe.maximumScreenSpaceError = 0.5; // Load higher resolution tiles for more detailed shading

// Light the scene with a hillshade effect similar to https://pro.arcgis.com/en/pro-app/latest/tool-reference/3d-analyst/how-hillshade-works.htm
const scene = viewer.scene;
scene.light = new Cesium.DirectionalLight({
direction: new Cesium.Cartesian3(1, 0, 0), // Updated every frame
});

// Update the light position base on the camera
const scratchNormal = new Cesium.Cartesian3();
scene.preRender.addEventListener(function (scene, time) {
const surfaceNormal = globe.ellipsoid.geodeticSurfaceNormal(
scene.camera.positionWC,
scratchNormal
);
const negativeNormal = Cesium.Cartesian3.negate(
surfaceNormal,
surfaceNormal
);
scene.light.direction = Cesium.Cartesian3.normalize(
Cesium.Cartesian3.add(
negativeNormal,
scene.camera.rightWC,
surfaceNormal
),
scene.light.direction
);
});
//Sandcastle_End
};
if (typeof Cesium !== "undefined") {
window.startupCalled = true;
window.startup(Cesium).catch((error) => {
"use strict";
console.error(error);
});
Sandcastle.finishedLoading();
}
</script>
</body>
</html>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion Apps/Sandcastle/gallery/Globe Materials.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
name="viewport"
content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no"
/>
<meta name="description" content="Apply materials to the globe." />
<meta name="description" content="Apply an elevation map to the globe." />
<meta name="cesium-sandcastle-labels" content="Showcases" />
<title>Cesium Demo</title>
<script type="text/javascript" src="../Sandcastle-header.js"></script>
Expand Down
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
- Enable MSAA by default with 4 samples. To turn MSAA off set `scene.msaaSamples = 1` [#12158](https://github.com/CesiumGS/cesium/pull/12158)
- Made the `time` parameter optional for `Property`, using `JulianDate.now()` as default. [#12099](https://github.com/CesiumGS/cesium/pull/12099)
- Exposes `ScreenSpaceCameraController.zoomFactor` to allow adjusting the zoom factor (speed). [#9145](https://github.com/CesiumGS/cesium/pull/9145)
- Added `WaterMask` globe material, which visualizes areas of water or land based on the terrain's water mask. [#12149](https://github.com/CesiumGS/cesium/pull/12149)

##### Fixes :wrench:

Expand Down
2 changes: 1 addition & 1 deletion packages/engine/Source/Scene/Globe.js
Original file line number Diff line number Diff line change
Expand Up @@ -979,7 +979,6 @@ Globe.prototype.beginFrame = function (frameState) {
const tileProvider = surface.tileProvider;
const terrainProvider = this.terrainProvider;
const hasWaterMask =
this.showWaterEffect &&
defined(terrainProvider) &&
terrainProvider.hasWaterMask &&
terrainProvider.hasWaterMask;
Expand Down Expand Up @@ -1034,6 +1033,7 @@ Globe.prototype.beginFrame = function (frameState) {
tileProvider.zoomedOutOceanSpecularIntensity =
mode === SceneMode.SCENE3D ? this._zoomedOutOceanSpecularIntensity : 0.0;
tileProvider.hasWaterMask = hasWaterMask;
tileProvider.showWaterEffect = this.showWaterEffect;
tileProvider.oceanNormalMap = this._oceanNormalMap;
tileProvider.enableLighting = this.enableLighting;
tileProvider.dynamicAtmosphereLighting = this.dynamicAtmosphereLighting;
Expand Down
53 changes: 29 additions & 24 deletions packages/engine/Source/Scene/GlobeSurfaceShaderSet.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ GlobeSurfaceShaderSet.prototype.getShaderProgram = function (options) {
const applyAlpha = options.applyAlpha;
const applyDayNightAlpha = options.applyDayNightAlpha;
const applySplit = options.applySplit;
const hasWaterMask = options.hasWaterMask;
const showReflectiveOcean = options.showReflectiveOcean;
const showOceanWaves = options.showOceanWaves;
const enableLighting = options.enableLighting;
Expand Down Expand Up @@ -168,30 +169,31 @@ GlobeSurfaceShaderSet.prototype.getShaderProgram = function (options) {
(applySaturation << 5) |
(applyGamma << 6) |
(applyAlpha << 7) |
(showReflectiveOcean << 8) |
(showOceanWaves << 9) |
(enableLighting << 10) |
(dynamicAtmosphereLighting << 11) |
(dynamicAtmosphereLightingFromSun << 12) |
(showGroundAtmosphere << 13) |
(perFragmentGroundAtmosphere << 14) |
(hasVertexNormals << 15) |
(useWebMercatorProjection << 16) |
(enableFog << 17) |
(quantization << 18) |
(applySplit << 19) |
(enableClippingPlanes << 20) |
(enableClippingPolygons << 21) |
(cartographicLimitRectangleFlag << 22) |
(imageryCutoutFlag << 23) |
(colorCorrect << 24) |
(highlightFillTile << 25) |
(colorToAlpha << 26) |
(hasGeodeticSurfaceNormals << 27) |
(hasExaggeration << 28) |
(showUndergroundColor << 29) |
(translucent << 30) |
(applyDayNightAlpha << 31);
(hasWaterMask << 8) |
(showReflectiveOcean << 9) |
(showOceanWaves << 10) |
(enableLighting << 11) |
(dynamicAtmosphereLighting << 12) |
(dynamicAtmosphereLightingFromSun << 13) |
(showGroundAtmosphere << 14) |
(perFragmentGroundAtmosphere << 15) |
(hasVertexNormals << 16) |
(useWebMercatorProjection << 17) |
(enableFog << 18) |
(quantization << 19) |
(applySplit << 20) |
(enableClippingPlanes << 21) |
(enableClippingPolygons << 22) |
(cartographicLimitRectangleFlag << 23) |
(imageryCutoutFlag << 24) |
(colorCorrect << 25) |
(highlightFillTile << 26) |
(colorToAlpha << 27) |
(hasGeodeticSurfaceNormals << 28) |
(hasExaggeration << 29) |
(showUndergroundColor << 30) |
(translucent << 31) |
(applyDayNightAlpha << 32);

let currentClippingShaderState = 0;
if (defined(clippingPlanes) && clippingPlanes.length > 0) {
Expand Down Expand Up @@ -279,6 +281,9 @@ GlobeSurfaceShaderSet.prototype.getShaderProgram = function (options) {
if (applyDayNightAlpha) {
fs.defines.push("APPLY_DAY_NIGHT_ALPHA");
}
if (hasWaterMask) {
fs.defines.push("HAS_WATER_MASK");
}
if (showReflectiveOcean) {
fs.defines.push("SHOW_REFLECTIVE_OCEAN");
vs.defines.push("SHOW_REFLECTIVE_OCEAN");
Expand Down
8 changes: 5 additions & 3 deletions packages/engine/Source/Scene/GlobeSurfaceTileProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ function GlobeSurfaceTileProvider(options) {
this.lightingFadeOutDistance = 6500000.0;
this.lightingFadeInDistance = 9000000.0;
this.hasWaterMask = false;
this.showWaterEffect = false;
this.oceanNormalMap = undefined;
this.zoomedOutOceanSpecularIntensity = 0.5;
this.enableLighting = false;
Expand Down Expand Up @@ -2181,8 +2182,8 @@ function addDrawCommandsForTile(tileProvider, tile, frameState) {
const lambertDiffuseMultiplier = tileProvider.lambertDiffuseMultiplier;
const vertexShadowDarkness = tileProvider.vertexShadowDarkness;

const showReflectiveOcean =
tileProvider.hasWaterMask && defined(waterMaskTexture);
const hasWaterMask = tileProvider.hasWaterMask && defined(waterMaskTexture);
const showReflectiveOcean = hasWaterMask && tileProvider.showWaterEffect;
const oceanNormalMap = tileProvider.oceanNormalMap;
const showOceanWaves = showReflectiveOcean && defined(oceanNormalMap);
const terrainProvider = tileProvider.terrainProvider;
Expand Down Expand Up @@ -2214,7 +2215,7 @@ function addDrawCommandsForTile(tileProvider, tile, frameState) {
perFragmentGroundAtmosphere = cameraDistance > fadeOutDistance;
}

if (showReflectiveOcean) {
if (hasWaterMask) {
--maxTextures;
}
if (showOceanWaves) {
Expand Down Expand Up @@ -2329,6 +2330,7 @@ function addDrawCommandsForTile(tileProvider, tile, frameState) {
const surfaceShaderSetOptions = surfaceShaderSetOptionsScratch;
surfaceShaderSetOptions.frameState = frameState;
surfaceShaderSetOptions.surfaceTile = surfaceTile;
surfaceShaderSetOptions.hasWaterMask = hasWaterMask;
surfaceShaderSetOptions.showReflectiveOcean = showReflectiveOcean;
surfaceShaderSetOptions.showOceanWaves = showOceanWaves;
surfaceShaderSetOptions.enableLighting = tileProvider.enableLighting;
Expand Down
26 changes: 24 additions & 2 deletions packages/engine/Source/Scene/Material.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import SlopeRampMaterial from "../Shaders/Materials/SlopeRampMaterial.js";
import StripeMaterial from "../Shaders/Materials/StripeMaterial.js";
import TextureMagnificationFilter from "../Renderer/TextureMagnificationFilter.js";
import TextureMinificationFilter from "../Renderer/TextureMinificationFilter.js";
import WaterMaskMaterial from "../Shaders/Materials/WaterMaskMaterial.js";
import WaterMaterial from "../Shaders/Materials/Water.js";

/**
Expand Down Expand Up @@ -219,6 +220,11 @@ import WaterMaterial from "../Shaders/Materials/Water.js";
* <li><code>heights</code>: image of heights sorted from lowest to highest.</li>
* <li><code>colors</code>: image of colors at the corresponding heights.</li>
* </ul>
* <li>WaterMask</li>
* <ul>
* <li><code>waterColor</code>: diffuse color and alpha for the areas covered by water.</li>
* <li><code>landColor</code>: diffuse color and alpha for the areas covered by land.</li>
* </ul>
* </ul>
* </ul>
* </div>
Expand All @@ -233,7 +239,6 @@ import WaterMaterial from "../Shaders/Materials/Water.js";
* @param {TextureMinificationFilter} [options.minificationFilter=TextureMinificationFilter.LINEAR] The {@link TextureMinificationFilter} to apply to this material's textures.
* @param {TextureMagnificationFilter} [options.magnificationFilter=TextureMagnificationFilter.LINEAR] The {@link TextureMagnificationFilter} to apply to this material's textures.
* @param {object} options.fabric The fabric JSON used to generate the material.
*ructor
*
* @exception {DeveloperError} fabric: uniform has invalid type.
* @exception {DeveloperError} fabric: uniforms and materials cannot share the same property.
Expand All @@ -245,7 +250,6 @@ import WaterMaterial from "../Shaders/Materials/Water.js";
* @exception {DeveloperError} strict: shader source does not use material.
*
* @see {@link https://github.com/CesiumGS/cesium/wiki/Fabric|Fabric wiki page} for a more detailed options of Fabric.
*
* @demo {@link https://sandcastle.cesium.com/index.html?src=Materials.html|Cesium Sandcastle Materials Demo}
*
* @example
Expand Down Expand Up @@ -1739,4 +1743,22 @@ Material._materialCache.addMaterial(Material.ElevationBandType, {
translucent: true,
});

/**
* Gets the name of the water mask material.
* @type {string}
* @readonly
*/
Material.WaterMaskType = "WaterMask";
Material._materialCache.addMaterial(Material.WaterMaskType, {
fabric: {
type: Material.WaterMaskType,
source: WaterMaskMaterial,
uniforms: {
waterColor: new Color(1.0, 1.0, 1.0, 1.0),
landColor: new Color(0.0, 0.0, 0.0, 0.0),
},
},
translucent: false,
});

export default Material;
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
* @property {float} height The height of the terrain in meters above or below the ellipsoid. Only available for globe materials.
* @property {float} slope The slope of the terrain in radians. 0 is flat; pi/2 is vertical. Only available for globe materials.
* @property {float} aspect The aspect of the terrain in radians. 0 is East, pi/2 is North, pi is West, 3pi/2 is South. Only available for globe materials.
* @property {float} waterMask The value of the water mask. 0 is land, 1 is water. Only available for globe materials.
*/
struct czm_materialInput
{
Expand All @@ -25,4 +26,5 @@ struct czm_materialInput
float height;
float slope;
float aspect;
float waterMask;
};
Loading

0 comments on commit 349364c

Please sign in to comment.