c++ 如何保证齐次坐标系中的矢量变换后仍是矢量

mepcadol  于 2023-03-05  发布在  其他
关注(0)|答案(2)|浏览(173)

我在模型的顶点上做了一个MVP transformation。理论上,我必须将MVP变换的逆转置矩阵应用到***法线***上。
以下是推导过程:
(A, B, C)是点(x, y, z)所在平面的法线

对于一个向量,如(x0, y0, z0),在齐次坐标中为(x0, y0, z0, 0),变换后仍应为向量,如(x1, y1, z1, 0),这就要求4 * 4变换矩阵的最后一行除最后一列元素外,其余均为0,否则变换后为(x1, y1, z1, n)
事实上,我的MVP变换矩阵在经历逆转置变换之后不能满足这一点。
代码:

Mat<4, 4> View(const Vec3& pos){
    Mat<4, 4> pan{1, 0, 0, -pos.x,
                0, 1, 0, -pos.y,
                0, 0, 1, -pos.z,
                0, 0, 0, 1};
    Vec3 v = Cross(camera.lookAt, camera.upDirection).Normalize();
    Mat<4, 4> rotate{v.x, v.y, v.z, 0,
                     camera.upDirection.x, camera.upDirection.y, camera.upDirection.z, 0,
                     -camera.lookAt.x, -camera.lookAt.y, -camera.lookAt.z, 0,
                     0, 0, 0, 1};
    return rotate * pan;
}

Mat<4, 4> Projection(double near, double far, double fov, double aspectRatio){
    double angle = fov * PI / 180;

    double t = -near * tan(angle / 2);
    double b = -t;
    double r = t * aspectRatio;
    double l = -r;

    Mat<4, 4> zoom{2 / (r - l), 0, 0, 0,
                    0, 2 / (t - b), 0, 0,
                    0, 0, 2 / (near - far), 0,
                    0, 0, 0, 1};
    Mat<4, 4> pan{1, 0, 0, -(l + r) / 2,
                    0, 1, 0, -(t + b) / 2,
                    0, 0, 1, -(near + far) / 2,
                    0, 0, 0, 1};
    Mat<4, 4> extrusion{near, 0, 0, 0,
                        0, near, 0, 0,
                        0, 0, near + far, -near * far,
                        0, 0, 1, 0};

    Mat<4, 4> ret = zoom * pan * extrusion;
    return ret;
}
Mat<4, 4> modelMatrix = Mat<4, 4>::identity();
Mat<4, 4> viewMatrix = View(camera.position);
Mat<4, 4> projectionMatrix = Projection(-0.1, -50, camera.fov, camera.aspectRatio);
Mat<4, 4> mvp = projectionMatrix * viewMatrix * modelMatrix;
Mat<4, 4> mvpInverseTranspose = mvp.Inverse().Transpose();

MVP:

-2.29032  0        0.763441   -2.68032e-16 
0         -2.41421 0          0 
-0.317495 0        -0.952486  2.97455 
0.316228  0        0.948683   -3.16228

mvp反向转置:

-0.392957   0          0.130986   0 
0           -0.414214  0          0 
-4.99       0          -14.97     -4.99 
-4.69377    0          -14.0813   -5.01
67up9zun

67up9zun1#

我似乎明白了这个问题。光照应该在世界空间中计算,所以我只需要将the model transformation的逆转置矩阵应用于***法线***。

ubby3x7f

ubby3x7f2#

对于您的问题,使用模型视图矩阵的逆转置矩阵而不是模型视图投影矩阵来将法向量变换到视图空间而不是剪辑空间应该就足够了。
在用逆转置变换之后,w分量不一定是零是可以的。

    • 原因:**4x4仿射矩阵(模型视图矩阵的类型)的逆转置已经将形式为[x,y,z,0]的法向量Map到向量[x',y',z',w'],通常w '!= 0。

原因如下:考虑仿射矩阵的逆矩阵

/ R t \^-1 = /R^-1 -R^-1*t\
\ 0 1 /      \0    1      /

其中0是1x3的零矢量,t是3x1的平移矢量,R是3x3的线性部分,^-1是其逆。
所以它的逆转置是

/ R t \^-T = / (R^-1)^T         0^T \
\ 0 1 /      \ (-R^-1 * t)^T     1  /

现在将逆转置乘以[x,y,z,0]^T,你会得到w'=(-R ^-1 * t)^T *[x,y,z]^T +0 * 1!= 0

    • 你能做些什么**
    • 选项1:对于法线,忽略w '部分。**

用仿射矩阵变换法向量的诀窍是取其逆转置,然后忽略结果的w '部分。
也就是说,只需要使用[x',y',z'],你甚至可以做除法(如果w '!= 0),即1/w'*[x',y',z'],因为normalize(1/w *[x',y',z'])= normalize([x',y',z']),记住,对于法向量,你感兴趣的是它们的方向,而不是它们的长度。

    • 选项2:使用3x3子矩阵**

平移一个方向是没有意义的。记得你的数学老师在黑板上的不同位置画箭头,并说明基本向量是相同的。对于法向量,使用仿射矩阵左上角的3x3子矩阵就足够了。
但是,您仍然需要对其进行逆转置。

    • 法向量到剪辑空间**我一直说仿射矩阵,因为像MVP这样用投影矩阵变换法向量是我不推荐的。

法线通常用于照明。这发生在模型、视图、切线、世界或光空间中。然而,这里的模型-视图-投影(MVP)矩阵包含投影矩阵P形式的投影部分。投影矩阵Map到剪辑空间。剪辑空间不是执行照明的空间,而是剪辑、剔除和准备透视z分割的坐标。
我想,如果你仔细算一下,你应该能做到,但从来没有考虑过甚至想一想。
在剪辑空间之前,在任何空间中进行照明都要容易得多。

相关问题