Picking Tutorial
拾取教程
Processing the Hit Records
处理命中记录
In order to process the hit records the application must first return to the normal rendering mode. This is done calling glRenderMode with GL_RENDER. This function returns the number of hit records that were created during rendering in the selection mode. After this step the application can process the selection buffer. Note that before calling glRender with GL_RENDER there is no guarantee that the hit records have been saved into the selection buffer.
为了处理命中记录,应用程序必须首先返回绘制模式。通过调用glRenderMode,参数为GL_RENDER,来返回绘制模式。这个函数返回在选择模式绘制时命中记录产生的数量。在这一步之后,应用程序能够处理选择缓冲区。需要注意的是在使用参数GL_RENDER来调用glRenderMode之前,命中记录不确保会在选择缓冲区内。
Furthermore, it is necessary to restore the original projection matrix. Since this matrix was saved when entering the selection mode with glPushMatrix, all that is required is to pop the matrix. The following excerpt of code shows the required steps:
此外,也必须恢复原来的投影矩阵。由于当进入选择模式时使用glPushMatrix,原来的矩阵被保存起来了,所以只需要弹出这个矩阵就行了。这下的代码展示了所需要的步骤:
void stopPicking() {
int hits;
// restoring the original projection matrix
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glFlush();
// returning to normal rendering mode
hits = glRenderMode(GL_RENDER);
// if there are hits process them
if (hits != 0)
processHits(hits,selectBuf);
}
So the final issue is related to the selection buffer structure. The selection buffer stores the hit records sequentially by the order they occurred, i.e. by the order that the primitives we're drawn. Note that primitives that wouldn't be drawn due to Z buffer depth culling still produce hit records.
所以最后的问题是关于选择缓冲区的结构。选择缓冲区以命中记录产生的顺序来保存它们,例如,通过我们绘制图元的顺序。注意,即使图元由于Z深度剪裁而不会绘制的图元也产生命中记录。
The hit records are potentially variable size records due to the number of names they contain.
命中记录可能是一个可变大小记录,是因为它包含的名字的数量。
- The first field of the hit record is the number of names it contains.
- 命中记录的第一部分是它包含的名字的数量。
- The second and third fields represent the minimum and maximum depth of the hit. Note that only the vertices of the primitives that intersect the viewing volume are taken into account. For polygons that get clipped OpenGL reconstructs the vertices. So basically you get the maximum and minimum depths of the rendered segment of the primitives that intersects the viewing volume, not the minimum and maximum depths of the entire primitive. The depth is taken from the Z buffer (where it lies in the range [0,1]), it gets multiplied by 2^32 -1 and it is rounded to the nearest integer. Note that the depths you get are not linearly proportional to the distance to the viewpoint due to the nonlinear nature of the z buffer.
- 第二、三部分代表最大、最小命中深度。只有图元的顶点与视锥体相交才被记录。对于被剪裁的多边形,OpenGL重构了顶点。所以基本上,你得到的是与视锥体相交的绘制片断的最大、最小的深度,而不是整个图元的最大、最小深度值。深度值从Z缓冲区里取出来(在那里面,它的范围是0-1),然后乘以2^32-1,然后取最近的整数。注意你得到的深度值并不是线性的,它与viewport的距离成成正比,因为Z缓冲区也不是线性的。
- The sequence of names recorded for the hit. These names are the contents of the name stack when the hit was recorded. Note that since the number of names can be zero this sequence can be the empty sequence.
- 命中的名字序列也被记录。那些名字是当命中被记录时名字栈里的内容。因为名字数量可能为0,所以名字序列可能为空序列。
An example of a selection buffer with 3 hit records is presented next:
一个3个命中记录的选择缓冲区的例子:
Hit Record Contents | Description |
0 | No names have been stored for the first hit |
4.2822e+009 | Minimum depth for first hit |
4.28436e+009 | Maximum depth for first hit |
1 | Number of names for the second hit |
4.2732e+009 | Minimum depth for second hit |
4.27334e+009 | Maximum depth for second hit |
6 | A single name for the second hit |
2 | Number of names for the third hit |
4.27138e+009 | Minimum depth for third hit |
4.27155e+009 | Maximum depth for third hit |
2 | First name for third hit |
5 | Second name for third hit |
In order to detect which object was closest to the viewpoint you use the depth information. For instance you can select the object with the smalest minimum depth has the one which the user intended to click on. In the above example the relevant hit is the third one. The following function, adapted from the one in the Red Book, prints out the names for the closest object.
为了哪个对象离视口最近,你要使用深度信息。例如,你可以选择有最小深度值,也是用户试图去点击的对象。在上面的例子中,相关的命中是第三个。下面的函数来自于红宝书,它打印最近的对象的名字.
void processHits2 (GLint hits, GLuint buffer[])
{
unsigned int i, j;
GLuint names, *ptr, minZ,*ptrNames, numberOfNames;
printf ("hits = %d\n", hits);
ptr = (GLuint *) buffer;
minZ = 0xffffffff;
for (i = 0; i < hits; i++) {
names = *ptr;
ptr++;
if (*ptr < minZ) {
numberOfNames = names;
minZ = *ptr;
ptrNames = ptr+2;
}
ptr += names+2;
}
printf ("The closest hit names are ");
ptr = ptrNames;
for (j = 0; j < numberOfNames; j++,ptr++) {
printf ("%d ", *ptr);
}
printf ("\n");
}