仿射变换下的法向量
根据原题,这个是顶点着色中常见的问题。
1楼Jiana关于这个说的很明确:“法线不是一般概念的真正向量”。这句话的本意是对的,就是平面(或切平面)的法线不能简单的通过仿射变换矩阵直接得到变换后空间的法线,但并不是像从字面上理解,法线(法向量)就不是向量了。
其次链接中讨论了法向量方向的确定,经过下面论证可以发现,还是有必要进行一番工作的。长话短说,以下就交给数学了,所有的算式未加说明采用matlab符号,但等号用'='表示。
一般三维平面向量方程:
dot( (p - p0), n ) = 0
但是仿射空间延拓到四维,内积就不为0了,但也不能轻易写dot( (p - p0), n ) = 1,因为这样解出来的显然不是所需要的平面(即使n和p0都归一化),因为这增加的一维增加了自由度。另一方面,仿射空间向量减法不是一般的四维向量减法,它必须在归一化只做后前三分量相减,我们只能记: p[-]p0和n正交,其中[-]表示这种特殊的减法。
假设仿射变换为A,
A =
a11 a12 a13 a14
a21 a22 a23 a24
a31 a32 a33 a34
0 0 0 1
那么经过变换后发现和平面交点p0变为A*p0,平面上任一点p变为A*p,变换后平面的法向量设为m,显然要求有:
A*p [-] A*p0和m正交。设p0和p都是归一化的(以下向量不加说明均为归一化的),并令e4 = [0 0 0 1]',于是有:
A*p [-] A*p0= A*p - A*p0 + e4 = A*(p-p0) + e4 = Ah*(p [-] p0)
其中Ah是A的去除平移分量版本,即a14,a24和a34为0的A。
这样就有:Ah*(p [-] p0)和m正交。令A3=Ah的3x3主子式,p3、n3和m3分别是(p [-] p0)、n和m的的前三分量构成的三维分量(退化的三维向量),根据Ah的性质,可以发现这个正交和前一个正交分别等价于:
(A3 * p3)' * m = 0,p3' * n = 0
上述方程组的解包括:
m = inv(A3)'*n或m = -inv(A3)'*n
上述过程的意义是p [-] p0(即切向量)在仿射变换中应退化到3维空间讨论(与平移无关)。
需要注意,其中一个解只保证了和平面的垂直,但是没有保持法向量原有的方向。例如原先在曲面外侧的法向量在变换后应当仍保持在外侧。
法向量的方向保持只有通过以切向量内积求法向量达成。这个过程非常繁琐,而且我们本身知道的是法向量而并没有从切向量出发,因此我们在此只是构造一下,通过比较以找出这个保持方向的正负号。
所谓切向量集就是满足平面方程的p [-] p0,我们取其中两个线性无关的分别记做u和v,根据上面讨论,在三维空间中讨论,仿射变换后,他们变为A3*u和A3*v。规定平面法向量是已定的一对切向量的外积,即cross(u,v),这样变换后的法向量为cross(A3*u, A3*v)。下面对这两个外积进行展开,这个过程相对比较繁琐。
n = cross(u, v) = [u2*u3 - u3*v2, u3*v1-u1*v3, u1*v2-u2*v1]'
m = cross(A3*u, A3*v) = [(a2*u)*(a3*v) - (a3*u)*(a2*v), (a3*u)*(a1*v), (a1*u)*(a2*v)-(a2*u)*(a1*v)]'
其中a1、a2和a3分别为A3的第1、2和3行。
简便起见,我们只对m的第一个分量进行展开:
(a2*u)*(a3*v) - (a3*u)*(a2*v)
= (a21*u1 + a22*u2 + a23*u3)(a31*v1 + a32*v2 + a33*v3)
- (a21*v1 + a22*v2 + a23*v3)(a31*u1 + a32*u2 + a33*u3)
= (a22*a33 - a23*a32) * (u2*v3 - u3*v2)
+ (a23*a31 - a21*a33) * (u3*v1 - u1*v3)
+ (a21*a32 - a22*a31) * (u1*v2 - u2*v1)
再来看算式inv(A3)'*n的展开:
inv(A3)' =
1 [ M11 -M12 M13]
------- [-M21 M22 -M23]
det(A3) [ M31 -M32 M33]
这样,撇开最前面的系数,inv(A3)'*n的第一个分量为:
M11*(u2*v3 - u3*v2) - M12*(u3*v1 - u1*v3) + M13(u1*v2 - u2*v1)
M11 = a22*a33 - a23*a32
-M12 = a23*a31 - a21*a33
M13 = a21*a32 - a22*a31
在预料之中,这个结果和上述的第一分量一致。
但最终inv(A3)'*n和上述的结果相差1/det(A3)系数,我们这里关心正负性,因此在inv(A3)'*n之前需要补偿det(A3)的符号。
因此最终在仿射空间中,这个方向纠正的法向量可以通过 sign(det(Ah))*inv(Ah)'*n得出。