В основе визуализации Deck.gl лежит WebGL-пайплайн, в котором графическая обработка данных делится между двумя ключевыми программами: vertex shader и fragment shader. Эти этапы выполняются на GPU и определяют, каким образом геометрия преобразуется в пиксели на экране.
Vertex shader отвечает за обработку вершин геометрии. На этом этапе выполняются преобразования координат, вычисление атрибутов, передача данных в следующий этап пайплайна.
Fragment shader определяет цвет каждого пикселя, который попадает в финальное изображение. Он работает уже с интерполированными данными после растеризации.
В Deck.gl оба типа шейдеров являются частью слоя (Layer) и могут быть
переопределены через систему shaders в классе слоя.
Каждый слой Deck.gl содержит собственный набор шейдеров, обычно организованных как:
vertexShaderfragmentShadermodules (GLSL-модули)defines (константы препроцессора)uniforms (передаваемые параметры)Базовая структура слоя с кастомными шейдерами:
import {ScatterplotLayer} from '@deck.gl/layers';
const layer = new ScatterplotLayer({
id: 'scatter',
data: [],
getPosition: d => d.position,
getRadius: d => d.radius,
getFillColor: d => d.color,
shaders: {
vertexShader: `
attribute vec3 positions;
attribute float sizes;
varying float vSize;
void main(void) {
vSize = sizes;
gl_Position = project_position_to_clipspace(positions);
gl_PointSize = sizes;
}
`,
fragmentShader: `
precision highp float;
varying float vSize;
void main(void) {
gl_FragColor = vec4(1.0, 0.5, 0.2, 1.0);
}
`
}
});
Vertex shader в Deck.gl работает с абстракцией геопространственных данных. В отличие от «чистого» WebGL, здесь координаты часто находятся в системе WGS84 или локальных мировых координатах.
Ключевая особенность Deck.gl — наличие встроенной системы проекций
через project_* функции.
Основные задачи vertex shader:
Пример ключевой функции Deck.gl:
gl_Position = project_position_to_clipspace(position);
Эта функция инкапсулирует сложную цепочку преобразований:
Vertex shader получает данные через атрибуты:
attribute vec3 positionsattribute vec4 colorsattribute float radiusDeck.gl автоматически связывает эти атрибуты с функциями доступа слоя
(getPosition, getColor, и т.д.).
Передача данных в fragment shader осуществляется через
varying:
varying vec4 vColor;
varying float vRadius;
Пример:
void main(void) {
vColor = colors;
vRadius = radius;
gl_Position = project_position_to_clipspace(positions);
}
Fragment shader выполняется после растеризации. Каждый фрагмент соответствует потенциальному пикселю.
Основные задачи:
Пример базового fragment shader:
precision highp float;
varying vec4 vColor;
void main(void) {
gl_FragColor = vColor;
}
Deck.gl активно использует альфа-канал. Fragment shader часто возвращает цвета с прозрачностью:
gl_FragColor = vec4(color.rgb, 0.5);
При этом итоговое отображение зависит от режима blending WebGL:
SRC_ALPHAONE_MINUS_SRC_ALPHAНеправильная настройка приводит к артефактам наложения слоёв.
Uniforms позволяют передавать данные из JavaScript в шейдер без привязки к вершинам.
Пример:
new ScatterplotLayer({
id: 'layer',
data: [],
uniforms: {
opacity: 0.7
}
});
GLSL:
uniform float opacity;
void main(void) {
gl_FragColor = vec4(1.0, 0.0, 0.0, opacity);
}
Uniforms часто используются для:
Deck.gl предоставляет модульную систему GLSL:
project32pickinglightingfp64Подключение модулей:
import {COORDINATE_SYSTEM} from '@deck.gl/core';
const layer = new ScatterplotLayer({
data: [],
coordinateSystem: COORDINATE_SYSTEM.LNGLAT
});
В шейдере появляются дополнительные функции:
project_positionproject_normalgetPickingColorFragment shader почти всегда начинается с определения precision:
precision highp float;
В WebGL ES ограничения точности влияют на:
Deck.gl использует high precision для минимизации ошибок в геопространственных данных.
Связь между стадиями строго типизирована:
Vertex shader:
varying vec3 vPosition;
Fragment shader:
varying vec3 vPosition;
Передача происходит с интерполяцией, что означает:
Deck.gl позволяет модифицировать шейдеры на лету через Jav * aScript:
const layer = new ScatterplotLayer({
shaders: {
inject: {
'vs:#decl': `
varying float vDebug;
`,
'vs:#main-end': `
vDebug = positions.x;
`,
'fs:#decl': `
varying float vDebug;
`,
'fs:#main-start': `
if (vDebug > 0.5) {
gl_FragColor.rgb *= 2.0;
}
`
}
}
});
Этот механизм основан на инъекциях в predefined точки шейдера.
Deck.gl скрывает сложность работы с географическими координатами.
Типичный pipeline:
В GLSL:
vec4 projected = project_position(vec4(position, 1.0));
gl_Position = projected;
Fragment shader используется для реализации:
Пример радиального градиента:
precision highp float;
varying vec2 uv;
void main(void) {
float dist = length(uv);
float alpha = 1.0 - smoothstep(0.0, 1.0, dist);
gl_FragColor = vec4(0.2, 0.6, 1.0, alpha);
}
Критические факторы производительности:
Fragment shader является наиболее дорогой частью pipeline, так как выполняется на каждый пиксель.
Часто вычисления переносятся из fragment shader в vertex shader:
Причина — vertex shader выполняется реже (по количеству вершин, а не пикселей).
Пайплайн можно представить как последовательность: