一、实验目的
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
#include "math.h"
#include "time.h"
#include
#include
#include
#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); //模型矩阵模式
}