實驗4 二維幾何變換
1.實驗目的:
- 鞏固對二維幾何變換的認識與理解;
- 學習OpenGL平移、旋轉、縮放變換函式及其使用方法;
- 學習基本圖形變換與複合圖形變換的方法;
- 綜合運用上述函式,設計複雜圖形。
2.實驗內容:
根據示範程式碼1,使用OpenGL平移、旋轉、縮放變換函式來改寫程式碼實現所要求的功能。示範程式碼1的程式碼執行結果為圖1。
(1) 使用glTranslatef()函式,實現圖形平移,並結合glTranslatef()函式的不同引數輸入,實現x,y和z方向的平移,將測試結果存為圖1-3,與對應修改的平移函式程式碼一起儲存至word實驗文件中(20分鐘);
(2) 使用glRotatef()函式,實現圖形旋轉,並結合glRotatef()函式的不同引數輸入,實現x,y和z方向的旋轉,將測試結果存為圖4-6,與對應修改的旋轉函式程式碼一起儲存至word實驗文件中(20分鐘);
(3) 使用glScalef()函式,實現圖形縮放,並結合glScalef()函式的不同引數輸入,實現x,y和z方向的旋轉,將測試結果存為圖7-9,與對應修改的縮放函式程式碼一起儲存至word實驗文件中(20分鐘);
(4)示範程式碼2,程式碼執行結果為圖2,請參考它繪製如圖3所示的圖形,將繪圖結果與程式碼儲存至word實驗文件中(30分鐘);
(5) 整理word實驗文件,將其命名為“序號-姓名-Prj4.doc”,電子版提交至雨課堂,A4列印稿下一次課前或實驗課前提交。
3.實驗原理:
(1)OpenGL下的幾何變換
在OpenGL的核心庫中,每一種幾何變換都有一個獨立的函式,所有變換都在三維空間中定義。
平移矩陣建構函式為glTranslate<f,d>(tx, ty, tz)
旋轉矩陣建構函式為
glRotate<f,d>(theta, vx, vy, vz)
,作用是把當前矩陣和一個表示旋轉物體的矩陣相乘。theta,vx,vy,vz指定這個旋轉物體的矩陣,物體將圍繞(0,0,0)到(x,y,z)的直線以逆時針旋轉,引數theta表示旋轉的角度。向量v=(vx,vy,vz)的分量可以是任意的實數值,該向量用於定義通過座標原點的旋轉軸的方向,字尾為f(單精度浮點float)或d(雙精度浮點double),對於二維旋轉來說,vx=0.0,vy=0.0,vz=1.0。縮放矩陣建構函式為
glScale<f,d>(sx, sy, sz)
注意這裡都是說“把當前矩陣和一個表示移動<旋轉, 縮放>物體的矩陣相乘”,而不是直接說“這個函式就是旋轉”或者“這個函式就是移動”,這是有原因的,馬上就會講到。
假設當前矩陣為單位矩陣,然後先乘以一個表示旋轉的矩陣R,再乘以一個表示移動的矩陣T,最後得到的矩陣再乘上每一個頂點的座標矩陣v。那麼,經過變換得到的頂點座標就是((RT)v)。由於矩陣乘法滿足結合率,((RT)v) = R(Tv)),換句話說,實際上是先進行移動,然後進行旋轉。即:實際變換的順序與程式碼中寫的順序是相反的。由於“先移動後旋轉”和“先旋轉後移動”得到的結果很可能不同,初學的時候需要特別注意這一點。
(2)OpenGL下的各種變換簡介
我們生活在一個三維的世界,如果要觀察一個物體,我們可以:
① 從不同的位置去觀察它(人運動,選定某個位置去看)。(檢視變換)
② 移動或者旋轉它,當然了,如果它只是計算機裡面的物體,我們還可以放大或縮小它(物體運動,讓人看它的不同部分)。(模型變換)
③ 如果把物體畫下來,我們可以選擇是否需要一種“近大遠小”的透視效果。另外,我們可能只希望看到物體的一部分,而不是全部(指定看的範圍)。(投影變換)
④ 我們可能希望把整個看到的圖形畫下來,但它只佔據紙張的一部分,而不是全部(指定在顯示器視窗的那個位置顯示)。(視口變換)
這些,都可以在OpenGL中實現。
從“相對移動”的觀點來看,改變觀察點的位置與方向和改變物體本身的位置與方向具有等效性。在OpenGL中,實現這兩種功能甚至使用的是同樣的函式。
由於模型和檢視的變換都通過矩陣運算來實現,在進行變換前,應先設定當前操作的矩陣為“模型檢視矩陣”。設定的方法是以GL_MODELVIEW為引數呼叫glMatrixMode函式,例如:
glMatrixMode(GL_MODELVIEW);
該語句指定一個4×4的建模矩陣作為當前矩陣。
通常,我們需要在進行變換前把當前矩陣設定為單位矩陣。把當前矩陣設定為單位矩陣的函式為:
glLoadIdentity();
我們在進行矩陣操作時,有可能需要先儲存某個矩陣,過一段時間再恢復它。當我們需要儲存時,呼叫glPushMatrix()函式,它相當於把當前矩陣壓入堆疊。當需要恢復最近一次的儲存時,呼叫glPopMatrix()函式,它相當於從堆疊棧頂彈出一個矩陣為當前矩陣。OpenGL規定堆疊至少可以容納32個矩陣,某些OpenGL實現中,堆疊的容量實際上超過了32個。因此不必過於擔心矩陣的容量問題。
通常,用這種先儲存後恢復的措施,比先變換再逆變換要更方便、更快速。注意:模型檢視矩陣和投影矩陣都有相應的堆疊。使用glMatrixMode來指定當前操作的究竟是模型檢視矩陣還是投影矩陣。
(3) 某圖形繞任意點(cx, cy)旋轉 角。
清屏
glMatrixMode(GL_MODELVIEW); //設定矩陣模式為模型變換模式,表示在世界座標系下
glLoadIdentity(); //將當前矩陣設定為單位矩陣
glTranslatef(cx,cy,0); //平移回去
glRotatef( alpha, 0,0,1); //繞原點旋轉ALPHA角度
glTranslatef(-cx,-cy,0); //平移回原點
drawSquare();
圖形繞任意點縮放方法的程式碼只需把旋轉函式換為縮放函式即可,不再贅述。
4.示範程式碼:
示範程式碼1
#include <GL/glut.h>
void init (void)
{
glClearColor (1.0, 1.0, 1.0, 0.0);
glMatrixMode (GL_PROJECTION);
gluOrtho2D (-5.0, 5.0, -5.0, 5.0);
//設定顯示的範圍是X:-5.0~5.0, Y:-5.0~5.0
glMatrixMode (GL_MODELVIEW);
}
void drawSquare(void) //繪製中心在原點,邊長為2的正方形
{
glBegin (GL_POLYGON); //頂點指定需要按逆時針方向
glVertex2f (-1.0f,-1.0f); //左下點
glVertex2f (1.0f,-1.0f); //右下點
glVertex2f (1.0f, 1.0f); //右上點
glVertex2f (-1.0f,1.0f); //左上點
glEnd ( );
}
void myDraw (void)
{
glClear (GL_COLOR_BUFFER_BIT); //清空
glLoadIdentity(); //將當前矩陣設為單位矩陣
glColor3f (1.0, 0.0, 0.0);
drawSquare(); //上面紅色矩形
glFlush ( );
}
void main (int argc, char** argv)
{
glutInit (&argc, argv);
glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB);
glutInitWindowPosition (0, 0);
glutInitWindowSize (600, 600);
glutCreateWindow ("幾何變換示例1");
init();
glutDisplayFunc (myDraw);
glutMainLoop();
}
程式執行結果:
圖1
示範程式碼2
#include <GL/glut.h>
void init (void)
{
glClearColor (1.0, 1.0, 1.0, 0.0);
glMatrixMode (GL_PROJECTION);
gluOrtho2D (-5.0, 5.0, -5.0, 5.0);
//設定顯示的範圍是X:-5.0~5.0, Y:-5.0~5.0
glMatrixMode (GL_MODELVIEW);
}
void drawSquare(void) //繪製中心在原點,邊長為2的正方形
{
glBegin (GL_POLYGON); //頂點指定需要按逆時針方向
glVertex2f (-1.0f,-1.0f); //左下點
glVertex2f (1.0f,-1.0f); //右下點
glVertex2f (1.0f, 1.0f); //右上點
glVertex2f (-1.0f,1.0f); //左上點
glEnd ( );
}
void myDraw (void)
{
glClear (GL_COLOR_BUFFER_BIT); //清空
glLoadIdentity(); //將當前矩陣設為單位矩陣
glPushMatrix();
glTranslatef(0.0f,2.0f,0.0f);
glScalef(3.0,0.5,1.0);
glColor3f (1.0, 0.0, 0.0);
drawSquare(); //上面紅色矩形
glPopMatrix();
glPushMatrix();
glTranslatef(-3.0,0.0,0.0);
glPushMatrix();
glRotatef(45.0,0.0,0.0,1.0);
glColor3f (0.0, 1.0, 0.0);
drawSquare(); //中間左菱形
glPopMatrix();
glTranslatef(3.0,0.0,0.0);
glPushMatrix();
glRotatef(45.0,0.0,0.0,1.0);
glColor3f (0.0, 0.7, 0.0);
drawSquare(); //中間中菱形
glPopMatrix();
glTranslatef(3.0,0.0,0.0);
glPushMatrix();
glRotatef(45.0,0.0,0.0,1.0);
glColor3f (0.0, 0.4, 0.0);
drawSquare(); //中間右菱形
glPopMatrix();
glPopMatrix();
glTranslatef(0.0,-3.0,0.0);
glScalef(4.0,1.5,1.0);
glColor3f (0.0, 0.0, 1.0);
drawSquare(); //下面藍色矩形
glFlush ( );
}
void main (int argc, char** argv)
{
glutInit (&argc, argv);
glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB);
glutInitWindowPosition (0, 0);
glutInitWindowSize (600, 600);
glutCreateWindow ("幾何變換示例2");
init();
glutDisplayFunc (myDraw);
glutMainLoop();
}
程式執行結果:
圖2
圖3
5.實驗思考
在繞任意點旋轉時,若將相關程式碼改為如下:
清屏
glMatrixMode(GL_MODELVIEW); //設定矩陣模式為模型變換模式,表示在世界座標系下
glLoadIdentity(); //將當前矩陣設定為單位矩陣
glTranslatef(-cx,-cy,0); //平移回去
glRotatef(theta,0,0,1); //繞原點旋轉ALPHA角度
glTranslatef(cx,cy,0); //平移回原點
drawSquare();
圖形將變成怎樣?試解釋原因。