[英]OpenGL 3D Picking
我阅读了许多有关opengl挑选的示例代码。 几乎所有人都使用gluPerspective函数进行投影。我使用glOrtho代替了gluPerspective函数。
我的选择函数如下(DrawBuffer是我的绘画代码):
void Selection( int x, int y )
{
GLuint buffer[512];
GLint hits;
GLint viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport);
glSelectBuffer(512, buffer);
(void)glRenderMode(GL_SELECT);
glInitNames();
glPushName(0);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
GLdouble w = (double)m_ClientRect.Width();
GLdouble h = (double)m_ClientRect.Height();
gluPickMatrix((GLdouble)x, (GLdouble)(viewport[3] - y), 500, 500, viewport);
glOrtho(-w / 2, w / 2, -h / 2, h / 2, -1000000.0, 100000.0);
glMatrixMode(GL_MODELVIEW);
DrawBuffer();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
hits = glRenderMode(GL_RENDER);
if (hits > 0)
{
TRACE(_T("%d %d %d %d %d\n"), hits, buffer[0], buffer[1], buffer[2], buffer[3]);
}
}
但这不起作用,我不知道原因吗? 另一个问题是:当我使用glDrawArrays函数绘制多条线时,如何调用glLoadName来标记每条线?
我的raytracer算法如下:
void CGraphicView::KDSearch( PICKING_VERTEX *root, CRay *pRay, PICKING_VERTEX **found, double *dCurSplit )
{
if (NULL == root)
{
return;
}
SearchNode(root, m_pRay, m_globaltMin, m_globaltMax, found, dCurSplit);
}
void CGraphicView::SearchNode( PICKING_VERTEX *node, CRay *pRay, double tmin, double tmax, PICKING_VERTEX **found, double *dCurSplit )
{
if (NULL == node)
{
return;
}
if (node->bLeaf)
{
SearchLeaf(node, pRay, tmin, tmax, found, dCurSplit);
}
else
{
SearchSplit(node, pRay, tmin, tmax, found, dCurSplit);
}
}
void CGraphicView::SearchSplit( PICKING_VERTEX *split, CRay *pRay, double tmin, double tmax, PICKING_VERTEX **found, double *dCurSplit )
{
if (NULL == split)
{
return;
}
int axis = split->axis;
double thit = pRay->GetSplit(axis, split->coor[axis]);
Point3D pSrc(split->coor[0], split->coor[1], split->coor[2]);
double scale = m_pCam->GetScale();
double disP2L = DistanceP2L(pSrc, m_RayStart, m_RayEnd);
if (disP2L * scale < MAX_DISTANCE && thit < *dCurSplit)
{
*found = split;
*dCurSplit = thit;
}
PICKING_VERTEX *first = NULL, *second = NULL;
if (IS_EQUAL_FLOAT(pRay->m_direction[axis], 0.0))
{
first = (pRay->m_origin[axis] < split->coor[axis]) ? split->left : split->right;
}
else
{
first = (pRay->m_direction[axis] > 0.0) ? split->left: split->right;
second = (pRay->m_direction[axis] < 0.0) ? split->left : split->right;
}
if ((thit >= tmax || thit < 0))
{
SearchNode(first, pRay, tmin, tmax, found, dCurSplit);
}
else if (thit <= tmin)
{
SearchNode(second, pRay, tmin, tmax, found, dCurSplit);
}
else
{
SearchNode(first, pRay, tmin, thit, found, dCurSplit);
}
}
void CGraphicView::SearchLeaf( PICKING_VERTEX *leaf, CRay *pRay, double tmin, double tmax, PICKING_VERTEX **found, double *dCurSplit )
{
if (NULL == leaf)
{
return;
}
int axis = leaf->axis;
double thit = pRay->GetSplit(axis, leaf->coor[axis]);
Point3D pSrc(leaf->coor[0], leaf->coor[1], leaf->coor[2]);
double scale = m_pCam->GetScale();
double disP2L = DistanceP2L(pSrc, m_RayStart, m_RayEnd);
if (disP2L * scale < MAX_DISTANCE && thit < *dCurSplit)
{
*found = leaf;
*dCurSplit = thit;
}
ContinueSearch(leaf, pRay, tmin, tmax, found, dCurSplit);
}
void CGraphicView::ContinueSearch( PICKING_VERTEX *leaf, CRay *pRay, double tmin, double tmax, PICKING_VERTEX **found, double *dCurSplit )
{
if (IS_EQUAL_FLOAT(tmax, m_globaltMax))
{
return;
}
else
{
tmin = tmax;
tmax = m_globaltMax;
SearchNode(m_root, pRay, tmin, tmax, found, dCurSplit);
}
}
当我使用glDrawArrays函数绘制多条线时,如何调用glLoadName来标记每条线?
你不能 坦率地说:首先您不应该使用OpenGL选择模式! 它很慢,没有当前的驱动程序支持(您将始终使用它来回到软件仿真模式),它不适用于着色器,并且使用起来很麻烦。
更好的替代方法是将选择光线投射到场景中,或者(如果使用现代OpenGL)使用边界框(或其他类型的边界体积)中应用的变换反馈缓冲区将几何体分类到屏幕空间Kd-tree中您可以在其中快速选择要单击的内容。
好了,从内存中调试命中== 0的一种方法是对普通渲染使用完全相同的拾取矩阵,或者在这种情况下,只需注释掉glRenderMode调用即可。 如果看不到任何内容,则场景的任何部分都不会与拾取区域相交,并且选择代码只是按照您的指示进行操作。
但是,datenwolf是正确的,您确实应该避免使用OpenGL选择模式。 这太糟糕了。
不需要光线投射或Kd树的实现拾取的一种相当简单的方法是用不同的颜色绘制每个对象。 假设每个对象都有一个唯一的标识符号(无论如何,glLoadName都需要),将其转换为3字节RGB颜色值。 仅将场景绘制到后台缓冲区中,然后读取鼠标坐标下的像素。 该RGB值将是最前面的对象的标识符。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.