В среде OpenGL ES виды проекции и камеры позволяют отображать нарисованные объекты таким образом, который больше напоминает то, как вы видите физические объекты своими глазами. Эта симуляция физического просмотра выполняется с помощью математических преобразований координат нарисованного объекта:
- Проекция . Это преобразование корректирует координаты нарисованных объектов в зависимости от ширины и высоты
GLSurfaceView
, в котором они отображаются. Без этого расчета объекты, нарисованные OpenGL ES, искажаются из-за неравных пропорций окна просмотра. Преобразование проекции обычно необходимо рассчитывать только тогда, когда пропорции представления OpenGL устанавливаются или изменяются в методеonSurfaceChanged()
вашего средства визуализации. Дополнительные сведения о проекциях OpenGL ES и сопоставлении координат см. в разделе «Сопоставление координат рисуемых объектов» . - Вид камеры . Это преобразование корректирует координаты нарисованных объектов в зависимости от положения виртуальной камеры. Важно отметить, что OpenGL ES не определяет реальный объект камеры, а вместо этого предоставляет служебные методы, которые имитируют камеру путем преобразования отображения нарисованных объектов. Преобразование вида камеры может рассчитываться только один раз, когда вы устанавливаете
GLSurfaceView
, или может меняться динамически в зависимости от действий пользователя или функции вашего приложения.
В этом уроке описывается, как создать проекцию и вид с камеры и применить их к фигурам, нарисованным в 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]; @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()
, который показан в следующем разделе.
Примечание. Простое применение преобразования проекции к объектам рисования обычно приводит к очень пустому отображению. В общем, вам также необходимо применить преобразование вида камеры, чтобы что-либо отображалось на экране.
Определить вид камеры
Завершите процесс преобразования нарисованных объектов, добавив преобразование вида камеры как часть процесса рисования в средстве визуализации. В следующем примере кода преобразование вида камеры рассчитывается с помощью метода 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)
Ява
@Override public void onDrawFrame(GL10 unused) { ... // Set the camera position (View matrix) Matrix.setLookAtM(viewMatrix, 0, 0, 0, 3, 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); }
Применение преобразований проекции и камеры
Чтобы использовать комбинированную матрицу преобразования проекции и вида камеры, показанную в разделах предварительного просмотра, сначала добавьте переменную матрицы в вершинный шейдер , ранее определенный в классе 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 ... }
Ява
public class Triangle { private final String 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 int vPMatrixHandle; ... }
Затем измените метод draw()
ваших графических объектов, чтобы он принимал комбинированную матрицу преобразования и применял ее к фигуре:
Котлин
fun draw(mvpMatrix: FloatArray) { // pass in the calculated transformation matrix // get handle to shape's transformation matrix vPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix") // Pass the projection and view transformation to the shader GLES20.glUniformMatrix4fv(vPMatrixHandle, 1, false, mvpMatrix, 0) // Draw the triangle GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount) // Disable vertex array GLES20.glDisableVertexAttribArray(positionHandle) }
Ява
public void draw(float[] mvpMatrix) { // pass in the calculated transformation matrix ... // get handle to shape's transformation matrix vPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix"); // Pass the projection and view transformation to the shader GLES20.glUniformMatrix4fv(vPMatrixHandle, 1, false, mvpMatrix, 0); // Draw the triangle GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount); // Disable vertex array GLES20.glDisableVertexAttribArray(positionHandle); }
После того, как вы правильно рассчитали и применили преобразования проекции и вида камеры, ваши графические объекты будут нарисованы в правильных пропорциях и должны выглядеть следующим образом:
![](https://developer.android.com/static/images/opengl/ogl-triangle-projected.png?hl=ru)
Рис. 1. Треугольник, нарисованный с применением проекции и вида камеры.
Теперь, когда у вас есть приложение, которое отображает ваши фигуры в правильных пропорциях, пришло время добавить к вашим фигурам движение.