Vertex шейдеры

Вершинный шейдер формирует базовый уровень управления геометрией в WebGL-пайплайне и задаёт математическое преобразование вершин из локального пространства модели в координаты отсечения. В контексте CesiumJS именно этот этап определяет, как объекты сцены проецируются на эллипсоид Земли, как учитываются трансформации тайлов, инстансинг и геометрия террейна.

Вершинный шейдер работает на уровне отдельных вершин и выполняется параллельно для каждой из них. Основная задача — преобразование входных атрибутов в координаты clip-space и передача промежуточных данных (varyings) в фрагментный шейдер.


Модель координат в CesiumJS

Геометрический конвейер CesiumJS опирается на несколько пространств:

  • Model Space — локальные координаты геометрии
  • World Space (ECEF) — геоцентрическая система координат
  • View Space — пространство камеры
  • Clip Space — пространство отсечения

Базовое преобразование выполняется через цепочку матриц:

clipPosition = ProjectionMatrix * ViewMatrix * ModelMatrix * position

В CesiumJS это выражается через встроенные uniform-переменные:

  • czm_modelView
  • czm_projection
  • czm_modelViewProjection

Атрибуты вершин

Вершинный шейдер получает данные через атрибуты, которые описывают геометрию:

  • a_position — позиция вершины
  • a_normal — нормаль
  • a_st — UV-координаты
  • инстанс-атрибуты (a_instanceMatrix, a_instanceColor)

Типичный минимальный набор:

attribute vec3 a_position;
attribute vec3 a_normal;

При инстансинге добавляются дополнительные данные:

attribute mat4 a_instanceMatrix;

Базовая структура вершинного шейдера

В CesiumJS вершинный шейдер обычно включает три ключевых этапа:

  1. Преобразование позиции
  2. Вычисление нормалей
  3. Передача данных во фрагментный шейдер

Пример:

attribute vec3 a_position;

uniform mat4 czm_modelViewProjection;

void main()
{
    gl_Position = czm_modelViewProjection * vec4(a_position, 1.0);
}

Использование czm_ префикса

CesiumJS предоставляет набор встроенных uniform- и varying-переменных с префиксом czm_. Они инкапсулируют сложную математику сцены.

Наиболее важные:

  • czm_modelViewProjection
  • czm_modelView
  • czm_inverseModelView
  • czm_eyeHeight
  • czm_frameNumber

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


Работа с нормалями

Нормали в вершинном шейдере используются для освещения и вычисления направления света. Однако их нельзя трансформировать тем же образом, что и позиции.

Корректная трансформация требует использования inverse-transpose матрицы:

attribute vec3 a_normal;
uniform mat3 czm_normalMatrix;

varying vec3 v_normal;

void main()
{
    v_normal = czm_normalMatrix * a_normal;
}

Нормализация часто выполняется либо здесь, либо во фрагментном шейдере:

v_normal = normalize(v_normal);

Варьирующие переменные (varyings)

Varyings служат мостом между вершинным и фрагментным шейдером.

Типичные varyings:

  • координаты
  • нормали
  • UV-координаты
  • высота или скалярные поля
varying vec2 v_st;
varying vec3 v_positionEC;

CesiumJS использует строгую схему именования для избежания конфликтов между материалами и встроенными шейдерами.


Преобразование в eye coordinates

Часто требуется промежуточное пространство — eye coordinates:

vec4 eyePosition = czm_modelView * vec4(a_position, 1.0);

Это пространство используется для:

  • освещения
  • расчёта расстояний до камеры
  • атмосферных эффектов

Инстансинг в вершинных шейдерах

Инстансинг позволяет рендерить тысячи объектов с минимальными затратами CPU. В CesiumJS он используется для маркеров, тайлов и динамических сущностей.

Пример:

attribute vec3 a_position;
attribute mat4 a_instanceMatrix;

void main()
{
    vec4 worldPosition = a_instanceMatrix * vec4(a_position, 1.0);
    gl_Position = czm_modelViewProjection * worldPosition;
}

Инстансинг снижает количество draw calls и переносит вариативность в GPU.


Defer трансформации и геометрия плит

В CesiumJS большая часть сцены разбивается на плиточную структуру. Вершинные шейдеры получают уже частично трансформированные координаты, но финальная проекция выполняется на GPU.

Это позволяет:

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

Работа с террейном

Для географических данных вершины часто представляют собой сетку на эллипсоиде. Высота добавляется как смещение по нормали поверхности:

attribute float a_height;
attribute vec3 a_position;

void main()
{
    vec3 displaced = a_position + normalize(a_position) * a_height;
    gl_Position = czm_modelViewProjection * vec4(displaced, 1.0);
}

Такой подход используется для отображения цифровых моделей рельефа.


Clipping и плоскости отсечения

Вершинный шейдер может передавать данные для последующего отсечения:

varying float v_clipDistance;

void main()
{
    v_clipDistance = dot(a_position, clipPlaneNormal);
    gl_Position = czm_modelViewProjection * vec4(a_position, 1.0);
}

Далее фрагментный шейдер использует discard, если значение выходит за пределы.


Оптимизация вычислений

Вершинные шейдеры должны минимизировать:

  • условные ветвления
  • сложные функции
  • повторные вычисления матриц

CesiumJS часто предварительно вычисляет:

  • inverse matrices
  • normal matrices
  • bounding transforms

Это снижает нагрузку на GPU при рендеринге глобальной сцены.


Матричная иерархия в сцене

В CesiumJS сцена строится из иерархии:

  • Globe
  • Tiles
  • Primitives
  • Instances

Каждый уровень может добавлять собственную трансформацию:

ModelMatrixTile * ModelMatrixPrimitive * ModelMatrixInstance

Вершинный шейдер объединяет их в единую матрицу перед финальным преобразованием.


Клип-пространство и gl_Position

Финальная цель вершинного шейдера — вычисление gl_Position:

gl_Position = czm_modelViewProjection * vec4(a_position, 1.0);

После этого GPU выполняет:

  • clipping
  • perspective divide
  • viewport transform

Любая ошибка на этом этапе приводит к некорректному отображению геометрии.


Передача данных для освещения

Для PBR-материалов в вершинном шейдере формируются данные:

  • world position
  • normal
  • tangent space basis
varying vec3 v_positionEC;
varying vec3 v_normalEC;

Эти данные используются фрагментным шейдером для расчёта:

  • диффузного освещения
  • specular highlights
  • атмосферного рассеяния

Связь с системой материалов CesiumJS

Материальная система CesiumJS генерирует GLSL-код динамически. Вершинный шейдер может быть частично сгенерирован на основе:

  • MaterialAppearance
  • PerInstanceColorAppearance
  • EllipsoidSurfaceAppearance

Каждый материал добавляет собственные блоки к main().


Динамическая генерация вершинных шейдеров

CesiumJS использует шаблонную систему:

  • вставка varyings
  • вставка uniform declarations
  • объединение функций

Это позволяет комбинировать эффекты:

  • инстансинг + освещение
  • террейн + текстуры
  • clipping + instanced geometry

Заключительные аспекты архитектуры GPU-пайплайна

Вершинные шейдеры в CesiumJS выступают центральным звеном между геометрической моделью и глобальной сценой. Они обеспечивают:

  • унифицированную систему координат
  • поддержку географических преобразований
  • масштабируемый инстансинг
  • интеграцию с PBR и освещением

Именно на этом уровне формируется основа визуальной целостности сцены, где локальная геометрия превращается в часть глобальной модели планеты.