Texture
一、总览
纹理映射本身属于光栅化阶段完成的事情,因为纹理说到底最后都会被画在像素上。纹理映射解决的核心问题有两个:
- 如何把世界空间的点映射到纹理空间。
- 如何解决纹理的走样问题。
关于问题一,思路有两个,以面为单位去思考纹理映射,也就是把纹理贴在整个面上,或者以网格为基础去思考纹理映射,也就是把纹理贴在每个三角形上。
关于问题二,其实还是抗锯齿的问题,不过有些不一样,纹理和像素的对应关系,有可能是一个像素对应多个纹理,这时需要采样(Sample)然后卷积,还有可能是一个像素对应的纹理不足一个,那就需要进行插值也就是重塑(Reconstruction)。
关于采样理论下文会有简单的介绍,不过说实话虎书的第十章我看的似懂非懂,现在重在整理整个图形学框架,所以知道个大概理念事后再一一深究也不迟。
二、世界空间到纹理空间的映射
2.1 以面为基础映射
这种映射的基本思路可以参考正交投影,假设世界空间中有一个平面,可以利用正交投影把整个纹理直接映射到这个面上,方法很简单,只需要去掉z轴然后把世界空间的坐标缩放成纹理空间大小就可以了,举这个例子主要是为了说明思路,虎书上说了球面的,立方体的,椭圆的面的纹理映射,基本思路都一样,就是以下两步:
- 把(x,y,z)当作参数去计算一个可以在平面上唯一确定的点(x’,y’)
- 把(x’,y’)缩放到纹理的大小从而实现世界空间的点到纹理空间点的映射
至于具体的例子这里就不说了,把握了思路去看例子是很清晰的,而且这个也不是重点。
2.2 以三角形为基础
对于以三角形为基础的映射,其实重点在于三个顶点的映射,只要映射完三个顶点,剩下的利用Rasterization文件中介绍的重心坐标插值就可以解决三角形内部的问题,而三角形的顶点纹理坐标一般在建模过程就已经提供了,值得一提的是三角形之间的缝隙和连接。
对于两个相邻的三角形来说,它们的公共边一定会以其中一个三角形为准进行插值,这种三角形的连续性和纹理的连续性就带来了一个问题,那就是公共边和纹理边界重合的时候,会导致纹理丢失。假设纹理本身是围在一个圆柱侧面,纹理图片是一张长方形的图片,那么一定存在一个地方是纹理首尾相接的位置,纹理的首边和尾边是不同的,但是如果这个首尾相接的地方正好是三角形的公共边,那一定会导致一个情况,首边或者尾边的纹理会被丢失,因为公共边如果按照首边的三角形映射就丢失了尾边,反之亦然。
解决这个问题的办法就是把三角形分开,也就是把公共边分开,也可以说把这条边的顶点复制一份,这样就不存在公共边,自然也就没有了问题。
三、纹理的反走样
3.1 信号处理基础知识
采样的数学表达其实就是在一个连续的函数上取点,以二维为例,只要这个点取得足够多,那就可以根据这个采取的点大致看出函数的本来面貌,相反,如果采的点太少,就会导致看不出也无法还原本来的函数。采样函数本身不是直接取点那么简单,这是一种方法,这种方法用的函数叫做冲击函数,但是更多时候采样其实是做平均或者加权平均。
重塑是建立在采样基础上,利用采样点为基础,用重构函数去把采样点平滑连接起来以达到再现函数的目的,如果采样点不够多,或者说采样频率不够高,就会导致重构出的函数和原函数完全就是两个函数,这就叫做alais,也就是失真,失真的程度不光和采样频率有关,也和重构函数有关。
从采样和重构的原理不难看出,对于函数上某些变化特别剧烈的部分,例如不可导的尖锐点,是很难还原的,这也就是所谓的高频部分会被丢失,为什么说这种部分是高频部分呢?因为变化快,用二维来讲就是自变量的变化引起应变量的变化足够剧烈,高频这个词本身出自电子信号的处理,电子信号的采样和还原也一样,对于那些变化快频率高周期短的电子信号还原难度是很大的,不可避免要导致失真。这也是为什么采样函数和重塑函数会被称为采样过滤器和重塑过滤器的原因,因为在这个过程自然就把高频信号滤掉了。
这里还要引入一个概念,重采样,这是为什么呢?其实就是为了过滤高频信号,先把一定范围的函数值做一个平均,然后采样函数进行采样的时候就可以根据之前每一部分做的平均再进行采样,这样可以一定程度避免完全漏掉高频信号,也可以使得采样和重塑的结果更加平滑。
那么,在图形学这一切有什么用呢?答案在光栅化过程中,当你把某个图元画在屏幕上的时候,就要先采样再重塑,这个过程同样也适用于纹理映射,首先把纹理点采样,然后在屏幕上重塑。
3.2纹理的反走样:一个像素对应多个纹理
在这种情况下,其实本质就是一个对多个纹理点做卷积的过程,问题在于,怎么确定要对那些纹理点做卷积,也就是说,像素的边界点映射到纹理空间后会到哪里,只要确定了边界点,就可以画出图形,由于这是从2D到2D的映射,所以写出如下等式:
Y
(
x
⃗
)
=
Y
(
x
⃗
0
)
+
J
(
x
⃗
−
x
⃗
0
)
J
=
[
d
u
d
x
d
u
d
y
d
v
d
x
d
v
d
y
]
Y(\vec x)=Y(\vec x_0)+J(\vec x-\vec x_0)\\ J=\left[\begin{matrix} \frac{du}{dx} \frac{du}{dy}\\ \frac{dv}{dx} \frac{dv}{dy} \end{matrix}\right]
Y(x)=Y(x0)+J(x−x0)J=[dxdudydudxdvdydv]
其中
Y
Y
Y就是要求的纹理坐标,
J
J
J是一个2*2的矩阵,至于导数的计算,需要用到本身建模过程中提供的纹理和模型的原始信息。这是一个比较理想的方式,但是就实际来说,计算导数的开销是很大的,所以说采用了MipMap等一系列方式,说白了这是一个采样问题。
3.3 纹理的反走样:一个像素对应不足一个纹理点
这种情况下其实就是一个插值问题,也是一个重塑问题,根据已有信息来进行插值出需要的信息,基本思路就是双线性插值,具体的不做过多阐述。
四、纹理映射的应用
4.1 凹凸表面
要实现一个表面的凹凸怎么做呢?有以下三种思路:
- 物体表面本身不做任何改变,但是映射到物体表面的纹理法向量不同,因而导致了不同的视觉效果
- 在物体模型不做改变,但是在纹理表面每一点处多加一个深度属性z,根据z=f(x,y)的导数来计算法向量
- 物体模型本身在顶点处理是就做了改变
这和纹理映射有什么关系呢?因为123其实都是在纹理上做了手脚,需要注意12这种情况下,由于物体表面本身就没变,所以阴影是不会体现物体本身的凹凸特性的。
4.2 其他应用
虎书上面还提到了很多其他应用,我就不一一列举了,搞懂最基本的原理是要紧的。