實驗二 OpenGL的簡單動畫
一、實驗目的
1.掌握OpenGL的閒置函式。
2.掌握OpenGL的時間函式。
3.掌握OpenGL的簡單動畫功能。
4.瞭解OpengGL裁剪視窗、視區、顯示視窗的概念和它們之間的關係。
5.進一步掌握OpenGL的基本圖元的繪製。
二、實驗內容
1.閒置函式的使用與簡單動畫。
1) 旋轉的六邊形,如圖2-1所示。
閱讀6.3.3節中旋轉的六邊形樣本框架程式,分析程式的實現步驟。執行該程式,觀察旋轉動畫效果。
思考: 如果要調整旋轉速度,旋轉更快或更慢,應該如何修改程式?
答:增大或減小旋轉增量theta。
圖2-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); //模型矩陣模式
}