实验三 OpenGL 的简单动画与交互
一、实验目的
1、掌握 OpenGL 鼠标交互功能及其简单应用。
2、掌握 OpenGL 的闲置函数与简单动画。
二、实验环境
硬件要求:
PC 机,主流配置,最好为独立显卡,显存 512M 以上。
软件环境:
操作系统:Windows XP。
语言开发工具:Microsoft Visual studio 2008,Visual C++。
三、实验内容与要求
1、闲置函数的使用与简单动画。
(1) 旋转的六边形,如图 3-1 所示
阅读 OpenGL 旋转的六边形样本框架程序,分析程序的实现步骤:
//样本程序: 旋转的六边形
#include "stdafx.h"
#include
#include
#define PI 3.14159
int n=6, R=10;
//设置圆周率
//多边形变数,外接圆半径
float theta=0.0; //旋转初始角度值
void Keyboard(unsigned char key, int x, int y);
void Display(void);
void Reshape(int w, int h);
void myidle();
int APIENTRY _tWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR
int
lpCmdLine,
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"); // 创建显示窗口
glutDisplayFunc(Display);
glutReshapeFunc(Reshape);
//注册显示回调函数
//注册窗口改变回调函数
glutIdleFunc(myidle);
glutMainLoop();
//进入事件处理循环
//注册闲置回调函数
return 0;
}
void Display(void)
{
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(1.0,0,0); //设置红色绘图颜色
glBegin(GL_POLYGON); //开始绘制六边形
for (int i=0;i=2*PI) theta-=2*PI;
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); //模型矩阵模式
}
运行该程序,观察旋转动画效果。
思考: 如果要调整旋转速度,旋转更快或更慢,应该如何修改程序?
(2) 六边形静止,直线单独旋转,如图 3-2 所示。
图 3-1
要求:
1)修改前面的程序,使得六边形保持静止,以六边形中心为起点画
一条直线,终点为六边形某一顶点,使得直线不停绕中心点旋转。
代码保存下来备用。
思考:如果需要直线保持与机器时钟的秒针节拍吻合,应该如何修改?
2、鼠标交互。
(1) 鼠标画线
图 3-2
阅读 OpenGL 鼠标画线程序,能够实现在绘制窗口用鼠标交互
绘制若干条直线,鼠标左键首先按下,确定直线的起始点,鼠标左
键按下同时移动,看到画线过程,鼠标左键松开时,确定直线的终
点,可重复画多条直线。
实现主要思路:
1) 写出画静止若干条直线程序框架,坐标用变量替代;
2) 在主函数里注册鼠标按钮响应函数和鼠标移动响应函数;
3) 在鼠标按钮响应子函数里,给出鼠标按钮响应事件;
4) 在鼠标移动响应子函数里,给出鼠标移动响应事件;
5) 读懂程序并分析程序,保留程序。
//鼠标画线小程序
#include "stdafx.h"
#include
#define N 1000
//maximum line numbers
int ww,hh;
int line[N][4], k=0; //for line's endpoint coordinates and line number
// for display window width and height
void Myinit(void)
{
glClearColor(0.0,0.0,0.0,0.0);
glLineWidth(3.0);
}
//鼠标按钮响应事件..
void myMouse(int button,int state,int x,int y)
{
if(button==GLUT_LEFT_BUTTON&&state==GLUT_DOWN)
{
line[k][0]=x;
line[k][1]=hh-y; //线段终点y坐标
//线段起点x坐标
}
if(button==GLUT_LEFT_BUTTON&&state==GLUT_UP)
{
line[k][2]=x;
line[k][3]=hh-y;
k++;
//线段起点x坐标
//线段终点y坐标
glutPostRedisplay();
}
}
//鼠标移动时获得鼠标移动中的坐标-----------------------------------------------------
void myMotion(int x,int y)
{
//get the line's motion point
line[k][2]=x;
line[k][3]=hh-y; //动态终点的y坐标
//动态终点的x坐标
glutPostRedisplay();
}
//画线子程序
void drawlines()
{
for(int i=0;i<=k;i++) //********
{
}
}
glBegin(GL_LINES);
glVertex2f(line[i][0],line[i][1]);
glVertex2f(line[i][2],line[i][3]);
glEnd();
//渲染绘制子程序--------------------------------------------------------------------------
void Display(void)
{
glClear(GL_COLOR_BUFFER_BIT);
drawlines(); //画线子程序;
glutSwapBuffers(); //双缓冲的刷新模式;
//刷新颜色缓冲区;
}
//-----------------------------------------------
void Reshape(int w, int h) //窗口改变时自动获取显示窗口的宽w和高h
{
//矩阵堆栈清空
glMatrixMode(GL_PROJECTION); //投影矩阵模式
glLoadIdentity();
glViewport(0, 0, w, h);
gluOrtho2D(0, w, 0, h);
ww=w;
hh=h;
//设置视区大小
//设置裁剪窗口大小
}
int APIENTRY _tWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR
int
lpCmdLine,
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(800, 600);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB); //设置显示模式;(注意双缓冲)
glutCreateWindow("鼠标画线小程序演示"); // 创建显示窗口
//设置显示窗口大小
Myinit();
glutDisplayFunc(Display); //注册显示回调函数
glutMouseFunc(myMouse);
//注册鼠标按钮回调函数
glutMotionFunc(myMotion); //注册鼠标移动回调函数
glutReshapeFunc(Reshape); //注册窗口改变回调函数
glutMainLoop(); //进入事件处理循环
return 0;
鼠标画线程序运行后,程序效果如图 3-3 所示。
图 3-3
(2) 鼠标绘制矩形
修改鼠标画线程序,要求:能够实现在绘制窗口用鼠标交互绘制若
干矩形,鼠标左键首先按下,确定矩形对角线的起始点,鼠标左键
按下同时移动时,看到画矩形过程,鼠标左键松开,确定矩形对角
线的另一点,可重复画多个矩形。如图 3-4 所示。
图 3-4
参考函数:
1、void glutIdleFunc((*f) (void)) //注册闲置响应函数
用于后台处理,当其他的事情处于挂起时,就可以执行函数 f。
如果将函数的参数设为 NULL 或 0,将取消该函数的执行。
该函数调用放在主程序 main()中,通常利用它可实现简单动画。
2、void myidle()
//闲置响应回调函数
{
}
//当时间空闲时系统要做的事情
3、注册鼠标按钮响应函数
glutMouseFunc(void(*f)(int button,int state,int x,int y))
该函数在主程序中调用。
参数 button:
GLUT_LEFT_BUTTON,GLUT_MIDDLE_BUTTON,GLUT_RIGHT_BUTTON
参数 state:
GLUT_UP,GLUT_DOWN
x,y:返回鼠标在窗口的位置(原点在左上角)。
4、鼠标按钮响应回调函数
mymousebutton(int button, int state, int x, int y)
{
···
}
5、注册鼠标移动响应函数
void glutMotionFunc(void (*f) (int x,int y))
该函数在主程序中调用。(x,y)鼠标的位置,原点在左上角
6、4、鼠标移动响应回调函数
mymousemotion(int x,int y)
{
}
···
5、多边形线框模型设置:
glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
6、时间函数的使用
void glutTimerFunc(unsigned int msecs, void (*Func)(int
value), int value);
注册一个回调函数,当指定时间值到达后,由 GLUT 调用注册的函数一次
msecs 是等待的时间,单位
Func 是注册的函数
value 是指定的一个数值,用来传递到回调函数 Func 中
这个函数注册了一个回调函数,当指定的毫秒数到达后,这个函数就调用注册的
函数,value 参数用来向这个注册的函数中传递参数.
四、思考题:程序合成
要求将以上 4 个小程序集成在一起,实现:
按下数字 1 键:清屏实现显示旋转的六边形
按下数字 2 键:清屏实现显示静止的六边形+旋转的直线
按下数字 3 键:清屏实现显示鼠标画线
按下数字 4 键:清屏实现显示鼠标绘制矩形
程序如何实现?
提示:1、增加 keyboard()函数,同时在 display()函数中添加 switch
语句,实现每个数字键的不同功能。