Шейдеры для векторных данных

Векторный слой в WebGL-режиме в OpenLayers строится вокруг преобразования геометрий в набор графических примитивов, которые затем обрабатываются GPU через вершинные и фрагментные шейдеры. Основная цель такого подхода — минимизация нагрузки на CPU и перенос вычислений визуализации на видеокарту.

Ключевая цепочка обработки:

  • геометрия Feature → нормализованные координаты
  • атрибуты свойства → буферы вершин
  • стиль → параметры шейдеров (uniforms / attributes)
  • GPU pipeline → rasterization → пиксели

В отличие от Canvas-рендеринга, WebGL использует пакетную обработку объектов, где каждый тип геометрии преобразуется в набор треугольников или линий, оптимизированных для отрисовки на GPU.


WebGL слой векторной графики

Векторный WebGL-рендеринг в OpenLayers реализуется через специализированные слои:

  • точки: WebGLPointsLayer
  • линии и полигоны: WebGLVectorLayer (в зависимости от версии)
  • тайловые вектора: WebGLVectorTileLayer

Каждый слой использует внутренний рендерер, который формирует:

  • vertex buffer (позиции)
  • attribute buffer (цвет, размер, угол, ID)
  • index buffer (топология)
  • uniform state (масштаб, разрешение, стили)

Основная особенность — динамическая генерация шейдеров на основе стиля слоя.


Принцип формирования шейдеров

Шейдерная программа формируется в два этапа:

  1. Генерация GLSL-кода на основе стиля
  2. Компиляция и кеширование программы WebGL

В OpenLayers стиль может включать выражения, которые компилируются в GLSL-эквиваленты:

  • интерполяция цветов
  • масштабирование размеров
  • условные выражения (case-like конструкции)
  • зависимости от атрибутов фичи

Vertex Shader (обобщённая структура)

Вершинный шейдер отвечает за:

  • трансформацию координат из мировых в экранные
  • масштабирование символов в зависимости от zoom
  • передачу данных во фрагментный шейдер

Типичная логика:

attribute vec2 a_position;
attribute float a_size;
attribute vec4 a_color;

uniform mat4 u_projectionMatrix;
uniform float u_resolution;

varying vec4 v_color;

void main() {
  vec2 scaledPosition = a_position / u_resolution;
  gl_Position = u_projectionMatrix * vec4(scaledPosition, 0.0, 1.0);

  gl_PointSize = a_size;
  v_color = a_color;
}

Фрагментный шейдер и пиксельная логика

Фрагментный шейдер отвечает за вычисление итогового цвета пикселя. Векторные данные обычно используют простую модель затенения без освещения, но с возможностью динамической стилизации.

precision mediump float;

varying vec4 v_color;

void main() {
  gl_FragColor = v_color;
}

Более сложные реализации добавляют:

  • градиенты
  • прозрачность по атрибутам
  • дискриминацию по ID фичи
  • псевдо-3D эффекты (теневые ореолы)

Атрибуты фичи и буферизация данных

Каждый объект Feature преобразуется в набор GPU-атрибутов.

Типовые атрибуты:

  • координаты (x, y)
  • цвет (rgba)
  • размер символа
  • rotation
  • category index
  • feature id

Буферизация происходит батчами:

  • уменьшение количества draw calls
  • группировка по стилям
  • сортировка по z-index

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

  • Float32Array для координат
  • Uint8Array для цветов
  • Uint16Array для индексов

Связь стилей OpenLayers и GLSL

Стиль векторного слоя не является конечным изображением. Он транслируется в параметры шейдера.

Пример структуры стиля:

style: {
  'circle-radius': ['interpolate', ['linear'], ['zoom'], 5, 2, 15, 10],
  'circle-color': ['match', ['get', 'type'],
    'city', '#ff0000',
    'village', '#00ff00',
    '#0000ff'
  ]
}

Данная декларация преобразуется в:

  • uniform функции интерполяции
  • условные ветвления GLSL
  • lookup таблицы категорий

Масштабирование и разрешение

Одной из ключевых задач шейдеров является корректная работа с масштабом карты.

Используются uniform-переменные:

  • resolution (размер пикселя в мировых единицах)
  • zoom level
  • pixel ratio

Формула преобразования координат:

vec2 position = a_position / u_resolution;

Это позволяет:

  • сохранять визуальный размер объектов при зуме
  • масштабировать символы без пересоздания геометрии

Рендеринг точечных данных

Точки являются наиболее оптимальным типом геометрии для WebGL.

Особенности:

  • каждая точка = один vertex
  • используется gl.POINTS
  • размер задаётся через gl_PointSize

Оптимизация:

  • instancing (в некоторых реализациях)
  • атлас текстур для символов
  • batch rendering

Линии и полигоны в WebGL

Линии и полигоны требуют триангуляции перед отправкой в GPU.

Процесс:

  1. геометрия → набор линий
  2. линии → треугольники (tessellation)
  3. треугольники → vertex buffer

Для линий применяются техники:

  • extrusion (для толщины)
  • miter joins
  • bevel joins
  • cap styles

Фрагментная часть может учитывать расстояние до центра линии для сглаживания краёв.


Выборка объектов (picking)

Shader-based picking позволяет определять объект под курсором без CPU-обхода всех фич.

Механизм:

  • каждой фиче назначается уникальный ID
  • ID кодируется в цвет
  • рендер в offscreen framebuffer
  • чтение пикселя под курсором

GLSL код:

varying float v_featureId;

void main() {
  gl_FragColor = vec4(v_featureId / 255.0, 0.0, 0.0, 1.0);
}

Декларативные выражения и компиляция в GLSL

OpenLayers использует выражения стиля, которые трансформируются в GLSL-операции.

Типы выражений:

  • математические (add, multiply)
  • логические (case, match)
  • интерполяционные (interpolate)
  • доступ к атрибутам (get)

Пример:

['interpolate', ['linear'], ['zoom'],
  5, 1,
  10, 2,
  15, 5
]

В GLSL это превращается в ступенчатую функцию с линейной интерполяцией.


Управление производительностью шейдеров

Основные ограничения WebGL-рендеринга:

  • количество атрибутов на вершину
  • размер uniform блоков
  • количество инструкций в фрагментном шейдере

Оптимизационные стратегии:

  • уменьшение ветвлений в fragment shader
  • предварительное вычисление стилей на CPU
  • использование атласов вместо процедурной генерации
  • группировка объектов по стилю

Динамическая перегенерация шейдеров

При изменении стиля происходит:

  • пересборка GLSL кода
  • перекомпиляция программы
  • перераспределение буферов

Чтобы уменьшить стоимость:

  • кеширование shader programs
  • reuse uniform locations
  • минимизация различий между стилями

Instancing и батчинг

При большом количестве объектов используется instanced rendering:

  • один набор геометрии
  • множество экземпляров с разными атрибутами

Преимущества:

  • резкое сокращение draw calls
  • стабильная производительность при больших данных

Глубина и z-index в векторных слоях

Хотя WebGL работает в 2D-карте, используется псевдо-глубина:

  • z-index слоя
  • порядок батчей
  • depth buffer (опционально)

Политика сортировки:

  • сначала слои
  • затем группы стилей
  • затем тип геометрии

Текстуры в шейдерах векторных данных

Текстуры применяются для:

  • символов маркеров
  • градиентов
  • атласов иконок

Фрагментный шейдер выборки:

uniform sampler2D u_texture;

varying vec2 v_texCoord;

void main() {
  gl_FragColor = texture2D(u_texture, v_texCoord);
}

Смешивание цветов и прозрачность

Blending в WebGL контролируется состоянием контекста:

  • SRC_ALPHA
  • ONE_MINUS_SRC_ALPHA

В шейдерах важна корректная альфа-логика:

  • предварительное умножение альфы
  • избежание двойного смешивания

Ограничения и особенности архитектуры

Шейдерная модель в OpenLayers накладывает ограничения:

  • ограниченная сложность условных конструкций
  • необходимость предкомпиляции выражений
  • невозможность динамического branching без потерь производительности

При этом достигается высокая масштабируемость:

  • десятки тысяч объектов без деградации FPS
  • стабильная отрисовка при изменении zoom
  • аппаратное ускорение всех операций трансформации