C++ 矩阵地狱-将3D纹理中的点转换为世界空间

C++ 矩阵地狱-将3D纹理中的点转换为世界空间,c++,math,matrix,directx,hlsl,C++,Math,Matrix,Directx,Hlsl,最近,我决定在DirectX的3D游戏中添加体积雾。我使用的技术来自GPU Pro 6,但您不必拥有该书的副本来帮助我:)。基本上,体积雾信息存储在3D纹理中。现在,我需要将3D纹理的每个纹理变换到世界空间。纹理是视图对齐的,我的意思是纹理的X和Y映射到屏幕的X和Y,纹理的Z在相机前面向前延伸。所以本质上我需要一个函数: float3 CalculateWorldPosition(uint3 Texel) { //Do math } 我知道视图矩阵、3D纹理的尺寸(190x90x64或

最近,我决定在DirectX的3D游戏中添加体积雾。我使用的技术来自GPU Pro 6,但您不必拥有该书的副本来帮助我:)。基本上,体积雾信息存储在3D纹理中。现在,我需要将3D纹理的每个纹理变换到世界空间。纹理是视图对齐的,我的意思是纹理的X和Y映射到屏幕的X和Y,纹理的Z在相机前面向前延伸。所以本质上我需要一个函数:

float3 CalculateWorldPosition(uint3 Texel)
{
    //Do math
}
我知道视图矩阵、3D纹理的尺寸(190x90x64或190x90x128)、屏幕的投影矩阵等

然而,不幸的是,这还不是全部

您可能知道,DirectX中的深度缓冲区不是线性的。同样的效果也需要应用到我的3D纹理中——纹理需要倾斜,这样离相机远的地方比离相机近的地方多,因为相机附近的细节一定比离相机远的地方好。但是,我认为我有一个函数来实现这一点,如果我错了,请纠正我:

//Where depth = 0, the texel is closest to the camera.
//Where depth = 1, the texel is the furthest from the camera.
//This function returns a new Z value between 0 and 1, skewing it
// so more Z values are near the camera.
float GetExponentialDepth(float depth /*0 to 1*/)
{
    depth = 1.0f - depth;

    //Near and far planes
    float near = 1.0f;
    //g_WorldDepth is the depth of the 3D texture in world/view space
    float far = g_WorldDepth;

    float linearZ = -(near + depth * (far - near));
    float a = (2.0f * near * far) / (near - far);
    float b = (far + near) / (near - far);
    float result = (a / -linearZ) - b;
    return -result * 0.5f + 0.5f;
}
下面是我当前的函数,它试图从texel中找到世界位置(请注意,它是错误的):

但是,投影矩阵对我来说也有点混乱,因此这是获取3D纹理的投影矩阵的直线,如果它不正确,可以纠正我:

//Note that the X and Y of the texture is 190 and 90 respectively.
//m_WorldDepth is the depth of the cuboid in world space.
XMMatrixPerspectiveFovLH(pCamera->GetFovY(), 190.0f / 90.0f, 1.0f, m_WorldDepth)
另外,我读到投影矩阵是不可逆的(它们的逆矩阵不存在)。如果这是真的,那么也许找到(视图*Proj)的倒数是不正确的,我不确定

那么,重申一下这个问题,给定一个三维纹理坐标到一个视图对齐的长方体,我如何才能找到该点的世界位置


提前非常感谢,这个问题占用了我很多时间

首先让我解释一下透视投影矩阵的作用

透视投影矩阵将向量从视图空间转换为剪辑空间,从而x/y坐标对应于屏幕上的水平/垂直位置,z坐标对应于深度。距离摄影机
znear
单位的顶点将映射到深度0。距离摄影机
zfar
单位的顶点映射到深度1。
znear
后面的深度值增加得很快,而
zfar
前面的深度值变化很慢

具体而言,给定z坐标,得到的深度为:

depth = zfar / (zfar - znear) * (z - znear) / z
如果在深度为偶数的空格后(例如,在每个
0.1
之后)用线绘制平截头体,则会得到单元格。前面的细胞比后面的细胞薄。如果绘制足够的单元,这些单元将映射到纹理。在此配置中,它完全符合您的要求。前面的单元比后面的单元多(因此分辨率更高)。因此,您可以只使用texel坐标作为深度值(标准化为[0,1]范围)。以下是给定深度值到视图空间的标准反投影(假设
znear=1,zfar=10

由于以下代码行,您的代码无法工作:

return mul(float4(pos, 1.0f), g_InverseViewProj).xyz;
我们使用4D向量和矩阵是有原因的。如果你把第四维度扔掉,你会得到错误的结果。相反,请执行w形夹:

float4 transformed = mul(float4(pos, 1.0f), g_InverseViewProj);
return (transformed / transformed.w).xyz;

顺便说一句,4D透视投影矩阵是完全可逆的。只有去掉一维,才能得到一个非二次矩阵,它是不可逆的。但这不是我们通常在计算机图形学中所做的。但是,这些矩阵也被称为投影(但在不同的上下文中)。

纹理真的是长方体还是平截体?它存储为长方体,但在世界空间中它是平截体。非常感谢!(很抱歉回复太晚)
float4 transformed = mul(float4(pos, 1.0f), g_InverseViewProj);
return (transformed / transformed.w).xyz;