选择与反馈

章节目标

读完此章之后,你将能够做到:
建立允许用户选择(select)屏幕区域或拾取(pick)绘制在屏幕上的物体的应用程序
利用OpenGL的反馈(feedback)模式获取绚染计算结果

有些图形应用程序只绘制两维和三维物体构成的静态图形,另一些允许用户识别屏幕上的物体并移动、修改、删除或用其它方法操纵这些物体。OpenGL正是设计用于支持这些交互式应用程序的。因为绘制在屏幕上的物体通常经过多次旋转、移动和透视变换,所以确定用户选中了三维场景中的哪个物体会很困难。为了帮助你,OpenGL提供了一个选取机制可惟自动告诉你哪个物体被绘制在窗口的提定区域里。你可以用这个机制与一个工具例程(a special utility routine)一道决定哪个物体在用户说明或用光标选取的区域里。

选择(selection)实际上是OpenGL的一个操作模式;反馈(feedback)是这类模式中的别一个。在反馈模式中,你用你的图形硬件和OpenGL完成通常的绚染计算。但与用这个计算结果去在屏幕上绘制图形相反,OpenGL返回(或反馈(feeds back))这些绘制信息给你。如果你想在绘图仪而不是屏幕上绘制图形,举个例子,你就得在反馈模式绘制它们,收集绘制指令,然后将这些指令转换为绘图仪可以理解的命令。

在选择和反馈模式中,绘制信息返回给应用程序而不是象在绚染模式中那样送往帧缓冲。因此,当OpenGL处于选择或反馈模式时,屏幕将被冻结-没有图形出现。这一章将会在各自的节中解释这些模式:

“选择(Selection)” 讨论怎样使用选择模式和相关的例程以使你程序的用户能拾取画在屏幕上的物体。

“反馈(Feedback)” 描述了怎样获取有关什么将被画在屏幕上的信息和这些信息是以什么格式组织的。

---------------------------------------------------
Section

通常,当你打算使用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、退出选择模式并处理返回的选择数据(命中记录)。

后面的段落将描述glSelectBuffer()和glRenderMode()。下一节则讲解名字栈的控制。

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。

-------------------------------------------------------------------------------
一个选择的例子

在Example 12-2中,4个三角形(绿、红、两个黄)在选择模式下绘制,相应的命中记录被处理。第一个三角形生成一个命中,第二个不,第三第四个共同生成一个命中。定义了两个例程:绘制三角形(drawTriangle()),绘制一个表示视见体的线框的盒子。processHits()例程打印出选择数组。最后,selectObjects()在选择模式下绘制三角形以生成命中记录。

/*****************************************************************************/
Example 12-2 : A Selection Example: select.c

#include <GL/gl.h>
#include <GL/glu.h>
#include "aux.h"

void drawTriangle (GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2, GLfloat x3, GLfloat y3, GLfloat z)
{
    glBegin (GL_TRIANGLES);
    glVertex3f (x1, y1, z);
    glVertex3f (x2, y2, z);
    glVertex3f (x3, y3, z);
    glEnd ();
}

void drawViewVolume (GLfloat x1, GLfloat x2, GLfloat y1, GLfloat y2, GLfloat z1, GLfloat z2)
{
    glColor3f (1.0, 1.0, 1.0);
    glBegin (GL_LINE_LOOP);
    glVertex3f (x1, y1, -z1);
    glVertex3f (x2, y1, -z1);
    glVertex3f (x2, y2, -z1);
    glVertex3f (x1, y2, -z1);
    glEnd ();

    glBegin (GL_LINE_LOOP);
    glVertex3f (x1, y1, -z2);
    glVertex3f (x2, y1, -z2);
    glVertex3f (x2, y2, -z2);
    glVertex3f (x1, y2, -z2);
    glEnd ();

    glBegin (GL_LINES); /* 4 lines */
    glVertex3f (x1, y1, -z1);
    glVertex3f (x1, y1, -z2);
    glVertex3f (x1, y2, -z1);
    glVertex3f (x1, y2, -z2);
    glVertex3f (x2, y1, -z1);
    glVertex3f (x2, y1, -z2);
    glVertex3f (x2, y2, -z1);
    glVertex3f (x2, y2, -z2);
    glEnd ();
}

void drawScene (void)
{
    glMatrixMode (GL_PROJECTION);
    glLoadIdentity ();
    gluPerspective (40.0, 4.0/3.0, 0.01, 100.0);

    glMatrixMode (GL_MODELVIEW);
    glLoadIdentity ();
    gluLookAt (7.5, 7.5, 12.5, 2.5, 2.5, -5.0, 0.0, 1.0, 0.0);
    glColor3f (0.0, 1.0, 0.0); /* green triangle */
    drawTriangle (2.0, 2.0, 3.0, 2.0, 2.5, 3.0, -5.0);
    glColor3f (1.0, 0.0, 0.0); /* red triangle */
    drawTriangle (2.0, 7.0, 3.0, 7.0, 2.5, 8.0, -5.0);
    glColor3f (1.0, 1.0, 0.0); /* yellow triangles */
    drawTriangle (2.0, 2.0, 3.0, 2.0, 2.5, 3.0, 0.0);
    drawTriangle (2.0, 2.0, 3.0, 2.0, 2.5, 3.0, -10.0);
    drawViewVolume (0.0, 5.0, 0.0, 5.0, 0.0, 10.0);
}

void processHits (GLint hits, GLuint buffer[])
{
    unsigned int i, j;
    GLuint names, *ptr;

    printf ("hits = %d\n", hits);
    ptr = (GLuint *) buffer;
    for (i = 0; i < hits; i++) { /* for each hit */
        names = *ptr;
        printf(" number of names for hit = %d\n", names); ptr++;
        printf (" z1 is %u;", *ptr); ptr++;
        printf (" z2 is %u\n", *ptr); ptr++;
        printf (" the name is ");
        for (j = 0; j < names; j++) { /* for each name */
            printf ("%d ", *ptr); ptr++;
        }
        printf ("\n");
    }
}

#define BUFSIZE 512

void selectObjects(void)
{
    GLuint selectBuf[BUFSIZE];
    GLint hits, viewport[4];

    glSelectBuffer (BUFSIZE, selectBuf);
    (void) glRenderMode (GL_SELECT);

    glInitNames();
    glPushName(-1);

    glPushMatrix ();
    glMatrixMode (GL_PROJECTION);
    glLoadIdentity ();
    glOrtho (0.0, 5.0, 0.0, 5.0, 0.0, 10.0);
    glMatrixMode (GL_MODELVIEW);
    glLoadIdentity ();
    glLoadName(1);
    drawTriangle (2.0, 2.0, 3.0, 2.0, 2.5, 3.0, -5.0);
    glLoadName(2);
    drawTriangle (2.0, 7.0, 3.0, 7.0, 2.5, 8.0, -5.0);
    glLoadName(3);
    drawTriangle (2.0, 2.0, 3.0, 2.0, 2.5, 3.0, 0.0);
    drawTriangle (2.0, 2.0, 3.0, 2.0, 2.5, 3.0, -10.0);
    glPopMatrix ();
    glFlush ();

    hits = glRenderMode (GL_RENDER);
    processHits (hits, selectBuf);
}

void myinit (void)
{
    glDepthFunc(GL_LEQUAL);
    glEnable(GL_DEPTH_TEST);
    glShadeModel(GL_FLAT);
}

void display(void)
{
    glClearColor (0.0, 0.0, 0.0, 0.0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    drawScene ();
    selectObjects ();
    glFlush();
}


int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_RGBA | AUX_DEPTH);
    auxInitPosition (0, 0, 200, 200);
    auxInitWindow (argv[0]);
    myinit ();
    auxMainLoop(display);
}
/************************************************************************************/


-------------------------------------------------------------------------------
拾取

作为前一节所描述的处理过程的一个扩展,你可以用选择模式确定物体是否被拾取了。要做到这一点,用一个与投影矩阵相关联的拾取矩阵将绘制限制在视口的一个很小的区域里,通常是在光标附近。然后允许某种形式的输入,如点击鼠标的一个键,引发选择模式的初始化。建立起选择模式并使用特定的拾取矩阵,绘制在光标附近的物体引发选择命中。即,通常在拾取的时候确定哪个物体被画在光标附近了。

拾取的建立几乎与正规的选择模式完全一样,只有以下的主要区别:

拾取通常由某个输入设备触发。在下面的例子中,按鼠标左键运行实现拾取的函数。
用例程gluPickMatrix()将一个特殊的投影矩阵乘到当前矩阵上。它要在把投影矩阵乘到栈上之前调用。

另外,实现拾取的完全不同的方法在"Object Selection Using the Back Buffer"里有所描述。这一技术使用颜色值标识物体的不同部件。

void gluPickMatrix(GLdouble x, GLdouble y, GLdouble width, GLdouble height, GLint viewport[4]);
建立一个投影矩阵用于将绘制限制在视口的一个小区域里,并将这个矩阵乘到当前矩阵栈上。拾取区域的中心是窗口坐标(x,y)处,通常是光标位置。width和height定义选取区域大小,用屏幕坐标。(你可以认为宽和高是设备第三的)。viewport[]表明当前视口边界,这可以用调用glGetIntegerv(GL_VIEWPORT,GLint *viewport);获得。

深入

gluPickMatrix()建立的矩阵的净结果(net result)是将裁剪区域变换到单位立方体-1&1e;(x,y,z)&1e;1(或-w&1e;(wx,wy,wz)&1e;w)。拾取矩阵实际上提供了一个正交变换将这个单位立方体的一个子区域映射到单位立方体。因为变换是任意的,你可以让拾取为不同类型的领域工作-如,用于旋转窗口的矩形区域。在某些情况下,你会发觉用附加的裁剪平面定义拾取区域要容易些。

Example 12-3演示了一个简单的选取。它也演示了怎样用多个名字标识同一图元的不同部件,在这里行和列是一个被选物体。一个3X3矩阵被画出来,每个广场有不同的颜色。数组board[3][3]维护每个块的当前点击总数。当前鼠标左键被按下,pickSquares()例子被调用来识别哪个方块被鼠标拾取了。在网格中两个名字标识一个方块-一个表示行、一个表示列。当鼠标左键被按下,光标位置下的所有方块的颜色也会改变。

/*******************************************************************/
Example 12-3 : A Picking Example: picksquare.c

#include <GL/gl.h>
#include <GL/glu.h>
#include "aux.h"

int board[3][3]; /* amount of color for each square */

/* Clear color value for every square on the board */
void myinit(void)
{
    int i, j;
    for (i = 0; i < 3; i++)
        for (j = 0; j < 3; j ++)
            board[i][j] = 0;
    glClearColor (0.0, 0.0, 0.0, 0.0);
}

void drawSquares(GLenum mode)
{
    GLuint i, j;
    for (i = 0; i < 3; i++) {
        if (mode == GL_SELECT)
            glLoadName (i);
        for (j = 0; j < 3; j ++) {
            if (mode == GL_SELECT)
                glPushName (j);
            glColor3f ((GLfloat) i/3.0, (GLfloat) j/3.0,
            (GLfloat) board[i][j]/3.0);
            glRecti (i, j, i+1, j+1);
            if (mode == GL_SELECT)
                glPopName ();
        }
    }
}

void processHits (GLint hits, GLuint buffer[])
{
    unsigned int i, j;
    GLuint ii, jj, names, *ptr;

    printf ("hits = %d\n", hits);
    ptr = (GLuint *) buffer;
    for (i = 0; i < hits; i++) { /* for each hit */
        names = *ptr;
        printf (" number of names for this hit = %d\n", names);
        ptr++;
        printf (" z1 is %u;", *ptr); ptr++;
        printf (" z2 is %u\n", *ptr); ptr++;
        printf (" names are ");
        for (j = 0; j < names; j++) { /* for each name */
            printf ("%d ", *ptr);
            if (j == 0) /* set row and column */
                ii = *ptr;
            else if (j == 1)
                jj = *ptr;
            ptr++;
        }
        printf ("\n");
        board[ii][jj] = (board[ii][jj] + 1) % 3;
    }
}

#define BUFSIZE 512

void pickSquares(AUX_EVENTREC *event)
{
    GLuint selectBuf[BUFSIZE];
    GLint hits;
    GLint viewport[4];
    int x, y;

    x = event->data[AUX_MOUSEX];
    y = event->data[AUX_MOUSEY];
    glGetIntegerv (GL_VIEWPORT, viewport);

    glSelectBuffer (BUFSIZE, selectBuf);
    (void) glRenderMode (GL_SELECT);

    glInitNames();
    glPushName(-1);

    glMatrixMode (GL_PROJECTION);
    glPushMatrix ();
    glLoadIdentity ();
    /* create 5x5 pixel picking region near cursor location */
    gluPickMatrix((GLdouble) x,
    (GLdouble) (viewport[3] - y), 5.0, 5.0, viewport);
    gluOrtho2D (0.0, 3.0, 0.0, 3.0);
    drawSquares (GL_SELECT);
    glPopMatrix ();
    glFlush ();

    hits = glRenderMode (GL_RENDER);
    processHits (hits, selectBuf);
}

void display(void)
{
    glClear(GL_COLOR_BUFFER_BIT);
    drawSquares (GL_RENDER);
    glFlush();
}

void myReshape(GLsizei w, GLsizei h)
{
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D (0.0, 3.0, 0.0, 3.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}


int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_RGBA);
    auxInitPosition (0, 0, 100, 100);
    auxInitWindow (argv[0]);
    myinit ();
    auxMouseFunc (AUX_LEFTBUTTON, AUX_MOUSEDOWN, pickSquares);
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
}

Picking with Multiple Names and a Hierarchical Model

Multiple names can also be used to choose parts of a hierarchical object in a scene. For example, if you were rendering an
assembly line of automobiles, you might want the user to move the mouse to pick the third bolt on the left front tire of the third
car in line. A different name can be used to identify each level of hierarchy: which car, which tire, and finally which bolt. As
another example, one name can be used to describe a single molecule among other molecules, and additional names can
differentiate individual atoms within that molecule.

Example 12-4 is a modification of Example 3-4 that draws an automobile with four identical wheels, each of which has five
identical bolts. Code has been added to manipulate the name stack with the object hierarchy.

Example 12-4 : Creating Multiple Names

draw_wheel_and_bolts()
{
    long i;

    draw_wheel_body();
    for (i = 0; i < 5; i++) {
        glPushMatrix();
        glRotate(72.0*i, 0.0, 0.0, 1.0);
        glTranslatef(3.0, 0.0, 0.0);
        glPushName(i);
        draw_bolt_body();
        glPopName();
        glPopMatrix();
    }
}

draw_body_and_wheel_and_bolts()
{
    draw_car_body();
    glPushMatrix();
    glTranslate(40, 0, 20); /* first wheel position*/
    glPushName(1); /* name of wheel number 1 */
    draw_wheel_and_bolts();
    glPopName();
    glPopMatrix();
    glPushMatrix();
    glTranslate(40, 0, -20); /* second wheel position */
    glPushName(2); /* name of wheel number 2 */
    draw_wheel_and_bolts();
    glPopName();
    glPopMatrix();

    /* draw last two wheels similarly */
}

Example 12-5 uses the routines in Example 12-4 to draw three different cars, numbered 1, 2, and 3.

Example 12-5 : Using Multiple Names

draw_three_cars()
{
    glInitNames();
    glPushMatrix();
    translate_to_first_car_position();
    glPushName(1);
    draw_body_and_wheel_and_bolts();
    glPopName();
    glPopMatrix();

    glPushMatrix();
    translate_to_second_car_position();
    glPushName(2);
    draw_body_and_wheel_and_bolts();
    glPopName();
    glPopMatrix();

    glPushMatrix();
    translate_to_third_car_position();
    glPushName(3);
    draw_body_and_wheel_and_bolts();
    glPopName();
    glPopMatrix();
}


Assuming that picking is performed, the following are some possible name-stack return values and their interpretations. In
these examples, at most one hit record is returned; also, d1 and d2 are depth values.

empty The pick was outside all cars

2 d1d2 2 1 Car 2, wheel 1

1 d1d2 3 Car 3 body

3 d1d2 1 1 0 Bolt 0 on wheel 1 on car 1

The last interpretation assumes that the bolt and wheel don't occupy the same picking region. A user might well pick both the
wheel and the bolt, yielding two hits. If you receive multiple hits, you have to decide which hit to process, perhaps by using the
depth values to determine which picked object is closest to the viewpoint. The use of depth values is explored further in the
next section.

Picking and Depth Values

Example 12-6 demonstrates how to use depth values when picking to determine which object is picked. This program draws
three overlapping rectangles in normal rendering mode. When the left mouse button is pressed, the pickRects() routine is
called. This routine returns the cursor position, enters selection mode, initializes the name stack, and multiplies the picking
matrix onto the stack before the orthographic projection matrix. A selection hit occurs for each rectangle the cursor is over
when the left mouse button is clicked. Finally, the contents of the selection buffer is examined to identify which named objects
were within the picking region near the cursor.

The rectangles in this program are drawn at different depth, or z, values. Since only one name is used to identify all three
rectangles, only one hit can be recorded. However, if more than one rectangle is picked, that single hit has different minimum
and maximum z values.

Example 12-6 : Picking with Depth Values: pickdepth.c

#include <GL/gl.h>
#include <GL/glu.h>
#include "aux.h"

void myinit(void)
{
    glClearColor (0.0, 0.0, 0.0, 0.0);
    glDepthFunc(GL_LEQUAL);
    glEnable(GL_DEPTH_TEST);
    glShadeModel(GL_FLAT);
    glDepthRange (0.0, 1.0); /* The default z mapping */
}

void drawRects(GLenum mode)
{
    if (mode == GL_SELECT)
        glLoadName (1);
    glBegin (GL_QUADS);
    glColor3f (1.0, 1.0, 0.0);
    glVertex3i (2, 0, 0);
    glVertex3i (2, 6, 0);
    glVertex3i (6, 6, 0);
    glVertex3i (6, 0, 0);
    glColor3f (0.0, 1.0, 1.0);
    glVertex3i (3, 2, -1);
    glVertex3i (3, 8, -1);
    glVertex3i (8, 8, -1);
    glVertex3i (8, 2, -1);
    glColor3f (1.0, 0.0, 1.0);
    glVertex3i (0, 2, -2);
    glVertex3i (0, 7, -2);
    glVertex3i (5, 7, -2);
    glVertex3i (5, 2, -2);
    glEnd ();
}

void processHits (GLint hits, GLuint buffer[])
{
    unsigned int i, j;
    GLuint names, *ptr;

    printf ("hits = %d\n", hits);
    ptr = (GLuint *) buffer;
    for (i = 0; i < hits; i++) { /* for each hit */
        names = *ptr;
        printf (" number of names for hit = %d\n", names);
        ptr++;
        printf (" z1 is %u;", *ptr); ptr++;
        printf (" z2 is %u\n", *ptr); ptr++;
        printf (" the name is ");
        for (j = 0; j < names; j++) { /* for each name */
            printf ("%d ", *ptr); ptr++;
        }
        printf ("\n");
    }
}

#define BUFSIZE 512

void pickRects(AUX_EVENTREC *event)
{
    GLuint selectBuf[BUFSIZE];
    GLint hits;
    GLint viewport[4];
    int x, y;

    x = event->data[AUX_MOUSEX];
    y = event->data[AUX_MOUSEY];
    glGetIntegerv (GL_VIEWPORT, viewport);

    glSelectBuffer (BUFSIZE, selectBuf);
    (void) glRenderMode (GL_SELECT);

    glInitNames();
    glPushName(-1);

    glMatrixMode (GL_PROJECTION);
    glPushMatrix ();
    glLoadIdentity ();
    /* create 5x5 pixel picking region near cursor location */
    gluPickMatrix ((GLdouble) x,
    (GLdouble) (viewport[3] - y), 5.0, 5.0, viewport);
    glOrtho (0.0, 8.0, 0.0, 8.0, 0.0, 2.0);
    drawRects (GL_SELECT);
    glPopMatrix ();
    glFlush ();

    hits = glRenderMode (GL_RENDER);
    processHits (hits, selectBuf);
}

void display(void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    drawRects (GL_RENDER);
    glFlush();
}

void myReshape(GLsizei w, GLsizei h)
{
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho (0.0, 8.0, 0.0, 8.0, 0.0, 2.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}


int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_RGBA | AUX_DEPTH);
    auxInitPosition (0, 0, 100, 100);
    auxInitWindow (argv[0]);
    myinit ();
    auxMouseFunc (AUX_LEFTBUTTON, AUX_MOUSEDOWN, pickRects);
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
}


Try This

Try This

Modify Example 12-6 to add additional calls to glPushName() so that multiple names are on the stack when the
selection hit occurs. What will the contents of the selection buffer be?

By default, glDepthRange() sets the mapping of the z values to [0.0,1.0]. Try modifying the glDepthRange() values
and see how it affects the z values that are returned in the selection array.
-------------------------------------------------------------------------------
关于编写使用选择的程序的提示

posted on 2007-08-15 11:49 zmj 阅读(1329) 评论(3)  编辑 收藏 引用

评论

# re: 选择与反馈 2007-08-17 17:02 andyliu

嗯,很好,谢谢博主,现在在学OpenGL,刚入门,会经常来看看的,希望能发现更多的好东东!!

ps:你那篇《Z坐标、深度缓存和透视投影》给我同学看了下,得到很高的赞扬哦,学到了不少东西!!   回复  更多评论   

# re: 选择与反馈 2007-10-03 00:12 linben

开源gis中文镜像--我要开源

http://www.51kaiyuan.net/index.php?controller=BoDashboard&action=Search3&classId=212  回复  更多评论   

# re: 选择与反馈 2007-10-13 20:53 flyman

楼主精神可嘉,鼓励鼓励。  回复  更多评论   


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理