位置: 编程技术 - 正文

深入理解OpenGL拾取模式(OpenGL Picking)(opengl learning)

编辑:rootadmin

推荐整理分享深入理解OpenGL拾取模式(OpenGL Picking)(opengl learning),希望有所帮助,仅作参考,欢迎阅读内容。

文章相关热门搜索词:opengl learn,opengl实验,opengl从入门到精通,opengl learn,opengl拾取,opengl详解,深入理解opengl webgl电子版,opengl拾取,内容如对您有帮助,希望把文章链接给更多的朋友!

深入理解OpenGL拾取模式(OpenGL Picking)

转自 在用OpenGL进行图形编程的时候,通常要用鼠标进行交互操作,比如用鼠标点选择画面中的物体,我们称之为拾取(Picking),在网上看了很多OpenGL拾取的文章,但大多是只是介绍在OpenGL中如何拾取,如何利用OpenGL提供的一系列函数来完成拾取,最多再简单介绍下OpenGL的名字栈(Name stack),拾取矩阵(Picking Matrix)等等,但是拾取真正的原理确没有提到。所以,我在这里为大家详细介绍下OpenGL中拾取是怎样实现的,以及其背后的真正原理。

OpenGL中的拾取是对OpenGL图形管线的一个应用。所以OpenGL中的拾取并不是像D3D一样采用射线交叉测试来判断是否选中一个目标,而是在图形管线的投影变换(Projection Transformation)阶段利用拾取矩阵来实现的。为了理解这个过程,先来复习一下OpenGL的图形管线。

总的来说,OpenGL图形管线大体分为上面的五个阶段。在编程的时候使用glMatrixMode(GL_MODELVIEW),或者 glMatrixMode(GL_PROJECTION)就是告诉OpenGL我们是要在那个阶段进行操作。先来看看投影变换,因为理解投影变换是理解OpenGL拾取的前提条件。为了简单起见,这里以正交投影(Orthogonal Projection)为例。在OpenGL中,使用正交投影可以调用glOrtho (left, right, bottom, top, zNear, zFar),其中的六个参数分别对应正交投影视体的六个平面到观察坐标系原点的距离。一旦在程序中调用了这个函数,OpenGL会马上创建根据给定的六个参数创建一个视体,并且把视体的大小归一化到-1到1之间,也就是说,OpenGL会自动把你给的参数所对应的x,y,z&#;转换为-1到1之间的&#;,并且这个视体的中心就是观察坐标系的原点。要注意的是,当视体归一化后,z轴的方向要反向,也就是说,这里OpenGL的右手坐标系要换成左手坐标系。原因很简单,z轴朝向显示器里的方向更符合我们的常识,越向里就离我们越远,z的&#;也就越大。

当我们调用了glOrtho()这个函数后,OpenGL会建立一个矩阵,也就是投影矩阵。这个矩阵可以分解为三个步骤,首先将我们设置的视体移动到观察坐标是的原点,然后在缩放为边长为2的单位视体。因为转化后的视体坐标都在-1和1之间,所以视体的边长就是2。然后再对z进行反方向。最后的投影矩阵我们用来表示的话,那么有

上面的矩阵虽然看起来很复杂,其实很简单。它就是进行移动,缩放,反号三个操作而已。现在我们在OpenGL中检查一下是不是进行了这样的操作。添加下面的代码。

[cpp] view plaincopyglMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(-, , -, , -, ); GLfloat m[]; glGetFloatv(GL_PROJECTION_MATRIX, m);

首先进入投影变换阶段,然后我们使用glLoadIdentity()在矩阵栈中存入单位矩阵。设置视体为边长为的正方体。这里我们把glortho中的6个参数带入上面公式计算,得到的结果为

代码中我们使用了glGetFloatv来获得当前操作矩阵,然后设定断点来检查一下。

可以看到,得到的数据和我们计算的一样。说明OpenGL的确是创建了这样的矩阵来进行计算。

弄清楚了OpenGL中的投影变换,现在就开看看大家关心的拾取操作。OpenGL的拾取就是利用投影变换中归一化视体这个操作来实现的。拾取的时候,我们可以想象想用一个方框来选择我们要选择的物体。比如一个边长为2的正方形,我们用鼠标在窗口上点击的时候,一旦点到一个位置,那么就在这个位置生成一个边长为2的正方形,那么正方形内包围的物体就是我们要选择的物体,如果这个正方形内没有包围任何东西,那么就说明什么都没有选择到。这个过程就和我们归一化投影,然后再剪裁的过程是一样的。OpenGL会自动剪裁掉在归一化视体之外的物体,那么如果我们把选择物体用的方框转换为用投影时的视体,那么在这个方框外的东西,也就是我们没有选择的东西,OpenGL会自动的为我们扔掉。所以OpenGL提供了选择模式glRenderMode(GL_SELECT),当我们进行拾取前进入这个模式,然后设定好我们的选择框的大小,再为我们要选择的物体设定好名字,也就是我们说的名字栈。接下来的操作和投影变换就一样了,先把这个选择框移归一化为边长为-1到1的正方体,然后移动到原点,最后放大为我们窗口的大小。(这时OpenGL已经把在选择框外的东西剪裁掉了,如果这个时候我们显示投影矩阵中的内容的话,就会只看到我们选择到的东西,并且放大和窗口一样大。)然后OpenGL会把选中的物体信息记录在一个叫做SelectBuffer的缓冲中,这个缓冲就是个一维数组,里面保存了名字栈中名字的个数,选择到的物体的最小最大深度&#;,也就是z的&#;,这个&#;是个0到1之间的&#;,也就是里我们最近的为0,最远的为1。selectBuffer是个整型的数组,所以这里保存的深度&#;是乘以0xFFFFFFFF后的&#;。当然最重要的,其中还保存了我们选择到的物体的名字,这样只要在程序中判断选择到物体的名字,我们就可以判断是不是选择到了要选择的物体了。整个拾取的过程可如下。

上图中左边的正方体是我们归一化的视体,拾取的时候就是在这个空间中拾取的。红色的小框是我们的选择框。里面的红色就是我们选择到的物体的一部分。现在要做的就是把这个小框转变为视体,这样OpenGL才能为我们把不要的东西扔掉。所以,首先还是把这个小框移动到观察坐标系的原点,然后再放大为我们归一化视体的大小,这样整个视体中就只有我们选中的东西了,上图中间显示了这个过程。视体外的东西已经被OpenGL剪裁掉,选中的记录会保存到selectbuffer中。因为这些操作是在选择模式下完成的,所以看不到我们选择的过程,但是如果我们把选择的过程显示出来的话,就会看到上图右边的样子。整个窗口就铺满了我们选择的部分。在OpenGL中,提供了这个设置拾取框的函数。

gluPickMatrix (x, y, width, height, viewport[4]);

其中x,y是鼠标点击到窗口上的坐标,width和height就是这个拾取框的长宽,viewport是为了得到我们窗口的大小。一但调用了该函数,OpenGL就会创建一个拾取矩阵,分解这个矩阵的话,可以看到,这个矩阵就是上面的移动拾取框到原点,然后再放大为视体大小这两个步骤。所以即使我们不使用这个函数,也可以自己计算出这个拾取矩阵

同样,我们还是在OpenGL中检查一下,是不是做了这样的操作。在OpenGL中添加下面的代码。

深入理解OpenGL拾取模式(OpenGL Picking)(opengl learning)

[cpp] view plaincopyvoid SelectObject(GLint x, GLint y) { GLuint selectBuff[]={0};//创建一个保存选择结果的数组 GLint hits, viewport[4]; glGetIntegerv(GL_VIEWPORT, viewport); //获得viewport glSelectBuffer(, selectBuff); //告诉OpenGL初始化selectbuffer glRenderMode(GL_SELECT); //进入选择模式 glInitNames(); //初始化名字栈 glPushName(0); //在名字栈中放入一个初始化名字,这里为‘0’ glMatrixMode(GL_PROJECTION); //进入投影阶段准备拾取 glPushMatrix(); //保存以前的投影矩阵 glLoadIdentity(); //载入单位矩阵 float m[]; glGetFloatv(GL_PROJECTION_MATRIX, m); gluPickMatrix( x, // 设定我们选择框的大小,建立拾取矩阵,就是上面的公式 viewport[3]-y, // viewport[3]保存的是窗口的高度,窗口坐标转换为OpenGL坐标 2,2, // 选择框的大小为2,2 viewport // 视口信息,包括视口的起始位置和大小 ); glGetFloatv(GL_PROJECTION_MATRIX, m); glOrtho(-, , -, , -, ); //拾取矩阵乘以投影矩阵,这样就可以让选择框放大为和视体一样大 glGetFloatv(GL_PROJECTION_MATRIX, m); draw(GL_SELECT); // 该函数中渲染物体,并且给物体设定名字 glMatrixMode(GL_PROJECTION); glPopMatrix(); // 返回正常的投影变换 glGetFloatv(GL_PROJECTION_MATRIX, m); hits = glRenderMode(GL_RENDER); // 从选择模式返回正常模式,该函数返回选择到对象的个数 if(hits > 0) processSelect(selectBuff); // 选择结果处理 }

[cpp] view plaincopyvoid draw(GLenum model=GL_RENDER) { if(model==GL_SELECT) { glColor3f(1.0,0.0,0.0); glLoadName(); glPushMatrix(); glTranslatef(-5, 0.0, .0); glBegin(GL_QUADS); glVertex3f(-1, -1, 0); glVertex3f( 1, -1, 0); glVertex3f( 1, 1, 0); glVertex3f(-1, 1, 0); glEnd(); glPopMatrix(); glColor3f(0.0,0.0,1.0); glLoadName(); glPushMatrix(); glTranslatef(5, 0.0, -.0); glBegin(GL_QUADS); glVertex3f(-1, -1, 0); glVertex3f( 1, -1, 0); glVertex3f( 1, 1, 0); glVertex3f(-1, 1, 0); glEnd(); glPopMatrix(); } else { glColor3f(1.0,0.0,0.0); glPushMatrix(); glTranslatef(-5, 0.0, -5.0); glBegin(GL_QUADS); glVertex3f(-1, -1, 0); glVertex3f( 1, -1, 0); glVertex3f( 1, 1, 0); glVertex3f(-1, 1, 0); glEnd(); glPopMatrix(); glColor3f(0.0,0.0,1.0); glPushMatrix(); glTranslatef(5, 0.0, -.0); glBegin(GL_QUADS); glVertex3f(-1, -1, 0); glVertex3f( 1, -1, 0); glVertex3f( 1, 1, 0); glVertex3f(-1, 1, 0); glEnd(); glPopMatrix(); } }

然后设点断点来检查一下。

上面看到我们的视口的起始位置就是窗口的原点,大小和窗口的大小一样,×。然后在gluPickMatrix( x, viewport[3]-y, 2,2, viewport )函数调用后,会马上建立一个拾取矩阵,根据提供的参数,在屏幕上点击的坐标为 x=,y=,这是屏幕坐标,转换成openGL坐标为x=,y=-=。拾取框的另一个坐标就是,,因为这个给的长,宽都是2。现在就得到了拾取框的2个坐标了,,然后把这2个坐标再转换为-1到1之间的坐标得到

把这个坐标带入拾取矩阵中计算,可以得到

然后,在程序中设置断点,获取当前操作矩阵检查一下。

从上面可以看到,和我们计算的结果是一样的。在设置了拾取矩阵后,又调用了语句glOrtho(-, , -, , -, ),这表示让这个拾取框的大小铺满我们整个窗口,OpenGL会把刚才得到的拾取矩阵再乘以这个投影矩阵,我们可以得到

再在程序中设置断点来检查一下。

的确和我们计算的结果一样。然后代码中使用了glPopMatrix(),由于我们的拾取操作已经完成,到里位置,拾取的结果信息已经被OpenGL保存到了selectbuffer中了,所以这时我们就要不在需要这个拾取矩阵,由于之前使用了glPushMatrix()保存了原来的投影矩阵,现在只要glPopMatrix()就可以了。glPopMatrix()后,我们可以再设置断点看一下是不是真的返回到原来的投影矩阵。

同样我们可以从上图中看到,的确在PopMatrix后,返回了原来的投影变换,所以现在从选择模式返回到正常模式的时候我们就可以看到正常的画面。

上图中可以看到红色正方形中有个白色的小方框,这个就是在设置拾取矩阵时的拾取框,拾取框在红色正方形内部,说明我们已经选中了红色的正方形了。如果这个时候把选择框中的东西显示出来的话,就可以看到我们选择到的部分铺满整个屏幕。

在前面的代码中,我们已经为红色正方形命名为,蓝色的为,现在我们来看看selectbuffer里被选中的物体。

可以看到数组的前4个部分有&#;,第一个表示被选中物体的个数,当然现在是1。第二个表示和第三个表示物体的最小深度&#;和最大深度&#;,由于物体是个平面,所以这两个&#;是一样的。这里的深度&#;是整数,除以0xffffffff以后就得到了0到1之间的深度&#;了。第四个&#;,也就是我们选择到的物体的名字。这里就是红色的正方形。

OpenGL的整个拾取过程就是这样的。它是利用了图形管线中投影变换阶段来实现拾取操作的。对于OpenGL图形管线不了解的朋友可能对这种方法会感到困难。但是一旦理解了图形管线后,再来理解拾取就很容易了。

*原创内容,转载请注明出处*

如何使用Android中的OpenGL ES媒体效果 Android的媒体效果框架允许开发者可以很容易的应用多种令人印象深刻的视觉效果到照片或视频之上。作为这个媒体效果的框架,它使用GPU来处理图片处

Nehe第六章纹理映射 纹理映射的时候,使用了AUX_RGBImageRec结构体和auxDIBImageLoad函数,这两个是位于glaux头文件中的,glaux是gl的辅助类,并不是opengl中自带的,所以系统中可

OpenGL 矩阵变换 Overview几何数据——顶点位置,和标准向量(normalvectors),在OpenGL管道raterization处理过程之前可通过顶点操作(VertexOperation)和基本组合操作改变这些数据。Obj

标签: opengl learning

本文链接地址:https://www.jiuchutong.com/biancheng/369458.html 转载请保留说明!

上一篇:opengl光照 Blinn-Phone模型与实现(opengl光照效果)

下一篇:如何使用Android中的OpenGL ES媒体效果(如何使用ai软件)

  • 银行电子回单有没有法律效应
  • 项目税后现金流量的计算方法
  • 税负率的计算公式举例说明
  • 旅客运输服务解释
  • 无票收入在增值税申报表的哪里填
  • 月初没报税可以领发票吗
  • 服务业的费用都有哪些
  • 快递费未支付应该寄走了吗
  • 汇算清缴中研发费用中其他费用可抵扣的比例是多少
  • 厂家给经销商的搭赠政策
  • 票据行为为什么没有付款
  • 产品质量赔款的账务处理
  • 风险溢价包括哪些违约风险溢价 流动性风险溢价
  • 专用发票包括增值税吗
  • 合同印花税怎么交
  • 增值税普通电子发票
  • 2020金税四期上线
  • 外籍人员来华工作
  • 提高主营业务收入的意义
  • 写支票人民币大写有角需要写整吗
  • 吸收合并税费
  • 经营性租入的固定资产需要计提折旧吗
  • 收到投资者投入设备一台,价值100000元
  • 疏通下水道入什么经络
  • 给别人的返利是属于什么费用
  • 公司注销时账面处理
  • 社保会计分录怎么写
  • php 签名算法
  • ffmpeg安装教程linux
  • 实例简介php的一般过程
  • 建筑施工企业是落实施工现场带班制度的第一责任人
  • 收到对方公司开的发票怎么做账
  • 递延所得税当期要交吗
  • macOS Big Sur 11.2 RC 3(版本号20D64)预览版正式发布(附更新内容)
  • linux网络接口状态命令
  • 劳务报酬所得与经营所得
  • 图神经网络gat
  • php jsondecode
  • 建筑业挂靠工程会计与税务处理怎么做?
  • vue开发环境配置
  • 【K210】K210学习笔记七——使用K210拍摄照片并在MaixHub上进行训练
  • 小规模纳税人增值税月末处理
  • 固定资产清理的会计科目处理
  • 社保可以不计提直接缴纳吗
  • 零余额账户什么时候开始停用
  • 更新php文件
  • mongodb doc
  • mongodb快速入门
  • 房地产公司计提税金
  • 认缴制和实缴制的时间
  • 未发货先开票是否违法
  • 购买监控器计入什么科目
  • 企业长期资金来源有哪些
  • 公司买的茶叶怎么入账
  • 现金流量表和其他报表的勾稽关系
  • 财务预付账款情况说明
  • 委托生产的会计分录
  • 员工出差车票属于哪个科目
  • 如何处理公益性岗位问题
  • 出口不退税进项要转出吗
  • 慈善捐款流程
  • 税控盘开发票怎么测试打印机?
  • 企业试生产时间规定
  • 超市会员能便宜多少
  • 平行结转分步法例题
  • mysql中注释
  • centos bz
  • ubuntu命令行中文乱码怎么解决
  • steam怎么sl
  • mac命令行窗口怎么打开
  • win7 设置
  • linux ii
  • 安卓listview控件map的用法前面每行加图片
  • js引用传递
  • python多线程爬虫代码
  • 基层税务所工作现状
  • 电子税务局怎么添加开票员
  • 土地增值税可否免税
  • 地税税额怎么计算
  • 审计如何配合教师工作
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

    网站地图: 企业信息 工商信息 财税知识 网络常识 编程技术

    友情链接: 武汉网站建设