参考文章:https://www.cnblogs.com/icmzn/p/7373663.html
本来想着可以像Unity一样用射线去拾取物体,但是找资料的时候发现这么一句话:OpenGL中的拾取是对OpenGL图形管线的一个应用。OpenGL中的拾取并不是像D3D一样采用射线交叉测试来判断是否选中一个目标,而是在图形管线的投影变换(Projection Transformation)阶段利用拾取矩阵来实现的。
就是说,OpenGL拾取物体的流程大概是这样的:
初始化projection矩阵
鼠标在窗口上点击,获取到鼠标的位置,在该位置产生一个拾取矩阵赋值给projection矩阵:
gluPickMatrix(x, viewport[3] - y, 2, 2, viewport);
x,y为鼠标位置;
viewport[3] - y:由于OpenGL的原点是左上角,和鼠标相反要减掉鼠标坐标。
GLint viewport[4];视口信息;
glGetIntegerv(GL_VIEWPORT, viewport);//获取视口信息
2,2: 拾取窗口的大小;
再通过 glOrtho(-10, 10, -10, 10, -camera->farDist, 0); (注意:z的方向是反的)创建一个投影矩阵;
设置一下物体的渲染模式glRenderMode(GL_SELECT);
创建select缓存 GLuint selectBuff[32] = { 0 }; glSelectBuffer(64, selectBuff);
再设置好物体的name(id);
加渲染物体的代码就可以拾取物体了。
Object* MyWidget::PickObj(GLint x,GLint y)
{
QMatrix4x4 tempMat;
GLuint selectBuff[32] = { 0 };
GLint hits, viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport);
glSelectBuffer(64, selectBuff);
glRenderMode(GL_SELECT);
glInitNames();
glPushName(0);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
float m[16];
glGetFloatv(GL_PROJECTION_MATRIX, m);
gluPickMatrix(x, viewport[3] - y, 2, 2, viewport);
glGetFloatv(GL_PROJECTION_MATRIX, m);
glOrtho(-10, 10, -10, 10, -camera->farDist, 0);
glGetFloatv(GL_PROJECTION_MATRIX, m);
for (int i = 0; i < sizeof(m) / sizeof(m[0]); i++)
{
tempMat.data()[i] = m[i];
}
for (int i = 0; i < objList.size(); i++)
{
objList[i]->OnSelectMode();//物体切换至Select模式 glRenderMode(GL_SELECT)
objList[i]->Render(f, tempMat, camera->GetViewMatrix());//渲染物体以拾取
}
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glGetFloatv(GL_PROJECTION_MATRIX, m);
hits = glRenderMode(GL_RENDER);//获取拾取物体的数量
qDebug() << "hits:" <OnRenderMode();//物体切换至Render模式 glRenderMode(GL_RENDER)
}
if (hits > 0)
{
qDebug() << "id: " << selectBuff[3];//最后结果存在缓存的第四个元素中
for (int i = 0; i < objList.size(); i++)
{
if (selectBuff[3] == objList[i]->id)
return objList[i];
}
}
else
return nullptr;
}
渲染物体的代码里加上
if (mode == GL_SELECT)
{
glLoadName(id);
}
必须加在glDrawArrays之前!
获取到物体的实例之后再对物体的model矩阵进行操作就可以实现拖拽物体的功能了。