Projektions- und Kameraansichten anwenden

In der OpenGL ES-Umgebung können Sie gezeichnete Objekte mithilfe der Projektions- und Kameraansichten so darstellen, wie Sie physische Objekte mit Ihren Augen sehen. Diese Simulation des physischen Betrachtens erfolgt mit mathematischen Transformationen gezeichneter Objektkoordinaten:

  • Projection (Projektion): Bei dieser Transformation werden die Koordinaten der gezeichneten Objekte basierend auf der Breite und Höhe der GLSurfaceView angepasst, auf der sie angezeigt werden. Ohne diese Berechnung sind die von OpenGL ES gezeichneten Objekte durch die ungleichen Proportionen des Ansichtsfensters verzerrt. Eine Projektionstransformation muss in der Regel nur dann berechnet werden, wenn die Anteile der OpenGL-Ansicht in der onSurfaceChanged()-Methode Ihres Renderers festgelegt oder geändert werden. Weitere Informationen zu OpenGL ES-Projektionen und Koordinatenzuordnungen finden Sie unter Koordinaten für gezeichnete Objekte zuordnen.
  • Kameraansicht: Bei dieser Transformation werden die Koordinaten von gezeichneten Objekten basierend auf einer virtuellen Kameraposition angepasst. OpenGL ES definiert kein tatsächliches Kameraobjekt, sondern bietet Dienstprogrammmethoden, die eine Kamera simulieren, indem die Anzeige gezeichneter Objekte angepasst wird. Eine Transformation der Kameraansicht wird möglicherweise nur einmal berechnet, wenn Sie die GLSurfaceView einrichten, oder sie kann sich je nach Nutzeraktionen oder der Funktion Ihrer Anwendung dynamisch ändern.

In dieser Lektion wird beschrieben, wie Sie eine Projektions- und Kameraansicht erstellen und auf Formen anwenden, die in GLSurfaceView gezeichnet werden.

Projektion definieren

Die Daten für eine Projektionstransformation werden in der Methode onSurfaceChanged() der Klasse GLSurfaceView.Renderer berechnet. Im folgenden Beispielcode werden die Höhe und Breite von GLSurfaceView verwendet, um mit der Methode Matrix.frustumM() eine Projektionstransformation Matrix zu füllen:

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

Mit diesem Code wird die Projektionsmatrix mProjectionMatrix ausgefüllt, die Sie dann mit einer Kameraansichtstransformation in der Methode onDrawFrame() kombinieren können, die im nächsten Abschnitt gezeigt wird.

Hinweis:Wenn Sie einfach eine Projektionstransformation auf Ihre Zeichnungsobjekte anwenden, ist die Anzeige in der Regel sehr leer. Im Allgemeinen müssen Sie auch eine Kameraansichtstransformation anwenden, damit etwas auf dem Bildschirm zu sehen ist.

Kameraansicht festlegen

Zum Transformieren der gezeichneten Objekte fügen Sie im Renderer als Teil des Zeichenprozesses eine Kameraansichtstransformation hinzu. Im folgenden Beispielcode wird die Transformation der Kameraansicht mit der Methode Matrix.setLookAtM() berechnet und dann mit der zuvor berechneten Projektionsmatrix kombiniert. Die kombinierten Transformationsmatrizen werden dann an die gezeichnete Form übergeben.

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

Projektions- und Kameratransformationen anwenden

Um die kombinierte Matrix für die Transformation von Projektion und Kameraansicht zu verwenden, die in den Vorschauabschnitten gezeigt wird, fügen Sie zuerst dem Vertex-Shader, der zuvor in der Klasse Triangle definiert wurde, eine Matrixvariable hinzu:

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;

    ...
}

Modifizieren Sie als Nächstes die Methode draw() Ihrer grafischen Objekte, um die kombinierte Transformationsmatrix zu akzeptieren und auf die Form anzuwenden:

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

Nachdem Sie die Transformationen von Projektion und Kameraansicht korrekt berechnet und angewendet haben, werden Ihre Grafikobjekte mit den richtigen Proportionen gezeichnet und sollten so aussehen:

Abbildung 1: Dreieck mit Projektion und Kameraansicht gezeichnet.

Da Sie nun eine Anwendung haben, in der Ihre Formen in den richtigen Proportionen dargestellt werden, ist es an der Zeit, sie zu bewegen.