OpenGL學習隨筆(四)

久違了,我的blog。這幾天被透視投影卡住了,雖然到現在也沒有完全搞明白,但也還是應該階段性總結一下。

 

3D圖像在2D平面顯示要經過以下幾個步驟,

1. 數據配置環節,配置頂點、顏色等數據,確定了物體的大小和外觀。
2. 指定模型視點變換參數,將物體到3D場景中,並確定我們的觀察點,也就是人眼的位置。
3. 指定投影變換參數,確定投影方式和方向。
4. 指定視口變換參數,將投影結果映射至視口(2D顯示平面)。

 

以上4步都屬於配置動作,分別用於修改OpenGL狀態機中的不同配置參數(頂點數組,變換矩陣等),真正觸發OpenGL開始工作的是類似glDrawElements這樣的API。記住,OpenGL就是一堆數據配置和動作。

 

2,3,4中涉及的各種變換都是通過矩陣來表示的(這裏有太多數學知識,再單獨總結吧),和各種數據緩存不同的是,整個OpenGL似乎只有一個矩陣堆棧,各種變換是共享的,所以還需要一個狀態量來表示當前矩陣是用於做什麼變換的。這個狀態量通過glMatrixMode來指定,參數可以是GL_PROJECTION, GL_MODELVIEW等等。

 

1. 模型視點變換

模型視點變換,亦可分別稱爲視點變換、模型變換,但由於兩者關係比較密切,所以一般會連在一起說。爲什麼兩者關係密切呢?因爲很多情況下,“人的觀察點不變,改變物體的位置”(模型變換)與“物體不變,改變人的位置”(視點變換)可以達到同樣的效果。

模型變換相關的函數有,glRotate*(旋轉),glTranslate*(平移),glScale*(縮放)等等。。

對於視點變換OpenGL並沒有提供專門的函數(這更體現了模型視點變換的互換性),但在glu裏提供了gluLookAt函數。它有三組參數,分別表示人眼所處的位置(eyex, eyey, eyez),人眼所觀察的位置(或者說所聚焦的位置,兩點一線,(centerx, centery, centerz)),以及眼睛與頸部所形成的向量(upx, upy, upz)。

拿拍攝來比喻,僅改變第一組參數表示相機繞物體移動,僅改變第二組參數表示相機原地轉動(在xoz平面),僅改變第三組參數表示相機旋轉(在xoy平面)。如果仍然不能理解,建議去看Nate Robin OpenGL教程中的projection演示。

 

2. 投影變換

投影(projection)的目的是將3D圖形轉換至2D平面顯示。OpenGL提供了正交投影(orthogonal projection)和透視投影(perspective projection)兩種投影方式。正交的另一種說法是垂直(數學沒學好...),其投影結果不會改變物體的大小。透視投影和人眼的視覺模型,或者說小孔成像比較類似,投影之後遠處的東西看上去會小一些。因此,絕大多數情況下使用的是透視投影。

透視投影的模型定義了一個視景體,這是一個平截頭體(倒放的棱臺),所有在這個視景體內的物體纔會被顯示出來。由於平截頭體的底面比頂面要大,所以可以理解爲,底面經過投影之後縮小成頂面般的大小,也就是人眼模型所要的效果。至於位於視景體內的物體是經過怎樣的運算而最終變成2D平面圖像的,這是一個內部算法的問題,如開篇所說我還沒有完全掌握......

OpenGL提供了glFrustum接口來實現透視投影,這個API完全是以視景體模型來定義的,如果覺得用起來不太直接,還有一個glu的接口 gluPerspective。

 

3. 視口變換

視口可以理解爲最終在顯示器上顯示的大小,雖然一般來說就是OpenGL窗口的大小,但理論上兩者並不一定要相同。視口大小通過glViewport接口來指定。注意每當窗口大小發生改變時都需要重新指定。同時也要注意透視投影的輸出與視口大小之間的關係,gluPerspective有個參數是寬高比,通常該值應該等於視口的寬高比,否則會造成圖像被縮放。


想要真正掌握這些概念,還需要親自實踐,寫段代碼來體會一下。

static void display(void)
{
    glClear(GL_COLOR_BUFFER_BIT);

    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_COLOR_ARRAY);
    glColorPointer(3, GL_FLOAT, 0, colors);
    glVertexPointer(3, GL_INT, 0, vetices);

    glDrawElements(GL_QUADS, 4, GL_UNSIGNED_BYTE, front);
    glDrawElements(GL_QUADS, 4, GL_UNSIGNED_BYTE, end);
    glDrawElements(GL_QUADS, 4, GL_UNSIGNED_BYTE, left);
    glDrawElements(GL_QUADS, 4, GL_UNSIGNED_BYTE, right);
    glDrawElements(GL_QUADS, 4, GL_UNSIGNED_BYTE, top);
    glDrawElements(GL_QUADS, 4, GL_UNSIGNED_BYTE, bot);

    glFlush();
}

static void reshape(int w, int h)
{
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    gluPerspective(60, (GLfloat)w/(GLfloat)h, 0.1f, 100);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    //眼睛在(-5, 0, 5),盯着(0, 0, 0)點,身體朝向正上方與y軸平行
    gluLookAt(-5, 0, 5, 0, 0, 0, 0, 1, 0);
}

一些心得:
1. 試着調整一下gluLookAt的參數,看看結果是否與你想像中的一致。
2. 可以在display中把x,y,z軸畫出來,以輔助理解
3. 看不到圖像,有可能是不在視線範圍內,也有可能是離得太近,甚至還有可能是顏色填錯了,這些心得只有靠自己在實踐中去總結,才能變成自己的財富。
4. 別人的教程寫得再好,只能縮短你學習的過程,但永遠也不能替代自己動手這一環節。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章