在尝试从头开始编写 3D 软件渲染器时,我正在实现矩阵,特别是投影矩阵,这给我带来了不小的困惑。
我已按照此处的说明(https://mathinsight.org/matrix_vector_multiplication)了解如何将两个矩阵相乘以及如何将矩阵和向量相乘,这是我的代码:
Matrix4x4 Matrix4x4::operator*(Matrix4x4& m2) {
Matrix4x4 r;
// row 1
r.m00 = (this->m00 * m2.m00) + (this->m01 * m2.m10) + (this->m02 * m2.m20) + (this->m03 * m2.m30);
r.m01 = (this->m00 * m2.m01) + (this->m01 * m2.m11) + (this->m02 * m2.m21) + (this->m03 * m2.m31);
r.m02 = (this->m00 * m2.m02) + (this->m01 * m2.m12) + (this->m02 * m2.m22) + (this->m03 * m2.m32);
r.m03 = (this->m00 * m2.m03) + (this->m01 * m2.m13) + (this->m02 * m2.m23) + (this->m03 * m2.m33);
// row 2
r.m10 = (this->m10 * m2.m00) + (this->m11 * m2.m10) + (this->m12 * m2.m20) + (this->m13 * m2.m30);
r.m11 = (this->m10 * m2.m01) + (this->m11 * m2.m11) + (this->m12 * m2.m21) + (this->m13 * m2.m31);
r.m12 = (this->m10 * m2.m02) + (this->m11 * m2.m12) + (this->m12 * m2.m22) + (this->m13 * m2.m32);
r.m13 = (this->m10 * m2.m03) + (this->m11 * m2.m13) + (this->m12 * m2.m23) + (this->m13 * m2.m33);
// row 3
r.m20 = (this->m20 * m2.m00) + (this->m21 * m2.m10) + (this->m22 * m2.m20) + (this->m23 * m2.m30);
r.m21 = (this->m20 * m2.m01) + (this->m21 * m2.m11) + (this->m22 * m2.m21) + (this->m23 * m2.m31);
r.m22 = (this->m20 * m2.m02) + (this->m21 * m2.m12) + (this->m22 * m2.m22) + (this->m23 * m2.m32);
r.m23 = (this->m20 * m2.m03) + (this->m21 * m2.m13) + (this->m22 * m2.m23) + (this->m23 * m2.m33);
// row 4
r.m30 = (this->m30 * m2.m00) + (this->m31 * m2.m10) + (this->m32 * m2.m20) + (this->m33 * m2.m30);
r.m31 = (this->m30 * m2.m01) + (this->m31 * m2.m11) + (this->m32 * m2.m21) + (this->m33 * m2.m31);
r.m32 = (this->m30 * m2.m02) + (this->m31 * m2.m12) + (this->m32 * m2.m22) + (this->m33 * m2.m32);
r.m33 = (this->m30 * m2.m03) + (this->m31 * m2.m13) + (this->m32 * m2.m23) + (this->m33 * m2.m33);
return r;
}
Vector4 Matrix4x4::operator*(Vector4& v) {
Vector4 r(
this->m00 * v.x + this->m01 * v.y + this->m02 * v.z + this->m03 * v.w,
this->m10 * v.x + this->m11 * v.y + this->m12 * v.z + this->m13 * v.w,
this->m20 * v.x + this->m21 * v.y + this->m22 * v.z + this->m23 * v.w,
this->m30 * v.x + this->m31 * v.y + this->m32 * v.z + this->m33 * v.w);
return r;
}
它可以工作,我可以在 2d 中平移、旋转和缩放我的对象,但是当我尝试实现投影和相机矩阵时,我遇到了问题。 我从 SharpDx 复制了投影和相机矩阵,因为它的数学库使用与我相同的行专业,它的矩阵乘法与我的相同,但这是我的困惑,SharpDx 只有一个: 坐标变换 (https://github.com/sharpdx/SharpDX/blob/master/Source/SharpDX.Mathematics/Vector3.cs#L1388)对于Vector3 和a:变换 (https://github.com/sharpdx/SharpDX/blob/master/Source/SharpDX.Mathematics/Vector4.cs#L1125)对于Vector4 执行矩阵*向量运算,并且它们似乎都属于列专业 ?
所以我编写了这段代码来在 3d 中旋转 8 个点的立方体:
Vector4 cubeAr[8];
cubeAr[0].x = -1; cubeAr[0].y = 1; cubeAr[0].z = 1; cubeAr[0].w = 1;
cubeAr[1].x = 1; cubeAr[1].y = 1; cubeAr[1].z = 1; cubeAr[1].w = 1;
cubeAr[2].x = -1; cubeAr[2].y = -1; cubeAr[2].z = 1; cubeAr[2].w = 1;
cubeAr[3].x = -1; cubeAr[3].y = -1; cubeAr[3].z = -1; cubeAr[3].w = 1;
cubeAr[4].x = -1; cubeAr[4].y = 1; cubeAr[4].z = -1; cubeAr[4].w = 1;
cubeAr[5].x = 1; cubeAr[5].y = 1; cubeAr[5].z = -1; cubeAr[5].w = 1;
cubeAr[6].x = 1; cubeAr[6].y = -1; cubeAr[6].z = 1; cubeAr[6].w = 1;
cubeAr[7].x = 1; cubeAr[7].y = -1; cubeAr[7].z = -1; cubeAr[7].w = 1;
Vector3 cameraPos(0, 0, 10);
Vector3 cameraTarget(0, 0, 0);
Matrix4x4 mT = Matrix4x4::GetTranslation(0, 0, 0);
Matrix4x4 mR = Matrix4x4::GetRotationY(radians/2);
Matrix4x4 mS = Matrix4x4::GetScale(1, 1, 1);
Matrix4x4 mTRS = mT * mR * mS;
Matrix4x4 viewMatrix = Matrix4x4::GetLookAtLH(cameraPos, cameraTarget, Vector3(0, 1, 0));
Matrix4x4 projectionMatrix = Matrix4x4::GetPerspectiveFovRH(0.78f, (float)win.GetWidth() / win.GetHeight(), 0.01f, 1.0f);
Matrix4x4 mMVP = mTRS * viewMatrix * projectionMatrix;
Vector4 tmpV, tmpV2;
for (int i = 0; i < 8; i++) {
mMVP.Transpose();
tmpV = mMVP * cubeAr[i];
mMVP.Transpose();
tmpV.w = 1.0 / tmpV.w;
tmpV.x = tmpV.x * tmpV.w;
tmpV.y = tmpV.y * tmpV.w;
tmpV.z = tmpV.z * tmpV.w;
float x = tmpV.x * (float)win.GetWidth() + (float)win.GetWidth() / 2.0f;
float y = -tmpV.y * (float)win.GetHeight() + (float)win.GetHeight() / 2.0f;
bool done = true;
LR.DrawPixel(x, y, 0, 255, 0);
}
它工作成功,但我必须先转置我的 modelViewProjection 矩阵,然后再将其与我的向量相乘。
我可以编写一个类似于 SharpDx 的 TransformCooperative 或 Transform 函数,并避免进行转置,但我想了解发生了什么,为什么 SharpDx 会这样写?
我遵循了这些教程: https://mathinsight.org/matrix_vector_multiplication https://stunlock.gg/posts/linear_algebra/#matrices/thebasicrulesofmatrices/multiplyingmatrices(点积)
我期望能够将我的矩阵与我的向量相乘,但是我能找到的透视矩阵和相机观察函数的所有其他示例都以另一种方式执行,就像在 SharpDx 中一样,您在向量类中使用变换函数,即专栏专业?
..执行矩阵*向量运算的 Vector4 的变换,它们似乎都属于列专业?
是的,它们似乎是列主矩阵*向量运算,但它们实际上是行主向量矩阵运算..其工作方式相同,因此很容易混淆它们,但它们的意图不同。
在 D3D 世界中,通常通过将左侧的第一个变换乘以右侧的最后一个变换来组合变换。您可能会认为这是显而易见的方法,这就是您编写变换的方式,但这也意味着要变换的顶点必须位于变换矩阵的左侧:
v * mMVP
SharpDX 遵循该约定,因此它有一个实现这种变换的函数,它的参数首先是顶点,然后是矩阵,这并不是巧合或奇怪的怪癖,它的意思是这样做
v * mMVP
。
使用
mMVP * v
你会得到这个:
mMVP * v =
(mTRS * viewMatrix * projectionMatrix) * v ==
mTRS * (viewMatrix * (projectionMatrix * v))
IE,先投影,然后应用视图变换,然后 TRS,这没有意义,但可以通过转置
mMVP
来修复它,因为 AB = (BTAT)T 并且向量隐式转置。
OpenGL 世界使用相反的约定,那么
mMVP * v
是变换矢量的传统方法,但 mMVP
可以通过组合变换来创建,第一个变换在右侧,最后一个变换在左侧。无论哪种方式,向量都与第一个变换位于同一侧,这可能被认为是变换的“输入侧”。