В основе рендеринга Mapbox GL JS лежит WebGL, а ключевым механизмом его работы выступают программируемые шейдеры. Каждый кадр карты формируется через графический конвейер, где геометрия преобразуется и окрашивается с помощью двух основных типов шейдеров — vertex shader и fragment shader. Их взаимодействие определяет, как векторные данные превращаются в пиксели на экране.
Mapbox GL JS использует WebGL 1.0 (и частично WebGL 2.0 в современных версиях окружений), а значит шейдеры пишутся на GLSL ES (OpenGL Shading Language for Embedded Systems). Эти программы выполняются непосредственно на GPU, обеспечивая высокую производительность при отрисовке больших объемов геоданных.
Vertex shader выполняется для каждой вершины геометрии. В контексте Mapbox GL JS вершины представляют собой точки, из которых формируются линии, полигоны и символы. Основная задача vertex shader — преобразование координат из пространств данных в экранное пространство.
Типичный vertex shader в Mapbox GL JS включает работу с матрицами:
attribute vec2 a_pos;
uniform mat4 u_matrix;
uniform float u_zoom;
varying vec2 v_pos;
void main() {
vec4 projected = u_matrix * vec4(a_pos, 0.0, 1.0);
gl_Position = projected;
v_pos = a_pos;
}
В процессе работы vertex shader оперирует несколькими системами координат:
Mapbox GL JS выполняет большую часть математики трансформации в
uniform-матрице u_matrix, которая объединяет:
Vertex shader передает данные в fragment shader через
varying переменные. Эти значения автоматически
интерполируются для каждого пикселя внутри примитива.
varying float v_line_distance;
varying vec2 v_texcoord;
Пример использования — вычисление градиента вдоль линии или передача координат для текстурирования символов.
В отличие от классических 3D движков, vertex shader в Mapbox GL JS оптимизирован под:
Zoom играет ключевую роль: многие атрибуты интерполируются в зависимости от уровня приближения, что позволяет менять толщину линий, размеры символов и другие параметры без пересоздания геометрии.
Fragment shader выполняется для каждого пикселя, покрываемого примитивом. Его задача — определить итоговый цвет фрагмента, включая прозрачность.
В Mapbox GL JS именно fragment shader отвечает за визуальное оформление слоев: заливки, линии, символы, эффекты и стилизацию данных.
precision mediump float;
varying vec2 v_pos;
uniform vec4 u_color;
void main() {
gl_FragColor = u_color;
}
В этом примере каждый пиксель получает одинаковый цвет, заданный через uniform.
Fragment shader получает интерполированные значения из vertex shader, что позволяет создавать плавные переходы:
varying float v_opacity;
uniform vec4 u_fill;
void main() {
gl_FragColor = vec4(u_fill.rgb, u_fill.a * v_opacity);
}
Такой подход используется, например, для плавного изменения прозрачности вдоль линии.
В Mapbox GL JS fragment shader активно работает с текстурами, особенно для символов и иконок.
uniform sampler2D u_texture;
varying vec2 v_texcoord;
void main() {
vec4 color = texture2D(u_texture, v_texcoord);
gl_FragColor = color;
}
Это основной механизм отрисовки иконок в symbol layers.
Одна из характерных задач fragment shader — реализация пунктирных линий без разбиения геометрии:
varying float v_line_distance;
uniform float u_dash_array;
void main() {
float pattern = mod(v_line_distance, u_dash_array);
if (pattern > u_dash_array * 0.5) {
discard;
}
gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
}
Использование discard позволяет пропускать пиксели,
создавая визуальные разрывы.
Связь между двумя шейдерами осуществляется через varyings. Vertex shader определяет геометрическую основу, fragment shader — визуальную интерпретацию.
Типичный поток данных:
Производительность шейдеров критична при рендеринге больших карт. Основные стратегии оптимизации включают:
Fragment shader особенно чувствителен к сложным операциям, так как выполняется для каждого пикселя экрана.
Mapbox GL JS активно использует зависимость от zoom уровня. Это реализуется через uniform-переменные и интерполяции:
uniform float u_zoom;
float scale = smoothstep(10.0, 15.0, u_zoom);
Такие вычисления позволяют изменять визуальные свойства без пересборки буферов.
Mapbox GL JS предоставляет возможность модификации шейдеров через custom layers. Это позволяет внедрять собственную GLSL-логику в рендеринг карты.
Пример структуры custom layer:
const customLayer = {
id: 'custom-layer',
type: 'custom',
renderingMode: '2d',
onAdd(map, gl) {
// инициализация буферов и шейдеров
},
render(gl, matrix) {
// передача uniform и отрисовка
}
};
Внутри таких слоев можно использовать собственные vertex и fragment shaders, интегрированные в pipeline Mapbox GL JS.
Шейдеры напрямую определяют визуальный язык карты:
Fragment shader часто становится местом реализации визуальных эффектов, тогда как vertex shader отвечает за геометрическую основу и трансформации.
Внутренняя модель Mapbox GL JS строится вокруг унифицированного набора шейдеров, где различные типы слоев (fill, line, circle, symbol) используют специализированные вариации vertex и fragment программ.
Эта архитектура обеспечивает: