GLSL основы

GLSL (OpenGL Shading Language) представляет собой язык программирования шейдеров, исполняемых на GPU в рамках графического конвейера WebGL. В экосистеме CesiumJS GLSL используется для описания поведения вершин и пикселей при рендеринге глобуса, 3D-тайлов, примитивов и пользовательских визуальных эффектов.

Шейдеры разделяются на два ключевых типа: вершинные (vertex) и фрагментные (fragment). В CesiumJS они применяются для кастомизации материалов, постобработки, а также расширения стандартного визуального пайплайна через Custom Shader и низкоуровневые GLSL-фрагменты.


Графический конвейер и место GLSL

GPU-пайплайн состоит из последовательных стадий обработки геометрии:

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

GLSL задействуется на двух стадиях:

Vertex Shader

  • трансформация вершин
  • вычисление промежуточных данных для передачи в фрагментный шейдер

Fragment Shader

  • вычисление итогового цвета пикселя
  • работа с освещением, текстурами, прозрачностью

CesiumJS добавляет собственные соглашения и утилитарные функции, начинающиеся с префикса czm_, упрощающие работу с координатами и матрицами.


Базовая структура GLSL-шейдера

Типичный фрагментный шейдер имеет следующую форму:

precision highp float;

uniform sampler2D u_texture;

varying vec2 v_uv;

void main() {
    vec4 color = texture2D(u_texture, v_uv);
    gl_FragColor = color;
}

Ключевые элементы:

  • precision — точность вычислений
  • uniform — глобальные параметры, одинаковые для всех вызовов шейдера
  • varying — данные, интерполируемые между vertex и fragment стадиями
  • gl_FragColor — итоговый цвет пикселя (в WebGL1)

Типы данных GLSL

GLSL использует строгую типизацию:

Скалярные типы

  • float
  • int
  • bool

Векторные типы

  • vec2, vec3, vec4
  • ivec2, ivec3, ivec4
  • bvec2, bvec3, bvec4

Матрицы

  • mat2, mat3, mat4

Пример:

vec3 position = vec3(1.0, 2.0, 3.0);
mat4 transform = mat4(1.0);

Векторные операции

GLSL предоставляет встроенную поддержку математических операций над векторами:

vec3 a = vec3(1.0, 2.0, 3.0);
vec3 b = vec3(3.0, 2.0, 1.0);

vec3 sum = a + b;
vec3 scaled = a * 2.0;
float dotProduct = dot(a, b);
vec3 crossProduct = cross(a, b);

Компоненты вектора доступны через swizzling:

vec4 color = vec4(1.0, 0.5, 0.2, 1.0);
vec3 rgb = color.rgb;
float alpha = color.a;

Uniform-переменные в CesiumJS

Uniform-значения передаются из JavaScript-кода в шейдер:

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

Пример концептуального использования:

uniform mat4 u_modelViewMatrix;
uniform mat4 u_projectionMatrix;
uniform float u_time;

CesiumJS автоматически предоставляет множество системных uniform-переменных, включая матрицы координатных преобразований и параметры сцены.


Varying и передача данных между стадиями

Varying-переменные обеспечивают интерполяцию данных между вершинным и фрагментным шейдером:

Vertex shader:

varying vec2 v_uv;

void main() {
    v_uv = uvCoordinates;
}

Fragment shader:

varying vec2 v_uv;

void main() {
    gl_FragColor = vec4(v_uv, 0.0, 1.0);
}

Пространства координат в CesiumJS

Работа с GLSL в CesiumJS тесно связана с множеством пространств координат:

  • Object Space — локальная система модели
  • World Space — глобальная сцена
  • Eye Space — пространство камеры
  • Clip Space — нормализованное пространство отсечения
  • Screen Space — координаты экрана

Матрицы трансформации выполняют переходы между ними:

gl_Position = czm_projection * czm_view * czm_model * vec4(position, 1.0);

czm_projection, czm_view, czm_model — стандартные матрицы CesiumJS.


Precision qualifiers

Точность вычислений критична для мобильных GPU:

precision highp float;
precision mediump int;
  • lowp — низкая точность, высокая производительность
  • mediump — баланс
  • highp — максимальная точность

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


Встроенные функции GLSL

GLSL включает широкий набор математических функций:

Тригонометрия

sin(angle)
cos(angle)
tan(angle)

Математические операции

abs(x)
min(a, b)
max(a, b)
clamp(x, 0.0, 1.0)

Интерполяция

mix(a, b, t)
smoothstep(edge0, edge1, x)

Шейдеры в CesiumJS и czm_ API

CesiumJS расширяет GLSL набором встроенных функций:

  • czm_transformPoint
  • czm_inverseView
  • czm_view
  • czm_projection
  • czm_normal

Пример использования:

vec4 eyePosition = czm_view * vec4(position, 1.0);

Такие функции обеспечивают корректную работу с эллипсоидальной моделью Земли и глобальной системой координат.


Фрагментные шейдеры и освещение

Фрагментный шейдер часто реализует модель освещения:

vec3 normal = normalize(v_normal);
vec3 lightDir = normalize(u_lightPosition - v_position);

float diff = max(dot(normal, lightDir), 0.0);

vec3 diffuse = diff * u_color;

Модель освещения может расширяться до Phong или PBR-подходов, используемых в более сложных материалах CesiumJS.


Работа с текстурами

Текстуры загружаются как sampler2D:

uniform sampler2D u_texture;

vec4 texColor = texture2D(u_texture, v_uv);

UV-координаты обычно передаются из вершинного шейдера и интерполируются.


Условные операторы и ветвления

GLSL поддерживает условные конструкции, но их использование в GPU требует осторожности:

if (diff > 0.5) {
    color = vec3(1.0);
} else {
    color = vec3(0.0);
}

Избыточные ветвления могут снижать производительность из-за SIMD-архитектуры GPU.


Циклы и ограничения

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

for (int i = 0; i < 5; i++) {
    sum += values[i];
}

Динамические циклы ограничены или не поддерживаются в зависимости от GPU и WebGL версии.


Типичные паттерны в CesiumJS шейдерах

Пульсация цвета по времени

float pulse = sin(u_time * 2.0) * 0.5 + 0.5;
vec3 color = mix(vec3(0.0, 0.2, 1.0), vec3(1.0, 0.8, 0.0), pulse);

Высотное окрашивание

float heightFactor = clamp(v_position.z / 10000.0, 0.0, 1.0);
vec3 color = mix(vec3(0.0, 0.3, 0.6), vec3(0.2, 1.0, 0.4), heightFactor);

Ошибки и особенности отладки

Типичные проблемы GLSL в CesiumJS:

  • несоответствие precision
  • неправильные пространства координат
  • ошибки интерполяции varyings
  • переполнение float при больших координатах
  • несогласованность uniform-переменных

Отладка часто сводится к визуализации промежуточных значений через цветовые каналы:

gl_FragColor = vec4(v_normal * 0.5 + 0.5, 1.0);