写了文章《如何写一个软件渲染器》以后,不少网友希望进一步解释背后的数学公式,询问以及自己加一个 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 处理方法相同。
相关阅读:
如何写一个软件渲染器
OpenGL / DirectX 如何在知道顶点的情况下得到像素位置?
还原被摄像机透视的纹理
最近正好遇到这个问题。多谢!