Фрагментные шейдеры в CesiumJS выполняют ключевую роль в формировании финального изображения сцены, определяя итоговый цвет каждого пикселя после прохождения геометрии через растеризацию. Именно на этом этапе происходит вычисление освещения, применение текстур, процедурных эффектов, атмосферных явлений и любых пользовательских визуальных модификаций.
Фрагментный шейдер работает на уровне пикселя и получает интерполированные данные от вершинного шейдера. В контексте CesiumJS он тесно связан с системой материалов, физически корректным рендерингом (PBR) и глобальными эффектами среды, такими как туман, атмосфера и глубина.
Графический конвейер WebGL, на котором основан CesiumJS, можно упростить до следующих этапов:
Фрагментный шейдер запускается для каждого фрагмента, потенциально соответствующего пикселю экрана. Его задача — вычислить финальный цвет с учётом всех входных параметров: текстур, освещения, прозрачности и эффектов постобработки.
В CesiumJS этот этап дополнительно обогащается системными
uniform-переменными, начинающимися с префикса czm_, которые
предоставляют доступ к состоянию сцены.
CesiumJS использует GLSL ES 1.0 (WebGL 1) и GLSL ES 3.0 (WebGL 2 в современных конфигурациях). При этом движок добавляет собственный слой абстракции, расширяющий стандартный GLSL.
Ключевые элементы среды шейдеров:
uniform — глобальные параметры сцены (матрицы, время,
освещение)varying — интерполированные данные между вершинным и
фрагментным этапамиczm_* — системные переменные CesiumJSТипичный набор входных данных фрагментного шейдера:
CesiumJS предоставляет широкий набор встроенных uniform-переменных:
czm_frameNumber — номер кадра, используется для
анимацийczm_viewer — параметры камерыczm_sceneMode — режим сцены (3D, Columbus View,
2D)czm_lightDirectionEC — направление света в eye
coordinatesczm_inverseViewMatrix — обратная матрица видаczm_material — структура материалаЭти переменные позволяют фрагментному шейдеру учитывать глобальное состояние сцены без необходимости передавать данные вручную.
Фрагментный шейдер в CesiumJS обычно начинается с объявления precision и входных данных:
#ifdef GL_ES
precision highp float;
#endif
varying vec2 v_textureCoordinates;
varying vec3 v_normalEC;
uniform sampler2D colorTexture;
Финальный цвет записывается в gl_FragColor (WebGL 1) или
возвращается через выходную переменную (WebGL 2).
Фрагментный шейдер часто используется для постобработки базового цвета:
void main() {
vec4 baseColor = texture2D(colorTexture, v_textureCoordinates);
// Усиление красного канала
baseColor.r *= 1.2;
gl_FragColor = baseColor;
}
Такой подход применяется в материалах поверхности, где необходимо слегка изменить визуальные свойства без полной переработки освещения.
В CesiumJS существует система материалов, позволяющая описывать визуальные свойства объектов через JSON-подобные конструкции. Эти материалы компилируются в GLSL-код, включающий фрагментный шейдер.
Структура материала:
Фрагментный шейдер материала обычно работает с объектом
czm_material:
czm_materialInput materialInput;
czm_material material = czm_getMaterial(materialInput);
gl_FragColor = vec4(material.diffuse, material.alpha);
Современный подход в CesiumJS предполагает использование
CustomShader, который позволяет напрямую переопределять
фрагментное поведение без вмешательства в старую систему материалов.
Пример подключения:
const customShader = new Cesium.CustomShader({
fragmentShaderText: `
void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material) {
material.diffuse *= vec3(1.0, 0.5, 0.5);
}
`
});
В этом случае Cesium сам интегрирует пользовательский код в общий PBR-пайплайн.
В CustomShader используется специальная функция:
fragmentMain — точка входа фрагментного шейдераFragmentInput — входные данныеczm_modelMaterial — материал для модификацииПример доступа к данным:
void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material) {
vec2 uv = fsInput.attributes.texCoord_0;
float intensity = sin(uv.x * 10.0);
material.emissive += vec3(intensity);
}
Фрагментные шейдеры в CesiumJS тесно связаны с моделью освещения. Основные элементы:
Пример использования направления света:
float diffuse = max(dot(normalize(v_normalEC), czm_lightDirectionEC), 0.0);
material.diffuse *= diffuse;
Эта формула реализует классическое ламбертово освещение.
Фрагментный шейдер активно участвует в расчёте атмосферы Земли и тумана.
Глубина используется для:
Пример псевдотумана:
float depth = gl_FragCoord.z / gl_FragCoord.w;
float fogFactor = exp(-depth * 0.02);
vec3 fogColor = vec3(0.7, 0.8, 1.0);
vec3 finalColor = mix(fogColor, material.diffuse, fogFactor);
Фрагментные шейдеры позволяют генерировать текстуры без изображений.
Типовые применения:
Пример градиента по высоте:
float h = fsInput.attributes.positionMC.z;
vec3 color = mix(vec3(0.1, 0.2, 0.5), vec3(0.8, 0.9, 0.3), h);
material.diffuse = color;
CesiumJS использует несколько пространств координат:
Фрагментный шейдер часто оперирует уже интерполированными значениями, но понимание системы критично для корректных эффектов освещения и деформации.
Пример нормализации в eye space:
vec3 normalEC = normalize(v_normalEC);
Фрагментный этап является наиболее затратным в GPU-пайплайне, так как выполняется для каждого пикселя.
Основные ограничения:
Оптимизационные подходы:
Отладка в CesiumJS выполняется косвенными методами:
Пример визуализации нормали:
gl_FragColor = vec4(normalize(v_normalEC) * 0.5 + 0.5, 1.0);
Такой подход позволяет проверять корректность геометрии и интерполяции.
CesiumJS использует физически корректный рендеринг, где фрагментный шейдер участвует в расчёте:
Эти параметры комбинируются в итоговый цвет материала:
material.metallic = 0.8;
material.roughness = 0.2;
material.emissive = vec3(0.1);
Фрагментный шейдер определяет alpha-компонент, влияющий на смешивание:
material.alpha *= 0.5;
При этом CesiumJS учитывает режимы blending:
Часть эффектов реализуется как экранные фрагментные шейдеры:
Они работают не с объектами сцены напрямую, а с итоговым буфером изображения.
Анимация в фрагментных шейдерах часто строится на:
czm_frameNumberПример пульсации:
float pulse = sin(czm_frameNumber * 0.05);
material.emissive += vec3(pulse);
Фрагментные шейдеры в CesiumJS формируют визуальный слой, на котором объединяются геометрия, освещение, атмосфера и процедурные эффекты, обеспечивая гибкость визуального контроля на уровне каждого пикселя сцены.