在使用OpenGL进行三维建模工作的时候,gluProject无疑是必须使用的最重要的函数之一,但是我在很长一段时间内一直没有弄明白这个函数的内在工作过程,相信很多作相关研究的新手也并不真正清楚(我本身也是新手)。OpenGL红宝书中的相关讲解也只是针对函数的用途、参数、注意事项等的说明。当我们需要自己需要加载定标参数来写一个新的投影函数,或者说将我们定标得到的原始参数扩展成为符合OpenGL对应的模型视图矩阵、投影矩阵、视口向量的时候,我们必须知道OpenGL的这个函数究竟是如何工作的。本文只就gluProject的工作过程进行分析,对于各种定标参数矩阵的扩展等内容将在以后的文章中说明。
先来揭秘一下gluProject函数的本来面目(源代码):
从这个源码中我们可以看出以下几点:
(1)观察transform_point函数中矩阵元素和列向量的相乘过程可知,这个矩阵是被转置后再和列向量相乘的。这就是为什么说OpenGL的矩阵相乘是遵循列主元的,而我们使用gluProject函数的时候输入的矩阵参数却是按照行主元的方式。
(2)从列向量被变换的顺序可以看出,一个齐次表示的3D点是先经过模型矩阵变换(蕴含照相机的位移、旋转、缩放等几何变换),在进行投影变换(蕴含照相机的内参数,即本身的参数),最后使用视口向量将齐次表示的2D点限定在当前的视口内。
(3)根据函数的输入输出可以看出,输入的3D点是通过直接将向量的第四个元素in[3]赋值为1而齐次化的,输出的齐次表示其实是(winx,winy,1)。winz并不是投影在视口内的2D坐标的齐次表示的第三个元素,它携带的含义是多方面的:表面上看来,它表示了在视口上的深度值;实际上,它是由于照相机参数的不精确性(所有照相机模型都是对实际照相机的模拟,因而不可能完全精确的表达照相机本身)和矩阵相乘过程中的计算误差所造成的;因此假如照相机参数对照相机的建模是完美的,并且计算过程是没有误差的,那么winz的最终结果应该是0,在实际的计算中此参数所返回的值也应该是一个逼近于0的数,这个数的大小反映了误差的总量。所以我们在使用gluProject时通常不必关心winz返回的参数。
(4)对于返回的(winx,winy),论坛上曾有朋友发问说为什么得到的投影点和实际的点是关于图像上下倒置的。这个是因为OpenGL窗口坐标系统默认(0,0)点是窗口的左下角,而通常Windows窗口和一般图像坐标系的(0,0)点默认在左上角,因此,当我们在这种不一致的坐标系之间进行点的运算时,要先进行一步winy = height – winy; 的转换。
(李文凯,2009年5月7日,于HIT-VILab)