最近,我决定将体积雾添加到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查找世界位置(请注意,这是错误的):
float3 CalculateWorldPos(uint3 texel)
{
//Divide the texel by the dimensions, to get a value between 0 and 1 for
// each of the components
float3 pos = (float3)texel * float3(1.0f / 190.0f, 1.0f / 90.0f, 1.0f / (float)(g_Depth-1));
pos.xy = 2.0f * pos.xy - float2(1.0f, 1.0f);
//Skew the depth
pos.z = GetExponentialDepth(pos.z);
//Multiply this point, which should be in NDC coordinates,
// by the inverse of (View * Proj)
return mul(float4(pos, 1.0f), g_InverseViewProj).xyz;
}
但是,投影矩阵也让我有些困惑,因此这是获取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)
另外,我已经读到投影矩阵是不可逆的(它们的逆不存在)。如果是这样,那么可能不确定(View * Proj)的逆函数是否正确,我不确定。
因此,仅重申一个问题,给定与视图对齐的长方体的3D纹理坐标,我如何找到该点的世界位置?
在此先多谢,这个问题已经把我的时间吃光了!
首先让我解释一下透视投影矩阵的作用。
透视投影矩阵将矢量从视图空间转换为剪辑空间,以使x / y坐标对应于屏幕上的水平/垂直位置,而z坐标对应于深度。znear
距离相机远的位置的顶点映射到深度0。zfar
距离相机远的位置的顶点映射到深度1。紧随其后的深度值znear
非常快地增加,而zfar
仅紧靠在正前方的深度值就增加了慢慢改变。
具体来说,给定z坐标,结果深度为:
depth = zfar / (zfar - znear) * (z - znear) / z
如果在深度相等的位置(例如,每隔0.1
)之后用直线绘制平截头体,则会得到单元格。而且前面的单元格比后面的单元格薄。如果您绘制足够的单元格,这些单元格将映射到您的纹理像素。在此配置中,这正是您所希望的。前面的单元(导致更高的分辨率)比后面的单元更多。因此,您可以仅使用纹理像素坐标作为深度值(归一化为[0,1]范围)。这是给定深度值进入视图空间的标准背投影(假设znear=1, zfar=10
)
由于以下原因,您的代码无法正常工作:
return mul(float4(pos, 1.0f), g_InverseViewProj).xyz;
我们使用4D向量和矩阵是有原因的。如果只是丢掉第四维,您将得到错误的结果。而是使用w-clip:
float4 transformed = mul(float4(pos, 1.0f), g_InverseViewProj);
return (transformed / transformed.w).xyz;
顺便说一句,4D透视投影矩阵是完全可逆的。仅当移除一个维度时,您才能获得非二次矩阵,这是不可逆的。但这不是我们通常在计算机图形学中所做的。但是,这些矩阵也称为投影(但在不同的上下文中)。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句