Barycentric Coordinates
重心坐标 Barycentric Coordinates,重心坐标我们已经在作业2中介绍过了。
重心坐标有什用呢?在图形学中我们可以通过重心坐标使用三角形的三个顶点进行插值得到三角形内部点的属性。
在三角形的顶点上我们可以定义许多属性,例如:
- 法线
- 颜色
- 纹理坐标
- 深度
- 等等
例如我们想要内部点的法线,那么我们就可以通过重心坐标插值获得内部点的法线
而具体定义如下
在三角形 $ABC$ 所在的平面内,任何一个点 $P$ 都可以表示为三个顶点坐标的线性组合: $$P = \alpha A + \beta B + \gamma C$$ 其中 $\alpha + \beta + \gamma = 1$。这三个系数 $(\alpha, \beta, \gamma)$ 就是点 $P$ 的重心坐标。 而对于三角形内部的任何一个点$P$,它与三角形三个顶点之间进行连线可以组成三个三角形,而重心坐标 $(\alpha, \beta, \gamma)$ 与面积的关系如下: $$\alpha = \frac{\text{Area}(P, B, C)}{\text{Area}(A, B, C)}$$ $$\beta = \frac{\text{Area}(P, A, C)}{\text{Area}(A, B, C)}$$ $$\gamma = \frac{\text{Area}(P, A, B)}{\text{Area}(A, B, C)}$$
对于一个三角形的面积我们可以使用这个三角形的两个边组成的向量进行叉乘然后求模再除以2获得。
有了重心坐标我们就可以通过插值的方式获取三角形内部的属性了。例如:
法线插值 $n = \alpha v_0.n + \beta v_1.n+ \gamma v_2.n$ 而对于深度值我们需要使用: $$\frac{I}{w_P} = \alpha \frac{I_0}{w_0} + \beta \frac{I_1}{w_1} + \gamma \frac{I_2}{w_2}$$ 你可能会问为什么不是 $z = \alpha v_0.z + \beta v_1.z + \gamma v_2.z$
这是因为透视投影是非线性的,在 3D 空间中是线性的属性,经过透视投影变换到 2D 屏幕空间后,就不再是线性的了,如果你直接在屏幕空间用重心坐标插值,会导致纹理扭曲或深度错误,为了修正这个问题,我们需要利用齐次坐标中的 $w$ 分量进行透视矫正插值。在屏幕空间插值任何属性 $I$。 并且有时候我们需要将插值后获得的深度逆变换到三维空间中使用。
所以在使用顶点的纹理坐标进行插值后我们可以获得内部点的纹理坐标,然后我们使用这个纹理坐标在纹理上查询一下这个纹理的颜色值,最后应用到漫反射的$k_d$系数上,最后配合高光项和环境光就能渲染出一个带有纹理的物体了。
但实际上我们不能这么简单的做,这么做回带来什么问题呢?
Texture Magnification
双线性插值
当我们有一个很大的物体,但是纹理图非常小,那么这个物体的纹理就会失真。
这是因为我们的物体上的三角形插值得到的纹理坐标是小数的,为纹理上的每个像素的索引都是整数,所以我们会对插值的到的纹理坐标进行round,就会导致物体上不同的像素使用同一个纹理像素,
所以我们可以通过一种方式对纹理进行模糊操作让其有更高的精度,而这种方式就是双线性插值。

上述图介绍了三种过滤(模糊)方式,分别是
- Nearest :最近邻过滤,使用纹理坐标最近的纹理像素
- Bilinear :双线性过滤,使用理坐标最近的 2×2 区域,进行加权平均
- Bicubic :双三维过滤,使用纹理坐标周围 4×4 的像素矩阵
Bilinear Interpolation
在介绍双线性插值之前,我们先介绍一下线性插值
Linear Interpolation
线性插值就是通过两个点和一个参数来获取两个点之间的点的操作,公式如下
$$lerp(a, b, t) = a + t(b - a)$$例如,例如我们要获取点$a$和点$b$之间的中点,那么这个点可以表示为:
$$mid = lerp(a, b, 0.5) = a + 0.5(b - a) = \frac{b+a}{2}$$当然不仅仅可以插值点,还可以插值颜色
Bilinear Interpolation
而双线性插值就是三次线性插值,如图我们可以分别对$u_00$和$u_10$,$u_01$和$u_11$,使用参数$s$,进行一次线性插值得到$u_0$和$u_1$,我们再使用得到的这两个点使用参数$t$进行线性插值,最后的到我们要的像素颜色

TODO下面这段没写完(今天太累了),需优化
Mipmap
上面是纹理比模型小的问题,下面我们来看看纹理比模型大的问题
当我们直接使用插值得到的纹理坐标进行纹理映射,那么我们会得到上面这幅图的情形,摩尔纹和锯齿。
我们来分析一下这个问题,在近处一个像素覆盖到纹理上的区域相对较小,而远处一个像素覆盖到一个纹理的区域相对较大,如果一个近处的像素查询纹理的值由于这个像素覆盖的纹理的大小和像素差不多大,那么我们得到的就是我们想要的值。可是如果一个远处的像素,我们他在纹理上覆盖了很大的位置,而我们只使用这个区域的中心的纹理像素颜色作为查询结果,这就会导致这些问题。我们可以使用SSAA来解决这个问题,但是太费时间了!
这是一个走样的问题,而在这里近处可能是纹理像素的频率跟不上像素的频率,而远处可能是像素的频率跟不上纹理像素的频率导致走样的。所以我们需要让不同位置的纹理的频率与像素的频率统一起来,怎么做呢?生成包含多张分辨率的图片,这个方法叫做Mipmap

