1. 程式人生 > >實驗二   OpenGL的簡單動畫

實驗二   OpenGL的簡單動畫

一、實驗目的

1.掌握OpenGL的閒置函式。

2.掌握OpenGL的時間函式。

3.掌握OpenGL的簡單動畫功能。

4.瞭解OpengGL裁剪視窗、視區、顯示視窗的概念和它們之間的關係。

5.進一步掌握OpenGL的基本圖元的繪製。

、實驗內容

1.閒置函式的使用與簡單動畫。

1) 旋轉的六邊形,如圖2-1所示

閱讀6.3.3節中旋轉的六邊形樣本框架程式,分析程式的實現步驟執行該程式觀察旋轉動畫效果。

思考: 如果要調整旋轉速度,旋轉更快或更慢,應該如何修改程式?

答:增大或減小旋轉增量theta。

        

圖2-1  六邊形繪製

  1. 線框六邊形

在display 函式中新增多邊形模式設定語句觀看效果。

glPolygonMode(GL_FRONT_AND_BACK,GL_LINE); //線框模式

新增線寬語句觀看效果

glLineWidth(2.0); //設定線寬

重回多邊形填充模式:

glPolygonMode(GL_FRONT_AND_BACK,GL_FILL); //填充模式

圖同2-1

3) 在圖形中新增字元"Hello",觀察結果;然後將"Hello"字元改為自己名字的拼音或英文名字。如圖3-2所示。

提示:在圖形中新增如下程式碼:

glColor3f(1,0,0);  //設定紅色繪製顏色

glRasterPos2i(30,20);    //定位當前游標,起始字元位置

glutBitmapCharacter(GLUT_BITMAP_9_BY_15,'H');  //寫字元"H"

glutBitmapCharacter(GLUT_BITMAP_9_BY_15,'e');  //寫字元"e"

glutBitmapCharacter(GLUT_BITMAP_9_BY_15,'l');   //寫字元"l"

glutBitmapCharacter(GLUT_BITMAP_9_BY_15,'l');   //寫字元"l"

glutBitmapCharacter(GLUT_BITMAP_9_BY_15,'o');   //寫字元"o"

4) 變色技術舉例:

在程式頭部設定全部變數:

int k=0;

在myidle函式中新增程式碼:

if (k==1)

{

glColor3f(1,0,0) ;

k=0;

}

else

{

glColor3f(1,1,0) ;

k=1;

}

然後在繪製函式中遮蔽原來的繪製顏色,執行檢視效果

效果就是:紅色和黃色交替變化,很閃。

5) 六邊形靜止,直線單獨旋轉,如圖2-2所示。

修改前面的程式,使得六邊形保持靜止,以六邊形中心為起點畫一條不同顏色的直線,終點為六邊形某一頂點,使得直線不停繞中心點旋轉。程式碼儲存下來備用。思考:如果需要直線保持與機器時鐘的秒針節拍吻合,應該如何修改?

答:1秒重新整理一次。

提示:可用延時 Sleep()函式,如Sleep(1000)表示延時1秒,放在 myidle函式中

             

圖2-2  六邊形和直線

2.時間函式的使用與簡單動畫。

將以上程式中的閒置函式替換為時間函式,

1)主程式中的glutIdleFunc(myidle);    //註冊閒置回撥函式   

改為:

glutTimerFunc(1000, mytime,10); //1000毫秒後呼叫時間函式 mytime

2)myidle()閒置回撥函式改為時間函式 mytime(t)在程式頂部,函式宣告語句也要相應更改:

void myidle();  

改為:

void mytime(int t);

3)在時間函式 mytime(int t)最後再新增:

glutTimerFunc(1000, mytime,10); //1000毫秒後呼叫時間函式 mytime

此處圖同2-2

3.簡單時鐘的設計

1)在程式頭部定義系統時間變數,時分秒變數:

SYSTEMTIME timeNow;

float hh,mm,ss;

2)在程式頭部定義Π常量:

#define PI 3.1415926

3)在程式頭部引入數學標頭檔案、時間標頭檔案:

#include "math.h"

#include "time.h"

4)在初始化函式中獲取系統時間:

在主程式中頂部宣告初始化子函式

void init();

在main函式中 新增子函式呼叫語句,可放在建立視窗之後:

init();

在main函式後面,新增初始化子函式,並在函式中新增獲取系統時間語句:

void init()

{

    GetLocalTime(&timeNow);    //獲取系統時間

    hh=timeNow.wHour;    //獲取小時時間

mm=timeNow.wMinute;   //獲取分鐘時間

ss=timeNow.wSecond;      //獲取秒時間

}

5)在繪製函式中計算時分秒,確定繪製時分秒針起始點座標,例如

//xc,yc為時針中心點座標

//xs,ys為秒針終止點座標

//xm,ym針終止點座標

xs=xc+R*cos(PI/2.0-ss/60*2*PI);

  ys=yc+R*sin(PI/2.0-ss/60*2*PI);

xm=xc+R*cos(PI/2.0-(mm+ss/60.0)/60.0*2.0*PI);

ym=yc+R*sin(PI/2.0-(mm+ss/60.0)/60.0*2.0*PI);

xh=xc+(R-5)*cos(PI/2.0-(hh+(mm+ss/60.0)/60.0)/12.0*2.0*PI);

yh=yc+(R-5)*sin(PI/2.0-(hh+(mm+ss/60.0)/60.0)/12.0*2.0*PI);

6)在繪製函式中以直線方式簡易繪製時分秒針

glColor3f(1,0,0);

glBegin(GL_LINES);

glVertex2f(xc,yc);

glVertex2f(xs,ys);

glEnd();

glColor3f(1,1,0);

glBegin(GL_LINES);

glVertex2f(xc,yc);

glVertex2f(xm,ym);

glEnd();

glColor3f(0,1,1);

glBegin(GL_LINES);

glVertex2f(xc,yc);

glVertex2f(xh,yh);

glEnd();

7)閒置函式中 或 時間函式中重複獲取系統時間

GetLocalTime(&timeNow);    //獲取系統時間

hh=timeNow.wHour;    //獲取小時時間

mm=timeNow.wMinute;   //獲取分鐘時間

ss=timeNow.wSecond;      //獲取秒時間

、參考函式和相關知識

1)void glutIdleFunc((*f) (void)) //註冊閒置響應函式

2)void myidle() //閒置響應回撥函式

{

      //當時間空閒時系統要做的事情

}

3)void glPolygonMode(GLenum face,GLenum mode)//多邊形線框模型設定

4)void glutTimerFunc(unsigned int msecs, void (*Func)(int value), int value); //註冊一個回撥函式,當指定時間值到達後,由GLUT呼叫註冊的函式一次

msecs是等待的時間,單位

Func是註冊的函式它的引數value是指定的一個數值,用來傳遞到回撥函式Func中

5)glutPostRedisplay();  //重畫函式,相當於重新呼叫Display(),改編後的變數得以傳給繪製函式

6)時鐘相關知識

系統時間轉換成角度技術

1)把系統時間取出後,分鐘應考慮秒鐘的影響,時鐘應考慮分鐘的影響例如,

mm=mm+ss/60

hh=hh+mm/60

2)角度座標提示窗體角度座標如圖2-3,2-4所示,

  圖2-3 直線方向表示0度方向                                圖2-4 直線方向表示90度方向

3)時分秒針角度計算

秒針:當ss=0時,秒針角度=90度=1/2*Pi弧度60秒轉一圈 即1秒鐘轉  360/60=6度=Pi/30弧度

分針:當mm=0時,分針角度=90度=1/2*Pi弧度60分轉一圈, 即1分鐘轉  360/60=6度=Pi/30弧度

時針:當hh=0時,時針角度=90度=1/2*Pi弧度12小時轉一圈, 即1小時轉  360/12=30度=Pi/6弧度/

時分秒針繪製技術

關鍵是獲取時分秒針的終止點的座標假設時針中心點(xc,yc),秒針,分針和時針長度分別為slength,mlength,hlength

秒針終止點:xc + slength * Cos(1/2*3.14 + ss* 3.14/ 30), yc + slength *.Sin(1/2*3.14 + ss *3.14 / 30)

分針終止點:xc + mlength *Cos(1/2*3.14 +mm* 3.14/ 30), yc + mlength *sin(1/2*3.14 + mm *3.14 / 30)

時針終止點:xc + hlength *cos(3/2*3.14 +hh* 3.14/ 6), yc + hlength *sin(3/2*3.14 + hh *3.14 /6)

四、完整程式碼

#include <glut.h>
#include "math.h"
#include "time.h"
#include <stdio.h>
#include <Windows.h>
#include <tchar.h>
#define PI 3.1415926
int n = 6, R = 10;  //多邊形變數,外接圓半徑

float theta = 0.0;  //旋轉初始角度值
SYSTEMTIME timeNow;
float hh, mm, ss;

void init();
void Keyboard(unsigned char key, int x, int y);
void Display(void);
void Reshape(int w, int h);
void mytime(int t);

int APIENTRY _tWinMain(HINSTANCE hInstance,
	HINSTANCE hPrevInstance,
	LPTSTR    lpCmdLine,
	int       nCmdShow)
{
	UNREFERENCED_PARAMETER(hPrevInstance);
	UNREFERENCED_PARAMETER(lpCmdLine);

	char *argv[] = { "hello ", " " };
	int argc = 2; // must/should match the number of strings in argv

	glutInit(&argc, argv);  //初始化GLUT庫;
	glutInitWindowSize(700, 700);  //設定顯示視窗大小
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);  //設定顯示模式;(注意雙緩衝)
	glutCreateWindow("A Rotating Square"); // 建立顯示視窗

	init();
	glutDisplayFunc(Display);  //註冊顯示回撥函式
	glutReshapeFunc(Reshape);  //註冊視窗改變回調函式
	glutTimerFunc(1000, mytime, 10); //1000毫秒後呼叫時間函式 mytime
	glutMainLoop();  //進入事件處理迴圈

	return 0;
}

void init()
{
	GetLocalTime(&timeNow);    //獲取系統時間
	hh = timeNow.wHour;    //獲取小時時間
	mm = timeNow.wMinute;   //獲取分鐘時間
	ss = timeNow.wSecond;      //獲取秒時間
}

void Display(void)
{

	glClear(GL_COLOR_BUFFER_BIT);

	glColor3f(1, 1, 1); //設定白色繪圖顏色
	glBegin(GL_POLYGON);  //開始繪製六邊形
	for (int i = 0; i < n; i++)
		glVertex2f(R*cos(i * 2 * PI / n), R*sin(i * 2 * PI / n));
	glEnd();

	//xc,yc為起點座標
	//xh,yh為時針中心點座標
	//xs,ys為秒針終止點座標 
	//xm,ym為分針終止點座標
	int xs, ys, xm, ym, xc=0, yc=0,xh,yh;
	xs = xc + R * cos(PI / 2.0 - ss / 60 * 2 * PI);
	ys = yc + R * sin(PI / 2.0 - ss / 60 * 2 * PI);
	xm = xc + R * cos(PI / 2.0 - (mm + ss / 60.0) / 60.0*2.0*PI);
	ym = yc + R * sin(PI / 2.0 - (mm + ss / 60.0) / 60.0*2.0*PI);
	xh = xc + (R - 5)*cos(PI / 2.0 - (hh + (mm + ss / 60.0) / 60.0) / 12.0*2.0*PI);
	yh = yc + (R - 5)*sin(PI / 2.0 - (hh + (mm + ss / 60.0) / 60.0) / 12.0*2.0*PI);
	glColor3f(1, 0, 0);
	glBegin(GL_LINES);
	glVertex2f(xc, yc);
	glVertex2f(xs, ys);
	glEnd();

	glColor3f(1, 1, 0);
	glBegin(GL_LINES);
	glVertex2f(xc, yc);
	glVertex2f(xm, ym);
	glEnd();

	glColor3f(0, 1, 1);
	glBegin(GL_LINES);
	glVertex2f(xc, yc);
	glVertex2f(xh, yh);
	glEnd();

	/*glColor3f(0, 0, 0); //設定灰色繪圖顏色
	glBegin(GL_LINES);  //開始繪製線
	glVertex2f(0, 0);
	glVertex2f(R*cos(theta + 2 * PI / n), R*sin(theta + 2 * PI / n));
	glEnd();*/

	glutSwapBuffers();   //雙緩衝的重新整理模式;
}

void mytime(int t)
{
	GetLocalTime(&timeNow);    //獲取系統時間
	hh = timeNow.wHour;    //獲取小時時間
	mm = timeNow.wMinute;   //獲取分鐘時間
	ss = timeNow.wSecond;      //獲取秒時間
	theta += 0.01;
	if (theta >= 2 * PI) theta -= 2 * PI;
	glutTimerFunc(1000, mytime, 10); //1000毫秒後呼叫時間函式 mytime
	glutPostRedisplay();  //重畫,相當於重新呼叫Display(),改編後的變數得以傳給繪製函式
}

void Reshape(GLsizei w, GLsizei h)
{
	glMatrixMode(GL_PROJECTION);  //投影矩陣模式
	glLoadIdentity();  //矩陣堆疊清空
	gluOrtho2D(-1.5*R*w / h, 1.5*R*w / h, -1.5*R, 1.5*R);  //設定裁剪視窗大小
	glViewport(0, 0, w, h); //設定視區大小
	glMatrixMode(GL_MODELVIEW);  //模型矩陣模式 
}