全新的全球 tileset——Cesium World Bathymetry可以帮助你构建用于可视化水下环境(如海底、海岸线或内陆水体)的应用。本文将介绍在 CesiumJS 中增强测深地形可视化的一些最佳实践,比如照明、等高线与深度着色。
文中的代码示例摘自一个完整的 Sandcastle 示例(可直接运行、调整参数)。
下文会把示例中展示的功能逐一拆解说明。
连接 Cesium ion,并将 Cesium World Bathymetry 设为地形
const viewer = new Cesium.Viewer("cesiumContainer", {
terrainProvider: await Cesium.CesiumTerrainProvider.fromIonAssetId(2426648),
timeline: false,
animation: false,
});
启用照明(Enable lighting)
给场景打光可以更容易看清 3D 地形的起伏轮廓。虽然通常贴在地形上的卫星影像在拍摄时自带光照与阴影,但水体区域的影像往往并不明亮。启用 CesiumJS 的光照选项可以突出“山”和“谷”,更易理解水下地形的形态。
“未开启照明”的效果(夏威夷区域)

“开启照明”的效果(同一区域)

由于更强调了地形几何,后续示例中我们会把全局的 maximumScreenSpaceError 从默认值下调,以便更早加载更高细节。
关闭地表大气与雾效(Disable ground atmosphere and fog)
大气与雾是对陆地显示很有用的可视化功能,但对测深可视化不一定友好。
- “大气”结合了光照与颜色效果,让地球与天空更逼真,帮助观众感知距离与时间。
- “雾”则在地平线方向将大气与远处几何进行混合,既更真实,也有助于性能。
不过,这两者都会让水下高程(深度)变化更难分辨。下面的示例代码会:打开照明、关闭大气与雾;并用 Cesium.DirectionalLight 做坡向加亮(hillside lighting),让原本容易“糊成一片”的斜坡更立体。注意:光照计算需要顶点法线,向地形请求时务必带上 requestVertexNormals: true。
const viewer = new Cesium.Viewer("cesiumContainer", {
terrainProvider: await Cesium.CesiumTerrainProvider.fromIonAssetId(
2426648,
{ requestVertexNormals: true }
),
});
const scene = viewer.scene;
const globe = scene.globe;
const camera = scene.camera;
scene.fog.enabled = false;
globe.showGroundAtmosphere = false;
globe.enableLighting = true;
// 使用定向光,并在每帧根据相机方向更新光照方向
scene.light = new Cesium.DirectionalLight({
direction: new Cesium.Cartesian3(1, 0, 0), // 每帧更新
});
const scratchNormal = new Cesium.Cartesian3();
scene.preRender.addEventListener(function (scene, time) {
const surfaceNormal = globe.ellipsoid.geodeticSurfaceNormal(
camera.positionWC,
scratchNormal
);
const negativeNormal = Cesium.Cartesian3.negate(surfaceNormal, surfaceNormal);
scene.light.direction = Cesium.Cartesian3.normalize(
Cesium.Cartesian3.add(
negativeNormal,
camera.rightWC,
surfaceNormal
),
scene.light.direction
);
});
// 更低的 SSE 让高分辨率 tile 更早加载,利于海底阴影/着色
globe.maximumScreenSpaceError = 1.0;
“开启雾效”的地平线视图(夏威夷区域)

“关闭雾效”的地平线视图(同一区域)

选择更适合的全球影像(Choose the best global imagery)
CesiumJS 默认的影像提供者是 Bing Aerial imagery,它在水体区域偏暗,缺乏对比度会让测深更难看清。作为 Cesium ion 的一部分,Sentinel-2 影像更明亮,通常能更好突出海洋中的地形数据。
Bing Aerial imagery(加州外海)

Sentinel-2 imagery(同一区域)

可视化并不限于卫星影像。简化或风格化底图(例如 Stadia Alidade)与测深地形、光照搭配时,能获得更高对比度。
菲律宾外海,使用 Stadia Alidade 底图

夸张纵向比例(Exaggerate the elevation)
对测深高程做“夸张”处理可以放大高度差,从而更容易看清细微起伏。
黑海区域(无夸张)

黑海区域(开启夸张)

示例:将地形夸张系数设为 5
viewer.scene.verticalExaggeration = 5.0;
建议在应用中加入滑杆,允许用户自行调整夸张程度。
用颜色与等高线表示高程(Use color and contour lines to indicate elevation)
除了光照与夸张,程序化着色(例如根据高程的 color ramp、等高线)可以提供更多视觉洞见。下面的示例包含一个常用的海洋配色方案,并允许用户在不同底色下反转等高线颜色以提升可见性。可根据你的场景选择更合适的配色与等高线参数,并在界面上提供图例帮助理解。
下面的代码会创建:
- 高程 color ramp
- 用于高程着色与等高线叠加的材质
- 随距离变化的等高线密度
- 当用户操作控件时动态更新材质,并应用到地球
// Globe material constants
const minHeight = -10000.0;
const approximateSeaLevel = 0.0;
const maxHeight = 2000.0;
const countourLineSpacing = 500.0;
const range = maxHeight - minHeight;
const d = (height) => (height - minHeight) / range;
// Create a color ramp based on https://matplotlib.org/cmocean/#deep
function getColorRamp() {
const ramp = document.createElement("canvas");
ramp.width = 100;
ramp.height = 15;
const ctx = ramp.getContext("2d");
const grd = ctx.createLinearGradient(0, 0, 100, 0);
grd.addColorStop(d(maxHeight), "#B79E6C");
grd.addColorStop(d(100.0), "#FBFFEE");
grd.addColorStop(d(0.0), "#F9FCCA");
grd.addColorStop(d(-500.0), "#BDE7AD");
grd.addColorStop(d(-1000.0), "#81D2A3");
grd.addColorStop(d(-1500.0), "#5AB7A4");
grd.addColorStop(d(-2000.0), "#4C9AA0");
grd.addColorStop(d(-2500.0), "#437D9A");
grd.addColorStop(d(-4000.0), "#3E6194");
grd.addColorStop(d(-5000.0), "#424380");
grd.addColorStop(d(-8000.0), "#392D52");
grd.addColorStop(d(minHeight), "#291C2F");
ctx.fillStyle = grd;
ctx.fillRect(0, 0, ramp.width, ramp.height);
return ramp;
}
// Creates a composite material with both elevation shading and contour lines
function getElevationContourMaterial() {
return new Cesium.Material({
fabric: {
type: "ElevationColorContour",
materials: {
contourMaterial: {
type: "ElevationContour",
},
elevationRampMaterial: {
type: "ElevationRamp",
},
},
components: {
diffuse:
"(1.0 - contourMaterial.alpha) * elevationRampMaterial.diffuse + contourMaterial.alpha * contourMaterial.diffuse",
alpha: "max(contourMaterial.alpha, elevationRampMaterial.alpha)",
},
},
translucent: false,
});
}
// Apply the material to the globe
function updateGlobeMaterial() {
const material = getElevationContourMaterial();
// 高程着色
let shadingUniforms = material.materials.elevationRampMaterial.uniforms;
shadingUniforms.image = getColorRamp();
shadingUniforms.minimumHeight =
minHeight * viewer.scene.verticalExaggeration;
shadingUniforms.maximumHeight =
maxHeight * viewer.scene.verticalExaggeration;
// 等高线
shadingUniforms = material.materials.contourMaterial.uniforms;
shadingUniforms.width = 1.0;
shadingUniforms.spacing =
countourLineSpacing * viewer.scene.verticalExaggeration;
shadingUniforms.color = Cesium.Color.BLACK.withAlpha(0.5);
viewer.scene.globe.material = material;
}
updateGlobeMaterial();
南加州外海,Esri World Ocean Imagery(仅底图)

同一区域,叠加高程 color ramp 与等高线

原文地址:https://cesium.com/blog/2024/01/29/cesium-world-bathymetry-in-cesiumjs/

2980

被折叠的 条评论
为什么被折叠?



