【计算机图形学】实验2:橡皮筋技术及拾取操作
实验2 橡皮筋技术及拾取操作
一、实验目的
本实验旨在通过实现橡皮筋技术和拾取操作,帮助学生深入理解OpenGL中的交互式图形编程。学生将学习如何使用鼠标输入来动态创建和修改图形,掌握橡皮筋技术的实现方法,通过实时反馈增强用户体验。同时,实验将介绍如何实现对象的拾取操作,使学生能够选择和操作屏幕上的图形对象,培养对坐标转换和碰撞检测的理解。通过这一过程,学生将提高对OpenGL事件处理机制的掌握,增强编程能力和解决实际问题的能力,为后续更复杂的图形应用打下坚实基础。
二、实验步骤
2.1 OpenGL中利用鼠标实现橡皮筋技术
1)在鼠标点击界面之前,页面将不显示任何内容;
2)当鼠标点击第一次,会确定第一个坐标,鼠标移动函数的实时状态的坐标会临时作为第二个坐标,此刻等于第一个坐标(重合为点),所以绘制函数也只能画成一个点,不过仍然会切换缓存,并清空OpenGL命令缓存区。
3)当鼠标点击第一次后移动,鼠标移动函数的坐标会随着鼠标的位置动态变化,并会动态存为第二个坐标,所以绘制函数画成的线也会动态的变化(模拟出橡皮筋的效果),每变一次就会切换一次缓存,并清空OpenGL命令缓存区。
4)当鼠标点击第二次,就会确定第二个坐标,鼠标移动函数的实时状态的坐标会临时作为第二个坐标,此刻等于第二个坐标,绘制函数便画出一条确定的线,会切换缓存,并清空OpenGL命令缓存区。
5)当鼠标点击第一次后移动,移动函数便不会起作用了。
6)当鼠标再一次点击,就会确定该点击点坐标为第一个坐标,同(1),原来的直线会在切换缓存,清空OpenGL命令缓存区中消失,如此往复下去
7)上面说到的都是点击鼠标左键,可以设置点击鼠标右键,将任意时刻作出的图(线、点)清空。
完整代码如下:
#include <gl/glut.h>
int iPointNum = 0;
int x1 = 0, x2 = 0, y1 = 0, y2 = 0;
int winWidth = 400, winHeight = 300;
void Initial(void)
{
glClearColor(1.0f,1.0f,1.0f,1.0f);
}
void ChangeSize(int w, int h)
{
winWidth = w; winHeight = h;
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity(); //调用单位矩阵,去掉以前的投影参数设置
gluOrtho2D(0.0, winWidth, 0.0, winHeight); //设置投影参数
}
void Display(void)
{
glClear(GL_COLOR_BUFFER_BIT); //使用背景色填充窗口
glColor3f(1.0f, 0.0f, 0.0f); //指定绘图颜色为红色
if (iPointNum >= 1) //绘制直线段
{
glBegin(GL_LINES);
glVertex2i(x1, y1);
glVertex2i(x2, y2);
glEnd();
}
glutSwapBuffers(); //交换缓存区
}
void MousePlot(GLint button, GLint action, GLint xMouse, GLint yMouse)
{
if (button == GLUT_LEFT_BUTTON && action == GLUT_DOWN){//按下左键
if(iPointNum == 0 || iPointNum == 2){
iPointNum = 1;
x1 = xMouse; y1 = winHeight - yMouse;
}
else {
iPointNum = 2;
x2 = xMouse; y2 = winHeight - yMouse;
glutPostRedisplay(); //强制刷新窗口 指定窗口重新绘制
}
}
if (button == GLUT_RIGHT_BUTTON && action == GLUT_DOWN) {//按下右键
iPointNum = 0;
glutPostRedisplay();
}
}
void PassiveMouseMove(GLint xMouse, GLint yMouse)
{
if (iPointNum == 1) {
x2 = xMouse; y2 = winHeight - yMouse;
glutPostRedisplay();
}
}
int main(int argc,char *argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
glutInitWindowSize(400, 300);
glutInitWindowPosition(100,100);
glutCreateWindow("橡皮筋技术");
glutDisplayFunc(Display);
glutReshapeFunc(ChangeSize); //指定窗口再整形回调函数
glutMouseFunc(MousePlot); //指定鼠标响应函数
glutPassiveMotionFunc(PassiveMouseMove); //指定鼠标移动响应函数
Initial();
glutMainLoop();
return 0;
}
2.2 OpenGL中实现拾取操作
1.设置拾取缓冲区
拾取时,在特定的视景体中绘制每个对象都会产生一个命中消息,命中消息将存放在一个名字堆栈中,这个名字堆栈就是拾取缓冲区。函数:
void glSelectBuffer(GLsizei n, GLunint*buff);
指定了一个具有n个元素的整形数组buffer作为拾取缓冲区。对于每个命中消息,都会在拾取缓冲区数组中添加一条记录,每条记录包含了以下的信息:
(1)命中发生时堆栈中的名称序号;
(2)拾取图元所有顶点的最大和最小窗口z坐标。这两个值的范围都位于[0,1]内,他们都乘以232-1,然后四舍五入为最接近的无符号整数。
(3)命中发生时堆栈中的内容,最下面的名称排在最前面。
2.进入选择模式
在定义了拾取缓冲区后,需要激活选择模式。选择模式的指定采用函数:
GLint glRenderMode(GLenum mode);
其中,参数mode值可以为GL_RENDER(默认值)、GL_SELECT或GL_FEEDBACK,分别指定应用程序处于渲染模式、选择模式和反馈模式。应用程序一直处于当前模式下,直到调用本函数改变为其他模式为止。
3.名字堆栈操作
在选择模式下,需要对名字堆栈进行一系列操作,包括初始化、压栈、弹栈以及栈顶元素操作等。
void glInitNames();//初始化名字堆栈,其初始状态为空
void glPushName(GLuint name);//将一个名字压入堆栈,其中name是标识图元的一个无符号整数值
void glLoad Name(GLuint name);//将名字堆栈的栈顶元素替换为name
void glPopName();//将栈顶元素弹出
4.设置合适的变换过程
拾取操作可以通过矩形拾取窗口来实现,我们可以用下面的函数调用:
gluPickMatrix(xPick, yPick, widthPick, heightPick,*vp);
其中参数xPick和yPick指定相对于显示区域左下角的拾取窗口中心的双精度浮点屏幕坐标值。当使用鼠标进行选择操作时,xPick和yPick由鼠标位置确定,但要注意y坐标的反转。参数widthPick和heightPick指定拾取窗口的双精度浮点宽高值。参数vp指定了一个包含当前显示区域的坐标位置和尺寸等参数的整型数组,该参数可以通过函数glGetIntegerv来获得。这个函数可以设置一个用于拾取操作的观察空间。
5.为每个图元分配名字并绘制
为了标识图元,在图元绘制过程中需要用一个整型值指定图元的名称,并在选择模式下,将这个名字压入到名字堆栈中。为了节省名字堆栈的空间,应该在图元绘制完成后,将其名字从堆栈中弹出。
6.切换回渲染模式
在选择模式下,所有的图元绘制完成后,应该再次调用函数glRenderMode选择渲染模式,在帧缓冲存储器中绘制图元,并返回被选中图元的个数。
7.分析选择缓冲区中的数据
拾取操作完成之后,可以根据选择缓冲区中的内容进行分析,以确定拾取的图元。
完整代码如下:
#include <gl/glut.h>
#include "stdio.h"
const GLint pickSize = 32;
int winWidth = 400, winHeight = 300;
voidInitial(void)
{
glClearColor(1.0f, 1.0f, 1.0f,1.0f);
}
voidDrawRect(GLenum mode)
{
if(mode == GL_SELECT) glPushName(1);//压入堆栈
glColor3f(1.0f,0.0f,0.0f);
glRectf(60.0f,50.0f,150.0f,150.0f);
if(mode == GL_SELECT) glPushName(2); //压入堆栈
glColor3f(0.0f,1.0f,0.0f);
glRectf(230.0f,50.0f,330.0f,150.0f);
if(mode ==GL_SELECT) glPushName(3); //压入堆栈
glColor3f(0.0f,0.0f,1.0f);
glRectf(140.0f,140.0f,240.0f,240.0f);
}
voidProcessPicks(GLint nPicks, GLuint pickBuffer[])
{
GLint i;
GLuint name, *ptr;
printf("选中的数目为%d个\n",nPicks);
ptr=pickBuffer;
for(i=0;i<nPicks; i++){
name=*ptr; //选中图元在堆栈中的位置
ptr+=3; //跳过名字和深度信息
ptr+=name-1; //根据位置信息获得选中的图元名字
if(*ptr==1) printf("你选择了红色图元\n");
if(*ptr==2) printf("你选择了绿色图元\n");
if(*ptr==3) printf("你选择了蓝色图元\n");
ptr++;
}
printf("\n\n");
}
voidChangeSize(int w, int h)
{
winWidth = w;
winHeight = h;
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0,winWidth,0.0,winHeight);
}
voidDisplay(void)
{
glClear(GL_COLOR_BUFFER_BIT);
DrawRect(GL_RENDER);
glFlush();
}
voidMousePlot(GLint button, GLint action, GLint xMouse, GLintyMouse)
{
GLuint pickBuffer[pickSize];
GLint nPicks, vp[4];
if(button== GLUT_LEFT_BUTTON && action ==GLUT_DOWN){
glSelectBuffer(pickSize,pickBuffer);//设置选择缓冲区
glRenderMode(GL_SELECT); //激活选择模式
glInitNames(); //初始化名字堆栈
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glGetIntegerv(GL_VIEWPORT, vp);
//定义一个10×10的选择区域
gluPickMatrix(GLdouble(xMouse),GLdouble(vp[3]-yMouse),10.0,10.0,vp);
gluOrtho2D(0.0,winWidth,0.0,winHeight);
DrawRect(GL_SELECT);
//恢复投影变换
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glFlush();
//获得选择集并输出
nPicks = glRenderMode(GL_RENDER);
ProcessPicks(nPicks, pickBuffer);
glutPostRedisplay();
}
}
intmain(int argc, char* argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE |GLUT_RGB);
glutInitWindowSize(400,300);
glutInitWindowPosition(100,100);
glutCreateWindow("拾取操作");
glutDisplayFunc(Display);
glutReshapeFunc(ChangeSize);
glutMouseFunc(MousePlot);
Initial();
glutMainLoop();
return 0;
}
三、实验结果
上述程序的运行结果如下:
图1 OpenGL实现橡皮筋技术
图2 OpenGL实现拾取操作
四、实验总结
-
橡皮筋技术的实现
本实验通过橡皮筋技术的实现,使学生能够直观地理解图形的动态创建与修改。学生通过鼠标输入,实时绘制和调整图形,增强了交互体验。这一过程帮助学生掌握了如何在OpenGL中处理鼠标事件,并通过线段的绘制展示了图形的变化过程。 -
对象拾取操作的掌握
实验中引入了对象拾取操作,学生学习了如何选择和操作屏幕上的图形对象。通过坐标转换和碰撞检测的实现,学生能够理解如何将鼠标坐标转换为世界坐标,从而有效地与图形进行交互。这不仅提升了学生对OpenGL事件处理机制的理解,也让他们在实际应用中能够灵活运用这些技术。 -
用户体验的增强
实时反馈机制的应用显著提升了用户体验。通过橡皮筋技术,学生能够即时看到操作的结果,这种互动性使得学习过程更加生动有趣。实验强调了用户体验在图形编程中的重要性,培养了学生在设计交互式应用时关注用户反馈的意识。 -
编程能力的提升
通过本实验,学生不仅提高了对OpenGL编程的理解,还锻炼了问题解决能力。在实现橡皮筋效果和对象拾取操作的过程中,学生需要面对各种编程挑战,从而增强了他们的逻辑思维和调试能力。这为后续更复杂的图形应用开发打下了坚实的基础,激发了他们对计算机图形学的兴趣。