OpenGL选择机制

      通常,当你打算使用OpenGL的选择机制时,你首先把你的场景画进帧缓冲,然后进入选择模式并重新绘制这个场景。然而,一旦你进入了选择模式,帧缓冲的内容将保存不变,直到你退出选择模式。当你退出时,OpenGL返回一个图元(premitives)清单,图元可能被视见体(viewing volume)分割(记住,视见体是由当前模式视见和投影矩阵及你定义的所有裁剪面定义,裁剪面详见"Additional Clipping Planes.")。每个被视见体图元引出一资选择命中(hit)。确切的说,图元清单是作为一个取整数值的名字(integer-valued names)数组和相关的数据-命中记录(hit record)-对应名字栈(name stack)的当前内容。当你在选择模式下发布图元绘制命令时向名字栈中加入名字就可建立起名字栈。这样,当名字清单被返回后,你就可以用它来确定屏幕上的哪个图元可能被用户选中了。
      除了这个选择机制之外,OpenGL提供了一个工具例程,以便在某些情况下通过限定在视口(viewport)一个小区域内绘制来简化选择。通常你可以用这个例程决定哪个物体被画在光标附近了,这样你就能识别用户拾取了哪个物体。你也可以通过指定附加的裁剪面来界定一个选择区域;详见"Additional Clipping Planes"。因为拾取是选择的一个特殊情况,所以本章选讲选择,然后讲拾取。

 

   基本步骤
 建立名字矩阵
 命中记录
 一个选择的例子
 拾取
 关于编写使用选择的程序的提示

 

------------------------------------------------------------------------
  基本步骤
  
  为使用选择机制,你得作以下几步:
  1、用glSelectBuffer()指定用于返回命中记录的数组。
  2、以GL_SELECT为参数调用glRenderMode()进入选择模式。
  3、用glInitName()和glPushName()初始化名字栈。
  4、定义用于选择的视见体。通常它与你原来用于绘制场景的视见体不同。因此你或许会想用glPushMatrix()和glPopMatrix()来保存和恢复当前的变换矩阵。
  5、交替发布图元绘制命令和名字栈控制命令,这样每个感兴趣的图元都会被指定适当的名字。
  6、退出选择模式并处理返回的选择数据(命中记录)。

 

void glSelectBuffer(GLsizei size, GLuint *buffer);
  指定用于返回选择数据的数组。参数buffer是指向无符号整数(unsigned integer)数组的指针,数据就存在这个数组中,size参数说明数组中最多能够保存的值的个数。要在进入选择模式之前调用glSelectBuffer()!
  
  GLint glRenderMode(GLenum mode);
  控制应用程序是否进入绚染(rendering)、选择或反馈模式。mode参数可以是GL_RENDER(默认)、GL_SELECT或GL_FEEDBACK之一。应用程序将保持处于给定模式,直到再次以不同的参数调用glRenderMode()。在进入选择模式之前必须调用glSelectBuffer()指定选择数组。类似的,进入反馈模式之前要调用glFeedbackBuffer()指定反馈数组。如果当前模式是GL_SELECT或GL_FEEDBACK之一,那么glRenderMode()的返回值有意义。返回值是当前退出当前模式时,选择命中数或放在反馈数组中的值的个数。(译者注:调用此函数就会退出当前模式);负值意味着选择或反馈数组溢出(overflowed)。你可以用GL_RENDER_MODE调用glGetIntegerv()获取当前模式。
-------------------------------------------------------------------------------
  建立名字矩阵
  
  正如前面提到的,名字栈是返回给你的选择信息的基础。要建立名字栈,首先用glInitNames()初始化它,这将简单地清空栈。然后当你发布相应的绘制命令时向其中加入整数名字。正如你可能想象,栈控制命令允许你压入名字(glPushName()),弹出名字(glPopName()),替换栈顶的名字(glLoadName())。
  /********************************************************************/
  Example 12-1: Creating a Name Stack
  glInitNames();
  glPushName(-1);
  
  glPushMatrix(); /* save the current transformation state */
  
  /*to do: create your desired viewing volume here */
  
  glLoadName(1);
  drawSomeObject();
  glLoadName(2);
  drawAnotherObject();
  glLoadName(3);
  drawYetAnotherObject();
  drawJustOneMoreObject();
  
  glPopMatrix (); /* restore the previous transformation state*/
  /********************************************************************/
  
  在这个例子中,前两个被绘制的物体有自己的名字,第三和第四个共用一个名字。这样,如果第三或第四个物体中的一个或全部引起一个选择命中,只有一个命中记录返回给你。如果处理命中记录时不想区分各个物体的话,可以让多个物体共享一个名字。
  
  void glInitNames(void);
  清空名字栈。
  
  void glPushName(GLuint name);
  将name压入名字栈。压入名字超过栈容量时将生成一个GL_STACK_OVERFLOW错误。名字栈深度因OpenGL实现(implementations)不同而不同,但最少要能容纳64个名字。你可以用参数GL_NAME_STACK_DEPTH调用glGetIntegerv()以获取名字栈深度。
  
  void glPopName(void);
  弹出名字栈栈顶的那一个名字。从空栈中弹出名字引发GL_STACK_UNDERFLOW错误。
  
  void glLoadName(GLuint name);
  用name取代名字栈栈顶的那个名字。如果栈是空的,刚调用过glInitName()后就是这样,glLoadName()生成一个GL_INVALID_OPRATION错。为避免这种情况,如果栈初始时是空的,那么在调用glLoadName()之前至少调用一次glPushName()以在名字栈中放上点东西。
  
  如果不是在选择模式下,对glPushName()、glPopName()、glLoadName()的调用将被忽略。这使得在选择模式和正常的绚染模式下用相同的绘制代码大为简化。
  
  -------------------------------------------------------------------------------
  命中记录
  
  在选择模式下,被视见体裁剪的每个图元引起一个选择命中。当前一个名字栈控制命令被执行或glRenderMode()被调用后,OpenGL将一个命中记录写进选择数组,如果从上一次名字栈操纵或glRenderMode()调用以来有了一个命中记录的话。这个过程中,共用同样名字的物体-例如:由多个图元组成的物体-不生成多个命中记录。当然,命中记录不保证会被写进数组中直到glRenderMode()被调用。
  
  除图元之外,glRasterPos()产生的有效坐标也可以引起选择命中。在多边形的情况下,如果它已经被消隐掉的话不会有命中记录出现。
  
  每个命中记录由四项组成,依次是:
  当命中出现时名字栈中的名字数
  至上次记录的命中以来,被视见体裁剪后的图元的所有顶点的窗口Z坐标的 最大和最小值
  本次命中时名字栈的内容,最底元素最前。
  
  当前你进入选择模式时,OpenGL初始化一个指针指向选择数组的起点。每写入一个命中记录,指针相应更新。如果写入一个命中记录会使数组中值的个数超过glSelectBuffer()的size参数时,OpenGL会写入尽可能多的记录并设置一个溢出标志。当用glRenderMode()退出选择模式时,这条命令返回被写入的记录的个数(包括一条部分记录如果有的话),清除名字栈,复位溢出标识,重置栈指针。如设定溢了出标识则返回值是-1。

你可能感兴趣的:(OpenGL)