DirectX将像素世界位置转换为阴影贴图位置会产生奇怪的平铺结果

时间:2016-09-02 04:02:48

标签: c++ directx hlsl

我已经尝试了一段时间来获得一个屏幕空间像素(由延迟的HLSL着色器提供)以转换为光照空间。结果令我感到惊讶,因为我的灯光渲染似乎是在调整深度缓冲区。

重要的是,场景摄影机(或眼睛)和正在渲染的灯光从同一位置开始。

首先,我使用下面的代码提取像素的世界位置:

float3 eye = Eye;
float4 position = {
    IN.texCoord.x * 2 - 1,
    (1 - IN.texCoord.y) * 2 - 1,
    zbuffer.r,
    1
};
float4 hposition = mul(position, EyeViewProjectionInverse);
position = float4(hposition.xyz / hposition.w, hposition.w);
float3 eyeDirection = normalize(eye - position.xyz);

结果似乎是正确的,因为将XYZ位置渲染为RGB分别产生这个(显然是正确的)结果:

Correctly rendered XYZ

红色组件在向右移动时似乎正在输出X,蓝色显示Z向前移动。 Y因子也看起来正确,因为地面略低于Y轴。

接下来(为了确保我不会发疯),我决定输出原始深度缓冲区。通常我将深度缓冲区保存在名为DepthMap的Texture2D中,作为输入传递给着色器。然而,在这种情况下,我试图通过将像素转换偏移回适当的位置并将其乘以眼睛的视图投影矩阵来撤消像素转换:

float4 cpos = mul(position, EyeViewProjection);
cpos.xyz = cpos.xyz / cpos.w;
cpos.x = cpos.x * 0.5f + 0.5f;
cpos.y = 1 - (cpos.y * 0.5f + 0.5f);
float camera_depth = pow(DepthMap.Sample(Sampler, cpos.xy).r, 100); // Power 100 just to visualize the map since scales are really tiny
return float4(camera_depth, camera_depth, camera_depth, 1);

这也产生了正确的结果(尽管我不是100%确定Z值)。另请注意,我已将结果指数化以更好地可视化深度信息(在尝试实时比较时不会这样做):

Reinterpreting XYZ back to ZBuffer space

所以理论上,我可以使用相同的代码通过乘以光的视图 - 投影矩阵将该像素世界位置转换为光空间。正确?这是我试过的:

float4 lpos = mul(position, ShadowLightViewProjection[0]);
lpos.xyz = lpos.xyz / lpos.w;
lpos.x = lpos.x * 0.5f + 0.5f;
lpos.y = 1 - (lpos.y * 0.5f + 0.5f);
float shadow_map_depth = pow(ShadowLightMap[0].Sample(Sampler, lpos.xy).r, 100); // Power 100 just to visualize the map since scales are really tiny
return float4(shadow_map_depth, shadow_map_depth, shadow_map_depth, 1);

这是结果:

Broken light space

另一个更好地展示了它如何映射到世界:

Broken light space #2

我不明白这里发生了什么。它似乎可能与投影矩阵有关,但我对数学的了解不是很清楚发生了什么。它绝对不是光照贴图的宽度/高度,因为我尝试了多种贴图尺寸,投影矩阵是使用FOV和宽高比计算的,永远不会输入宽度/高度。

最后,这里有一些C ++代码显示我的透视矩阵(用于眼睛和光线)是如何计算的:

    const auto ys = std::tan((T)1.57079632679f - (fov / (T)2.0));
    const auto xs = ys / aspect;
    const auto& zf = view_far;
    const auto& zn = view_near;
    const auto zfn = zf - zn;

    row1(xs, 0, 0, 0);
    row2(0, ys, 0, 0);
    row3(0, 0, zf / zfn, 1);
    row4(0, 0, -zn * zf / zfn, 0);
    return *this;

我在这里完全不知所措。任何指导或建议将不胜感激!

编辑 - 我也忘了提到平铺的图像是颠倒的,好像y翻转它一样。这对我来说很奇怪,因为它需要正确地将它恢复到眼睛纹理空间。

1 个答案:

答案 0 :(得分:1)

我做了一些调整并在这里和那里修理了一些东西。最终,我最大的问题是意外转换的矩阵。关于矩阵如何转置有点复杂,但这就是事情被翻转的原因。我也改为D32深度缓冲区(虽然我不确定它有什么帮助)并确保任何位置除以它们的W影响所有组件(包括W)。

所以这样的代码:hposition.xyz = hposition.xyz / hposition.w 变成了这个:hposition = hposition / hposition.w

在完成所有这些调整之后,它开始变得更像阴影贴图。

哦,转置矩阵是光的ViewProjection。