天天看點

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軸負半軸的方向。

OpenGL坐标系與幾何變換

進行旋轉操作時需要指定的角度θ的方向則由右手法則來決定,即右手握拳,大拇指直向某個坐标軸的正方向,那麼其餘四指指向的方向即為該坐标軸上的θ角的正方向(即θ角增加的方向),圖中用圓弧形箭頭标出:

OpenGL坐标系與幾何變換

對象坐标系

這是對象在被應用任何變換之前的初始位置和方向所在的坐标系,也就是目前繪圖坐标系。該坐标系不是固定的,且僅對該對象适用。在預設情況下,該坐标系與世界坐标系重合。這裡能用到的函數有glTranslatef(),glScalef(), glRotatef(),當用這些函數對目前繪圖坐标系進行平移、伸縮、旋轉變換之後, 世界坐标系和目前繪圖坐标系不再重合。改變以後,再用glVertex3f()等繪圖函數繪圖時,都是在目前繪圖坐标系進行繪圖,所有的函數參數也都是相對目前繪圖坐标系來講的。如圖則是對物體進行變換後,對象坐标系與世界坐标系的相對位置。對象坐标系也叫本地(局部)坐标系。

眼坐标系

模型變換:對象坐标系->世界坐标系

視變換:世界坐标系->眼坐标系

GL_MODELVIEW矩陣是模型變換矩陣和視點變換矩陣的組合(Mview*Mmodel),前面已經說了,并不存在單獨的模型變換(Model)和視點變換(View)。是以使用GL_MODELVIEW矩陣就可以使對象從對象坐标系轉換到眼坐标系。

OpenGL坐标系與幾何變換

為啥要轉換到眼坐标系呢?

可以這樣了解,通過前面的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)代表視線向上方向,其中視點和參考點的連線與視線向上方向要保持垂直關系。

隻需控制這三個量,便可定義新的視點。

tips

使用glTranslatef(),glScalef(), glRotatef()這些函數是對對象坐标系進行變動;使用void gluLookAt()是對眼坐标系進行變動,兩者可以達到相同的變換效果。相當于對象不動移動相機,和相機不動移動對象。比如場景向x軸正方向移動1個機關(相機不動),相當于相機向x軸負方向移動一個機關(對象不動),glTranslatef(1.0, 0.0, 0.0) <=> gluLookAt(-1.0, 0.0, 0.0, ..., ... )。

裁剪坐标系

眼坐标到裁剪坐标是通過投影完成的。眼坐标通過乘以GL_PROJECTION矩陣變成了裁剪坐标。

OpenGL坐标系與幾何變換

這個GL_PROJECTION矩陣定義了視景體( viewing volume),即确定哪些物體位于視野之内,位于視景體外的對象會被剪裁掉。除了視景體,投影變換還定義了頂點是如何投影到螢幕上的,是透視投影(perspective projection)還是正交投影(orthographic projection)。

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()建立一個用于正交投影的平行視景體, 将其與目前矩陣相乘。

void glOrtho(GLdouble left, GLdouble right,
           GLdouble bottom, GLdouble top, 
           GLdouble near, GLdouble far);      

關于透視投影和正交投影詳見:​​OpenGL之glMatrixMode函數的用法​​

歸一化裝置坐标系

 既然要規範化,那麼就得先有一個規範。前面在投影部分也已經看到,每種投影,都有一個剪裁空間,稱之為觀察體,對正交投影來說是一個立方體,對透視投影來說是一個棱台。如果一個觀察體是一個x、y、z坐标範圍都是 [-1, 1] 的立方體,則稱之為規範化立方體,這個就是所謂的規範。那麼,将原來的觀察體,映射到規範化立方體的過程,就是規範化。

一個格外需要注意的地方是,由于後面的螢幕坐标系通常是左手坐标系,是以這裡的規範化觀察體也使用左手坐标系,意味着 x 軸和 y 軸沒有改變,但是 z 軸的正方向轉了個。這帶來的結果是,在這樣的坐标系下,z 的坐标值越小,距離觀察者(也就是你)越近。實際上,在opengl中,進行規範化之後,近裁剪平面的z軸坐标是 -1,遠裁剪平面的z軸坐标是1。

由裁剪坐标系下通過除以W分量得到。這個操作稱為透視除法。NDC坐标很像螢幕坐标,但是還沒有經過平移和縮放到螢幕像素。現在3個軸上的值範圍均為[-1,1]。

螢幕坐标系

繼續閱讀