《OGL dev》Etay Meiri Tutorial 29 - 3D Picking 笔记
3D拾取指获取屏幕上点击的像素对应的图元的技术。
3D拾取的原理是在正常渲染pass之前,插入一个将图元信息渲染到picking texture的各个通道中的pass。之后,点击屏幕时,从picking texture中读取到点击的像素对应的图元信息。
渲染到picking texture时,必须启用深度测试,当像素重叠时丢弃离摄影机较远的像素。以保证鼠标点击,读取picking texture时,获取的是离屏幕(摄影机)最近的图元。
场景中对象的索引,绘制一个对象内部调用draw call的索引,draw call内部图元的索引,三者唯一确定一个图元。
GS和FS的内置变量gl_PrimitiveID存储draw call内部图元的索引。如果存在GS,在GS中可以直接使用gl_PrimitiveID,在FS只能接收从GS输出的全局变量以获取gl_PrimitiveID的值。如果不存在GS,在FS中可以直接使用gl_PrimitiveID。gl_PrimitiveID中图元索引,从0开始编号。
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, WindowWidth, WindowHeight, 0, GL_RGB, GL_FLOAT, NULL);
- GL_RGB32F是纹理内部格式,表示每个texel有三个浮点通道,对应的数据源格式为GL_RGB和GL_FLOAT。虽然数据源为NULL,但纹理要被写入,数据源格式是必须正确填写的。
PickingTexture::PixelInfo PickingTexture::ReadPixel(unsigned int x, unsigned int y)
{
glBindFramebuffer(GL_READ_FRAMEBUFFER, m_fbo);
glReadBuffer(GL_COLOR_ATTACHMENT0);
PixelInfo Pixel;
glReadPixels(x, y, 1, 1, GL_RGB, GL_FLOAT, &Pixel);
glReadBuffer(GL_NONE);
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
return Pixel;
}
glBindFramebuffer(GL_READ_FRAMEBUFFER, m_fbo);
,将framebuffer m_fbo绑定到读取。glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
,将framebuffer重置为默认framebuffer。glReadBuffer(GL_COLOR_ATTACHMENT0);
,表示从索引为0的color buffer读取,因为framebuffer支持包含多个color buffer,一次draw call,FS可以同时渲染到多个color buffer。虽然可以一次渲染到多个color buffer,但一次只能从一个color buffer读取。glReadBuffer(GL_NONE);
,禁用从framebuffer读取。glReadPixels(x, y, 1, 1, GL_RGB, GL_FLOAT, &Pixel);
,从buffer中读取一个矩形区域的像素信息,返回到最后一个参数Pixel的地址中。Pixel的类型是由倒数第二和第三个参数确定的,这里使用GL_RGB和GL_FLOAT获取原始数据,如果使用其他参数,函数会在输出时转换到参数确定的类型。第一和第二个参数确定矩阵区域的左下角,之后两个参数是长宽,这里使用1和1表示只获取一个像素。
static void MouseCB(int Button, int State, int x, int y)
{
s_pCallbacks->MouseCB(Button, State, x, y);
}
static void InitCallbacks()
{
...
glutMouseFunc(MouseCB);
}
- glutMouseFunc()获取鼠标点击事件。
- MouseCB()的第一参数Button为GLUT_LEFT_BUTTON、 GLUT_MIDDLE_BUTTON和GLUT_RIGHT_BUTTON其中之一,表示哪个按键被按下。第二个参数State为GLUT_DOWN或GLUT_UP,表示按下或抬起。
虽然OpenGL默认是禁用混合的,但在某些没有明确禁用混合的平台上,需要调用glDisable(GL_BLEND);
显式禁用混合。
glutReshapeFunc()获取窗口大小变化。当窗口大小变化时需要重新设定picking texture的宽高,才能保证3D拾取正确。