Высотные карты в CesiumJS являются основным механизмом формирования рельефа на глобусе и в локальных сценах. Они определяют геометрию поверхности Земли или любой другой планеты через регулярную сетку значений высот, которые интерпретируются движком как отклонение от эллипсоида или геоида. Внутри движка высотные данные тесно связаны с системой тайлов, где каждый участок поверхности разбивается на иерархическую структуру плиток, соответствующих уровням детализации.
Высотная карта представляет собой двумерный массив значений, где каждая ячейка хранит высоту в метрах над референсной поверхностью. В контексте CesiumJS эта сетка преобразуется в геометрическую поверхность через тесселяцию тайлов.
Ключевой принцип заключается в том, что:
При увеличении масштаба CesiumJS автоматически подгружает более детализированные тайлы, заменяя грубую сетку более плотной.
Внутренне высотная карта преобразуется в трёхмерные координаты с использованием эллипсоида WGS84. Каждая точка сетки задаётся координатами:
Функция преобразования выглядит концептуально следующим образом:
Cartesian3 = Ellipsoid.WGS84.cartographicToCartesian(
new Cartographic(longitude, latitude, height)
);
Однако в случае terrain pipeline высота применяется уже после проекции координат тайла, что позволяет оптимизировать вычисления и использовать батчинг.
CesiumJS поддерживает несколько форматов terrain, каждый из которых по-разному кодирует heightmap.
Наиболее распространённый формат. Использует квантизацию высот и топологические соседства треугольников.
Особенности:
Структура включает:
Более простой формат, где высоты представлены прямоугольной сеткой.
Используется в случаях:
Пример загрузки heightmap-тайлов:
const terrainProvider = new Cesium.HeightmapTerrainProvider({
url: "https://server.example.com/terrain",
width: 65,
height: 65
});
const viewer = new Cesium.Viewer("cesiumContainer", {
terrainProvider: terrainProvider
});
Система terrain в CesiumJS использует quadtree-структуру. Каждый тайл может быть рекурсивно разделён на четыре дочерних.
Основные принципы:
Формула оценки ошибки:
SSE ≈ geometricError * screenSpaceFactor
Где geometricError зависит от максимального отклонения высот внутри тайла.
При превышении порога SSE тайл subdivide-ится.
После загрузки terrain данные проходят несколько стадий:
На уровне GPU terrain представлен как набор треугольников, где высота уже встроена в vertex position.
Одной из ключевых проблем heightmap-рендеринга является появление щелей между тайлами разного уровня детализации.
CesiumJS решает это через skirts — дополнительные вертикальные полигоны по краям тайла.
Принцип:
Это позволяет избежать необходимости сложного edge stitching в runtime.
Для уменьшения объёма данных высоты часто хранятся в нормализованном виде.
Типичная схема:
Формула восстановления:
height = minHeight + (quantizedValue / 65535) * (maxHeight - minHeight)
Это позволяет резко сократить сетевой трафик при передаче terrain.
CesiumJS позволяет интегрировать собственные источники высот через TerrainProvider API.
Пример кастомного провайдера:
class CustomTerrainProvider {
constructor() {
this.tilingScheme = new Cesium.GeographicTilingScheme();
this.errorEvent = new Cesium.Event();
this.ready = true;
this.hasWaterMask = false;
this.hasVertexNormals = false;
}
requestTileGeometry(x, y, level) {
const url = `/terrain/${level}/${x}/${y}.bin`;
return Cesium.Resource.fetchArrayBuffer(url)
.then(buffer => {
return Cesium.HeightmapTessellator.computeVertices({
buffer: new Uint16Array(buffer),
width: 65,
height: 65,
skirtHeight: 2000
});
});
}
}
LOD-выбор основан на метрике ошибки, которая измеряет максимальное отклонение поверхности тайла от аппроксимации.
Каждый тайл хранит:
Bounding volume может быть:
Чем выше error, тем раньше тайл будет subdivide-нут.
Heightmaps не существуют изолированно: они тесно связаны с текстурными слоями.
Порядок рендеринга:
UV-координаты вычисляются на основе географической проекции тайла.
При работе с глобальными моделями применяются оптимизации:
CesiumJS поддерживает автоматическое управление памятью, выгружая невидимые тайлы.
Используемая проекция (обычно WGS84 ellipsoid) влияет на распределение сетки.
Особенности:
Это требует адаптивного LOD для равномерного качества визуализации.
На уровне GPU heightmap превращается в vertex buffer objects:
Фрагмент шейдера концептуально:
vec3 positionWC = czm_ellipsoidScale * positionMC;
gl_Position = czm_modelViewProjection * vec4(positionWC, 1.0);
Высота уже встроена в positionMC.
Основные ограничения при работе с heightmaps:
Оптимизация достигается через:
Помимо серверных данных, heightmap может быть сгенерирован процедурно.
Используется:
Пример генерации:
function getHeight(x, y) {
return noise(x * 0.01, y * 0.01) * 1200;
}
Такие данные интегрируются через custom terrain provider.
Проблема несоответствия границ возникает при разных LOD. CesiumJS решает её через:
В некоторых форматах quantized mesh дополнительно хранит edge adjacency.
Heightmaps применяются не только для визуализации:
CesiumJS предоставляет доступ к высотным данным через sampling API:
viewer.scene.sampleHeight(
new Cesium.Cartographic(longitude, latitude)
);