C++_OpenGL选择模式理解.docx_第1页
C++_OpenGL选择模式理解.docx_第2页
C++_OpenGL选择模式理解.docx_第3页
C++_OpenGL选择模式理解.docx_第4页
C++_OpenGL选择模式理解.docx_第5页
已阅读5页,还剩9页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

在用OpenGL进行图形编程的时候,通常要用鼠标进行交互操作,比如用鼠标点选择画面中的物体,我们称之为拾取(Picking),在网上看了很多OpenGL拾取的文,但大多是只是介绍在OpenGL中如何拾取,如何利用OpenGL提供的一系列函数来完成拾取,最多再简单介绍下OpenGL的*名字栈(Name stack),拾取矩阵(PickingMatrix)*等等,但是拾取真正的原理确没有提到。所以,我在这里为大家详细介绍下OpenGL中拾取是怎样实现的,以及其背后的真正原理。OpenGL中的拾取是对OpenGL图形管线的一个应用。所以OpenGL中的拾取并不是像D3D一样采用射线交叉测试来判断是否选中一个目标,而是在*图形管线*的*投影变换(Projection Transformation)*阶段利用拾取矩阵来实现的。为了理解这个过程,先来复习一下OpenGL的图形管线。总的来说,OpenGL图形管线大体分为上面的五个阶段。在编程的时候使用*glMatrixMode(GL_MODELVIEW),或者 glMatrixMode(GL_PROJECTION)*就是告诉OpenGL我们是要在那个阶段进行操作。先来看看投影变换,因为理解投影变换是理解OpenGL拾取的前提条件。为了简单起见,这里以*正交投影(OrthogonalProjection)*为例。在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中检查一下是不是进行了这样的操作。添加下面的代码。 1. glMatrixMode(GL_PROJECTION); 2. glLoadIdentity(); 3. glOrtho(-10, 10, -10, 10, -10, 10); 4. 5. GLfloat m16; 6. glGetFloatv(GL_PROJECTION_MATRIX, m);glMatrixMode(GL_PROJECTION);glLoadIdentity();glOrtho(-10, 10, -10, 10, -10, 10);GLfloat m16;glGetFloatv(GL_PROJECTION_MATRIX, m);首先进入投影变换阶段,然后我们使用*glLoadIdentity()*在矩阵栈中存入单位矩阵。设置视体为边长为20的正方体。这里我们把*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, viewport4);*其中*x,y是鼠标点击到窗口上的坐标*,*width和height就是这个拾取框的长宽*,*viewport是为了得到我们窗口的大小*。一但调用了该函数,OpenGL就会创建一个拾取矩阵,分解这个矩阵的话,可以看到,这个矩阵就是上面的移动拾取框到原点,然后再放大为视体大小这两个步骤。所以即使我们不使用这个函数,也可以自己计算出这个拾取矩阵.同样,我们还是在OpenGL中检查一下,是不是做了这样的操作。在OpenGL中添加下面的代码。1. void SelectObject(GLint x, GLint y) 2. 3. GLuint selectBuff32=0;/创建一个保存选择结果的数组 4. GLint hits, viewport4; 5. 6. glGetIntegerv(GL_VIEWPORT, viewport); /获得viewport 7. glSelectBuffer(64, selectBuff); /告诉OpenGL初始化 selectbuffer 8. glRenderMode(GL_SELECT); /进入选择模式 9.10. glInitNames(); /初始化名字栈11. glPushName(0); /在名字栈中放入一个初始化名字,这里为012.13. glMatrixMode(GL_PROJECTION); /进入投影阶段准备拾取14. glPushMatrix(); /保存以前的投影矩阵15. glLoadIdentity(); /载入单位矩阵16.17. float m16;18. glGetFloatv(GL_PROJECTION_MATRIX, m);19.20. gluPickMatrix( x, / 设定我们选择框的大小,建立拾取矩 阵,就是上面的公式 viewport3-y, / viewport3保存的是窗口的 高度,窗口坐标转换为OpenGL坐标21. 2,2, / 选择框的大小为2,222. viewport / 视口信息,包括视口的起始位置和大小23. );24.25. glGetFloatv(GL_PROJECTION_MATRIX, m);26. glOrtho(-10, 10, -10, 10, -10, 10); /拾取矩阵乘以投影矩阵,这 样就可以让选择框放大为和视体一样大27. glGetFloatv(GL_PROJECTION_MATRIX, m);28.29. draw(GL_SELECT); / 该函数中渲染物体,并且给物体设定名字30.31. glMatrixMode(GL_PROJECTION);32. glPopMatrix(); / 返回正常的投影变换33. glGetFloatv(GL_PROJECTION_MATRIX, m);34. hits = glRenderMode(GL_RENDER); / 从选择模式返回正常模式,该函 数返回选择到对象的个数35. if(hits 0)36. processSelect(selectBuff); / 选择结果处理37. 38.39. void draw(GLenum model=GL_RENDER)40. 41. if(model=GL_SELECT)42. 43. glColor3f(1.0,0.0,0.0);44. glLoadName(100);45. glPushMatrix();46. glTranslatef(-5, 0.0, 10.0);47. glBegin(GL_QUADS);48. glVertex3f(-1, -1, 0);49. glVertex3f( 1, -1, 0);50. glVertex3f( 1, 1, 0);51. glVertex3f(-1, 1, 0);52. glEnd();53. glPopMatrix();54.55. glColor3f(0.0,0.0,1.0);56. glLoadName(101);57. glPushMatrix();58. glTranslatef(5, 0.0, -10.0);59. glBegin(GL_QUADS);60. glVertex3f(-1, -1, 0);61. glVertex3f( 1, -1, 0);62. glVertex3f( 1, 1, 0);63. glVertex3f(-1, 1, 0);64. glEnd();65. glPopMatrix();66. 67. else68. 69. glColor3f(1.0,0.0,0.0);70. glPushMatrix();71. glTranslatef(-5, 0.0, -5.0);72. glBegin(GL_QUADS);73. glVertex3f(-1, -1, 0);74. glVertex3f( 1, -1, 0);75. glVertex3f( 1, 1, 0);76. glVertex3f(-1, 1, 0);77. glEnd();78. glPopMatrix();79.80. glColor3f(0.0,0.0,1.0);81. glPushMatrix();82. glTranslatef(5, 0.0, -10.0);83. glBegin(GL_QUADS);84. glVertex3f(-1, -1, 0);85. glVertex3f( 1, -1, 0);86. glVertex3f( 1, 1, 0);87. glVertex3f(-1, 1, 0);88. glEnd();89. glPopMatrix();90. 91. void SelectObject(GLint x, GLint y)GLuint selectBuff32=0;/创建一个保存选择结果的数组GLint hits, viewport4;glGetIntegerv(GL_VIEWPORT, viewport); /获得viewportglSelectBuffer(64, selectBuff); /告诉OpenGL初始化 selectbufferglRenderMode(GL_SELECT); /进入选择模式glInitNames(); /初始化名字栈glPushName(0); /在名字栈中放入一个初始化名字,这里为0glMatrixMode(GL_PROJECTION); /进入投影阶段准备拾取glPushMatrix(); /保存以前的投影矩阵glLoadIdentity(); /载入单位矩阵float m16;glGetFloatv(GL_PROJECTION_MATRIX, m);gluPickMatrix( x, / 设定我们选择框的大小,建立拾取矩阵,就是上面的公式 viewport3-y, / viewport3保存的是窗口的高度,窗口坐标转换为OpenGL坐标 2,2, / 选择框的大小为2,2 viewport / 视口信息,包括视口的起始位置和大小); glGetFloatv(GL_PROJECTION_MATRIX, m);glOrtho(-10, 10, -10, 10, -10, 10); /拾取矩阵乘以投影矩阵,这样就可以让选择框放大为和视体一样大 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); / 选择结果处理 void draw(GLenum model=GL_RENDER) if(model=GL_SELECT) glColor3f(1.0,0.0,0.0); glLoadName(100); glPushMatrix(); glTranslatef(-5, 0.0, 10.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(101); glPushMatrix(); glTranslatef(5, 0.0, -10.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, -10.0); glBegin(GL_QUADS); glVertex3f(-1, -1, 0); glVertex3f( 1, -1, 0); glVertex3f( 1, 1, 0); glVertex3f(-1, 1, 0); glEnd(); glPopMatrix(); 然后设点断点来检查一下。上面看到我们的视口的起始位置就是窗口的原点,大小和窗口的大小一样,500500。然后在*gluPickMatrix( x, viewport3-y, 2,2, viewport)*函数调用后,会马上建立一个拾取矩阵,根据提供的参数,在屏幕上点击的坐标为 x=136,y=261,这是屏幕坐标,转换成openGL坐标为x=136,y=500-261=239。拾取框的另一个坐标就是138,242,因为这个给的长,宽都是2。现在就得到了拾取框的2个坐标了, ,然后把这2个坐标再转换为-1到1之间的坐标得到把这个坐标带入拾取矩阵中计算,可以得到然后,在程序中设置断点,获取当前操作矩阵检查一下。从上面可以看到,和我们计算的结果是一样的。在设置了拾取矩阵后,又调用

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论