Применение изображений проекции и камеры

В среде 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);
}

После того, как вы правильно рассчитали и применили преобразования проекции и вида камеры, ваши графические объекты будут нарисованы в правильных пропорциях и должны выглядеть следующим образом:

Рис. 1. Треугольник, нарисованный с применением проекции и вида камеры.

Теперь, когда у вас есть приложение, которое отображает ваши фигуры в правильных пропорциях, пришло время добавить к вашим фигурам движение.