上一篇文章《OpenGL入門(三)-- OpenGL坐標系解析與坐標變換》,我們大概了解了OpenGL坐標系的一些常用概念認知,現在,我們繼續深入介紹OpenGL坐標系的相關知識與坐標系的幾何變換,讓我們繼續挖坑(入坑)吧。本文內容來自于:
坐標系統
- 想要弄懂幾何變換,一定要搞清楚OpenGL中的坐標系統。
從我們構造模型的局部坐標系(Local/Object Space)經過一系列處理最終渲染到屏幕坐標(Screen Space)下,這過程中有6種坐標系:
-- World Coordinates(世界坐標系)
-- Object Coordinates(對象坐標系、模型坐標系、局部坐標系或當前繪圖坐標系)
-- Eye Coordinates(眼坐標系或照相機坐標系)
-- Clip Coordinates(裁剪坐標系)
-- Normalized Device Coordinates (NDC) (歸一化設備坐標系)
-- Window Coordinates (Screen Coordinates)(屏幕坐標)實際上,并不存在單獨的模型變換(Model)和視點變換(View),通常將這兩種變換合稱為ModelView變換。則OpenGL的頂點變換過程如圖所示:
OpenGL的頂點變換過程
世界坐標系
世界坐標系始終是固定不變的。OpenGL使用右手坐標,這里有一個形象的方法:使用右手定則
X 是你的拇指
Y 是你的食指
Z 是你的中指
如果你把你的拇指指向右邊,食指指向天空,那么中指將指向你的背后。我們的觀察方向是Z軸負半軸的方向。
進行旋轉操作時需要指定的角度θ的方向則由右手法則來決定,即右手握拳,大拇指直向某個坐標軸的正方向,那么其余四指指向的方向即為該坐標軸上的θ角的正方向(即θ角增加的方向),圖中用圓弧形箭頭標出:
對象坐標系
這是對象在被應用任何變換之前的初始位置和方向所在的坐標系,也就是當前繪圖坐標系。該坐標系不是固定的,且僅對該對象適用。在默認情況下,該坐標系與世界坐標系重合。這里能用到的函數有glTranslatef(),glScalef(), glRotatef(),當用這些函數對當前繪圖坐標系進行平移、伸縮、旋轉變換之后, 世界坐標系和當前繪圖坐標系不再重合。改變以后,再用glVertex3f()等繪圖函數繪圖時,都是在當前繪圖坐標系進行繪圖,所有的函數參數也都是相對當前繪圖坐標系來講的。如圖則是對物體進行變換后,對象坐標系與世界坐標系的相對位置。對象坐標系也叫本地(局部)坐標系。
眼坐標系
模型變換:對象坐標系->世界坐標系
視變換:世界坐標系->眼坐標系
GL_MODELVIEW矩陣是模型變換矩陣和視點變換矩陣的組合(Mview*Mmodel),前面已經說了,并不存在單獨的模型變換(Model)和視點變換(View)。所以使用GL_MODELVIEW矩陣就可以使對象從對象坐標系轉換到眼坐標系。
為啥要轉換到眼坐標系呢?
可以這樣理解,通過前面的MODEVIEW變換,這個世界坐標系中的場景已經繪制好了。這時候我們還不能看到場景哦,因為我們的觀察位置還沒定呢,而且如果我們眼睛(照相機)的位置不同,那么觀察物體的角度則不同,那看到的場景的樣子肯定也不同,所以要有這一步,把場景與我們的觀察位置對應起來。
默認情況下,眼坐標系與世界坐標系也是重合的。使用函數 gluLookAt()則可以指定眼睛(相機)的位置和眼睛看向的方向。該函數的原型如下:
void gluLookAt(GLdouble eyex, GLdouble eyey, GLdouble eyez, GLdouble centerx, GLdouble centery, GLdouble centerz,GLdouble upx, GLdouble upy, GLdouble upz)
函數參數中:
- 點(eyex, eyey, eyez)代表眼睛所在位置;
- 點(centerx, centery,centerz)代表眼睛看向的位置;
- 向量(upx, upy, upz)代表視線向上方向,其中視點和參考點的連線與視線向上方向要保持垂直關系。
- 只需控制這三個量,便可定義新的視點。
提示:使用glTranslatef(),glScalef(), glRotatef()這些函數是對對象坐標系進行變動;使用void gluLookAt()是對眼坐標系進行變動,兩者可以達到相同的變換效果。相當于對象不動移動相機,和相機不動移動對象。比如場景向x軸正方向移動1個單位(相機不動),相當于相機向x軸負方向移動一個單位(對象不動),glTranslatef(1.0, 0.0, 0.0) <=> gluLookAt(-1.0, 0.0, 0.0, ..., ... )。
裁剪坐標系
眼坐標到裁剪坐標是通過投影完成的。眼坐標通過乘以GL_PROJECTION矩陣變成了裁剪坐標。
20140909154227543.png
這個GL_PROJECTION矩陣定義了視景體( viewing volume),即確定哪些物體位于視野之內,位于視景體外的對象會被剪裁掉。除了視景體,投影變換還定義了頂點是如何投影到屏幕上的,是透視投影(perspective projection)還是正交投影(orthographic projection)。
透視投影似于日常生活看到的場景,遠處物體看起來小,近處看起來大。使用透視投影函數glFrustum()和gluPerspective().
void glFrustum(GLdouble left, GLdouble right,GLdouble bottom, GLdouble top, GLdouble near, GLdouble far)
far, near是指近裁剪面,遠剪裁面離視點的距離(>0);對角坐標,(left, bottom, -near)和(right, top, -near)定義了近裁剪面的左下角和右上角的(x, y, z)坐標。
void gluPerspective(GLdouble fovy, GLdouble aspect,GLdouble near, GLdouble far)
fovy視角,aspect = w/h
正交投影把物體直接映射到屏幕上,不影響它們的相對大小。也就是圖像反映物體的實際大小。函數glOrtho()創建一個用于正交投影的平行視景體, 將其與當前矩陣相乘。
關于透視投影和正交投影詳見:OpenGL之glMatrixMode函數的用法
歸一化設備坐標系
- 既然要規范化,那么就得先有一個規范。前面在投影部分也已經看到,每種投影,都有一個剪裁空間,稱之為觀察體,對正交投影來說是一個立方體,對透視投影來說是一個棱臺。如果一個觀察體是一個x、y、z坐標范圍都是 [-1, 1] 的立方體,則稱之為規范化立方體,這個就是所謂的規范。那么,將原來的觀察體,映射到規范化立方體的過程,就是規范化。
- 一個格外需要注意的地方是,由于后面的屏幕坐標系通常是左手坐標系,所以這里的規范化觀察體也使用左手坐標系,意味著 x 軸和 y 軸沒有改變,但是 z 軸的正方向轉了個。這帶來的結果是,在這樣的坐標系下,z 的坐標值越小,距離觀察者(也就是你)越近。實際上,在opengl中,進行規范化之后,近裁剪平面的z軸坐標是 -1,遠裁剪平面的z軸坐標是1。
由裁剪坐標系下通過除以W分量得到。這個操作稱為透視除法。NDC坐標很像屏幕坐標,但是還沒有經過平移和縮放到屏幕像素。現在3個軸上的值范圍均為[-1,1]。
屏幕坐標系
通常將屏幕上的設備坐標稱為屏幕坐標。設備坐標又稱為物理坐標,是指輸出設備上的坐標。設備坐標用對象距離窗口左上角的水平距離和垂直距離來指定對象的位置,是以像素為單位來表示的,設備坐標的 X 軸向右為正,Y 軸向下為正,坐標原點位于窗口的左上角。
從NDC坐標到屏幕坐標基本上是一個線性映射關系。通過對NDC坐標進行視口變換得到。這時候就要用到函數glViewport(),該函數用來定義渲染區域的矩形,也就是最終圖像映射到的區域。
坐標變換
OpenGL通過相機模擬、可以實現計算機圖形學中基本的三維變換,即幾何變換、投影變換、裁剪變換、視口變換等,同時,OpenGL還實現了矩陣堆棧等,理解掌握了有關坐標變換的內容,就算真正走進了精彩的三維世界,此部分內容來自以下文章:
總的來說,OpenGL中的坐標處理過程包括模型變換、視變換、投影變換、視口變換等過程,三維物體的顯示過程(OpenGL坐標變換全局過程)如下:
注意,OpenGL只定義了裁剪坐標系、規范化設備坐標系和屏幕坐標系,而局部坐標系(模型坐標系)、世界坐標系和照相機坐標系都是為了方便用戶設計而自定義的坐標系,它們的關系如下圖所示:
圖中左邊的過程包括模型變換、視變換,投影變換,這些變換可以由用戶根據需要自行指定,這些內容在頂點著色器中完成;而圖中右邊的兩個步驟,包括透視除法、視口變換,這兩個步驟是OpenGL自動執行的,在頂點著色器處理后的階段完成。
模型變換——從模型坐標系到世界坐標系
局部坐標系(模型坐標系)是為了方便構造模型而設立的坐標系,建立模型時我們無需關心最終對象顯示在屏幕哪個位置。模型的原點定位也可以有所不同,例如下面在模型坐標系定義的模型:
模型變換的主要目的是通過變換使得用頂點屬性定義或者3d建模軟件構造的模型,能夠按照需要,通過縮小、平移等操作放置到場景中合適的位置。通過模型變換后,物體放置在一個全局的世界坐標系中,世界坐標系是所有物體交互的一個公共坐標系。
- 模型變換包括:旋轉、平移、縮放、錯切等內容。
- 應用多個模型變換時,注意變換執行的順序影響變換的結果,一般按照縮放–>旋轉—》平移的順序執行;另外,注意旋轉和縮放變換的不動點問題。
視變換——從世界坐標系到相機坐標系
視變換是為了方便觀察場景中物體而設立的坐標系,在這個坐標系中相機是個假想的概念,是為了便于計算而引入的。相機坐標系中的坐標,就是從相機的角度來解釋世界坐標系中位置。相機和場景的示意圖如下所示:
OpenGL中相機始終位于原點,指向-Z軸,而以相反的方式來調整場景中物體,從而達到相同的觀察效果。例如要觀察-z軸方向的一個立方體的右側面,可以有兩種方式:
- 立方體不動,讓相機繞著+y軸,旋轉+90度,此時相機鏡頭朝向立方體的右側面,實現目的。完成這一旋轉的矩陣記作Ry(π2)Ry(π2)
- 相機不動,讓立方體繞著+y軸,旋轉-90度,此時也能實現同樣的目的。注意這時相機沒有轉動。完成這一旋轉的矩陣記作Ry(?π2)
OpenGL中采用方式2的觀點來解釋視變換。再舉一個例子,比如,一個物體中心位于原點,照相機也位于初始位置原點,方向指向-Z軸。為了對物體的+Z面成像,那么必須將照相機從原點移走,如果照相機仍然指向-Z軸,需要將照相機沿著+Z軸方向后退。通過在世界坐標系中指定相機的位置,指向的目標位置,以及viewUp向量來構造一個相機坐標系,通過視變換矩陣將物體坐標由世界坐標系轉換到相機坐標系。
--
投影變換——從世界坐標系到裁剪坐標系
投影方式決定以何種方式成像,投影方式有很多種,OpenGL中主要使用兩種方式,即透視投影(perspective projection)和正交投影( orthographic projection)。
1.正交投影是平行投影的一種特殊情形,正交投影的投影線垂直于觀察平面。平行投影的投影線相互平行,投影的結果與原物體的大小相等,因此廣泛地應用于工程制圖等方面。
2.透視投影的投影線相交于一點,因此投影的結果與原物體的實際大小并不一致,而是會近大遠小。因此透視投影更接近于真實世界的投影方式。
兩者的示意圖如下:
在OpenGL中成像時的效果如下所示:
上面的圖中,紅色和黃色球在視見體內,因而呈現在投影平面上,而綠色球在視見體外,沒有在投影平面上成像。指定視見體通過(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble nearVal, GLdouble farVal)6個參數來指定。注意在相機坐標系下,相機指向-z軸,nearVal和farVal表示的剪裁平面分別為:近裁剪平面z=?nearValz=?nearVal,以及遠裁剪平面z=?farValz=?farVal。
視口變換——從NDC到屏幕坐標
視變換是將規范化設備坐標(NDC)轉換為屏幕坐標的過程,如下圖所示:
坐標變換的計算過程
最后我們來看一下坐標變換過程從坐標計算角度的示意圖:
--
總結:從上面的內容,我們可以了解了OpenGL坐標系和坐標變換的一些原理和關系,篇幅有點長,一定要堅持下去。下一篇文章盡請期待。。。
--
系列連載
OpenGL入門(一)-- 圖形API簡介與作用
OpenGL入門(二)-- 快速了解OpenGL下的專業名詞
OpenGL入門(三)-- OpenGL坐標系解析與坐標變換
OpenGL入門(四)-- OpenGL坐標系與坐標變換
OpenGL入門(五)-- OpenGL渲染流程圖解析
OpenGL入門(六)-- OpenGL 固定存儲著色器的理解使用
OpenGL入門(七)-- 圖形圖像渲染中的深度緩沖區
OpenGL入門(八)-- OpenGL向量和矩陣簡介
OpenGL入門(九)-- OpenGL 紋理簡單介紹