三维建模不仅要向屏幕绘制图像,而且还要允许标识屏幕上的物体,然后移动,修改和操纵这些物体,有时甚至还要获取物体的图像数据,以便在绘图仪或打印机上输出图像。为此,OpenGL提供选择,拾取和反馈机制。
由于绘制的屏幕上的物体通常经过了旋转,平移,缩放和透视变换,因此要确定哪一个物体被操作者选中,是一件非常困难的事情。OpenGL提供的选择机制能自动地告知用户,在一个指定的区域中,哪些物体被选中。拾取是选择的一种特例。
反馈是另一种绘图模式。在这种模式下,OpenGL使用通常的绘图算法,但不将绘图结果送往屏幕,而是以某种格式将绘图信息反馈给应用程序,使应用程序做进一步的处理。
选择,反馈,绘制时操作中相互独立的模式。其中绘制操作是标准,是光栅化产生片断过程中的默认模式。在选择和反馈模式下,没有片断产生,因此,没有针对帧缓冲区的数据修改。在选择模式下,可以决定在窗口的一些区域中将写入哪个基本图元。在反馈模式下,可以将光栅化了的基本图元的信息反馈到应用程序中。
选择:使用选择机制时,首页以一般绘图模式将场景中的物体绘入帧缓冲区,然后进入选择模式,定义一个选择体并重新绘制物体。一旦进入选择模式,帧缓冲区的内容就不会受绘图命令的影响。退出选择模式时,OpenGL返回一个图元序列,这些图元在选择体重活与选择体相交。每一个选择体重活与选择体相交的物体都是一个被选中机制命中的物体。这个图元序列实际上是一个由整数值命名的相关数据组成的矢量,称为命中记录,这个命中记录与当前名字堆栈有关。在选择模式下,在发送绘图命令的同时,将相应的命中压入名子堆栈。这样当命中记录返回时,就能用名字堆栈中的内容来确定屏幕上的哪些物体被选中。
选择模式的操作步骤:
1:在使用选择模式之前,首页调用glSelectBuffer()函数指定存放返回命中记录的数组。
void glSelectBuffer(GLsizei size, GLint *buffer);
功能:为选择模式产生的值建立一个缓冲区。
2:调用glRenderModel(GL_SELECT)函数进入选择模式。
GLint glRenderMode(GLenum mode);
功能:设置光栅化模式。
mode的取值:
GL_RENDER绘图模式。在这种模式中,图元被光栅化产生像素片断,然后这些片断被写入帧缓冲区中。这是一种常用模式,也是默认模式。
GL_SELECT选择模式。在这种模式中,图元被光栅化产生像素片断,也不改变帧缓冲区中的内容。然而,如果绘图模式时GL_RENDER,要绘制的一个图元的名称记录将被返回到一个选择缓冲区中。这样,在进入选择模式之前,首先应该创建该缓冲区。
GL_FEEDBACK反馈模式。在这种模式中,不产生像素片断,也不改变帧缓冲区中的内容。然而,如果绘图模式时GL_RENDER,要绘制的顶点的坐标和属性将被返回到一个反馈缓冲区中。这时,在进入选择模式之前。首先应该创建该缓冲区。
函数glRenderMode()的返回值是有调用该函数使得绘图模式而不是由参数mode来决定的。3种绘图模式的返回值如下:
GL_RENDER 0
GL_SELECT 传送至选择缓存的命中记录数目
GL_FEEDBACK 传送至反馈缓冲区的值的个数。
3:调用glInitNames()函数初始化命中堆栈
void glInitNames(void);
功能:初始化名子堆栈
在选择模式中,名字堆栈是区别绘图命令集中地命令的唯一标志。它由一个有序的无符号整数集所构成。该函数的作用时初始化命中堆栈,即将它初始化成其默认的空堆栈。
4:调用glPushName()函数将物体的名称压入名字堆栈中
void PushName(GLuint name);
功能:压入命中堆栈。
5:定义选择体。一般情况下,选择体与场景的观察体有所不同,可以用函数glPushMatrix()和glPopMatrix()来保存和恢复观察体。
6:交替发送绘图命令和名字堆栈操作命令,使每一个图元都被赋予一个适当的整数名称。
7:调用glRenderMode(GL_RENDER)函数退出选择模式重新回到一般绘图模式,处理命中记录数组中的数据。
名字堆栈:名字堆栈是构成选择信息的基础。为了建立名字堆栈,首先调用glInitNames()函数清空名字堆栈,然后在发送绘图命令时,向名字堆栈增加名字。名字堆栈操作函数允许向堆栈中亚人一个名字(glPushName()),从堆栈中弹出一个名字(glPopName()),或者用一个不同的名字替换位于栈顶的名字(glLoadName())
如果当前绘图模式不是选择模式,glPushName()glPopName()和glLoadName()将被忽略。
当压入名字栈的名称超过容量时,将产生错误GL_STACK_UNDERFLOW。名字栈深度的容量在不同的系统中可能有所不同,但不管在什么系统中都至少包含64个名称。调用glGetIntegerv()函数,将参数设置为GL_NAME_STACK_DEPTH,可获取当前系统的名字栈深度。
void glLoadName(GLuint name);
功能:装入一个名字到名字堆栈中。
当调用glInitNames()函数后名字堆栈清空,如果此时立即调用glLoadName(),则会产生GL_INVALID_OPERATION错误。为了防止发生这种错误,当名字堆栈初始清空时,紧跟着调用一次glPushName()以使名字堆栈不空。
命中记录:在选择模式中,与选择体相交的物体都会产生选择命中。只要执行名字堆栈操作命令,或者从调用glRenderMode()函数后最近时间以来选中一个记录,则OpenGL将命中记录写入到选择数组中。利用这个过程,共用同一个名称的对象,不产生多重名字记录。另外,命中记录要等到调用glRenderMode()后才真正写到数组中。
拾取:拾取是选中处理的一种扩充,利用选择模式可确定是否拾取对象。拾取对象利用一个特殊的拾取矩阵和投影矩阵,通常将绘图限制在光标附近的视口的小区域中。然后,允许某种形式的输入,例如单击鼠标,初始化选择模式等。利用建立的选择模式及特殊的拾取矩阵,使在光标附近所绘制的对象发生选择命中。因此,的那个拾取时,通常确定在光标附近绘制哪些对象。
设置拾取几乎与常规选择模式一样,主要有以下两点不同:
1:拾取操作通常有一个输入设备触发。
2:使用实用库函数gluPickMatrix()将一个特殊的投影矩阵与当前矩阵相乘
void gluPickMatrix(GLdouble x, GLdouble y,GLdouble delX,GLdouble delY,GLint *viewport);
功能:定义一个拾取区域。