Opengl 从窗口转换的深度分量->;世界坐标

Opengl 从窗口转换的深度分量->;世界坐标,opengl,opengl-3,Opengl,Opengl 3,我正在开发一个程序,它绘制一个100x100的网格,允许用户点击一个单元格并改变颜色 但是,仅当查看屏幕上的网格面(即camPos.z等于camLook.z)以及网格位于屏幕中心时,单击当前也有效 在过去的几天里,我遇到的问题是,当从不同的摄像机位置或屏幕上的不同区域查看网格时,选择正确的单元格 我唯一的猜测是,不知何故,深度缓冲区并不反映相机的当前位置,或者缓冲区深度范围与相机的近距离和远距离值之间存在一些不一致。或者,我应用投影/视图矩阵的方式可以显示图像,但在返回管道时出现了一些问题。但我

我正在开发一个程序,它绘制一个100x100的网格,允许用户点击一个单元格并改变颜色

但是,仅当查看屏幕上的网格面(即
camPos.z
等于
camLook.z
)以及网格位于屏幕中心时,单击当前也有效

在过去的几天里,我遇到的问题是,当从不同的摄像机位置或屏幕上的不同区域查看网格时,选择正确的单元格

我唯一的猜测是,不知何故,深度缓冲区并不反映相机的当前位置,或者缓冲区深度范围与相机的近距离和远距离值之间存在一些不一致。或者,我应用投影/视图矩阵的方式可以显示图像,但在返回管道时出现了一些问题。但我不太明白

(自最初发布以来代码更新/重构)

顶点着色器:

#版本330
vec4位置的布局(位置=0);
使颜色平滑;
vec4颜色均匀;
均匀mat4-pv;
void main(){
gl_位置=pv*位置;
颜色=颜色;
}
摄像机等级(投影视图矩阵()的结果是上述的
pv
标准):

Camera::Camera()
{
camPos=glm::vec3(1.0f、5.0f、2.0f);
camLook=glm::vec3(1.0f、0.0f、0.0f);
fovy=90.0f;
纵横比=1.0f;
近=0.1f;
far=1000.0f;
}
glm::mat4摄影机::projectionMatrix()
{
返回glm::透视图(fovy、aspect、near、far);
}
glm::mat4摄像头::viewMatrix()
{
返回glm::lookAt(
坎波斯,
camLook,
glm::vec3(0.0f、1.0f、0.0f)
);
}
glm::mat4摄影机::projectionViewMatrix()
{
返回projectionMatrix()*viewMatrix();
}
//视图控件
无效摄影机::向前移动()
{
坎波斯z-=1.0f;
camLook.z-=1.0f;
}
void Camera::moveBack()
{
camPos.z+=1.0f;
camLook.z+=1.0f;
}
无效摄影机::moveLeft()
{
坎波斯x-=1.0f;
camLook.x-=1.0f;
}
无效摄影机::moveRight()
{
camPos.x+=1.0f;
camLook.x+=1.0f;
}
无效摄影机::zoomIn()
{
坎波斯y-=1.0f;
}
无效摄影机::zoomOut()
{
坎波斯y+=1.0f;
}
void Camera::lookDown()
{
camLook.z+=0.1f;
}
void摄影机::lookAtAngle()
{
如果(camLook.z!=0.0f)
camLook.z-=0.1f;
}
我试图获取世界坐标的camera类中的特定函数(
x
y
是屏幕坐标):

glm::vec3摄像机::实验(intx,inty)
{
闪烁视口[4];
glGetIntegerv(GL_视口,视口);
glz;
glReadPixels(x、y、1、1、GL_深度分量、GL_浮点和winZ);
printf(“深度:%f\n”,winZ);
glm::vec3 pos=glm::取消项目(
glm::vec3(x,视口[3]-y,winZ),
viewMatrix(),
projectionMatrix(),
glm::vec4(0.0f,0.0f,视口[2],视口[3])
);
printf(“位置:(%f,%f,%f)\n”,位置x,位置y,位置z);
返回pos;
}
初始化和显示:

void init(void)
{
glewExperimental=GL_TRUE;
glewInit();
glEnable(GLU深度试验);
glDepthMask(GL_TRUE);
glDepthFunc(GL_LESS);
gldepsrange(0.0f,1.0f);
初始化程序();
初始化evao();
InitializeGrid();
glEnable(GL_CULL_面);
正面(背面);
GL正面(GL_CW);
}
作废显示(作废)
{
glClearColor(0.0f、0.0f、0.0f、0.0f);
glClear(GL_颜色_缓冲_位| GL_深度_缓冲_位);
glUseProgram(程序);
glBindVertexArray(vao);
glUniformMatrix4fv(projectionViewMatrixUnif,1,GL_FALSE,glm::value_ptr(camera.projectionViewMatrix());
DrawGrid();
glBindVertexArray(0);
glUseProgram(0);
glutSwapBuffers();
再发现();
}
int main(int argc,字符**argv)
{
glutInit(&argc,argv);
glutInitDisplayMode(GLUT_RGB | GLUT_深度);
glutInitContextVersion(3,2);
glutInitContextProfile(GLUT_核心_配置文件);
glutInitWindowSize(500500);
位置(300200);
测试窗口(“测试”);
init();
glutDisplayFunc(显示器);
GLUTREFORUNC(重塑);
键盘Func(键盘);
glutMouseFunc(小鼠);
glutMainLoop();
返回0;
}

实际上,在光标下投影光线以实现拾取非常简单。它将始终适用于几乎任何投影和modelview矩阵(除了一些将整个场景转换为无穷大的无效奇异情况,等等)

为了简单起见,我编写了一个小演示,它使用了不推荐使用的固定函数管道,但代码也可以与着色器一起使用。首先从OpenGL读取矩阵:

glm::mat4 proj, mv;
glGetFloatv(GL_PROJECTION_MATRIX, &proj[0][0]);
glGetFloatv(GL_MODELVIEW_MATRIX, &mv[0][0]);
glm::mat4 mvp = proj * mv;
这里的
mvp
是要传递给顶点着色器的内容。然后我们定义了两点:

glm::vec4 nearc(f_mouse_x, f_mouse_y, 0, 1);
glm::vec4 farc(f_mouse_x, f_mouse_y, 1, 1);
这些是标准化空间中的近光标坐标和远光标坐标(因此
f_mouse_x
f_mouse_y
[-1,1]
间隔内)。请注意,
z
坐标不需要是0和1,它们只需要是两个不同的任意数字。现在,我们可以使用
mvp
将它们取消投影到worldspace:

nearc = glm::inverse(mvp) * nearc;
nearc /= nearc.w; // dehomog
farc = glm::inverse(mvp) * farc;
farc /= farc.w; // dehomog
注意,同质划分在这里很重要。这为我们提供了光标在世界空间中的位置,在世界空间中定义了对象(除非它们有自己的模型矩阵,但这很容易合并)

最后,演示计算
nearc
farc
之间的光线与有纹理的平面(100x100网格)的交点:

glm::vec3平面_法线(0,0,1);//平面法线
浮动平面_d=0;//距原点的平面距离
//这是带有网格的平面
glm::vec3雷奥组织(nearc),雷奥迪尔(farc-nearc);
ray_dir=glm::normalize(ray_dir);
//这是鼠标光标下的光线
浮点t=glm::点(光线方向,平面法线);
if(晶圆厂(t)>1e-5f)
t=-(glm::dot(光线组织,平面法线)+平面d)/t;
其他的
t=0;//没有交点,平面和光线是共线的
glm::vec3 isect=射线组织+t*ra
glm::vec3 plane_normal(0, 0, 1); // plane normal
float plane_d = 0; // plane distance from origin
// this is the plane with the grid

glm::vec3 ray_org(nearc), ray_dir(farc - nearc);
ray_dir = glm::normalize(ray_dir);
// this is the ray under the mouse cursor

float t = glm::dot(ray_dir, plane_normal);
if(fabs(t) > 1e-5f)
    t = -(glm::dot(ray_org, plane_normal) + plane_d) / t;
else
    t = 0; // no intersection, the plane and ray is collinear
glm::vec3 isect = ray_org + t * ray_dir;
// calculate ray-plane intersection

float grid_x = N * (isect.x + 1) / 2;
float grid_y = N * (isect.y + 1) / 2;
if(t && grid_x >= 0 && grid_x < N && grid_y >= 0 && grid_y < N) {
    int x = int(grid_x), y = int(grid_y);
    // calculate integer coordinates

    tex_data[x + N * y] = 0xff0000ff; // red
    glBindTexture(GL_TEXTURE_2D, n_texture);
    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, N, N, GL_RGBA, GL_UNSIGNED_BYTE, &tex_data[0]);
    // change the texture to see
}
// calculate grid position in pixels