PerInstanceColorAppearance

CesiumJS предоставляет систему низкоуровневого рендеринга через примитивы (Primitive), где ключевую роль играет связка GeometryInstance + Appearance. Одним из наиболее производительных вариантов визуализации большого количества однотипных геометрий является PerInstanceColorAppearance.

Назначение и место в архитектуре рендеринга

PerInstanceColorAppearance предназначен для отображения множества геометрических экземпляров, где каждый экземпляр имеет собственный цвет, но при этом:

  • используется единый шейдерный пайплайн;
  • отсутствует необходимость создавать отдельный материал на каждый объект;
  • цвет передаётся как атрибут вершины (per-instance attribute);
  • минимизируется количество draw calls.

Ключевая идея заключается в том, что цвет не является частью материала, а хранится в данных экземпляра геометрии.


Сравнение с MaterialAppearance

Cesium предоставляет несколько типов appearance. Наиболее часто сравниваются:

  • MaterialAppearance — материал задаётся через procedural/material system;
  • PerInstanceColorAppearance — цвет задаётся на уровне экземпляра;
  • EllipsoidSurfaceAppearance — специализированный вариант для поверхности эллипсоида.

Основные отличия:

MaterialAppearance

  • гибкие шейдерные материалы;
  • поддержка сложных процедурных эффектов;
  • более высокая стоимость рендеринга при множестве объектов.

PerInstanceColorAppearance

  • минимальный overhead;
  • идеален для массовых объектов;
  • ограниченная модель shading (по сути flat shading + color attribute);
  • максимальная производительность при батчинге.

Модель данных: GeometryInstance и color attribute

Основная связка выглядит следующим образом:

  • GeometryInstance — описывает геометрию;
  • PerInstanceColorAppearance — интерпретирует атрибут цвета.

Цвет задаётся через ColorGeometryInstanceAttribute:

  • RGBA компоненты;
  • нормализованные значения 0–1;
  • может быть разный для каждого экземпляра.

Базовая структура использования

Типичный паттерн включает создание массива экземпляров:

const instances = [];

for (let i = 0; i < 1000; i++) {
  instances.push(
    new Cesium.GeometryInstance({
      geometry: new Cesium.BoxGeometry({
        vertexFormat: Cesium.PerInstanceColorAppearance.VERTEX_FORMAT,
        maximum: new Cesium.Cartesian3(10000, 10000, 10000),
        minimum: new Cesium.Cartesian3(-10000, -10000, -10000)
      }),
      modelMatrix: Cesium.Matrix4.multiplyByTranslation(
        Cesium.Transforms.eastNorthUpToFixedFrame(
          Cesium.Cartesian3.fromDegrees(30 + i * 0.01, 50)
        ),
        new Cesium.Cartesian3(0.0, 0.0, 0.0),
        new Cesium.Matrix4()
      ),
      attributes: {
        color: Cesium.ColorGeometryInstanceAttribute.fromColor(
          Cesium.Color.fromRandom({ alpha: 1.0 })
        )
      }
    })
  );
}

Далее все экземпляры передаются в Primitive:

const primitive = new Cesium.Primitive({
  geometryInstances: instances,
  appearance: new Cesium.PerInstanceColorAppearance({
    flat: true,
    translucent: false
  })
});

Внутренний принцип работы

1. Vertex format

PerInstanceColorAppearance.VERTEX_FORMAT включает:

  • position;
  • per-instance color attribute.

Это означает, что цвет не интерполируется как материал, а применяется на уровне вершин/инстанса.


2. Шейдерная модель

Шейдер PerInstanceColorAppearance минималистичен:

  • vertex shader:

    • трансформирует позицию;
    • передаёт цвет в fragment shader;
  • fragment shader:

    • возвращает интерполированный или flat цвет.

При flat: true интерполяция отключается, и используется единый цвет на примитив.


3. Render state

Appearance конфигурирует render state:

  • blending (для translucent объектов);
  • depth test;
  • culling;
  • write mask.

В режиме translucent: false включается оптимизация depth writing.


Flat shading и влияние на производительность

Параметр flat критически влияет на поведение:

  • flat: true

    • отключает интерполяцию нормалей;
    • снижает нагрузку на fragment shader;
    • ускоряет отрисовку больших массивов объектов.
  • flat: false

    • допускает сглаживание;
    • увеличивает стоимость shading pipeline;
    • используется редко для данного appearance.

Массовый рендеринг и batching

Главное преимущество PerInstanceColorAppearance — автоматический batching:

  • несколько сотен/тысяч объектов объединяются в один draw call;
  • GPU получает единый буфер геометрии;
  • различия между объектами кодируются через instance attributes.

Это особенно эффективно для:

  • визуализации тайлов;
  • построения 3D heatmaps;
  • отображения больших наборов маркеров в 3D;
  • генерации процедурных городских структур.

Ограничения модели

Несмотря на высокую производительность, существуют ограничения:

  • невозможность сложных материалов (texture mapping ограничен);
  • отсутствует физически корректное освещение;
  • нет поддержки индивидуальных shader materials;
  • цвет — единственный гибкий параметр инстанса;
  • сложные эффекты требуют перехода на MaterialAppearance.

Работа с прозрачностью

При использовании прозрачности:

new Cesium.PerInstanceColorAppearance({
  translucent: true
});

активируются:

  • blending режимы;
  • сортировка по глубине (частично);
  • потенциальное снижение производительности при большом количестве объектов.

Особенности:

  • прозрачные инстансы не всегда корректно сортируются;
  • возможны артефакты перекрытия;
  • предпочтительно использовать непрозрачный режим при массовом рендере.

Использование с различными геометриями

PerInstanceColorAppearance совместим с большинством встроенных геометрий:

  • BoxGeometry;
  • SphereGeometry;
  • CylinderGeometry;
  • EllipsoidGeometry;
  • PolygonGeometry (через соответствующие vertex formats).

Главное условие — поддержка PerInstanceColorAppearance.VERTEX_FORMAT.


Пример архитектуры крупной сцены

В высоконагруженных сценах структура обычно следующая:

  • слой 1: terrain tiles;
  • слой 2: primitives с PerInstanceColorAppearance;
  • слой 3: billboards / labels;
  • слой 4: dynamic entities.

PerInstanceColorAppearance чаще всего используется во 2-м слое как основной инструмент bulk-рендеринга.


Динамическое обновление цветов

Изменение цвета без пересоздания геометрии возможно через:

  • обновление GeometryInstance.attributes.color;
  • пересоздание primitive (дороже);
  • использование CallbackProperty в более высокоуровневых API (через Entity слой).

Однако прямое обновление instance attribute требует аккуратного управления буферами GPU.


Типичные ошибки при использовании

  • использование MaterialAppearance вместо PerInstanceColorAppearance для массовых объектов;
  • отсутствие vertexFormat у геометрии;
  • попытка применять сложные материалы;
  • неправильная настройка translucent режима;
  • создание слишком большого числа Primitive вместо одного батча.

Оптимизационные практики

  • объединение всех совместимых экземпляров в один Primitive;
  • использование flat: true по умолчанию;
  • минимизация translucent объектов;
  • предварительная генерация modelMatrix вместо вычислений в runtime;
  • избегание частых rebuild primitive.

Роль в WebGL pipeline Cesium

PerInstanceColorAppearance напрямую соответствует WebGL instancing подходу:

  • один vertex buffer;
  • instance buffer с цветами;
  • единый draw call;
  • GPU-side трансформация и раскраска.

Это делает его одним из ключевых механизмов масштабируемой визуализации в CesiumJS.