Разработка мобильных приложений. Программирование мобильных устройств на языке Kotlin. Учебное пособие для СПО
.pdf
Масштабирование (проекция)
В среде OpenGL ES виды проекции и камеры позволяют отображать нарисованные объекты таким образом, который больше напоминает то, как вы видите физические объекты своими глазами. Эта симуляция физического просмотра выполняется с помощью математических преобразований координат нарисованного объекта:
Проекция. Это преобразование корректирует координаты нарисованных объектов в зависимости от ширины и высоты GLSurfaceView , в котором они отображаются. Без этого расчета объекты, нарисованные OpenGL ES, искажаются из-за неравных пропорций окна просмотра. Преобразование проекции обычно необходимо рассчитывать только тогда, когда пропорции представления OpenGL устанавливаются или изменяются в методе onSurfaceChanged() вашего средства визуализации.
Вид камеры. Это преобразование корректирует координаты нарисованных объектов в зависимости от положения виртуальной камеры. Важно отметить, что OpenGL ES не определяет реальный объект камеры, а вместо этого предоставляет служебные методы, которые имитируют камеру путем преобразования отображения нарисованных объектов. Преобразование вида
камеры |
может |
|
рассчитываться |
только |
один |
раз, |
когда |
вы |
устанавливаете GLSurfaceView , или |
может |
меняться динамически |
в |
|||||
зависимости от действий пользователя или функции вашего приложения. |
|
|||||||
Данные |
для |
преобразования |
проекции |
рассчитываются |
в |
|||
методе onSurfaceChanged() вашего класса GLSurfaceView.Renderer . Следующий пример кода принимает высоту и ширину GLSurfaceView и использует их для
заполнения |
Matrix |
преобразования |
проекции |
с |
помощью |
метода Matrix.frustumM() : |
|
|
|
|
|
// vPMatrix is an abbreviation for "Model View Projection Matrix" |
|
|
|||
private val vPMatrix = FloatArray(16) |
|
|
|
||
private val projectionMatrix = FloatArray(16) |
|
|
|
||
private val viewMatrix = FloatArray(16) |
|
|
|
||
override fun onSurfaceChanged(unused: GL10, width: Int, height: Int) { GLES20.glViewport(0, 0, width, height)
val ratio: Float = width.toFloat() / height.toFloat()
//this projection matrix is applied to object coordinates
//in the onDrawFrame() method Matrix.frustumM(projectionMatrix, 0, -ratio, ratio, -1f, 1f, 3f, 7f)
}
// vPMatrix is an abbreviation for "Model View Projection Matrix" private final float[] vPMatrix = new float[16];
private final float[] projectionMatrix = new float[16]; private final float[] viewMatrix = new float[16];
22
21
@Override
public void onSurfaceChanged(GL10 unused, int width, int height) { GLES20.glViewport(0, 0, width, height);
float ratio = (float) width / height;
//this projection matrix is applied to object coordinates
//in the onDrawFrame() method Matrix.frustumM(projectionMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
}
Этот код заполняет матрицу проекции mProjectionMatrix, которую затем можно объединить с преобразованием вида камеры в методе onDrawFrame(), который показан в следующем разделе.
Чтобы использовать комбинированную матрицу преобразования проекции и вида камеры, показанную в разделах предварительного просмотра, сначала добавьте переменную матрицы в вершинный шейдер, ранее определенный в классе Triangle:
class Triangle {
private val vertexShaderCode =
//This matrix member variable provides a hook to manipulate
//the coordinates of the objects that use this vertex shader "uniform mat4 uMVPMatrix;" +
"attribute vec4 vPosition;" + "void main() {" +
//the matrix must be included as a modifier of gl_Position
//Note that the uMVPMatrix factor *must be first* in order
//for the matrix multiplication product to be correct.
" gl_Position = uMVPMatrix * vPosition;" + "}"
// Use to access and set the view transformation private var vPMatrixHandle: Int = 0
...
}
23
22
// vPMatrix is an abbreviation for "Model View Projection Matrix" private val vPMatrix = FloatArray(16)
private val projectionMatrix = FloatArray(16) private val viewMatrix = FloatArray(16)
override fun onSurfaceChanged(unused: GL10, width: Int, height: Int) { GLES20.glViewport(0, 0, width, height)
val ratio: Float = width.toFloat() / height.toFloat()
//this projection matrix is applied to object coordinates
//in the onDrawFrame() method Matrix.frustumM(projectionMatrix, 0, -ratio, ratio, -1f, 1f, 3f, 7f)
}
Этот код заполняет матрицу проекции mProjectionMatrix, которую затем можно объединить с преобразованием вида камеры в методе onDrawFrame(), который показан в следующем разделе.
Завершите процесс преобразования нарисованных объектов, добавив преобразование вида камеры как часть процесса рисования в средстве визуализации. В следующем примере кода преобразование вида камеры рассчитывается с помощью метода Matrix.setLookAtM(), а затем объединяется с ранее рассчитанной матрицей проекции. Объединенные матрицы преобразования затем передаются в нарисованную фигуру.
override fun onDrawFrame(unused: GL10) {
...
//Set the camera position (View matrix) Matrix.setLookAtM(viewMatrix, 0, 0f, 0f, 3f, 0f, 0f, 0f, 0f, 1.0f, 0.0f)
//Calculate the projection and view transformation Matrix.multiplyMM(vPMatrix, 0, projectionMatrix, 0, viewMatrix, 0)
//Draw shape
triangle.draw(vPMatrix)
24
23
ПРАКТИЧЕСКАЯ РАБОТА 2
Сделать классы OpenGL квадрат, куб и отобразить их на экране. На квадрат наложить текстуру галактики / звездного неба. Отодвинуть и отмасштабировать квадрат так, чтобы он воспринимался как фоновое изображение. Куб должен располагаться по центру экрана.
25
24
Вращение фигур
В OpenGL ES 1.0 (фиксированный конвейер) вращение фигур осуществляется с использованием матриц преобразования. Поскольку это фиксированный конвейер, у нас нет шейдеров для непосредственного управления преобразованием координат. Вместо этого мы используем функции OpenGL для управления матрицей моделирования (Model Matrix). Матрица моделирования определяет положение, ориентацию и масштаб объекта в мировом пространстве. Разберем ключевые понятия.
Матрица моделирования (Model Matrix). Матрица 4x4, представляющая преобразование объекта из его локальной системы координат в мировую систему координат.
Стек матриц (Matrix Stack). В OpenGL ES 1.0 используется стек матриц для управления матрицами преобразования. Мы можем помещать матрицы в стек, умножать текущую матрицу на новую матрицу и извлекать матрицы из стека.
glRotatef(angle, x, y, z). Функция OpenGL, которая создает матрицу вращения вокруг указанной оси и умножает ее на текущую матрицу моделирования.
o angle: Угол поворота в градусах.
o x, y, z: Компоненты вектора, определяющего ось вращения.
Алгоритм реализации вращения:
1.Выбор режима матрицы. Указываем, что мы хотим работать с матрицей моделирования с помощью glMatrixMode(GL10.GL_MODELVIEW).
2.Сброс матрицы. Загружаем единичную матрицу, чтобы убрать все
предыдущие |
преобразования. |
Это |
можно |
сделать |
с |
помощью glLoadIdentity(). Очень |
важно! |
Не сбросив |
матрицу, |
вы |
|
продолжите накапливать вращения и другие преобразования, что приведет
к непредсказуемым результатам.
3.Выполнение вращения. Вызываем функцию glRotatef() для выполнения вращения.
4.Отрисовка фигуры. После выполнения вращения рисуем фигуру. OpenGL использует текущую матрицу моделирования для преобразования координат вершин фигуры.
override fun onSurfaceCreated(gl: GL10, config: EGLConfig) { gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f) gl.glEnable(GL10.GL_DEPTH_TEST) // Включаем тест глубины
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY) // Включаем использование массива вершин
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer)
gl.glEnableClientState(GL10.GL_COLOR_ARRAY) // Включаем
26
25
использование массива цветов
gl.glColorPointer(4, GL10.GL_FLOAT, 0, colorBuffer)
}
override fun onSurfaceChanged(gl: GL10, width: Int, height: Int) { gl.glViewport(0, 0, width, height) gl.glMatrixMode(GL10.GL_PROJECTION)
gl.glLoadIdentity()
val ratio = width.toFloat() / height
GLU.gluPerspective(gl, 45.0f, ratio, 0.1f, 100.0f) // Перспективная
проекция
}
override fun onDrawFrame(gl: GL10) { gl.glClear(GL10.GL_COLOR_BUFFER_BIT or
GL10.GL_DEPTH_BUFFER_BIT) // Очищаем буфер цвета и глубины
gl.glMatrixMode(GL10.GL_MODELVIEW) // Указываем, что работаем с матрицей моделирования
gl.glLoadIdentity() // Загружаем единичную матрицу (сброс) gl.glTranslatef(0.0f, 0.0f, -6.0f) // Отдаляем куб от камеры gl.glRotatef(angle, 1.0f, 1.0f, 0.0f) // Вращаем куб вокруг оси (1, 1, 0)
// Рисуем куб с использованием индексов gl.glDrawElements(GL10.GL_TRIANGLES, indices.size,
GL10.GL_UNSIGNED_BYTE, indexBuffer)
angle += 1.0f // Увеличиваем угол для анимации
}
}
Kotlin
Впрограмме выше:
•MyGLRenderer: Класс, реализующий GLSurfaceView.Renderer, отвечает за отрисовку графики.
•angle: Переменная, хранящая угол поворота.
•vertices, colors, indices: Массивы данных для куба (вершины, цвета, индексы для отрисовки). Вам нужно будет адаптировать эти данные для вашей фигуры.
•vertexBuffer, colorBuffer, indexBuffer: Буферы для хранения данных вершин, цветов и индексов. OpenGL работает с данными, находящимися в буферах.
27
26
•onSurfaceCreated(): Инициализация OpenGL и буферов.
•onSurfaceChanged(): Настройка проекции (perspective).
•onDrawFrame():
o gl.glClear(): Очищает буфер цвета и глубины.
o gl.glMatrixMode(GL10.GL_MODELVIEW): Устанавливает режим матрицы в “модель-вид” (матрица моделирования).
o gl.glLoadIdentity(): Ключевой момент! Загружает единичную матрицу, сбрасывая предыдущие преобразования.
ogl.glTranslatef(): Перемещает объект в пространстве (чтобы его было видно, т.к. камера находится в точке (0,0,0)).
ogl.glRotatef(): Выполняет вращение. angle определяет угол, а 1.0f, 1.0f, 0.0f – ось вращения.
o gl.glDrawElements(): Рисует куб с использованием индексированного рисования.
o angle += 1.0f: Увеличивает угол поворота для анимации.
Важные моменты:
•Ось вращения: Вращение происходит вокруг оси, проходящей через начало координат (0, 0, 0). Если вы хотите вращать объект вокруг другой точки, вам нужно сначала переместить объект так, чтобы эта точка совпадала с началом координат, затем выполнить вращение, а затем переместить объект обратно.
•Порядок преобразований: Порядок, в котором выполняются преобразования, имеет значение. В коде выше сначала выполняется перемещение, а затем вращение. Это означает, что объект сначала перемещается на -6.0f по оси Z, а затем вращается вокруг оси, проходящей через начало координат.
•Система координат: OpenGL использует правую систему координат.
•Производительность: OpenGL ES 1.0 (фиксированный конвейер) менее гибок и менее оптимизирован, чем OpenGL ES 2.0 (программируемый
конвейер). В OpenGL ES 2.0 вы можете использовать шейдеры для более эффективного управления преобразованиями.
• Использование GLSurfaceView: Этот код предназначен для использования в GLSurfaceView в Android.
Альтернативные способы вращения:
•Использование кватернионов: Кватернионы – это математический способ представления вращения, который может быть более эффективным и стабильным, чем использование углов Эйлера (как в glRotatef). Однако реализация с использованием кватернионов сложнее.
28
27
Стереометрия
Стереометрия в OpenGL ES 1.0 на Kotlin подразумевает построение и отображение трехмерных объектов в двумерном пространстве экрана. Поскольку OpenGL является графической библиотекой, ориентированной на работу с примитивами, понимание трехмерной геометрии и ее преобразование в 2D проекцию является ключевым.
|
Основные концепции стереометрии в OpenGL ES 1.0: |
1. |
Вершины (Vertices): |
|
o Основной строительный блок для представления 3D-объектов. |
|
o Представляют собой точки в трехмерном пространстве, заданные |
|
координатами (x, y, z). |
|
o В OpenGL ES 1.0 вершины передаются в виде массивов данных |
|
(например, FloatBuffer). |
2. |
Примитивы (Primitives): |
|
o Способ соединения вершин для формирования геометрических |
|
фигур. |
oНаиболее распространенные примитивы:
▪GL_POINTS: Рисует отдельные точки для каждой вершины.
▪GL_LINES: Рисует отдельные линии между каждой парой вершин.
▪GL_LINE_STRIP: Рисует последовательность соединенных линий.
▪GL_LINE_LOOP: Как GL_LINE_STRIP, но замыкает контур,
соединяя последнюю вершину с первой.
▪GL_TRIANGLES: Рисует отдельные треугольники, используя каждую тройку вершин. (Самый распространенный для 3D)
▪GL_TRIANGLE_STRIP: Рисует последовательность соединенных треугольников. (Оптимизированная версия для определенных случаев)
▪GL_TRIANGLE_FAN: Рисует веер треугольников, используя первую вершину как общую для всех треугольников.
3.Индексированное рисование (Indexed Drawing):
oСпособ оптимизации отрисовки, особенно для сложных моделей, содержащих общие вершины.
oИспользуется массив индексов, указывающий порядок использования вершин для формирования примитивов.
oФункция glDrawElements() используется для выполнения индексированного рисования.
4.Цвет (Color):
oКаждой вершине или всему примитиву можно назначить цвет.
oЦвета обычно задаются в формате RGBA (красный, зеленый, синий, альфа), где каждое значение находится в диапазоне от 0.0 до
1.0.
29
28
oВ OpenGL ES 1.0 цвета передаются в виде массивов данных
(например, FloatBuffer).
5.Матрицы преобразований (Transformation Matrices):
oИспользуются для изменения положения, ориентации и масштаба объектов в трехмерном пространстве.
oОсновные типы матриц преобразований:
▪Моделирования (Model Matrix): Преобразует объект из его локальной системы координат в мировую систему координат.
▪Вида (View Matrix): Преобразует мировую систему координат в систему координат камеры (глаза).
▪Проекции (Projection Matrix): Преобразует 3D координаты в 2D координаты экрана.
oВ OpenGL ES 1.0 матрицы преобразований управляются с использованием стека матриц и функций, таких
как glTranslatef(), glRotatef(), glScalef(), glFrustumf(), gluPerspective()
6.Проекция (Projection):
oПроцесс преобразования трехмерных координат в двухмерные
координаты экрана.
oДва основных типа проекций:
▪Перспективная проекция (Perspective Projection): Создает эффект перспективы, где объекты, находящиеся дальше от камеры, кажутся меньше. Используется
функция gluPerspective() или glFrustumf().
▪Ортогональная проекция (Orthographic Projection): Не создает эффект перспективы; объекты сохраняют свой размер независимо от расстояния до камеры. Используется
функция glOrthof().
7.Буфер глубины (Depth Buffer):
oИспользуется для определения, какие объекты находятся ближе к
камере и должны быть отображены поверх других объектов. o Включается с помощью glEnable(GL10.GL_DEPTH_TEST).
oОчищается с помощью glClear(GL10.GL_DEPTH_BUFFER_BIT).
8.Освещение (Lighting): (Необязательно, но важно для реалистичного отображения)
oOpenGL ES 1.0 поддерживает базовые модели освещения, такие как диффузное, зеркальное и окружающее освещение.
oДля работы с освещением необходимо включить освещение
(glEnable(GL10.GL_LIGHTING)) и настроить параметры источников света и материалов.
o Также требуются нормали к поверхностям для расчета освещения (векторы, перпендикулярные поверхности).
Для создания более сложных трехмерных объектов необходимо:
1.Определить вершины, цвета и индексы для объекта.
2.Создать буферы для хранения этих данных.
30
29
3.Использовать glDrawElements() для отрисовки объекта с использованием индексов.
4.Использовать матрицы преобразований для изменения положения, ориентации и масштаба объекта.
5.Настроить освещение для реалистичного отображения.
ПРАКТИЧЕСКАЯ РАБОТА 3
Скройте куб. Добавьте в центр экрана модель солнечной системы. Солнце и вращающиеся планеты. Добавьте вращение Луны перпендикулярно плоскости эклиптики.
31
30
