Перейти к содержанию

OpenGL ES

OpenGL ES - графический API для 2D- и 3D-рендеринга на мобильных устройствах. В Android его используют для игр, эффектов камеры и собственной отрисовки, когда нужен контроль над геометрией, шейдерами и текстурами.

Основы OpenGL ES

Базовая архитектура

OpenGL ES - API с состоянием: код на CPU настраивает буферы, шейдеры, текстуры и вызовы отрисовки, а графический драйвер и GPU обрабатывают команды. EGL связывает контекст OpenGL ES с поверхностью для отрисовки.

GLSurfaceView управляет потоком рендеринга и настройкой EGL в типичных сценариях. Его Renderer получает вызовы onSurfaceCreated(), onSurfaceChanged() и onDrawFrame(). Для приведённого ниже синтаксиса шейдеров OpenGL ES 2.0 вызови setEGLContextClientVersion(2). SurfaceView, TextureView и прямая работа с EGL дают другие способы интеграции, но требуют больше внимания к жизненному циклу.

Создавай GL-ресурсы после появления контекста, обновляй состояние сцены и отправляй команды отрисовки. При потере EGL-контекста ресурсы, например текстуры и буферы, становятся недействительными; пересоздавай их в onSurfaceCreated(). Обрабатывай паузу и возобновление View и не меняй состояние GL напрямую из произвольного UI-потока.

Вершинный буфер

Объект вершинного буфера (VBO) хранит атрибуты вершин: позиции, текстурные координаты, нормали и цвета - в памяти под управлением GL. Он позволяет не отправлять неизменные массивы заново при каждой отрисовке; API не гарантирует конкретного физического размещения данных.

Позиции вершин треугольника до загрузки:

val vertices = floatArrayOf(
     0.0f,  0.5f, 0.0f,
    -0.5f, -0.5f, 0.0f,
     0.5f, -0.5f, 0.0f
)

Для VBO создай объект через glGenBuffers(), привяжи его, загрузи данные через glBufferData() и задай формат атрибутов через glVertexAttribPointer(). Передача прямого FloatBuffer в Android GLES API - ещё один вариант; VBO не обязателен для каждой команды отрисовки.

Вершинный шейдер

Вершинный шейдер выполняется для каждой вершины. Он преобразует позицию, обычно из локальных координат в пространство отсечения, и передаёт атрибуты следующим стадиям. В этом примере для OpenGL ES 2.0 используется матрица модели, вида и проекции (MVP):

attribute vec4 aPosition;
uniform mat4 uMvpMatrix;

void main() {
    gl_Position = uMvpMatrix * aPosition;
}

Фрагментный шейдер

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

precision mediump float;
uniform vec4 uColor;

void main() {
    gl_FragColor = uColor;
}

Сложные вычисления, выборка текстур и избыточная отрисовка могут сделать обработку фрагментов дорогой. Перед оптимизацией измеряй производительность на целевых устройствах.

Текстура

Текстура - изображение или данные под управлением GL, которые считывают шейдеры. Её используют для спрайтов, материалов, шрифтов, кадров камеры и постобработки. Размеры и формат влияют на расход памяти и стоимость загрузки. Фильтры уменьшения и увеличения управляют выборкой; mipmap-уровни могут улучшить качество уменьшенной текстуры, но требуют дополнительной памяти и подходящей фильтрации.

Системы координат

При отрисовке позиция обычно проходит через локальное пространство объекта, мировое пространство, пространство камеры и пространство отсечения. После деления на перспективную компоненту нормализованные координаты преобразуются через viewport в координаты окна:

модель -> мир -> камера -> отсечение -> нормализованные координаты -> окно

Основные преобразования задают матрицы модели, вида и проекции. Координаты касаний, bitmap и текстур могут иметь разные начала отсчёта; преобразуй их явно.

Конвейер рендеринга

В упрощённом вызове OpenGL ES CPU подготавливает состояние и отправляет команду отрисовки; вершинный шейдер и сборка примитивов обрабатывают геометрию; растеризация создаёт фрагменты; фрагментный шейдер вычисляет значения; проверки фрагментов и смешивание определяют обновление буфера кадра; затем поверхность выводится на экран. Точный порядок некоторых операций с фрагментами может отличаться.

При поиске проблем производительности оценивай количество вызовов отрисовки и вершин, стоимость фрагментов, избыточную отрисовку, пропускную способность текстур и синхронизацию CPU/GPU. Для локальных эффектов интерфейса без собственной геометрии смотри AGSL и RuntimeShader.