Изучаем OpenGL ES2 для Android
Урок №1
Данная статья написана для новичков, которые (как и я) хотят научиться писать программы для Андроид с использованием OpenGL. Основные мысли и код взяты из замечательной книги Кевина Бразалера «OpenGL ES 2 for Android. A Quick-Start Guide by Kevin Brothaler» (1).
Зачем же пересказывать, спросите вы? Дело в том, что перед этой книгой я прочитал еще десяток статей на эту тему и код Кевина у меня сразу не пошёл (в книге среда разработки Eclipse, а у меня Android Studio). Поэтому, решил написать эту статью так, чтобы было понятно, прежде всего, самому себе.
Для начала давайте выясним, что такое OpenGL. Если почитать Википедию (2), то увидим следующее:
«OpenGL (Open Graphics Library) — спецификация, определяющая независимый от языка программирования платформонезависимый программный интерфейс для написания приложений, использующих двумерную и трёхмерную компьютерную графику.
Включает более 300 функций для рисования сложных трёхмерных сцен из простых примитивов. Используется при создании компьютерных игр, САПР, виртуальной реальности, визуализации в научных исследованиях. На платформе Windows конкурирует с Direct3D.»
Мы будем изучать укороченную версию OpenGL ES 2 (в дальнейшем OpenGL). Укоротили её по понятным причинам, у Андроид малая операционная память по сравнению с настольными ПК, наличие виртуальной машины Java также накладывает определенные ограничения. Наверно есть еще много причин, почему это сделали, но нас это не должно волновать. Прост нужно знать, что есть прекрасный инструмент OpenGL и надо научиться им пользоваться!
Для начала у вас на компьютере должна быть установлена среда разработки Android Studio (3). Желательно к ней подключить реальное устройство, так как опыт показал, что эмуляторы плохо работают с OpenGL. В моем случае это будет планшет Samsung GT-P3113 Android 4.2.2, API 17.
Наша задача написать и запустить программу, которая будет циклично очищать экран и заливать его красным цветом с помощью средств OpenGL.
Шаг первый. Создание проекта.
1.1 Откройте Android Studio и создайте новый проект.
1.2. Выберите папку, где вы будете хранить проект и введите название приложения
First Open GL Project.
1.3. Выберите наименьшую версию устройства, на котором может работать ваше приложение. OpenGL ES2 поддерживается начиная с Android 2.3.3 (Gingerbread), таким образом, мы охватываем примерно 100% устройств на Google Play.
1.4. Выберите пустой бланк Активити. Назовите главное Активити FirstOpenGlProjectActivity, Лэйаут можно не создавать, но если решили создать, то назовите activity_first_open_glproject. Ждем некоторое время, пока идет сборка проекта.
Шаг второй. Запуск проекта
2.1 Запускаем проект на реальном устройстве.
2.2 Выберите для просмотра главное Активити и вместо того, что там сгенерировалось автоматически, вставьте следующий код. В дальнейшем мы разберем построчно, что он делает.
package com.adc2017gmail.firstopenglproject;
import android.app.Activity;
import android.app.ActivityManager;
import android.content.Context;
import android.content.pm.ConfigurationInfo;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.widget.Toast;
public class FirstOpenGLProjectActivity extends Activity {
private GLSurfaceView glSurfaceView;
private boolean rendererSet = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
glSurfaceView = new GLSurfaceView(this);
// Проверяем поддерживается ли OpenGL ES 2.0.
final ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
final ConfigurationInfo configurationInfo = activityManager.getDeviceConfigurationInfo();
final boolean supportsEs2 = configurationInfo.reqGlEsVersion >= 0x20000;
if (supportsEs2) {
// Request an OpenGL ES 2.0 compatible context. glSurfaceView.setEGLContextClientVersion(2);
// Assign our renderer.
glSurfaceView.setRenderer(new FirstOpenGLProjectRenderer()); rendererSet = true;
} else {
Toast.makeText(this, "This device does not support OpenGL ES 2.0.", Toast.LENGTH_LONG).show(); return;
}
setContentView(glSurfaceView);
}
@Override
protected void onPause() { super.onPause();
if (rendererSet) {
glSurfaceView.onPause();
}
}
@Override
protected void onResume() { super.onResume();
if (rendererSet) {
glSurfaceView.onResume();
}
}
}
Android Studio должна ругаться, так как в коде у нас есть ссылка на новый класс FirstOpenGLProjectRenderer. Студия предложит вам создать этот класс. Нажмите на красную лампочку у кода правой кнопкой мыши и выберите создать класс. Назовите его FirstOpenGLProjectRenderer.
В итоге у вас структура проекта будет такой.
Откройте документ FirstOpenGLProjectRenderer. Удалите то, что в нем сгенерировалось и вставьте следующий код.
package com.adc2017gmail.firstopenglproject;
import android.opengl.GLSurfaceView.Renderer;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import static android.opengl.GLES20.glClearColor;
import static android.opengl.GLES20.glClear;
import static android.opengl.GLES20.glViewport;
import static android.opengl.GLES20.GL_COLOR_BUFFER_BIT;
public class FirstOpenGLProjectRenderer implements Renderer {
@Override
public void onSurfaceCreated(GL10 glUnused, EGLConfig config) {glClearColor(1.0f, 0.0f, 0.0f, 0.0f);
}
@Override
// Set the OpenGL viewport to fill the entire surface.
public void onSurfaceChanged(GL10 glUnused, int width, int height) {
glViewport(0, 0, width, height);
}
@Override
// Clear the rendering surface.
public void onDrawFrame(GL10 glUnused) {
glClear(GL_COLOR_BUFFER_BIT);
}
}
Теперь ошибок быть не должно. Запускаем проект на исполнение.
На экране устройства должна появиться иконка приложения, а сам экран зальет красным цветом.
Шаг третий. Что произошло?
Разберем код построчно. Начнем с главного Активити.
Строка © 1. Объявляется название пакета. Сборщик проекта Андроид Студио по нему собирает наш проект.
С 3 — 9. Импортируются нужные библиотеки для работы нашей главной Активити и класса FirstOpenGLProjectActivity.
С 11. Объявляем о создании класса FirstOpenGLProjectActivity, который наследуется от класса Activity.
С 12. Создаем объект GLSurfaseView одноименного класса.
GLSurfaceView это специальный класс, который управляет поверхностью OpenGL и преобразует ее в понятную для Андроида среду отображения. У класса есть множество функций, которые делают OpenGL проще в использовании. Вот некоторые из функций, самые распространенные:
— GLSurfaceView обеспечивает работу в отдельном потоке (thread) для визуализации OpenGL, таким образом основной поток у нас не занят.
— поддерживает непрерывный или по требованию рендеринг (обновление, перерисовка содержимого на экране).
— поддерживает настройки экрана для EGL, интерфейса между OpenGL и нашей оконной системой.
С 13. Создаем булеву переменную rendererSet, которая по умолчанию принимает значение false. Она будет управлять работу класса GLSurfaceView в режимах паузы и ожидания основного Активити.
С 15. Аннотация, говорящая, что метод переопределён.
С 16. Объявление метода onCreate (работает при запуске приложения).
С 18. Создание нового объекта glSurfaseView.
С 20 — 30. Проверка, поддерживает ли наше устройство OpenGL ES 2? Похоже, что эта проверка устарела и не нужна. Уже все Андроид устройства поддерживают OpenGL ES 2, тем более мы ничего не предлагаем взамен, кроме фразы «This device does not support…». Без нее всё прекрасно работает, можно оставить только строку 27.
От ActivityManager получаем информацию, которая позволяет нам вытянуть из состояния системы конфигурацию устройства. А уже из нее мы получаем информацию, которая поможет нам понять поддерживает ли наше устройство OpenGL ES 2.
С 27. Задаем поверхность и параметры прорисовки с помощью класса FirstOpenGLProjectRenderer ().
С 32. Выводим на экран содержимое.
С 36 — 47 Два метода, которые приостанавливают вывод на экран содержимого, если наше Активити не активно и находится в режиме ожидания или паузы.
GLSurfaceView требует, чтобы мы вызывали его методы onResume () и OnPause (), когда родительское Активити вызывает свои методы onResume () и onPaused (). Мы добавляем вызов этих методов, чтобы правильно возобновлять и завершать нашу деятельность.
Теперь рассмотрим, что делает класс FirstOpenGLProjectRenderer.
С1. Объявляем имя пакета сборки приложения
С3-С9. Импортируем нужные библиотеки
С11. Объявляем класс
С13. Метод, который заливает поверхность красным цветом. Первые три параметра в скобках метода glClearColor задают весовые коэффициенты для красного, зеленого и синего, а последний — задает прозрачность слоя. Если мы изменим первый коэффициент на 0, а второй на 1, то получим зеленый экран. Уверен, что сразу вы и не скажите, какой цвет экрана будет, если два первых коэффициента будут равны 1. Поиграйтесь.
С18,19. Метод задает размеры слоя. В данном случае поверхность будет на всю длину и ширину экрана устройства.
С25. Метод очищает поверхность от содержимого. Его вызывают, когда пришло время, чтобы сделать новый кадр. Мы должны всё равно что-то нарисовать, даже если надо просто очистить экран. Буфер рендеринга будет заменен и содержимое отобразится на экране после того, как вызван этот метод, так что если мы ничего не нарисуем, то скорей всего получим мерцающий экран.
Почему мы делаем в этих методах ссылку на GL10? Разработчики заимствуют методы разработанные под OpenGL ES 1.0 API. Они хорошо работают и в версии ES2.
Подведём итог.
Мы создали проект, инициализировали OpenGL ES2, запустили поток очистки и прорисовки экрана. А главное — OpenGL не такой уж и страшный, по сравнению с тем, что мы нарисовали .
Ссылки:
1. pragprog.com/book/kbogla/opengl-es-2-for-android
2. ru.wikipedia.org/wiki/OpenGL
3. developer.android.com/sdk/index.html