3D 图形光栅化的透视校正问题
写了文章《如何写一个软件渲染器》以后,不少网友希望进一步解释背后的数学公式,询问以及自己加一个 phong 光照该如何加,本文将对透视纹理映射的插值原理做一个简单的解释,希望能帮助到大家:
透视纹理绘制发生在最后阶段,坐标已经完成projection,剔除,裁剪了,然后顶点/w,开始批量绘制扫描线之前,这时候开始计算纹理的位置。
使用w还是用z,关系不大,早年的3d引擎,直接/z的,只是后面标准化了以后,发现w更好用,可以同时表示透视投影和正交投影。同时顶点经过标准的mvp矩阵运算后,w和z是承线性关系的,方便对z/w做 [0,1] 的cvv裁剪。你可以理解成w就是另外一个z。以前屏幕坐标:
x' = x / z * d + A
y' = y / z * d + A
现在是
x' = x / w * d + A
y' = y / w * d + A
然后绘制纹理前,你需要先证明屏幕上两个点之间,1/w 承线性关系,即屏幕上两个点X1’, X2’之间任意取一点X3’,他们的(1/w)值的变化比例相同,即在 t 取任意值有:
x3' = x1' + (x2' - x1') * t
(1 / w3) = (1 / w1) + ((1 / w2) - (1 / w1)) * t
再根据他们在同一个平面上,证明屏幕上两个点之间,u/w, v/w 承线性关系,即 t 取任意值有:
x3’ = x1’ + (x2’ - x1’) * t (u3 / w3) = (u1 / w1) + ((u2 / w2) - (u1 / w1)) * t (v3 / w3) = (v1 / w1) + ((v2 / w2) - (v1 / w1)) * t
具体到代码里面的做法就是三角形的三个顶点/w以后,u和v也同时/w,然后把w换成自己的倒数:w = 1 / w,及把顶点数据:
(x, y, z, w) + (u, v)
变换成:
(x / w, y / w, z / w, 1 / w) + (u / w, v / w)
然后用 1/w, u/w, v/w进行屏幕空间插值,具体绘制某个点的时候,先从1/w求倒得到w,然后乘以 u/w, v/w得到 u, v,就可以了。
更进一步,可以证明,所有在三维空间里同x,y,z成线性关系的变量,不管是纹理坐标,顶点色或者法向还是其他,他们在屏幕空间里的插值规则都可以通过:插值前先/w,插值后要用时再 * w得到具体值,然后我们把这类三维空间里同x,y,z成线性关系的变量统进行统一的批量处理,和OpenGL的 attribute,varying处理方法相同。
相关阅读: 如何写一个软件渲染器 还原被摄像机透视的纹理