上篇文章介紹了 Android 中的 OpenGL 以及坐標映射等,在 OpenGL ES 環境中,通過投影和相機視圖,顯示的繪製對象更接近於眼睛看到的實物,這種呈現方式是通過對繪製對象坐標進行數學轉換來完成,這裡介紹一下投影和相機視圖相關知識,文中代碼案例變更可以參考前一篇文章:
主要內容如下:
- 投影類型
- 定義投影
- 定義相機視圖
- 應用投影和相機視圖
- 運行效果
投影類型#
OpenGL 中主要有兩種投影模式,分別是正交投影和透視投影,其特點分別如下:
- 透視投影:符合人眼習慣,呈現近大遠小的效果。
- 正交投影:所有物體在投影平面上保持原來的大小。
透視投影的視景體是截錐體,正交投影的視景體是長方體,透視投影和正交投影示意圖如下:
兩者對應的矩陣計算函數如下:
// 透視投影矩陣
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
,其變化參考如下動圖:
定義相機視圖#
相機視圖顧名思義就相當於站在相機的角度觀察某個物體,使用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 之間,不能在其範圍外觀察,參考如下動圖:
應用投影和相機視圖#
為了適應投影和視圖變換,修改上一篇中的著色器代碼如下:
// 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 渲染視頻時的視頻比例等。
運行效果#
可以與上一篇文章中的運行效果進行對比,運行效果如下:
可以回覆關鍵字【OpenGL】獲取源代碼,上文中出現的動圖程序回覆關鍵字【OTUTORS】獲取。