變換包括:旋轉,平移,縮放扭曲,投影。在指定了頂點之後,在屏幕上顯示頂點之前,會進行三種變換:視圖變換,模型變換,投影變換。
術語如下表:
變換類型 用途 視圖(Viewing) 指定觀察者和照相機的位置 模型(Modeling) 場景中的物體變換 模型視圖(ModelView) 描述視圖和模型變換的對偶性 投影(Projection) 改變可視區域的大小和形狀 視口(Viewport) 一種偽變換,對窗口的最終輸出進行縮放
視點坐標是觀察者的視角,與發生的變換無關。視點坐標是一個虛擬的固定的坐標系,通常作為參考系使用。許多變換都是相對於視點坐標系而言的。
如下:左圖是觀察者的觀察角度是垂直於顯示屏幕,右圖對視點坐標做了旋轉,這樣就能看到z軸的相對位置。
視圖變換是場景所應用的第一個變換。它用於確定場景的拍攝點。默認情況下,在透視投影鐘觀察者是從原點向z軸負方向看過去。這個觀察點相對於視點坐標系系統進行移動,以提供一個特定的拍攝點。當觀察點位於原點時,場景中所繪制的z值為正的物體就位於觀察者的後面。
視圖變換允許吧觀察點放在任意位置,並在任何方向上觀察場景。確定視圖變換就像在場景中放置照相機並讓它指向某個方向。
模型變換可以操作模型以及模型中的物體。模型變換包括移動物體,旋轉物體,縮放物體。下圖分別是平移,旋轉,縮放
模型變換的最終結果還取決於你的變換順序。下圖展示了先旋轉後平移,和先平移後旋轉所得到的不同結果。
導致這種最終結果不同的原因是,每一次變換都是基於上一次變換的結果的。上面的變換是物體坐標系的變換。
事實上,這模型變換和視圖變換在最終呈現的效果上市一樣的。之所以把這兩種變換區分開來,是為了方便程序員。相對於坐標向後移動物體和相對於物體把坐標系往前移的效果是一樣的。視圖變換像是把模型變換應用於整個場景。即在應用視圖變換之後,像是每個物體獨立進行了模型變換。模型視圖變換在變換管道中用一個矩陣來表示,即模型視圖矩陣。
視圖變換可以理解為在繪制物體之前,先把模型變換應用於觀察者。當在場景中放置更多的物體時,需要重新指定新的變換。所有其他的變換都是以最初的那個變換作為參考系的。
在模型視圖變換之後,投影變換才應用於頂點。投影變換定義了可視區域和裁剪面。裁剪面決定了幾何圖元是否能被觀察者看到。投影變換決定了在所有變換做完之後的場景投影到屏幕上的最終圖像。投影變換有兩種:正交投影和透視投影
正交投影即平行投影,所有多邊形按照指定的大小出現在屏幕上。直線和多邊形使用平行直線直接映射到2D屏幕上。不管物體有多遠,都按照相同的大小進行繪制。通常用於CAD或二維圖像的渲染。
透視投影顯示的場景則更加真實。遠處的物體看上去比相同大小的近處物體小一些。
下圖是正投影和透視投影的比較
正投影的方框
透視投影的方框:
在所有變換進行完之後,獲得的是將被映射到屏幕上的某個窗口的場景的二維投影。這種映射到物理窗口坐標的變換是最後做的變換——視口變換。通常,在顏色緩沖區和窗口像素之間有一一對應的關系,但並不總是如此。
上圖是簡單的變換管道。首先你的頂點被轉化為1x4的矩陣,頭三個值是x,y,z坐標,第四個數值代表著縮放因子,通常為1不需要更改。然後頂點乘以模型視圖矩陣,產生經過轉換後的視點坐標。然後視點坐標乘以投影矩陣,得到裁剪坐標。OpenGL會剔除所有在裁剪空間外的數據。然後裁剪坐標進行透視除法 除以w坐標得到規格化的設備坐標,原因是w值在模型視圖變換和投影變換的過程中可能會被改變。最終坐標通過視口變換映射到二維平面上。
模型視圖矩陣是一個4x4的矩陣,代表著你要放置的物體的位置和朝向的變換坐標。圖元中的頂點(一個單列的矩陣)乘以模型視圖矩陣得到變換過後的相對於視點坐標系系統的坐標。
void glTranslatef(GLfloat x, GLfloat y, GLfloat z);
這個函數有三個參數分別代表著向x,y,z軸平移。調用這個函數之後,OpenGL會構造一個適當的舉證乘以當前矩陣棧棧頂的矩陣。例子:
//向Y軸平移十個單元
glTranslatef(0.0f, 10.0f, 0.0f);
//畫立方體
gluWireCube(10.0f);
void glRotatef(GLfloat angle, GLfloat x, GLfloat y, GLfloat z);
這個函數告訴OpenGL要沿著指定的坐標軸旋轉多少角度。x,y,z為(1,0,0)則代表著沿x軸旋轉,你可以沿著任意坐標軸旋轉,用x,y,z值指定這個坐標軸。
// 沿著(1,1,1)坐標軸旋轉45度
glRotatef(45.0f, 1.0f, 1.0f, 1.0f);
glutWireCube(10.0f);
void glScalef(GLfloat x, GLfloat y, GLfloat z);
沿x,y,z軸縮放多少倍。例子:
glScalef(2.0f, 1.0f, 2.0f);
gluWireCube(10.0f);
對角線為1,除了對角線之外的元素為0的矩陣稱為單位陣。單位陣,即所有的矩陣乘以單位陣都得到原矩陣。OpenGL中重新加載單位陣到模型視圖矩陣(重置模型視圖矩陣)可以用下面兩個函數調用:
glMatrixMode(GL_MODEVIEW);
glLoadIdentity();
有時我們需要保存當前的變換狀態,然後繪制物體,再恢復它。那我們可以使用OpenGL提供的矩陣棧來實現。
glPushMatrix可以保存當前矩陣到矩陣棧中,glPopMatrix從矩陣棧彈出矩陣。
矩陣棧有最大的存儲數量。通過glGet(GL_MAX_MODELVIEW_STACK_DEPTH)可以獲得模型視圖矩陣棧的容量。glGet(GL_MAX_PROJECTION_STACK_DEPT)獲得投影矩陣棧的容量。
例子:
#include
#include
#include
static void SetupRC()
{
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glEnable(GL_DEPTH_TEST);
}
static void RenderScene()
{
static GLfloat fEffect = 0.0f;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
//先往裡平移畫原子核
glTranslatef(0.0f, 0.0f, -70.0f);
//紅色的原子核
glColor3f(1.0f, 0.0f, 0.0f);
glutSolidSphere(20.0f, 15, 15);
//青色的電子
glColor3f(0.0f, 1.0f, 1.0f);
glPushMatrix();
glRotatef(fEffect, 0.0f, 1.0f, 0.0f);
glTranslatef(80.0f, 0.0f, 0.0f);
glutSolidSphere(10.0f, 15, 15);
glPopMatrix();
//黃色的電子
glColor3f(1.0f, 1.0f, 0.0f);
glPushMatrix();
glRotatef(45.0f, 0.0f, 0.0f, 1.0f);
glRotatef(fEffect, 0.0f, 1.0f, 0.0f);
glTranslatef(80.0f, 0.0f, 0.0f);
glutSolidSphere(10.0f, 15, 15);
glPopMatrix();
//綠色的電子
glColor3f(0.0f, 1.0f, 0.0f);
glPushMatrix();
glRotatef(-45.0f, 0.0f, 0.0f, 1.0f);
glRotatef(fEffect, 0.0f, 1.0f, 0.0f);
glTranslatef(80.0f, 0.0f, 0.0f);
glutSolidSphere(10.0f, 15, 15);
glPopMatrix();
fEffect += 10.0f;
if (fEffect > 360.0)
{
fEffect = 0.0f;
}
glutSwapBuffers();
}
static void ChangeSize(GLsizei w, GLsizei h)
{
GLfloat nRange = 200.0f;
if (h == 0)
{
h = 1;
}
//設置視口
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
//設置可視區域
GLfloat aspect = (GLfloat)w / (GLfloat)h;
//設置為透視
gluPerspective(85.0, aspect, 80.0, 300.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
static void Timer( int value)
{
glutPostRedisplay();
glutTimerFunc(100, Timer, 1);
}
int main( int argc, char* argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
glutInitWindowSize(400,400);
glutCreateWindow( "atom Test");
glutReshapeFunc(ChangeSize);
glutDisplayFunc(RenderScene);
glutTimerFunc(100, Timer, 1);
SetupRC();
glutMainLoop();
return 0;
}
更高級的例子:
#include
#include
#include
static GLfloat whiteLight[] = {0.2f, 0.2f, 0.2f, 1.0f};
static GLfloat sourceLight[] = {0.8f, 0.8f, 0.8f, 1.0f};
static GLfloat lightPos[] = {0.0f, 0.0f, 0.0f, 1.0f};
static void SetupRC()
{
//開啟深度測試
glEnable(GL_DEPTH_TEST);
glFrontFace(GL_CCW);
//啟用裁剪面
glEnable(GL_CULL_FACE);
//開啟光照
glEnable(GL_LIGHTING);
//設置光照參數
glLightModelfv(GL_AMBIENT_AND_DIFFUSE, whiteLight);
glLightfv(GL_LIGHT0, GL_AMBIENT, sourceLight);
glLightfv(GL_LIGHT0, GL_POSITION, lightPos);
//開啟光源LIGHT0
glEnable(GL_LIGHT0);
//設置材料屬性
glEnable(GL_COLOR_MATERIAL);
glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
}
static void RenderScene()
{
static GLfloat fEarth = 0.0f;
static GLfloat fMoon = 0.0f;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
//往屏幕裡面平移
glTranslatef(0.0f, 0.0f, -300.0f);
//先關閉光照
glDisable(GL_LIGHTING);
//畫日
glColor3ub(255, 255, 0);
glutSolidSphere(20.0, 15, 15);
//開啟光照
glEnable(GL_LIGHTING);
//LIGHT0的發光點
glLightfv(GL_LIGHT0, GL_POSITION, lightPos);
//畫地球
glRotatef(fEarth, 0.0f, 1.0f, 0.0f);
glColor3ub(0,0,255);
glTranslatef(105.0f, 0.0f, 0.0f);
glutSolidSphere(15.0, 15, 15);
//畫月亮
glColor3ub(200, 200, 200);
glRotatef(fMoon, 0.0f, 1.0f, 0.0f);
glTranslatef(30.0f, 0.0f, 0.0f);
glutSolidSphere(10.0, 15, 15);
//月球的旋轉
fMoon += 15.0f;
if (fMoon > 360.0f)
{
fMoon = 0.0f;
}
//地球的旋轉
fEarth += 10.0f;
if (fEarth > 360.0f)
{
fEarth = 0.0f;
}
glPopMatrix();
glutSwapBuffers();
}
static void ChangeSize(GLsizei w, GLsizei h)
{
if (h == 0)
{
h = 1;
}
//設置視口
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
//設置可視區域
GLfloat aspect = (GLfloat)w / (GLfloat)h;
gluPerspective(45.0, aspect, 1.0, 400.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void TimerFunc( int value)
{
glutPostRedisplay();
glutTimerFunc(100, TimerFunc, 1);
}
int main( int argc, char* argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
glutInitWindowSize(400,400);
glutCreateWindow( "solar Test");
glutReshapeFunc(ChangeSize);
glutTimerFunc(100, TimerFunc, 1);
glutDisplayFunc(RenderScene);
SetupRC();
glutMainLoop();
return 0;
}
OpenGL超級寶典 第4版 中文版PDF+英文版+源代碼 見 http://www.linuxidc.com/Linux/2013-10/91413.htm
OpenGL編程指南(原書第7版)中文掃描版PDF 下載 http://www.linuxidc.com/Linux/2012-08/67925.htm
OpenGL 渲染篇 http://www.linuxidc.com/Linux/2011-10/45756.htm
Ubuntu 13.04 安裝 OpenGL http://www.linuxidc.com/Linux/2013-05/84815.htm
OpenGL三維球體數據生成與繪制【附源碼】 http://www.linuxidc.com/Linux/2013-04/83235.htm
Ubuntu下OpenGL編程基礎解析 http://www.linuxidc.com/Linux/2013-03/81675.htm
如何在Ubuntu使用eclipse for c++配置OpenGL http://www.linuxidc.com/Linux/2012-11/74191.htm
更多《OpenGL超級寶典學習筆記》相關知識 見 http://www.linuxidc.com/search.aspx?where=nkey&keyword=34581