拾取操作的实现[OpenGL]


   OpenGL中采用一种比较复杂的方式实现了拾取操作,即选择模式。选择模式是一种绘制模式,它基本思想是在一次拾取操作时,系统根据拾取操作的参数(如鼠标位置)生成一个特定视景体,然后由系统重新绘制场景中的所有图元,但这些图元并不会绘制到颜色缓存中,系统跟踪有哪些图元绘制到了这个特定的视景体中,并将这些对象的标识符保存到拾取缓冲区数组中。
   
OpenGL中实现拾取操作主要包括以下步骤。

   1.设置拾取缓冲区
   
拾取时,在特定的视景体中绘制每个对象都会产生一个命中消息,命中消息将存放在一个名字堆栈中,这个名字堆栈就是拾取缓冲区。函数:
       void glSelectBuffer(GLsizei n, GLunint*buff);
指定了一个具有n个元素的整形数组buffer作为拾取缓冲区。对于每个命中消息,都会在拾取缓冲区数组中添加一条记录,每条记录包含了以下的信息:
   
1)命中发生时堆栈中的名称序号;
   
2)拾取图元所有顶点的最大和最小窗口z坐标。这两个值的范围都位于[01]内,他们都乘以232-1,然后四舍五入为最接近的无符号整数。
   
3)命中发生时堆栈中的内容,最下面的名称排在最前面。

   2.进入选择模式
   
在定义了拾取缓冲区后,需要激活选择模式。选择模式的指定采用函数:
       GLint glRenderMode(GLenum mode);
其中,参数mode值可以为GL_RENDER(默认值)、GL_SELECTGL_FEEDBACK,分别指定应用程序处于渲染模式、选择模式和反馈模式。应用程序一直处于当前模式下,直到调用本函数改变为其他模式为止。

   3.名字堆栈操作
   
在选择模式下,需要对名字堆栈进行一系列操作,包括初始化、压栈、弹栈以及栈顶元素操作等。
       void glInitNames();//
初始化名字堆栈,其初始状态为空
       void glPushName(GLuint name);//
将一个名字压入堆栈,其中name是标识图元的一个无符号整数值
       void glLoad Name(GLuint name);//
将名字堆栈的栈顶元素替换为name
       void glPopName();//
将栈顶元素弹出

   4.设置合适的变换过程
   
拾取操作可以通过矩形拾取窗口来实现,我们可以用下面的函数调用:
       gluPickMatrix(xPick, yPick, widthPick, heightPick,*vp);
其中参数xPickyPick指定相对于显示区域左下角的拾取窗口中心的双精度浮点屏幕坐标值。当使用鼠标进行选择操作时,xPickyPick由鼠标位置确定,但要注意y坐标的反转。参数widthPickheightPick指定拾取窗口的双精度浮点宽高值。参数vp指定了一个包含当前显示区域的坐标位置和尺寸等参数的整型数组,该参数可以通过函数glGetIntegerv来获得。这个函数可以设置一个用于拾取操作的观察空间。

   5.为每个图元分配名字并绘制
   
为了标识图元,在图元绘制过程中需要用一个整型值指定图元的名称,并在选择模式下,将这个名字压入到名字堆栈中。为了节省名字堆栈的空间,应该在图元绘制完成后,将其名字从堆栈中弹出。

   6.切换回渲染模式
   
在选择模式下,所有的图元绘制完成后,应该再次调用函数glRenderMode选择渲染模式,在帧缓冲存储器中绘制图元,并返回被选中图元的个数。

   7.分析选择缓冲区中的数据
   
拾取操作完成之后,可以根据选择缓冲区中的内容进行分析,以确定拾取的图元。

程序3-3OpenGL实现的拾取操作的例子

#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
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值