DirectX:世界到视图矩阵-我的误解在哪里?

3
我正在学习DirectX(使用SharpDX,仅使用C# / hlsl编程),并尝试构建自己的相机类。它应该可以旋转,允许前后移动和“侧向”移动(通常映射为A和D的第一人称移动,加上我的情况下的上下移动)。为了更容易进行故障排除,在我的情况下模型空间和世界空间是相同的,透视投影尚未实现,相机应该朝着正Z轴(进入屏幕)看。我的故障修复模型是一个简单的四边形,宽度和高度均为1.f,z = 0,并位于屏幕中心。
为了更易于使用,我发现DirectX的Matrix.LookAtLH(),并使用它构建我的矩阵,以便从世界坐标系转换到视图坐标系,基于我的相机在世界坐标系中的位置,一个上向量(目前 - 没有旋转 - 总是正Y轴),以及世界坐标系中的目标点。
我的(顶点)着色器使用与此矩阵的简单乘法:
output.position = mul(position, worldToView);
LookAt-Matrix计算如下:
Matrix.LookAtLH(vec3(0, 0, -1), vec3(0, 0, 0.5f), vec3(0, 1, 0))
导致以下图片:
Camera at (0, 0, -1) looking at (0, 0, 0.5f
现在我想将相机向右移动,在这种情况下,通过为其X坐标添加1.f来实现。 我的预期结果是与之前相同的四边形,稍微向左移动了一点。
我构建了一个新的LookAt-matrix,通过相同的向量移动眼睛坐标和目标坐标:
Matrix.LookAtLH(vec3(1, 0, -1), vec3(1, 0, 0.5f), vec3(0, 1, 0))
导致以下结果:
Camera at (1, 0, -1) lookint at (1, 0, 0.5f

随着我移动相机,情况变得更加极端,但屏幕中心仍然保持四边形的中心。这是否与我对Matrix.LookAtLH的潜在误解有关?

1个回答

5
当您使用D3DX函数时,需要在将矩阵发送到着色器之前转置它们。
更详细的解释可以从此处获得:
在线性代数中,向量和矩阵使用标准矩阵乘法算法相乘。因此,涉及矩阵操作顺序和“形状”的几个规则。数学家通常将向量视为包含单列元素的矩阵,其中平移乘法看起来像这样:
[ 0, 0, 0, tx]  [ x]
[ 0, 0, 0, ty] *[ y]
[ 0, 0, 0, tz]  [ z]
[ 0, 0, 0,  1]  [ 1]

首先需要注意的是,矩阵乘法会根据以下简单规则产生特定行/列配置的结果:

AxB * BxC = AxC。

换句话说,一个大小为A行B列的矩阵乘以一个大小为B行C列的矩阵将产生一个大小为A行C列的矩阵。此外,为了正确相乘,B必须对两个矩阵都相等。在这种情况下,我们有4x4 * 4x1,它将产生一个4x1的矩阵,或者另一个列向量。如果我们改变乘法的顺序,那么就是4x1 * 4x4,这是不合法的。

然而,计算机科学家经常将向量视为具有单个行的矩阵。有几个原因,但通常是因为单个行表示单个线性存储块,或者单个维数组,因为数组通常被称为array[row][column]。为了避免在代码中使用二维数组,人们只需使用“行向量”即可。因此,为了通过矩阵乘法实现所需的结果,我们交换顺序变为1x4 * 4x4 = 1x4,或向量*矩阵:

[ x, y, z, 1] * [ 0, 0, 0, 0]
                [ 0, 0, 0, 0]               
            [ 0, 0, 0, 0]
            [ x, y, z, 1]

请注意,为了保留正确的乘法结果(在这种情况下是转置),必须移动平移矩阵的x、y、z元素。
当使用列向量时,typical transform操作的顺序为P * V * W * v,因为列向量必须放在最后才能产生正确的结果。记住,矩阵乘法是关联的,而不是可交换的,所以为了实现将向量变换为世界坐标系、变换为视图空间、变换为齐次屏幕空间的适当结果,我们必须按照这个顺序相乘。这给了我们(使用结合律)P *(V *(W * v)),因此从内部括号到外部括号工作,先进行世界变换,然后是视图,最后是投影。
如果我们使用行向量,则乘法如下:v * W * V * P。使用结合律,我们意识到它只是相同的操作顺序:((v * W) * V) * P。或者先进行世界变换,然后是视图,最后是投影。
两种乘法形式都是有效的,DX库选择使用后者,因为它匹配内存布局模式,并允许您从左到右读取变换顺序。
HLSL支持操作顺序的两种方式。 "*"运算符执行简单的逐元素缩放,而不执行矩阵乘法。这是通过使用“mul()”内部操作来执行的。如果将一个四元素向量作为mul()内部函数的第一个参数传递,则它会假定您希望将其视为“行向量”。因此,您必须提供已按正确顺序相乘并使用正确的行/列格式提供的矩阵。这是使用DX效果参数从DX库传入矩阵时的默认行为。如果将四元素向量作为mul()内部函数的第二个参数提供,则将其视为列向量,您必须为列向量提供正确形成和相乘的矩阵。

我遇到了与 OP 完全相同的问题。我尝试在传递给顶点着色器之前转置视图矩阵,但这只会使一切消失。在我的着色器中声明矩阵为 row_major 也是如此。反转 mul 函数中的参数也是如此。任何这些事情的组合都会导致空白屏幕或与 OP 相同的图像。有什么建议吗? - Camander
我找出了我的错误所在。我没有乘以投影矩阵,因此它超出了视口的最大深度。 - Camander

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接