Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/135.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 如何在给定视图空间深度值和ndc xy的情况下恢复视图空间位置_C++_Glsl_Shader_Coordinate Transformation_Perspectivecamera - Fatal编程技术网

C++ 如何在给定视图空间深度值和ndc xy的情况下恢复视图空间位置

C++ 如何在给定视图空间深度值和ndc xy的情况下恢复视图空间位置,c++,glsl,shader,coordinate-transformation,perspectivecamera,C++,Glsl,Shader,Coordinate Transformation,Perspectivecamera,我正在写一个延迟着色器,并试图将我的gbuffer打包得更紧。然而,鉴于视图空间深度,我似乎无法正确计算视图位置 //深度->(gl_ModelViewMatrix*vec4(pos.xyz,1)).z;其中pos是模型空间位置 //视野->以弧度表示的视野(0.62831855,0.47123888) //p->ndc位置,x,y[-1,1] vec3获取位置(浮动深度、vec2视野、vec2 p) { vec3位置; 位置x=-深度*tan(半圆周率-fov.x/2.0)*(p.x); 位置

我正在写一个延迟着色器,并试图将我的gbuffer打包得更紧。然而,鉴于视图空间深度,我似乎无法正确计算视图位置

//深度->(gl_ModelViewMatrix*vec4(pos.xyz,1)).z;其中pos是模型空间位置
//视野->以弧度表示的视野(0.62831855,0.47123888)
//p->ndc位置,x,y[-1,1]
vec3获取位置(浮动深度、vec2视野、vec2 p)
{
vec3位置;
位置x=-深度*tan(半圆周率-fov.x/2.0)*(p.x);
位置y=-深度*tan(半圆周率-fov.y/2.0)*(p.y);
位置z=深度;
返回pos;
}

计算出的位置是错误的。我知道这一点,因为我仍在gbuffer中存储正确的位置并使用它进行测试。

我编写了一个延迟着色器,并使用此代码重新计算屏幕空间定位:

vec3 getFragmentPosition()
{
     vec4 sPos = vec4(gl_TexCoord[0].x, gl_TexCoord[0].y, texture2D(depthTex, gl_TexCoord[0].xy).x, 1.0);
     sPos.z = 2.0 * sPos.z - 1.0;
     sPos = invPersp * sPos;

     return sPos.xyz / sPos.w;
}
其中,
depthTex
是纹理保持深度信息,
invPersp
是预先计算的逆透视矩阵。获取屏幕的片段位置,并将其乘以反向透视矩阵以获得模型视图坐标。然后除以
w
得到均匀坐标。乘二减一是将深度从[0,1](存储在纹理中)缩放到[-1,1]


此外,根据您使用的MRT类型,重新计算的结果将与存储的信息不完全相同,因为您失去了浮点精度。

我最终设法使其工作,因为这是一种与上面不同的方法,我将详细说明,以便任何看到这一点的人都能找到解决方案

  • 传递1:将视图空间中的深度值存储到gbuffer
  • 要在第二遍中重新创建(x、y、z)位置,请执行以下操作:
  • 将以弧度表示的水平和垂直视野传递到着色器中
  • 将“近平面距离”(near)传递给着色器。(从摄像机位置到近平面的距离)
  • 想象一条从摄影机到碎片位置的光线。这条光线在某个位置P与近平面相交。我们在ndc空间中有这个位置,我们想在视图空间中计算这个位置
  • 现在,我们有了视图空间中所需的所有值。我们可以利用相似三角形定律来确定碎片的实际位置P'

    P = P_ndc * near * tan(fov/2.0f) // computation is the same for x, y
    // Note that by law of similar triangles, P'.x / depth = P/near  
    P'.xy = P/near * -depth; // -depth because in opengl the camera is staring down the -z axis
    P'.z = depth;
    
3在透视投影中恢复视图空间位置的解决方案 投影矩阵描述从场景的三维点到视口的二维点的映射。它从视图(眼睛)空间转换到剪辑空间,剪辑空间中的坐标通过与剪辑坐标的w分量相除而转换为标准化设备坐标(NDC)。NDC在(-1,-1,-1)到(1,1,1)之间

在透视投影中,投影矩阵描述了从针孔相机的视野中看到的世界上的3D点到视口的2D点的映射。
摄影机平截头体(截断棱锥体)中的眼空间坐标映射到立方体(规范化设备坐标)

透视投影矩阵:

r = right, l = left, b = bottom, t = top, n = near, f = far

2*n/(r-l)      0              0               0
0              2*n/(t-b)      0               0
(r+l)/(r-l)    (t+b)/(t-b)    -(f+n)/(f-n)    -1    
0              0              -2*f*n/(f-n)    0
内容如下:

aspect = w / h
tanFov = tan( fov_y * 0.5 );

prjMat[0][0] = 2*n/(r-l) = 1.0 / (tanFov * aspect)
prjMat[1][1] = 2*n/(t-b) = 1.0 / tanFov
在透视投影时,Z分量由有理函数计算:

z_ndc = ( -z_eye * (f+n)/(f-n) - 2*f*n/(f-n) ) / -z_eye
深度(
gl_FragCoord.z
and)计算如下:

z_ndc = clip_space_pos.z / clip_space_pos.w;
depth = (((farZ-nearZ) * z_ndc) + nearZ + farZ) / 2.0;

1.视场和纵横比 由于投影矩阵由视野和纵横比定义,因此可以使用视野和纵横比恢复视口位置。如果它是对称透视投影,且归一化设备坐标,则深度以及近平面和远平面是已知的

恢复视图空间中的Z距离:

z_ndc = 2.0 * depth - 1.0;
z_eye = 2.0 * n * f / (f + n - z_ndc * (f - n));
A     = prj_mat[2][2];
B     = prj_mat[3][2];
z_ndc = 2.0 * depth - 1.0;
z_eye = B / (A + z_ndc);
通过XY规格化设备坐标恢复视图空间位置:

ndc_x, ndc_y = xy normalized device coordinates in range from (-1, -1) to (1, 1):

viewPos.x = z_eye * ndc_x * aspect * tanFov;
viewPos.y = z_eye * ndc_y * tanFov;
viewPos.z = -z_eye; 
viewPos.x = z_eye * ndc_x / prjMat[0][0];
viewPos.y = z_eye * ndc_y / prjMat[1][1];
viewPos.z = -z_eye; 

2.投影矩阵 由视场和纵横比定义的投影参数存储在投影矩阵中。因此,可以从对称透视投影中通过投影矩阵中的值恢复视口位置

注意投影矩阵、视野和纵横比之间的关系:

prjMat[0][0] = 2*n/(r-l) = 1.0 / (tanFov * aspect);
prjMat[1][1] = 2*n/(t-b) = 1.0 / tanFov;

prjMat[2][2] = -(f+n)/(f-n)
prjMat[3][2] = -2*f*n/(f-n)
恢复视图空间中的Z距离:

z_ndc = 2.0 * depth - 1.0;
z_eye = 2.0 * n * f / (f + n - z_ndc * (f - n));
A     = prj_mat[2][2];
B     = prj_mat[3][2];
z_ndc = 2.0 * depth - 1.0;
z_eye = B / (A + z_ndc);
通过XY规格化设备坐标恢复视图空间位置:

ndc_x, ndc_y = xy normalized device coordinates in range from (-1, -1) to (1, 1):

viewPos.x = z_eye * ndc_x * aspect * tanFov;
viewPos.y = z_eye * ndc_y * tanFov;
viewPos.z = -z_eye; 
viewPos.x = z_eye * ndc_x / prjMat[0][0];
viewPos.y = z_eye * ndc_y / prjMat[1][1];
viewPos.z = -z_eye; 

3.逆投影矩阵 当然,视口位置可以通过逆投影矩阵恢复

mat4 inversePrjMat = inverse( prjMat );
vec4 viewPosH      = inversePrjMat * vec3( ndc_x, ndc_y, 2.0 * depth - 1.0, 1.0 )
vec3 viewPos       = viewPos.xyz / viewPos.w;

另见以下问题的答案:


你希望返回的位置在什么空间?我希望它在视图空间中计算forumla在这里被抄袭了你的深度纹理是以一种格式存储的,这样你对所有坐标所做的
2*p-1
将产生正确的值?你存储到depthTex中的深度是什么空间?我存储它,在我的GBuffer着色器中,如
gl_FradCoord.z
。抱歉,只有
sPos
z
部分应该缩放-我编辑了我的原始帖子。是的,因为我非常确定
gl_FragCoord
也在视图空间中。(对不起,我刚才拼错了。)