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. 首先获得一系列参数信息:

1 const int BUFSIZE = 1024; //缓冲区selectBuf 2 GLuint selectBuf[BUFSIZE]; 3 glSelectBuffer (BUFSIZE, selectBuf); 4 5 GLint viewport[4]; //获取视口viewport的信息 6 glGetIntegerv (GL_VIEWPORT, viewport);

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

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

3. 保存一下矩阵信息:

           glPushMatrix();

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

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

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

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

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

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

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

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

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

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

         glPopMatrix();

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

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

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

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

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

击中的物件的名字的数目

这个物件中最近的点的深度值

这个物件中最远的点的深度值

击中的物件的名字之一

击中的物件的名字之二 
(若有多个名字,则如此类推...)

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

Java日期时间API系列31

  时间戳是指格林威治时间1970年01月01日00时00分00秒起至现在的总毫秒数,是所有时间的基础,其他时间可以通过时间戳转换得到。Java中本来已经有相关获取时间戳的方法,Java8后增加新的类Instant等专用于处理时间戳问题。 1获取时间戳的方法和性能对比1.1获取时间戳方法Java8以前