OpenGL中的矩阵Load
OpenGL的教程里说,OpenGL用的是列主序(column-major)矩阵,是的没错。而红宝书里面遮遮掩掩地讲全局坐标和局部坐标的时候,却没有说清楚Coder应该怎样传矩阵给OpenGL库,而只是说,使用全局坐标的时候,需要使用 Translate*Rotation*Scale*V(向量左乘)方式进行矩阵乘法,所以矩阵操作函数需要像这样:
glLoadIdentity();
glMultMatrixf( Translate );
glMultMatrixf( Rotation );
glMultMatrixf( Scale );
正常来说,如果OpenGL里需要列矩阵,那么也许你会保证传给它的是列向量。如果你自己实现了数学库,用的是行主序(row-major),而只使用glLoadTransposeMatrixf()或者glMultTransposeMatrixf()函数,并且按照红宝书所谓的全局坐标(即向量左乘法)来进行矩阵运算,OK,程序很正常。
问题没有到此为止,如果很不巧你使用了自己数学库里的右乘向量矩阵乘法函数(这样更符合思维习惯,D3D就这么做),并且仍然Transpose之后再进行Load或者Multiply,结果就不正确了,如果不进行Transpose而直接进行Load或者Multiply,结果确实正确的,诡异吗?是的…但是为什么会产生这样的结果?通过观察(OpenGL的矩阵操作隐藏得太深,没法看),OpenGL的FixPipe变换实际上是这样的形式:
Projection*View*Translate*Rotation*Scale*V
换成向量右乘,事实上是 V*Scale_T*Rotation_T*Translate_T*View_T*Projection_T ( _T 代表转置)
反过来说,如果基本变换矩阵是基于右乘才会产生意义,需要左乘某个矩阵产生同样的变换的话,就需要将这个矩阵进行转置。看看上面的变换,行向量不Transpose就进行Load或者Multiply的话,就会按原本的形式存进pipe,但请注意,Load进去的向量是用来被左乘向量的,这个行向量被直接当作列向量来处理了,其实这就是一步被隐藏着的转置,所以,它才能产生正确的变换结果。
那么如果多个行矩阵相乘以后,Load到OpenGL状态机中还是能够工作吗?
V*Scale_T*Rotation_T*Translate_T = ( Translate*Rotation*Scale )_T
这下可以明白,通过这种“隐含”的转置,OpenGL能够Load或者Multiply组合过的行变换矩阵,产生正常的结果。
顺便一提:红宝书里处于对OpenGL的性能考虑,所以建议不要用自己定义的单位矩阵Load到状态机中,而应当使用glLoadIdentity()。尽量不要使用自己的矩阵运算,例如上面提到的自己写的MathLib。自己写的矩阵运算并没有OpenGL内建的运算速度快,且还会加重OpenGL内部的通信负担。除非需要用四元数等OpenGL没有内建的工具,或者实现自己的软渲染引擎。不过话说回来,OpenGL实在是太不好调试了,还是自己的MathLib透明些,不过得花些时间,而且,Debug任重道远…