在3D图形处理器绘制三角形时,每个像素处的属性信息(包括颜色、纹理坐标等)是对扫描线左右端点对应的顶点属性信息进行插值运算得到的。
如下图所示对投影面上相等的空间步长L与R,它们在三角形面上对应的步长会随着离摄像机的距离的增加而变长,即L’>R’。因此对于处于L与R之间的那个像素点,虽然在投影面上其坐标处于P1与P2之间的3/4处,但是其在眼空间中的对应点并非处于X1与X2之间的3/4处,而顶点的属性信息却又都是在投影变换前的空间中指定的。
因此对像素属性信息的插值不能是简单的线性,尤其是纹理坐标在用线性插值时会出现明显的失真。那么应该怎么办呢?方法就是如下的“透视校正插值”。
可以证明,深度值z坐标的倒数恰好是按线性方式进行插值的:如上图,眼坐标系下端点坐标为、,其在投影面上投影坐标为、,如果有一个插值点
,则其眼坐标系下的深度值关系为式(1):
(1)
推导过程见《3D游戏与计算机图形学中的数学方法》66页,或英文版《Mathematics for 3D Game Programming and Computer Graphics》Second Edition, pp118。
在网格模型中顶点属性是与顶点的坐标值成线性关系,因此利用此特性对顶点属性如纹理坐标进行插值:
(2)
其中由上述公式(1)计算得到。
一般我们的顶点程序计算完的坐标都是投影坐标下的,那么我们的显卡是如何知道顶点在眼坐标下的z坐标的呢?看看我们的投影矩阵吧。OpenGL的glFrustum()函数的生成的透视投影矩阵如下:
一个眼坐标系下的顶点(x,y,z,w)乘完这个透视投影矩阵后成为(x',y',z',w'),我们发现w'坐标就等于原眼坐标系下z坐标的负值-z。这样显卡在对顶点属性进行插值时就利用这个w'坐标来进行z倒数的插值了。岂不妙哉?
利用这些知识,我们来做一件事:知道三维空间中眼坐标系下一条线段的两个端点坐标,要求线段上一点使得其在投影面上的投影点在两个端点的投影点正中间,那么应该怎么求这个点呢?可不能是,而应该是先利用公式(1)对z倒数进行插值得到z坐标,然后再对x/z、y/z分别进行插值得到x、y坐标。
ps:Blinn曾经对透视校正插值进行了深入讨论,他的三篇文章:“Hyperbolic Interpolation”,“Jim Blinn's coner:A Trip Down the Graphics Pipeline.","W Pleasure, W Fun".