转自:OpenGL学习笔记:拾取与选择

在开发OpenGL程序时,一个重要的问题就是互动,假设一个场景里面有很多元素,当用鼠标点击不同元素时,期待作出不同的反应,那么在OpenGL里面,是怎么知道我当前鼠标的位置是哪一个物体呢?

OpenGL有一套机制,叫做Picking, 里面涉及到几个核心概念:

1. selection mode. 选择模式

2. name stack. 名字栈

3. hit record。 命中记录

4. viewing volume。 视角范围

在OpenGL的picking中,选择物体不是选择一个单独的物体,而是选择一片范围内的所有物体。这种设计思路是有点奇怪,但是OpenGL就是这麽设计的。假如鼠标当前的位置是(200,200),普通的应用就是选择在点(200,200)处的物体ID, 但OpenGL不然,它是选择以(200,200)为中心的,比如长宽都为20的这个范围内可见的物体,也就是说,前面一种选择,是以点为选择依据,OpenGL的选择,是以中心为(200,200),长宽各位10的面为依据,好像举着一个画框,只要在这个画框内的物体,都会当作返回结果。

我不是很理解为什么OpenGL要这麽设计,但肯定有它的道理。以上的几个核心概念,就是配合这种设计思路的,不难看出:

1. Viewing volume。就是指用来选择的画框

2. hit record。就是所返回的物体数据

3. name stack。是用来分配并保存物体ID的堆栈

4. selection mode. OpenGL有三种模式,Render mode。就是普通的绘图模式,Select mode。在picking时的选择模式,Feedback mode。不画图,不选择,而是把所有最终的渲染完之后的绘画指令返回给用户。在绘图仪上作画等方面特别有用

当需要相应鼠标选择事件的时候,要首先进入selection mode, 然后才能执行相应的操作步骤

以下是详细的操作步骤:

1. 获取鼠标位置

2. 进入选择模式selection mode

3. 设置画框大小view volume,当然,是根据鼠标位置来的

4. 像往常一样绘制场景

5. 退出选择模式,得到选择结果

进入选择模式:

1. glSelectBuffer,设置选择缓冲区,hit record会被OpenGL保存在里面

2. glRenderMode(GL_SELECT),正式进入选择模式

设置画框大小:

1. gluPickMatrix。设置画框近平面大小

2. gluPerspective。影响画框的容量

实例代码:

glSelectBuffer(BUFSIZE,selectBuf);
glRenderMode(GL_SELECT); glMatrixMode(GL_PROJECTION); //保存投影矩阵
glPushMatrix();
glLoadIdentity(); glGetIntegerv(GL_VIEWPORT,viewport);
gluPickMatrix(cursorX,viewport[3]-cursorY,5,5,viewport);
gluPerspective(45,ratio,0.1,1000);

注意,上面的代码中在执行gluPickMatrix值钱先保存了投影矩阵,然后再重新创造一个新的供自己使用。原因是gluPickMatrix会操作投影举证,该函数和后续的gluPerspective共同作用,最终会生成一个全新的投影矩阵,该矩阵中的所有物体,都会被视为选中。为了不影响旧有的投影矩阵,因此需要保存先

绘制场景:

绘制场景的时候有一点比较重要,那就是对每个需要绘制的单独元素,起一个名字(ID),这样OpenGL才能在推出选择模式的时候告知调用者,哪个物体被选择了。当然如果非不取,那OpenGL也会返回被选择的物体,只是不带名字。因此绘制场景的步骤一般如下:

1. glInitNames, 初始化name stack。 OpenGL的中对元素名字的操作需要通过一个栈,叫做名字栈,为啥这麽墨迹,不直接指定名字?具体原因是OpenGL总要有个地方去读取名字,而且,一个名字下包括哪些需要绘画的内容?OpenGL怎么知道当前画的这个人头是属于上个人的还是上上个人的?只能通过名字栈的变化才能知道。而且一个元素可以有多个内容,怎么搞?于是乎名字栈就应运而生,OpenGL认为这个东东可以解决这一系列问题

2. glPushName。 创建一个名字。

3. 开始画啊画啊画

4. glPopName。一个元素绘制结束

5. 从步骤2循环到4的一个个元素绘制

实例代码:

	glInitNames();

	glPushName(BODY);
drawBody();
glPopName(); glPushName(HEAD);
drawHead();
drawEyes();
glPopName(); drawGround(); //不带名字的元素

还有另外一个有用的函数,glLoadName。它的作用是用一个新的名字替代栈顶当前的名字。说白了,就等同于:

glPopName()

glPushName(newName)

OpenGL里面,一个物体还可以有多个名字,这个貌似有点荒唐,确实也比较荒唐,但OpenGL也有它的道理,比如一个元素,它的名字叫“head",那么,如果我想知道这个头是谁的头,怎么搞?OpenGL告诉你压力不大,再给他取一个名字叫”Aka",那么你就知道这是Aka的头。也就是说,我绘制了一个元素,然后给它两个名字,一个叫head, 一个叫aka,那么当你选择了该物体的时候,你就知道了,这个物体是aka的头。

示例代码如下:

glPushName(Aka)

glPushName(Head)

draw()

glPopName(Head)

glPopName(Aka)

于是,draw出来的物体就有两个名字了.

处理选择结果:

处理选择接过之前,要先推出selection模式,然后OpenGl会返回一个数目,被选中物体的数目,跟据该数目,就知道在选择缓冲区里面保存了多少个物体,每个物体都有固定的数据结构,一个个的读取该数据结构,就知道哪些物体被选取了。

示例代码:

void stopPicking() {

	int hits;

	// restoring the original projection matrix
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glFlush(); // returning to normal rendering mode
hits = glRenderMode(GL_RENDER);//得到选中物体的数目 // if there are hits process them
if (hits != 0)
processHits(hits,selectBuf);
}

重要的是选择缓冲区中hit record的数据结构,该结构是一个GLuinit的类型,也就说里面所有的数据都是uinit,等等,名字不是也在里面吗,这可是string啊,no, 名字其实不是string,准确的说应该叫ID,普通的1,2,3而已

每一个元素的结构提如下:

1. 该物体的名字的数目。一个物体可以有多个名字,你懂的

2. 该物体被选中区域的最小Z值。我们往往根据这个值得知哪一个才是鼠标点所在的物体

3. 该物体被选中区域的最大Z值。

4. 名字

5. 名字。。

由于物体可以有多个名字,甚至可以木有名字,因此元素的结构不是固定大小的,以下为例:

Hit Record Contents Description
0 表示这个元素没名字
4.2822e+009 最小Z值
4.28436e+009 最大Z值
1 这个元素有1个名字
4.2732e+009 最小Z值
4.27334e+009 最大Z值
6 名字叫6
2 这个元素有2个名字
4.27138e+009 最小Z值
4.27155e+009 最大Z值
2 第一个名字叫2
5 第二个名字叫5

所以呢,只需要对着选择缓冲区,一个个的读下去就对了,示例代码:

void processHits2 (GLint hits, GLuint buffer[])
{
unsigned int i, j;
GLuint names, *ptr, minZ,*ptrNames, numberOfNames; printf ("hits = %d\n", hits);
ptr = (GLuint *) buffer;
minZ = 0xffffffff;
for (i = 0; i < hits; i++) {
names = *ptr;
ptr++;
if (*ptr < minZ) {
numberOfNames = names;
minZ = *ptr;
ptrNames = ptr+2;
} ptr += names+2;
}
printf ("The closest hit names are ");
ptr = ptrNames;
for (j = 0; j < numberOfNames; j++,ptr++) {
printf ("%d ", *ptr);
}
printf ("\n"); }

注意:以上所有的概念和函数,都只在selection mode下面有效。如果转到了Render mode,系统怎么处理这些函数呢?很简单,华丽的无视掉。

OpenGL学习笔记:拾取与选择的更多相关文章

  1. OpenGL学习笔记3——缓冲区对象

    在GL中特别提出了缓冲区对象这一概念,是针对提高绘图效率的一个手段.由于GL的架构是基于客户——服务器模型建立的,因此默认所有的绘图数据均是存储在本地客户端,通过GL内核渲染处理以后再将数据发往GPU ...

  2. OpenGL学习笔记2017/8/29

    OpenGL学习日志: 感谢doing5552 的OpenGL入门学习:http://www.cppblog.com/doing5552/archive/2009/01/08/71532.html 相 ...

  3. OpenGL学习笔记(1) 画一个三角形

    最近找实习有一丢丢蛋疼,沉迷鬼泣5,四周目通关,又不想写代码,写篇笔记复习一下,要好好学图形学啊 用OpenGL画一个三角形 项目的简介 记录一下跟着learnOpenGL学习的过程 笔记里的代码放在 ...

  4. OpenGL学习笔记0——安装库

    最近需要做一个基于Zigbee室内无线定位的系统,受到TI公司ZigBee Sensor Monitor软件的启发,打算用OpenGL来做一个3D显示空间内物体位置的程序.学习阶段选择VS2010+O ...

  5. OpenGL学习笔记:Console工程下如何不显示控制台黑窗口只显示Windows窗口

    刚学习OpenGL,绘制图形的时候,如果不进行设置,运行的时候会先出现黑窗口再出现Windows窗口. 其实要去除控制台窗口非常简单,只需要修改工程设置,把子系统改成Windows,程序的入口点改成m ...

  6. opengl学习笔记

    准备: 1.准备资源:从GLEW1.13.0下载GLEW,并且解压出glew-1.13.0目录.从FreeGLUT官网下载3.0.0版本.直接从这里下的编译后的FreeGLUT,选for MSVC,下 ...

  7. OpenGL学习笔记(四)纹理

    目录 要完成的纹理效果 纹理环绕方式 纹理过滤 多级渐远纹理 加载与创建纹理 stb_image库的使用方法 生成纹理对象 应用纹理 纹理单元 参考资料:OpenGL中文翻译 要完成的纹理效果 纹理是 ...

  8. OpenGL学习笔记5——嵌入Qt框架

    学习OpenGL也有段时间了,前几篇将GL最基本的画图过程解析了一下,后面进阶的就随项目需要再学.因为之前一直是用glut这个实用工具包来开发很方便,但是会附带一个控制台的窗口,实在觉得有些low,因 ...

  9. OpenGL 学习笔记 01 环境配置

    以下教程仅适用于Mac下的Xcode编程环境!其他的我也不会搞. 推荐教程:opengl-tutorial  本项目Github网址       OpenGL太可怕了...必需得把学的记下来,不然绝壁 ...

随机推荐

  1. DotNetBar RibbonControl 控件动态添加项

    想做个插件式开发,界面用Dotnetbar的RibbonControl,需要通过代码动态的向RibbonControl控件添加项 示例代码如下: RibbonTabItem rti = new Rib ...

  2. 不用逗号进行UNION

    ./download.php ...if($link){    //加密后ID$link = base64_decode(urldecode($link));$link_array = explode ...

  3. .net 后台设置meta的属性(keywords,description)

    首先在前台aspx文件中的head标记添加runat="server"的属性. 之后后台如下编写: protected void Page_Load(object sender, ...

  4. Yii2 验证码不显示

    siteController 中,要将captcha 列为任意用户可以访问

  5. JavaWEB入门

    首先,我们已知的是Web资源分为:静态web资源(如html 页面):指web页面中供人们浏览的数据始终是不变.常用的静态web开发技术即为html:动态web 资源:指web页面中供人们浏览的数据是 ...

  6. POI设置边框

    在做一个电子表格时,边框的设置有时是必不可少的.这一节就来介绍边框,设置时,可以指定边框的位置,边框的种类,边框的顔色. 首先是边框的位置和种类.对单元格设置边框时,有上下左右位置之分,所以POI也准 ...

  7. Grand Theft Auto V 图形研究(2)

    原文链接 http://www.adriancourreges.com/blog/2015/11/02/gta-v-graphics-study-part-2/   Level of Detail 如 ...

  8. ecshop lang用法

    ecshop lang用法 分类: ECSHOP2013-08-15 16:17 2184人阅读 评论(0) 收藏 举报 ecshop目录下的languages目录.这个是ecshop语言包所在.ec ...

  9. Yii2目录结构

    assets   前端资源文件夹,大致用于管理css js等前端资源文件等 commands   包含命令行命令  文件为控制器文件 config 应用程序的配置文件 controllers 控制器文 ...

  10. 【IOS笔记】Windows

    Windows Every iOS application needs at least one window—an instance of the UIWindow class—and some m ...