OpenGL 學習系列---投影矩陣

OpenGL 座標系統 文章中,根據點的座標變換得出了以下的公式:java

V_{clip}=M_{projection} \cdot M_{view} \cdot M_{model} \cdot V_{local}

這個公式每左乘一個矩陣,都表明了一種座標系的變換。git

轉化爲着色器腳本語言以下:github

attribute vec4 a_Position;
uniform mat4 u_ModelMatrix;
uniform mat4 u_ProjectionMatrix;
uniform mat4 u_ViewMatrix;
void main()
{
    gl_Position  = u_ProjectionMatrix * u_ViewMatrix * u_ModelMatrix * a_Position;
}
複製代碼

本篇文章就主要是對投影矩陣來分析的。bash

OpenGL 在觀察空間轉換到裁剪空間時,須要用到投影矩陣。而在着色器腳本中,也須要提供一個投影矩陣給對應的 u_ProjectionMatrix變量。微信

首先要在程序裏綁定到對應的變量,而後再給變量賦值。ide

// 綁定到着色器腳本中的對應變量
private static final String U_ProMatrix = "u_ProjectionMatrix";
private int uProMatrixLocation;
uProMatrixLocation = glGetUniformLocation(mProgram,U_ProMatrix);
// 給變量賦值,projectionMatrix 爲投影矩陣
glUniformMatrix4fv(uProMatrixLocation,1,false,projectionMatrix,0)
複製代碼

正如前文講到的,投影矩陣會建立一個視景體對物體座標進行裁剪,獲得的裁剪座標再通過透視除法以後,就會獲得歸一化設備座標。歸一化設備座標再通過視口轉換,最終將座標映射到了屏幕上。函數

OpenGL 提供了兩種投影方式:正交投影和透視投影。post

正交投影矩陣

無論是正交投影仍是透視投影,最終都是將視景體內的物體投影在近平面上,這也是 3D 座標轉換到 2D 座標的關鍵一步。ui

而近平面上的座標接着也會轉換成歸一化設備座標,再映射到屏幕視口上。spa

爲了解決以前的圖像拉伸問題,就是要保證近平面的寬高比和視口的寬高比一致,並且是以較短的那一邊做爲 1 的標準,讓圖像保持居中。

OpenGL 提供了 Matrix.orthoM 函數來生成正交投影矩陣。

/** * Computes an orthographic projection matrix. * * @param m returns the result 正交投影矩陣 * @param mOffset 偏移量,默認爲 0 ,不偏移 * @param left 左平面距離 * @param right 右平面距離 * @param bottom 下平面距離 * @param top 上平面距離 * @param near 近平面距離 * @param far 遠平面距離 */
    public static void orthoM(float[] m, int mOffset, float left, float right, float bottom, float top, float near, float far) 複製代碼

須要注意的是,咱們的左、上、右、下距離都是相對於近平面中心的。

近平面的座標原點位於中心,向右爲 X 軸正方向,向上爲 Y 軸正方向,因此咱們的 left、bottom 要爲負數,而 right、top 要爲正數。同時,近平面和遠平面的距離都是指相對於視點的距離,因此 near、far 要爲正數,並且 far > near

能夠在 GLSurfaceView 的 surfaceChanged 裏面來設定正交投影矩陣。

@Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        float aspectRatio = width > height ? (float) width / (float) height : (float) height / (float) width;
        if (width > height){
            Matrix.orthoM(projectionMatrix,0,-aspectRatio,aspectRatio,-1f,1f,0f,10f);
        }else {
            Matrix.orthoM(projectionMatrix,0,-1f,1f,-aspectRatio,aspectRatio,0f,10f);
        }
    }
複製代碼

這樣的話,就把近平面的寬高比設定與視口的寬高比一致了。

透視投影矩陣

OpenGL 提供了兩個函數來建立透視投影矩陣:frustumMperspectiveM

frustumM

frustumM 函數建立的視景體是一個錐形。

它的視景體有點相似於正交投影,在參數理解上基本都相同的。

/**
     * Defines a projection matrix in terms of six clip planes.
     *
     * @param m the float array that holds the output perspective matrix
     * @param offset the offset into float array m where the perspective
     *        matrix data is written
     * @param left 
     * @param right
     * @param bottom
     * @param top
     * @param near
     * @param far
     */
    public static void frustumM(float[] m, int offset,
            float left, float right, float bottom, float top,
            float near, float far)
複製代碼

須要注意的是 near 和 far 變量的值必需要大於 0 。由於它們都是相對於視點的距離,也就是照相機的距離。

當用視圖矩陣肯定了照相機的位置時,要確保物體距離視點的位置在 near 和 far 的區間範圍內,不然就會看不到物體。

因爲透視投影會產生近大遠小的效果,當照相機位置不變,改變 near 的值時也會改變物體大小,near 越小,則離視點越近,至關於物體越遠,那麼顯示的物體也就越小了。

固然也能夠 near 和 far 的距離不動,改變攝像機的位置來改變觀察到的物體大小。

perspectiveM

OpenGL 還提供了 perspectiveM 函數來建立投影矩陣,它的視景體和 frustumM 函數相同,可是構造的參數有所不一樣。

/**
     * Defines a projection matrix in terms of a field of view angle, an
     * aspect ratio, and z clip planes.
     *
     * @param m the float array that holds the perspective matrix
     * @param offset the offset into float array m where the perspective
     *        matrix data is written
     * @param fovy field of view in y direction, in degrees
     * @param aspect width to height aspect ratio of the viewport
     * @param zNear
     * @param zFar
     */
    public static void perspectiveM(float[] m, int offset,
          float fovy, float aspect, float zNear, float zFar)
複製代碼

視景體再也不須要肯定近平面左、上、右、下距離了。

經過視角來決定咱們能看到的視野大小。視角就是圖中所示的那個夾角。另外的參數是視口的寬高比,還有近平面和遠平面的距離,參數個數減小了。

上述圖片左邊是 90 視角,右邊是 45 度視角。顯然,視野角度越大,則看到的內容更多,可是物體顯得更小,而視野角度越小,則看的內容更少,但物體顯得更大。

frustumM不一樣的是,一旦肯定了視角和寬高比,那麼整個攝像機視野也就肯定了,此時完整的錐形視野已經造成了,也就是說物體的近大遠小效果已經完成了。這時,近平面距離和遠平面距離只是想要截取錐形視野中的那一部分了。不像在frustumM函數中,近、遠平面的距離還可以調整近大遠小的效果。

參考

  1. 《OpenGL ES 應用開發實踐指南》
  2. 《OpenGL ES 3.x 遊戲開發》

具體代碼詳情,能夠參考個人 Github 項目:

https://github.com/glumes/AndroidOpenGLTutorial

最後,若是以爲文章不錯,歡迎關注微信公衆號:【紙上淺談】

紙上淺談
相關文章
相關標籤/搜索