Zastosuj projekcje i widoki z kamery

W środowisku OpenGL ES projekcja i widoki z kamery umożliwiają wyświetlanie narysowanych obiektów w sposób bardziej zbliżony do tego, jak widzisz je wzrokiem. Ta symulacja widoku fizycznego odbywa się przez przekształcenia matematyczne rysowanych współrzędnych obiektu:

  • Odwzorowanie – to przekształcenie dostosowuje współrzędne rysowanych obiektów na podstawie szerokości i wysokości GLSurfaceView w miejscu, w którym są wyświetlane. Bez tych obliczeń obiekty rysowane przez OpenGL ES są zniekształcone przez nierówne proporcje okna widoku. Przekształcenie odwzorowania zwykle trzeba obliczać tylko wtedy, gdy proporcje widoku OpenGL zostały określone lub zmienione w metodzie onSurfaceChanged() mechanizmu renderowania. Więcej informacji o odwzorowywaniu OpenGL ES i mapowaniu współrzędnych znajdziesz w sekcji Mapowanie współrzędnych dla rysowanych obiektów.
  • Widok kamery – to przekształcenie dostosowuje współrzędne rysowanych obiektów na podstawie położenia kamery wirtualnej. Warto pamiętać, że OpenGL ES nie definiuje rzeczywistego obiektu kamery, ale udostępnia praktyczne metody, które symulują kamerę przez przekształcanie wyświetlania narysowanych obiektów. Przekształcenie widoku z aparatu może zostać obliczone tylko raz po utworzeniu obiektu GLSurfaceView lub może się zmieniać dynamicznie w zależności od działań użytkownika lub funkcji aplikacji.

Z tej lekcji dowiesz się, jak utworzyć obraz odwzorowania i widok z kamery oraz zastosować go do kształtów narysowanych w GLSurfaceView.

Zdefiniuj rzut

Dane do przekształcenia prognozy są obliczane w metodzie onSurfaceChanged() klasy GLSurfaceView.Renderer. Ten przykładowy kod pobiera wysokość i szerokość elementu GLSurfaceView oraz używa go do wypełnienia przekształcenia odwzorowania Matrix przy użyciu metody Matrix.frustumM():

Kotlin

// 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)
}

Java

// 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);
}

Ten kod wypełnia macierz projekcji mProjectionMatrix, którą możesz następnie połączyć z przekształceniem widoku kamery w metodzie onDrawFrame(), która została przedstawiona w następnej sekcji.

Uwaga: zastosowanie przekształcenia odwzorowania do rysowanych obiektów zwykle powoduje wyświetlenie bardzo pustej zawartości. Musisz też użyć przekształcenia widoku z kamery, aby cokolwiek pojawiło się na ekranie.

Definiowanie widoku z kamery

Dokończ proces przekształcania rysowanych obiektów, dodając transformację widoku z kamery w ramach procesu rysowania w mechanizmie renderowania. W poniższym przykładowym kodzie przekształcenie widoku kamery jest obliczane za pomocą metody Matrix.setLookAtM(), a potem łączone z wcześniej obliczoną matrycą projekcji. Połączone macierze przekształcenia są następnie przekazywane do narysowanego kształtu.

Kotlin

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)

Java

@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);
}

Zastosuj przekształcenia obrazu i aparatu

Aby użyć połączonej macierzy transformacji widoku i odwzorowania obrazu z kamery widocznej w sekcjach podglądu, najpierw dodaj zmienną macierzy do cienia wierzchołków zdefiniowanego wcześniej w klasie Triangle:

Kotlin

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

    ...
}

Java

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;

    ...
}

Następnie zmodyfikuj metodę draw() obiektów graficznych, by zaakceptować połączoną macierz przekształceń, i zastosuj ją do kształtu:

Kotlin

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)
}

Java

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);
}

Po prawidłowym obliczeniu i zastosowaniu przekształceń związanych z rzutem i widokiem z kamery obiekty graficzne są rysowane w odpowiednich proporcjach i powinny wyglądać tak:

Rysunek 1. Trójkąt narysowany z zastosowanym odwzorowaniem i widokiem z kamery.

Teraz, gdy masz już aplikację wyświetlającą kształty we właściwych proporcjach, czas dodać ruch do kształtów.