Вершинный шейдер формирует базовый уровень управления геометрией в WebGL-пайплайне и задаёт математическое преобразование вершин из локального пространства модели в координаты отсечения. В контексте CesiumJS именно этот этап определяет, как объекты сцены проецируются на эллипсоид Земли, как учитываются трансформации тайлов, инстансинг и геометрия террейна.
Вершинный шейдер работает на уровне отдельных вершин и выполняется параллельно для каждой из них. Основная задача — преобразование входных атрибутов в координаты clip-space и передача промежуточных данных (varyings) в фрагментный шейдер.
Геометрический конвейер CesiumJS опирается на несколько пространств:
Базовое преобразование выполняется через цепочку матриц:
clipPosition = ProjectionMatrix * ViewMatrix * ModelMatrix * position
В CesiumJS это выражается через встроенные uniform-переменные:
czm_modelViewczm_projectionczm_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 вершинный шейдер обычно включает три ключевых этапа:
Пример:
attribute vec3 a_position;
uniform mat4 czm_modelViewProjection;
void main()
{
gl_Position = czm_modelViewProjection * vec4(a_position, 1.0);
}
CesiumJS предоставляет набор встроенных uniform- и varying-переменных
с префиксом czm_. Они инкапсулируют сложную математику
сцены.
Наиболее важные:
czm_modelViewProjectionczm_modelViewczm_inverseModelViewczm_eyeHeightczm_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:
varying vec2 v_st;
varying vec3 v_positionEC;
CesiumJS использует строгую схему именования для избежания конфликтов между материалами и встроенными шейдерами.
Часто требуется промежуточное пространство — 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.
В 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);
}
Такой подход используется для отображения цифровых моделей рельефа.
Вершинный шейдер может передавать данные для последующего отсечения:
varying float v_clipDistance;
void main()
{
v_clipDistance = dot(a_position, clipPlaneNormal);
gl_Position = czm_modelViewProjection * vec4(a_position, 1.0);
}
Далее фрагментный шейдер использует discard, если
значение выходит за пределы.
Вершинные шейдеры должны минимизировать:
CesiumJS часто предварительно вычисляет:
Это снижает нагрузку на GPU при рендеринге глобальной сцены.
В CesiumJS сцена строится из иерархии:
Каждый уровень может добавлять собственную трансформацию:
ModelMatrixTile * ModelMatrixPrimitive * ModelMatrixInstance
Вершинный шейдер объединяет их в единую матрицу перед финальным преобразованием.
Финальная цель вершинного шейдера — вычисление
gl_Position:
gl_Position = czm_modelViewProjection * vec4(a_position, 1.0);
После этого GPU выполняет:
Любая ошибка на этом этапе приводит к некорректному отображению геометрии.
Для PBR-материалов в вершинном шейдере формируются данные:
varying vec3 v_positionEC;
varying vec3 v_normalEC;
Эти данные используются фрагментным шейдером для расчёта:
Материальная система CesiumJS генерирует GLSL-код динамически. Вершинный шейдер может быть частично сгенерирован на основе:
MaterialAppearancePerInstanceColorAppearanceEllipsoidSurfaceAppearanceКаждый материал добавляет собственные блоки к
main().
CesiumJS использует шаблонную систему:
Это позволяет комбинировать эффекты:
Вершинные шейдеры в CesiumJS выступают центральным звеном между геометрической моделью и глобальной сценой. Они обеспечивают:
Именно на этом уровне формируется основа визуальной целостности сцены, где локальная геометрия превращается в часть глобальной модели планеты.