OPEN GL ES 2 Android

Home » android » OPEN GL ES 2 Android
android, OPEN GL, OPEN GL ES 2 Комментариев нет

перевод урока

Как создать контекст OpenGL ES 2 и как рисовать на экране. Что такое шейдеры и как они работают, и как работают матрицы в преобразовании сцен в видимые на экране изображения. А также, что нужно добавить в файл манифеста, чтобы приложение на маркете было помечено, как приложение с использованием OpenGL ES 2.

Понадобится
Android SDK
Android Studio

Рекомендуется тестировать примеры на реальном устройстве, а не эмуляторе
Первую акитвити назовем LessonOneActivity.

// в первую очередь, добавим ссылку на GLSurfaceView в активити
private GLSurfaceView mGLSurfaceView;

GLSurfaceView – это специальный класс типа view, который позволяет отображать наши OpenGL сущности(поверхности (surfaces)) в как объекты view андроида. Он также добавляет целый ряд дополнительных возможностей, включая
1) создание отдельного потока под OpenGL, чтобы не задействовать для нужд отображения и прорисовки главный поток приложения(чтобы не застопорить его в случае тяжелой операции по отрисовке)
2) он поддерживает продолжительный рендеринг и рендеринг по требованию(рендеринг -отрисовка)
3) он отслеживает установки экрана, используя EGL, – интерфейс связывающий OpenGL и базовую оконную систему

вот так незатейливо выглядит внедрение нашей GLSurfaceView в onCreate() активити

Первое, что мы сделаем в onCreate, – до вызова метода суперкласса, мы создадим GLSurfaceView. Затем мы проверяем, поддерживает ли текущая система OpenGL ES 2, для этого мы получаем экземпляр ActivityManager, который хранит состояние системы. Затем мы получаем из него информацию о конфигурации устройства, из которой как раз и узнаем поддерживает ли наше устройство OpenGL ES 2.
Если устройство поддерживает OpenGL ES 2, мы устанавливаем для него наш отрисовщик(renderer), если же устройство не поддерживает OpenGL ES 2, мы можем прописать и установить здесь отрисовщик для OpenGL ES 1.
Далее, мы устанавливаем в качестве ContentView для активити нашу mGLSurfaceView.

Мы также должны вызвать соответствующие методы GLSurfaceView в методах onResume() и onPause() нашей Activity следующим образом:

Как работает OpenGL ES 2. Визуализация 3D мира. Отрисовщик GLSurfaceView.Renderer

Как работает OpenGL ES 2, и как отображаются различные трехмерные сущности на экране. Выше мы установили для mGLSurfaceView свой отрисовщик GLSurfaceView.Renderer. У отрисовщика есть три наиболее важных метода, эти методы вызываются в нем, чем бы ни занимался отрисовщик.

данный метод вызывается когда поверхность(surface) создается впервые. Он также будет вызван, если мы потеряем контекст поверхности и системе нужно будет его воссоздавать заново.

вызывается при любом изменении поверхности, например при переключении экрана из обычного режима(portrait) в альбомный(landscape). Также он всегда вызывается после создания поверхности.

вызывается всегда при отрисовке нового кадра(frame)(фигуры??)

Обратите внимание, что передаваемый в метод через параметр, экземпляр GL10 называется “glUnused”. Этот экземпляр не используется, когда отрисовка выполняется с использованием OpenGL ES 2. Вместо этого используются статические методы класса GLES20.(здесь этот параметр присутствует, потому что сходный интерфейс используется для OpenGL ES 1)

Перед тем, как отрисовщик отобразит что-то на экране, нужно ему это что-то передать. В OpenGL ES 2, чтобы создать визуальную сущность, необходимо задать значения специальным массивам(позиции, цвета и т.д.)
нарисуем три треугольника

Если в OpenGL 1 для установки свойств визуальных сущностей нам понадобилось бы задавать их в ряде методов

В OpenGL ES 2. вместо этого, мы создаем массив

В этом массиве, содержится информация о трех точках фигуры – трех координатах и коде цвета(три составляющие rgb и a – альфа канал(прозрачность))
В OpenGL ES 2 вместо того чтобы передавать информацию одновременно всю, полностью как в ES1, мы передаем ее постепенно, по кусочкам, по мере поступления

О буферах

Код реализации OpenGL ES 2 написан на C. И перед тем, как отправить информацию в OpenGL, необходимо привести ее в соответствующий вид, чему и способствует подобная организация кода.
Ява и системный код не хранят байты в одинаковом порядке, поэтому мы создаем специальные буферные классы и создаем объект ByteBuffer, достаточно большим, чтобы вместить всю нашу информацию. Далее, информация о визуальных сущностях из буфера передается родному коду системы, который “упаковывает” байты в нужном ему порядке. Затем мы конвертируем информацию в FloatBuffer, так чтобы мы могли ее использовать и с числами с плавающей точкой. И наконец, мы копируем наш массив в буфер.

Эти действия с буферами, могут показаться трудными для понимания, но их просто необходимо выполнять, перед отправкой информации о визуальных сущностеях в OpenGL.

К сведению, буферы с числами с плавающей точкой медленно работают в Froyo, и со средней скоростью в Gingerbread, поэтому не рекомендуется менять их(содержимое) слишком часто.

Матрицы

Тема матриц – одна из наиболее важных тем 3Д программирования.
Когда поверхность создана, первое, что мы должны сделать – очистить цвет до серого. Альфа также должна быть установлена в среднее значение. Но в данном примере альфа-канал не используется, поэтому мы пока это значение использовать не будем. Очистить цвет до серого нам нужно лишь единожды, больше его менять нам не потребуется.

Второе, что нам нужно сделать – установить нашу видовую матрицу. Существует несколько разных видов матриц.

Матрица модели – матрица используется, для расположения модели, где-либо в координатах мира. Например, если мыхотим расположить какую-то 3д модель в 100 условных метрах к востоку, можно использовать эту матрицу(грубо говоря-эта точка означает координату центра модели)
Матрица вида – эта матрица представляет камеру, чтобы уведеть нашу условную 3д модель находящуюся в 100 м к востоку, мы также, по крайней мере на 100м к востоку должны сдвинуть камеру или на 100 м к западу сдвинуть весь мир, для этого мы используем матрицу вида(грубо говоря-эта точка означает координату точки зрения)
Матрица проекции – так как наш экран плоский, мы должны получить плоскую проекцию мира с помощью 3х-мерной перспективы. Для этого и используется матрица проекции.(грубо говоря-эта точка означает координату центра проекции нашего экрана – плоскости, на которой отображается модель, плоскости между моделью и точкой зрения. По аналогии с человеческим глазом, матрица вида – это центральная точка на сетчатке где фокусируется изображение, а матрица проекции – это точка в центре зрительной поверхности зрачка)

о матрицах модели, вида, проекции можно почитать подробно здесь.
еще об OPEN GL

(Для сравнения, в реализации OpenGL 1, матрицы модеди и вида объединены в одну, с добавлением направления фокуса камеры -Z)

Нет необходимости создавать матрицу в ручную, для этого есть специальный класс(Matrix helper).
В примере создается матрица вида, в которой камера расположена над моделью и между ними находится проекция камеры

В OpenGL ES 2 все что мы хотим изобразить на экране, в первую очередь должно пройти через вершинный и фрагментный шейдер. Вершинные шейдеры выполняют операции по каждой вершине, а результаты этих операций используются фрагментными шейдерами, которые добавляют дпоплнительные вычисления относительно каждого пиксела. В основном, каждый шейдер состоит из входа(input) и выхода(output) и программы. В первую очередь очеределим форму, которая представляет собой комбинированную матрицу, которая содержит в себе все преобразования. Эта константа проходящая через все вершины и используемая для проецирования их на экран. Затем мы определим два атрибута для позиции и цвета. Эти атрибуты будут считываться из буфера, который мы определили ранее и определять позицию и цвет каждой вершины.

Затем, мы определяем варьирование, которое интерполирует значения в пределах треугольника и отправляет их в фрагментный шейдер. Когда они попадают в фрагментный шейдер, он удержит значение для каждого пиксела. Можно сказать, мы ,тем самым, определили треугольник, с красным, зеленым и синим каждой точки, и определили его размер в 10 пикселов на экране. Когда начнет выполняться фрагментый шейдер, он уже будет содержать цвета для каждого пиксела.

Кроме установки цвета, мы также указали OpenGL на позицию конечной вершины, отображаемой на экране. Затем мы определяем сам фрагментный шейдер.

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

информация о шейдерах – OpenGL ES 2.0 API Quick Reference Card

Загрузка шейдеров в OpenGL

Сначала мы создаем объект шейдера. Если успешно, получаем ссылку на него. Затем мы используем эту ссылку, чтобы передать в шейдер исходный код, и затем компилируем его. Из OpenGL мы можем получить статус компиляции нашего шейдера. Если обнаружатся ошибки, мы можем использовать метод GLES20.glGetShaderInfoLog(shader), чтобы определить их. Точно те же самые шаги мы выполняем и для фрагментного шейдера.

соединяем вместе вершинный и фрагментный шейдер и помещаем в программу

Перед тем, как мы сможем использовать наш вершинный и фрагментый шейдер, нам нужно связать их вместе в программе. Это обеспечит связывание выхода(output) вершинного шейдера со входом(input) фрагментного. Это также позволит нам выполнить вход в программу и использовать шейдеры для отрисовки наших форм.

Создаем новый программный объект, и если успешно прикрепляем наши шейдеры. Нам нужно отправить цвет и позицию как атрибуты, поэтому эти атрибуты нам нужно привязать. Затем соединяем вместе шейдеры.

После того, как мы успешно привязали нашу программу, нам нужно завершить манипуляции с ней, чтобы с ней можно было работать. Первое что нужно сделать – обеспечить связи, чтобы можно было отправлять информацию в программу. Затем мы скажем OpenGL использовать ее во время отрисовки. Так как в этом уроке освещается только одна программа, мы поместим этот код в onSurfaceCreated() вместо onDrawFrame().

Устанавливаем перспективную проекцию.

Наш onSurfaceChanged()вызывается по крайней мере один раз, и всегда при смене surface . Так как нам нужно сбрасывать нашу матрицу проекции всегда, когда экран на которую она проецируется меняется., onSurfaceChanged() – идеальное место для реализации этой операции.

Рисуем визуальные сущности на экран!

Это то самое место, где визуальные сущности, действительно отображаются на экране. Мы очищаемм экран, чтобы не получить эффекта коридора с зеркалами. Далее нам неплохо было бы создать мягкую анимацию кручения треугольников используя time(обычно для анимации лучше использовать time вместо framerate)

реализация отрисовки в drawTriangle:

Теперь вспомним о буферах, которые мы определили в отрисовщике. Наконец мы готовы их использовать. Нам нужно сообщить OpenGL как использовать эти данные, используя
GLES20.glVertexAttribPointer(). Давайте взглянем на первый вызов.

Мы установили позицию нашего буфера используя отступ от начала буфера. Затем мы указали OpenGL использовать эту информацию и отправить ее в вершинный шейдер, и применить ее к атрибуту позиции. Нам также неообходимо сообщить OpenGL сколько элементов между каждой вершиной или шагом.
Отметим: шаг нужно определять в байтах. Хотя у нас есть 7 элементов(3 для позиции и 4 для цвета) между вершинами, мы в действительности получаем 28 байтов, так как каждой число с плавающей точкой требует 4 байта. Если мы забудем про шаг, это не вызовет ошибок, но на экране просто ничего не отобразится

Наконец, мы задействем атрибут вершины и переместимся к следующему атрибуту. Чуть ниже мы построим комбинированную матрицу, чтобы спроецировать точки на экран. Нам следует сделать это в вершинном шейдере тоже, но так как это нам нужно сделать лишь единожды мы просто закешируем результат. Мы введем конечную матрицу в вершинный шейдер, используя GLES20.glUniformMatrix4fv() и конвертируем наши точки в треугольник и отобразим их на экране GLES20.glDrawArrays().

Вывод

В уроке было разобрано как создать OpenGL context, передать данные о форме, загрузить вершинный и пиксельный шейдеры, установить матрицы трансформации. И, наконец, собрать все это вместе. Если все сделано как надо на экране мы увидим треугольник.
Урок не простой, и возможно придется по шагам не раз его пройти, прежде чем понять. OpenGL ES 2 требует бльше времени для настройки, но с опытом процесс разработки ускорится.

Публикация в Android Market

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

эта строка говорит маркету, что приложение требует OpenGL ES 2, и маркет скроет приложение для тех устройств, где данная библиотека не поддерживается.

Что можно сделать с этим уроком
1. попытаться сменить скорость анимации, точки, цвета

Также рекомендуется посмотреть код в ApiDemos, в котором можно найти целую папку с примерами для андроид SDK. Код этих примеров помог автору урока создать этот урок.

LEAVE A COMMENT