1. 程式人生 > >實驗4 二維幾何變換

實驗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)

,作用是把當前矩陣和一個表示移動物體的矩陣相乘。tx、ty、tz指定這個移動物體的矩陣,它們可以是任意的實數值,字尾為f(單精度浮點float)或d(雙精度浮點double),對於二維應用來說,tz=0.0。
旋轉矩陣建構函式為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)
,作用是把當前矩陣和一個表示縮放物體的矩陣相乘。sx,sy,sz指定這個縮放物體的矩陣,分別表示在x,y,z方向上的縮放比例,它們可以是任意的實數值,當縮放參數為負值時,該函式為反射矩陣,縮放相對於原點進行,字尾為f(單精度浮點float)或d(雙精度浮點double)。
注意這裡都是說“把當前矩陣和一個表示移動<旋轉, 縮放>物體的矩陣相乘”,而不是直接說“這個函式就是旋轉”或者“這個函式就是移動”,這是有原因的,馬上就會講到。
假設當前矩陣為單位矩陣,然後先乘以一個表示旋轉的矩陣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)旋轉 α\alpha 角。

清屏
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();

圖形將變成怎樣?試解釋原因。