套用投影和相機檢視畫面

在 OpenGL ES 環境中,投影和相機檢視畫面可讓您以更接近肉眼觀看實體物件的方式顯示繪製物件的方式。這個模擬實體檢視模擬作業,是利用繪製的物件座標進行數學轉換:

  • 「Projection」:此轉換會根據已繪製物件的 GLSurfaceView 寬度和高度,調整其座標。如果沒有進行計算,OpenGL ES 繪製的物件就會因檢視區塊視窗的不等比例而扭曲。通常只有在轉譯器的 onSurfaceChanged() 方法中建立或變更 OpenGL 檢視畫面的比例時,才需要計算投影轉換。如要進一步瞭解 OpenGL ES 投影和座標對應,請參閱「繪製物件的對應座標」一文。
  • 相機檢視畫面:此轉換會根據虛擬相機位置調整已繪製物件的座標。請注意,OpenGL ES 不會定義實際的相機物件,而是提供可轉換繪製物件的顯示以模擬相機的公用程式方法。相機檢視畫面轉換只會在您建立 GLSurfaceView 時計算一次,或可能會根據使用者動作或應用程式功能動態變更。

本課程說明如何建立投影和相機檢視畫面,並套用至 GLSurfaceView 中繪製的形狀。

定義投影

投影轉換作業的資料會在 GLSurfaceView.Renderer 類別的 onSurfaceChanged() 方法中計算。下列程式碼範例採用 GLSurfaceView 的高度和寬度,並透過 Matrix.frustumM() 方法使用該程式碼填入投影轉換 Matrix

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

這段程式碼會填入投影矩陣 mProjectionMatrix,您接著可以與 onDrawFrame() 方法中的攝影機檢視畫面轉換合併,如下一節所示。

注意:只要對繪圖物件套用投影轉換,通常就會產生空無一物的顯示畫面。一般來說,您還必須套用相機檢視畫面轉換,才能在畫面上顯示任何內容。

定義攝影機檢視畫面

將相機檢視畫面轉換加入轉譯器繪圖程序,完成轉換所繪製物件的轉換程序。在以下範例程式碼中,系統會使用 Matrix.setLookAtM() 方法計算相機檢視畫面轉換,然後與先前計算的投影矩陣合併。接著,合併的轉換矩陣會傳遞至繪製的形狀。

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

套用投影和相機轉換

如要使用預覽部分中顯示的合併投影和相機檢視轉換矩陣,請先將矩陣變數新增至先前在 Triangle 類別中定義的 vertex 著色器

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;

    ...
}

接著,修改圖形物件的 draw() 方法,以接受合併的轉換矩陣並套用至形狀:

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

正確計算並套用投影和相機視角轉換後,圖形物件才能以正確的比例繪製,如下所示:

圖 1 已套用投影和相機檢視畫面繪製的三角形。

現在您有一個應用程式能以正確比例顯示形狀,接下來要為形狀新增動態。