В основе визуализации лежит WebGL-контекст, привязанный к HTMLCanvasElement, который создаётся автоматически при инициализации Viewer или CesiumWidget. Canvas выступает единственной поверхностью, в которую происходит финальный вывод сцены, включая террейн, 3D Tiles, примитивы и постобработку.
Ключевая особенность архитектуры заключается в том, что кадр формируется напрямую в GPU-буфере, а DOM-элемент canvas лишь отображает результат. Это определяет специфику всех операций экспорта: они работают либо через чтение пикселей из WebGL-контекста, либо через сериализацию уже отрисованного кадра.
После создания Viewer доступ к canvas и сцене осуществляется через стандартные свойства:
const viewer = new Cesium.Viewer("cesiumContainer");
const canvas = viewer.scene.canvas;
const scene = viewer.scene;
Canvas здесь — обычный HTMLCanvasElement, но управляемый WebGL-контекстом Cesium. Любая попытка экспорта должна учитывать, что данные находятся не в DOM, а в GPU framebuffer.
Самый прямолинейный способ получить изображение — использовать стандартный API canvas:
const canvas = viewer.scene.canvas;
const imageBase64 = canvas.toDataURL("image/png");
Этот метод работает только при выполнении нескольких условий:
В реальных сценах Cesium кадр может быть не финализирован в момент вызова, поэтому результат часто требует синхронизации с рендер-циклом.
Cesium использует непрерывный или условный рендеринг. Для гарантированного получения актуального кадра применяется событие postRender:
viewer.scene.postRender.addEventListener(function () {
const canvas = viewer.scene.canvas;
const image = canvas.toDataURL("image/png");
});
Этот подход фиксирует момент, когда GPU уже завершил отрисовку кадра.
При использовании requestRenderMode сцена перестаёт обновляться автоматически, и экспорт становится детерминированным:
const viewer = new Cesium.Viewer("cesiumContainer", {
requestRenderMode: true
});
viewer.scene.requestRender();
viewer.scene.postRender.addEventListener(() => {
const img = viewer.scene.canvas.toDataURL("image/png");
});
Более контролируемый способ получения изображения — прямое чтение буфера кадра:
const scene = viewer.scene;
const context = scene.context;
const width = scene.canvas.width;
const height = scene.canvas.height;
const pixels = new Uint8Array(width * height * 4);
context.readPixels({
x: 0,
y: 0,
width: width,
height: height,
arrayBufferView: pixels
});
Этот метод позволяет обойти ограничения canvas API и получить «сырые» данные изображения.
Дальнейшая сборка изображения выполняется через создание второго canvas:
const outputCanvas = document.createElement("canvas");
outputCanvas.width = width;
outputCanvas.height = height;
const ctx = outputCanvas.getContext("2d");
const imageData = ctx.createImageData(width, height);
imageData.data.set(pixels);
ctx.putImageData(imageData, 0, 0);
const finalImage = outputCanvas.toDataURL("image/png");
Особенность WebGL заключается в инверсии оси Y, поэтому изображение часто требует вертикального переворота:
ctx.translate(0, height);
ctx.scale(1, -1);
Стандартный canvas ограничен физическим размером экрана. Для получения изображений высокого разрешения применяется масштабирование viewport.
const scale = 2.0;
const width = viewer.scene.canvas.width * scale;
const height = viewer.scene.canvas.height * scale;
const offscreenCanvas = document.createElement("canvas");
offscreenCanvas.width = width;
offscreenCanvas.height = height;
const ctx = offscreenCanvas.getContext("2d");
ctx.drawImage(viewer.scene.canvas, 0, 0, width, height);
const hiResImage = offscreenCanvas.toDataURL("image/png");
Такой подход увеличивает размер изображения ценой производительности и памяти.
Внутренний цикл рендеринга Cesium может быть активен или пассивен. При активном режиме кадры обновляются автоматически, при пассивном — только при запросе.
Для стабильного экспорта часто используется принудительный рендер:
viewer.render();
const image = viewer.scene.canvas.toDataURL("image/png");
Однако viewer.render() не всегда гарантирует завершение GPU-пайплайна, поэтому более надёжной считается комбинация с postRender.
Экспорт canvas в Cesium подчиняется ограничениям WebGL:
Особенно критичны тайлы из внешних источников, если сервер не отдаёт корректные заголовки CORS.
Cesium применяет постобработку (bloom, FXAA, depth of field). Canvas экспортирует уже финальный результат, но readPixels может вернуть промежуточный буфер в зависимости от конфигурации.
При активных postProcessStages:
viewer.scene.postProcessStages.bloom.enabled = true;
экспорт через canvas сохраняет визуальный эффект, но прямой readPixels может требовать дополнительной обработки, если используется multiple render targets.
Canvas содержит только WebGL сцену. DOM-элементы интерфейса Viewer (компас, кнопки навигации) не входят в экспорт.
Для полного скриншота используется наложение DOM поверх canvas:
html2canvas(document.querySelector("#cesiumContainer")).then(canvas => {
const image = canvas.toDataURL("image/png");
});
Такой подход выходит за пределы Cesium и объединяет UI и сцену в единое изображение.
Распространённая проблема — экспорт до завершения загрузки тайлов. В этом случае canvas содержит частично отрисованную сцену.
Контроль состояния выполняется через:
viewer.scene.globe.tilesLoaded.addEventListener(function () {
const image = viewer.scene.canvas.toDataURL("image/png");
});
Дополнительно учитывается:
В современных браузерах возможен OffscreenCanvas:
const offscreen = new OffscreenCanvas(800, 600);
const context = offscreen.getContext("webgl");
const imageBitmap = offscreen.transferToImageBitmap();
Cesium может быть адаптирован к offscreen контексту, что позволяет выполнять рендер и экспорт в Web Worker, разгружая основной поток.
WebGL в Cesium работает в sRGB или linear space в зависимости от конфигурации контекста:
scene.highDynamicRange = true;
Это влияет на итоговый экспорт: изображение может выглядеть иначе при прямом сохранении canvas.
Для стабилизации результата часто фиксируется:
Стабильный экспорт требует фиксации состояния сцены:
Типовой сценарий:
viewer.scene.requestRenderMode = true;
viewer.scene.globe.tilesLoaded.addEventListener(() => {
viewer.scene.requestRender();
});
viewer.scene.postRender.addEventListener(() => {
const image = viewer.scene.canvas.toDataURL("image/png");
});
Чтение пикселей из GPU — одна из самых дорогих операций в WebGL пайплайне. При частом вызове:
По этой причине экспорт обычно выполняется как разовая операция, а не в цикле.
Canvas в Cesium является финальной точкой рендеринга, поэтому:
Разделение на данные и визуализацию происходит до стадии WebGL, и canvas отражает только результат rasterization pipeline.