banner
jzman

jzman

Coding、思考、自觉。
github

OpenGL ES投影和視圖變換

上篇文章介紹了 Android 中的 OpenGL 以及坐標映射等,在 OpenGL ES 環境中,通過投影和相機視圖,顯示的繪製對象更接近於眼睛看到的實物,這種呈現方式是通過對繪製對象坐標進行數學轉換來完成,這裡介紹一下投影和相機視圖相關知識,文中代碼案例變更可以參考前一篇文章:

主要內容如下:

  1. 投影類型
  2. 定義投影
  3. 定義相機視圖
  4. 應用投影和相機視圖
  5. 運行效果

投影類型#

OpenGL 中主要有兩種投影模式,分別是正交投影和透視投影,其特點分別如下:

  1. 透視投影:符合人眼習慣,呈現近大遠小的效果。
  2. 正交投影:所有物體在投影平面上保持原來的大小。

透視投影的視景體是截錐體,正交投影的視景體是長方體,透視投影和正交投影示意圖如下:

image

兩者對應的矩陣計算函數如下:

// 透視投影矩陣
Matrix.frustumM(float[] m, int offset, float left, float right, float bottom, float top, float near, float far);
// 正交投影矩陣
Matrix.orthoM(float[] m, int offset, float left, float right, float bottom, float top, float near, float far);

上述函數參數中 m 用來存儲對應的投影矩陣數據,near 和 far 分別表示視景體的近平面、遠屏幕距離,left、right、top、bottom 對應的是遠平面的參數。

定義投影#

根據上一小節內容,這裡使用透視投影,填充投影矩陣使用 Matrix.frustumM() ,如下

private val projectionMatrix = FloatArray(16)
override fun onSurfaceChanged(unused: GL10, width: Int, height: Int) {
    GLES20.glViewport(0, 0, width, height)
    val ratio: Float = width.toFloat() / height.toFloat()
    Matrix.frustumM(projectionMatrix, 0, -ratio, ratio, -1f, 1f, 3f, 7f)
}

上述代碼填充了一個投影矩陣projectionMatrix,其變化參考如下動圖:
frust.gif

定義相機視圖#

相機視圖顧名思義就相當於站在相機的角度觀察某個物體,使用Matrix.setLookAtM方法來填充視圖矩陣,關鍵參數主要是相機位置、目標位置和相機的正上方向量,然後將投影矩陣和視圖矩陣合併為vPMatrix,如下:

override fun onDrawFrame(gl: GL10?) {
    // 繪製當前frame,用於渲染處理具體的內容
    Log.d(tag, "onDrawFrame")
   
    // 設置相機位置(視圖矩陣)
    Matrix.setLookAtM(viewMatrix,0,
                  0.0f,0.0f,5.0f, // 相機位置
                  0.0f,0.0f,0.0f, // 目標位置
                  0.0f,1.0f,0.0f) // 相機正上方向量
    // 計算投影和視圖變換
    Matrix.multiplyMM(vPMatrix,0,projectionMatrix,0,viewMatrix,0)
    
    // 具體繪製
    triangle.draw(vPMatrix)
}

如上案例中相機位置 z 坐標為只能在 near 和 far 之間,也就是必須在 3 和 7 之間,不能在其範圍外觀察,參考如下動圖:
lookat.gif

應用投影和相機視圖#

為了適應投影和視圖變換,修改上一篇中的著色器代碼如下:

// default
attribute vec4 vPosition;
void main() {
    gl_Position = vPosition;
}
// 使用投影和視圖變換
uniform mat4 uMVPMatrix;
attribute vec4 vPosition;
void main() {
    gl_Position = uMVPMatrix * vPosition;
}

將上一小節中計算得到的的vPMatrix矩陣傳入著色器即可:

fun draw(mvpMatrix: FloatArray) {
    // 獲取attribute變量的地址索引
    // get handle to vertex shader's vPosition member
    positionHandle = GLES20.glGetAttribLocation(programHandle, "vPosition").also {
        // enable vertex attribute,默認是disable
        GLES20.glEnableVertexAttribArray(it)
        GLES20.glVertexAttribPointer(
            it,
            COORDINATE_PER_VERTEX,
            GLES20.GL_FLOAT,
            false,
            vertexStride,
            vertexBuffer
        )
    }
    // get handle to fragment shader's vColor member
    colorHandler = GLES20.glGetUniformLocation(programHandle, "vColor").also {
        GLES20.glUniform4fv(it, 1, color, 0)
    }
   
    // get handle to shape's transformation matrix
    vPMatrixHandle = GLES20.glGetUniformLocation(programHandle, "uMVPMatrix")
    // Pass the projection and view transformation to the shader
    GLES20.glUniformMatrix4fv(vPMatrixHandle, 1, false, mvpMatrix, 0)
    
    // draw triangle
    GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount)
    GLES20.glDisableVertexAttribArray(positionHandle)
}

到此通過代碼改造應用投影和相機視圖,解決了橫豎屏切換的變形問題,這種變形自然可以延伸到其他領域,比如 OpenGL 渲染視頻時的視頻比例等。

運行效果#

可以與上一篇文章中的運行效果進行對比,運行效果如下:

image

可以回覆關鍵字【OpenGL】獲取源代碼,上文中出現的動圖程序回覆關鍵字【OTUTORS】獲取。

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。