矩阵求逆计算的实现
学习计算机图形学,那么就应该了解计算机图形学的一些基础知识,比如说向量、矩阵、欧拉角以及四元数。其中矩阵尤其是4×4的矩阵在其中是比较重要的一部分了。因为它包含的信息多,一个4×4的矩阵可以表示平移(translation)、旋转(rotation)、缩放(scaling)以及错切(shearing)以及它们组合后的变换(transform)。其中平移、旋转是刚体转换、在三维程序中较常使用,而缩放以及错切由于改变了三维模型的形状,在实际开发中较少使用。为了表示三维图形的逆变换,引入了逆矩阵的概念。逆矩阵使得一些效果比如公告板效果的实现成为了可能。
在上大学本科的时候,我们了解到了求矩阵的逆的方法主要有两种,第一是通过计算让其成为倒三角矩阵,然后计算其逆矩阵;另外一种方法是将矩阵和单位矩阵从左至右拼接起来,通过运算让单位矩阵移至左边,那么该矩阵的逆就到了右边了。矩阵的逆也有可能不存在,因为它的行列式值有可能为0,这种情况是无法求出矩阵的逆的。
实际开发中,我们采用广泛使用的矩阵求逆算法:高斯 - 约旦法。有关高斯 - 约旦法的原理我也不甚了解,但是介绍高斯 - 约旦法的代码还是很多的,网上一搜就有一大堆。如果大家感兴趣这种算法的话,那么可以去到维基百科以及它提到的文献寻个究竟。
这里我也给出自己对于这种方法的实现。这里将代码贴出来,以便日后进行查阅,同时也能够方便需要同行们。
/*---------------------------------------------------------------------------*/ bool Matrix44F::Invert( void ) { // 使用全选主元高斯 - 约旦法 quint32 is[4]; quint32 js[4]; float det = 1.0f; int f = 1; #define MATRIX( row, col ) m[row * 4 + col] for ( quint32 k = 0; k < 4; k++ ) { // 第一步,全选主元 float max = 0.0f; for ( quint32 i = k; i < 4; ++i ) { for ( quint32 j = k; j < 4; ++j ) { const float f = fabsf( MATRIX( i, j ) ); if ( f > max ) { max = f; is[k] = i; js[k] = j; } } } if ( fabsf( max ) < 0.0001f ) return false;// 不能求逆 if ( is[k] != k ) { f = -f; qSwap( MATRIX( k, 0 ), MATRIX( is[k], 0 ) ); qSwap( MATRIX( k, 1 ), MATRIX( is[k], 1 ) ); qSwap( MATRIX( k, 2 ), MATRIX( is[k], 2 ) ); qSwap( MATRIX( k, 3 ), MATRIX( is[k], 3 ) ); } if ( js[k] != k ) { f = -f; qSwap( MATRIX( 0, k ), MATRIX( 0, js[k] ) ); qSwap( MATRIX( 1, k ), MATRIX( 1, js[k] ) ); qSwap( MATRIX( 2, k ), MATRIX( 2, js[k] ) ); qSwap( MATRIX( 3, k ), MATRIX( 3, js[k] ) ); } // 计算行列值 det *= MATRIX( k, k ); // 计算逆矩阵(第二步) MATRIX( k, k ) = 1.0f / MATRIX( k, k ); // 第三步 for ( quint32 j = 0; j < 4; ++j ) { if ( j != k ) MATRIX( k, j ) *= MATRIX( k, k ); } // 第四步 for ( quint32 i = 0; i < 4; ++i ) { if ( i != k ) { for ( quint32 j = 0; j < 4; ++j ) { if ( j != k ) MATRIX( i, j ) = MATRIX( i, j ) - MATRIX( i, k ) * MATRIX( k, j ); } } } // 第五步 for ( quint32 i = 0; i < 4; ++i ) { if ( i != k ) MATRIX( i, k ) *= -MATRIX( k, k ); } } for ( qint32 k = 3; k >= 0; --k ) { if ( js[k] != quint32( k ) ) { qSwap( MATRIX( k, 0 ), MATRIX( js[k], 0 ) ); qSwap( MATRIX( k, 1 ), MATRIX( js[k], 1 ) ); qSwap( MATRIX( k, 2 ), MATRIX( js[k], 2 ) ); qSwap( MATRIX( k, 3 ), MATRIX( js[k], 3 ) ); } if ( is[k] != quint32( k ) ) { qSwap( MATRIX( 0, k ), MATRIX( 0, is[k] ) ); qSwap( MATRIX( 1, k ), MATRIX( 1, is[k] ) ); qSwap( MATRIX( 2, k ), MATRIX( 2, is[k] ) ); qSwap( MATRIX( 3, k ), MATRIX( 3, is[k] ) ); } } float value = det * f; Q_UNUSED( value ); #undef MATRIX return true;// 可以求逆 }