OpenGL选择与拾取GL_SELECT 附源码

【一个提示】该方法虽然可行但是已经淘汰很多年,建议自行尝试,后面也许会写论文最好的方式是:

1. 使用Kd-tree组织场景中的物体,以便于快速查找。

2. 使用屏幕坐标->空间三维坐标的逆矩阵变换,实现选取。

在介绍开始,首先给出工程和可执行程序的下载链接:

         OpenGL选择和拾取obj模型_代码及可执行文件

         或者http://download.csdn.net/detail/mahabharata_/9709959

    程序的执行结果如下:

首先,我们在OpenGL的绘制方式有两种:GL_RENDER和GL_SELECT。顾名思义,GL_RENDER是渲染模式,也就是默认的绘制方式,通俗的讲,就是绘制操作都会被绘制在屏幕上;GL_SELECT则是选择模式,这种方式不会被绘制在屏幕上,之后的矩阵变化为选取矩阵的变化。

这里对GL_RENDER不做过多说明,因为这是默认方式,就是操纵显示在屏幕上时的矩阵变换。

对于GL_SELECT:在用OpenGL进行图形编程的时候,通常要用鼠标进行交互操作,比如用鼠标点选择画面中的物体,我们称之为拾取(Picking),这里我们介绍一下:在OpenGL中如何拾取,如何利用OpenGL提供的一系列函数来完成拾取,再简单介绍下OpenGL的名字栈(Name stack),拾取矩阵(Picking Matrix)等等。

 1. 首先获得一系列参数信息:

 

           const int BUFSIZE = 1024;  //缓冲区selectBuf
           GLuint selectBuf[BUFSIZE];
           glSelectBuffer (BUFSIZE, selectBuf);

           GLint viewport[4];        //获取视口viewport的信息
           glGetIntegerv (GL_VIEWPORT, viewport);

 

 

 

 

 

2. 进入GL_SELECT模式,并初始化名字栈

           glRenderMode(GL_SELECT);
           glInitNames();
           glPushName(-1);
 

 

3. 保存一下矩阵信息:

 

           glPushMatrix();

 

4. 在GL_SELECT模式下,指定拾取窗口,并进行投影变换(GL_PROJECTION)

投影变换事实上在GL_RENDER模式下已经进行过一次,但是需要在GL_SELECT模式下,再进行一次。

 

           glMatrixMode (GL_PROJECTION);      // 投影变换
           glLoadIdentity ();
           gluPickMatrix((GLdouble)x, (GLdouble) (viewport[3]-y), 5.0, 5.0, viewport); // 指定选取窗口(x,y为鼠标点击位置)
           gluPerspective(45.0, (GLfloat)width()/(GLfloat)height(), 0.1, 10000.0);     // 指定视景体

 

 

5. 在GL_SELECT模式下,进行模型视图变换GL_MODELVIEW

 

           glMatrixMode(GL_MODELVIEW);    // 2: GL_SELECT下模型视图变换
           glLoadIdentity();
           gluLookAt(0.0,0.0,10.0, 0.0,0.0,0.0, 0.0,1.0,0.0);   //指定摄像机位置
           /*
              为了保证选取结果的正确性,在GL_RENDER下进行的所有缩放、平移、旋转等操作,这里也要进行一次。
             *//*
              为了保证选取结果的正确性,在GL_RENDER下进行的所有缩放、平移、旋转等操作,这里也要进行一次。
             */

 

6. 在GL_SELECT重新绘制物体,这里使用我上传的程序中的例子。

 

调用绘制函数,总共有3个人物,绘制函数为renderNPC(GLenum mode);

 

          for(int i=0; i< 3 ;i++)
          {
               _characters[i]->renderNPC(GL_SELECT);  // 使用GL_SELECT方式渲染一次(并不绘制在屏幕上)
          }

 

关于renderNPC(GLenum mode)的具体实现,在后面给出。

7. 出栈,让之前进行的矩阵操作释放掉。

 

         glPopMatrix();

 

8. 对选取的名字栈进行处理(选取结果已经储存在名字栈中了)

 

          // 切换回GL_RENDER模式
          GLint hits = glRenderMode (GL_RENDER); // 返回的hit:表示名字栈中结果的个数。
          glMatrixMode (GL_PROJECTION);    /// 此处需要重新进行一次GL_RENDER模式下的投影变换。
          glLoadIdentity ();
          gluPerspective(45.0, (GLfloat)width()/(GLfloat)height(), 0.1, 10000.0);
 
          if(hits)  ///如果选中物体,设置为"选中"状态
          {
              _characters[selectBuf[3]]->_isSelected=!( _characters[selectBuf[3]]->_isSelected );
          }
  
          updateGL();  // 在屏幕上重绘,因为有的物体已经被选中。

 

 

       关于在上面出现的renderNPC(GLenum mode)函数,下面给出其内部细节(为了简单表述,以画方块为例)。

 

          void renderNPC(GLenum model)
          {
              glPushMatrix();           
              /*
                 这里进行属于本物体的旋转、平移、缩放变换
              */
             if(model == GL_SELECT)
             {
                 glLoadName(_name);   //这里的_name为预先设计的该物体的名字,为一个GLuint值,比如1、2、3、.....
             }
             
             if(_isSelected == true)  ///如果被选中
             {   
              /*
                    对于选取的物体,进行诸如修改颜色啊啥啥啥的操作。
              */
             }
             glutSolidCube(3.0);     //绘制出物体,这里以一个“方块”举例。
             glPopMatrix();
          }

 

 

此外,我们还需要了解的最后一点,名字栈的机制,名字栈最终保存在selectBuffer[]中。当某物件被“拾取”(被光束射中)的时候,对应的名字和相关信息便会被提交给HIT Record,存储在selectBuffer里面。相关信息包括该物件离光束发射处(相机/眼睛)最近的点的深度值和最远的点的深度值等等,以下反映了当一个物件被拾取后,名字栈机制向HIT Record发送的信息(然后HIT Record把此信息存入selectBuffer):  

击中的物件的名字的数目
这个物件中最近的点的深度值
这个物件中最远的点的深度值
击中的物件的名字之一
击中的物件的名字之二 
(若有多个名字,则如此类推...)
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值