В прошлой статье мы создали, наверное, самое простое приложение, использующее мощь OpenGL, которое только может быть. Это всего лишь микроскопическая часть возможностей этой библиотеки. В этой главе я хочу поговорить с Вами о матрицах.
Как пишут во всех уроках и статьях на данную тему, скажу Вам, что, действительно, необходимости знать все свойства матриц у Вас нет, но их понимание способствовало бы скорейшему усвоению всех процессов OpenGL. С помощью матриц происходят смещение, поворот и прочие искажения пространства.
В OpenGL 1.0(именно эту версию мы пока используем) есть три типа матриц: GL_MODELVIEW, GL_PROJECTION и GL_TEXTURE. Остановимся на каждой из них немного подробнее. [spoiler]
GL_MODELVIEW
Это матрица "активной камеры". В ней объединены две матрицы - матрица модели и матрица вида. /* Если Вы новичок, то это довольно размытое определение, я понимаю, но, скорее всего, скоро Вы все поймете. */
Она служит для отрисовки объемных объектов сцены. /* Сцена - это все, что мы видим в нашем GLSurfaceView, т.е. в пространстве OpenGL. */ Говоря другими словами, GL_MODELVIEW нужен нам когда мы хотим работать с 3D.
GL_PROJECTION
Это матрица проекции. Ее название говорит само за себя. Эта матрица служит для проецирования 3D пространства или его участков в 2D. Если есть сложности с пониманием того, что же такое проекция - ссылка на википедию.
GL_TEXTURE
Последняя из трех матриц по порядку и по количеству использования(мое личное мнение). Данная матрица используется для регулировки накладывания текстур на полигоны. /* Лично мне еще ни разу не довелось ее использовать. */
Для задания необходимой матрицы в OpenGL используется функция glMatrixMode(), принимающая в себя описанные выше константы.
Этой информации пока будет достаточно. Все остальное мы постараемся разобрать по мере написания кода. Сегодня мы напишем с вами программу, которая, как мне кажется, не раз Вам пригодится в будущем. Программу, где фон будет отрисован в 2D и иметь свои(экранные) координаты, а объект отрисуем в 3D(мировых) координатах.
Для начала мы повторим манипуляции из первой части серии и создадим GLSurfaceView и подключим его к проекту. Затем Вам необходимо снова создать класс OpenGLRenderer и подключить его к GLSurfaceView.
На этот раз базовый код OpenGLRenderer будет намного более функционален и сложен, но я все хорошо прокомментирую для Вас:
/*http://esate.ru, Freaky_Brainstorm(Brain Freaky)*/
package com.freakybrainstorm.oglpart2;
import android.opengl.GLSurfaceView;
import android.opengl.GLU;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
public class OpenGLRenderer implements GLSurfaceView.Renderer {
private float aspect, width, height;
private Background background;
private Object3D object3D;
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
//Создаем экземпляр классов Background и Object3D
background = new Background(width, height);
object3D = new Object3D();
//Переменная aspect будет хранить в себе значение соотношения сторон
aspect = (float)width / (float)height;
//Устанавливаем область отрисовки
gl.glViewport(0, 0, width, height);
//Выбираем матрицу проекции
gl.glMatrixMode(GL10.GL_PROJECTION);
//Умножаем ее на единичную
gl.glLoadIdentity();
//Выбираем матрицу вида
gl.glMatrixMode(GL10.GL_MODELVIEW);
//Ее тоже умножаем на единичную
gl.glLoadIdentity();
//Это все делается для того, чтобы при изменении размера экрана
//при повороте устройства, у нас ничего не "сломалось"
//Записываем в переменные значения высоты и ширины экрана
this.width = (float) width; this.height = (float)height;
}
@Override
public void onDrawFrame(GL10 gl) {
//Очищаем буферы цвета и глубины
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
//Выбираем матрицу проекции
gl.glMatrixMode(GL10.GL_PROJECTION);
//Умножаем на единичную
gl.glLoadIdentity();
//Устанавливаем ортогональную проекцию
gl.glOrthof(0.0f, this.width, 0.0f, this.height, -1.0f, 1.0f);
//Отключаем тест глубины
gl.glDisable(GL10.GL_DEPTH_TEST);
//Отключаем запись в буфер глубины
gl.glDepthMask(false);
//Выбираем матрицу вида
gl.glMatrixMode(GL10.GL_MODELVIEW);
//Умножаем на единичную
gl.glLoadIdentity();
//Начало зоны 2D
//Задаем цвет фона
gl.glColor4f(.7f, .7f, .7f, 1.0f);
//Запускаем прорисовку
background.Draw(gl);
//Конец зоны 2D
//Включаем запись в буфер глубины
gl.glDepthMask(true);
//Включаем тест глубины
gl.glEnable(GL10.GL_DEPTH_TEST);
//Выбираем матрицу проекции
gl.glMatrixMode(GL10.GL_PROJECTION);
//Умножаем на единичную матрицу
gl.glLoadIdentity();
//Устанавливаем область видимости. Об этом ниже
GLU.gluPerspective(gl, 45.0f, aspect, 0.1f, 1000.0f);
//Выбираем матрицу вида
gl.glMatrixMode(GL10.GL_MODELVIEW);
//Умножаем на единичную матрицу
gl.glLoadIdentity();
//Устанавливаем точку и направление камеры
GLU.gluLookAt(gl, 8.0f, 12.0f, 10.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f);
//Начало зоны 3D
//Задаем цвет объекта
gl.glColor4f(.1f, .1f, .1f, 1.0f);
//Запускаем прорисовку
object3D.Draw(gl);
//Конец зоны 3D
//Умножаем на единичную матрицу
gl.glLoadIdentity();
}
}
Код функции onDrawFrame() я когда-то нашел на просторах интернета, но он прекрасно работает и в будущем мы его дополним и преобразуем еще лучше.
Каждая строка кода прокомментирована и, я думаю, у Вас не возникнет много вопросов. Единственное, что я хотел бы пояснить это функции glOrthof(), GLU.gluPerspective() и gluLookAt().
glOrthof()
При помощи данной функции мы устанавливаем матрицу проекции в левосторонней системе координат. Другими словами, определяем систему координат так, как нам удобно. В нашем случае точка 0,0 расположена в верхнем левом углу.
GLU.gluPerspective()
Стандартная функция установки перспективы. Принимает экземпляр OpenGL, угол обзора(45 градусов это стандарт), соотношение сторон экрана, ближняя плоскость отсечения и дальняя плоскость отсечения. /* Плоскости отсечения ограничивают зону видимости. */
gluLookAt()
Эта функция установки точки обзора камеры(функция библиотеки GLU). Ее параметры достаточно просты. Первый параметр это экземпляр OpenGL, со второго по четвертый - точка положения камеры, с пятого по седьмой - точка, в которую направлена камера и последние три параметра определяют верх камеры. Последние три параметра практически всегда будут 0, 1, 0. /* Я выставил такие координаты положения камеры, что бы наглядно показать вращение матрицы вида и статичность матрицы проекции. */
С первоначальным скелетом класса OpenGLRenderer разобрались. Теперь дело за фоном и объектом. Для их реализации мы создадим два новых класса: Background и Object3D.
Я намерено не стал комментировать код этих двух классов. Они практически идентичны коду из прошлой части цикла. Поясню только один момент, хотя и он, скорее всего, будет Вам понятен.
Конструктор Background принимает два параметра:
/*http://esate.ru, Freaky_Brainstorm(Brain Freaky)*/
public Background(float width, float height)
Эти параметры нужны нам для того, что бы мы могли(в будущем) корректно работать с ним и для того, что бы "растянуть" его.
Результат работы нашего приложения ниже. Визуально он не отличим от предыдущего, но теперь у нас есть гораздо больше возможностей для творчества.
На этом все. В следующей статье мы поговорим об освещении, нормалях и нарисуем куб. Куб всегда интереснее плоскости.
Понравилась публикация? Сохраните ее, чтобы вернуться к изучению материала!
Да, эта зона в данном примере рисует плоскость на весь экран светло-серого цвета.
Такой подход на много лучше чем использование glClearColor. Вы получаете экранные координаты и можете располагать элементы UI и прочее + на плоскость можно натянуть текстуру. Плоскость так же может отражать свет.
т.е. свои элементы или можно что-то системное повесить, чтобы не изобретать велосипед
Системные можно добавлять и без этого, путем перетаскивания их на активити из визуального редактора.
С помощью такого разделения мы просто получаем возможность использовать более продвинутый background, ограниченный в своем функционале только возможностями OpenGL 1.0.
Хорошо получается Я бы еще в конце каждой статьи добавлял изображение с результатами работы. Понимаю, что не всегда оно будет впечатляющим или красочным, но без него чего-то не хватает, особенно если нет возможности попробовать сразу собрать приложение. (но это исключительно по моему скромному мнению
зона 2D рисует закрашенный фон?
вместо него можно использовать glClearColor? или с какой-то целью так?
Такой подход на много лучше чем использование glClearColor. Вы получаете экранные координаты и можете располагать элементы UI и прочее + на плоскость можно натянуть текстуру. Плоскость так же может отражать свет.
С помощью такого разделения мы просто получаем возможность использовать более продвинутый background, ограниченный в своем функционале только возможностями OpenGL 1.0.
Я бы еще в конце каждой статьи добавлял изображение с результатами работы.
Понимаю, что не всегда оно будет впечатляющим или красочным, но без него чего-то не хватает, особенно если нет возможности попробовать сразу собрать приложение. (но это исключительно по моему скромному мнению