计算机图形学七:纹理映射(Texture Mapping)及Mipmap技术与凹凸映射(Bump Mapping)


在本篇文章中,我们会具体的讨论如何利用纹理映射让场景拥有丰富的颜色信息,以及纹理贴图精度大小所带来的问题,怎么去解决,最后将会介绍Bump Mapping和Displacement Mapping

1 纹理映射(Texture Mapping)

首先让我们一起来观察这样一张图:
计算机图形学七:纹理映射(Texture Mapping)及Mipmap技术与凹凸映射(Bump Mapping)
无论是球上的图案,以及地板的木头纹理都呈现出了不同的颜色信息,那么回想在讲解Blinn-Phong反射模型的时候曾提到,一个点的颜色是由其漫反射系数决定的,反射什么颜色的光,人眼就能看见什么颜色。那么针对上面这幅图,难道要去针对每一个点自己去设定一个颜色吗?还是说有什么更方便的方法呢?那便是纹理映射了!

我们可以将三维物体上的任意一个点都映射到一个2维平面之上,举一个简单例子,地球仪:
计算机图形学七:纹理映射(Texture Mapping)及Mipmap技术与凹凸映射(Bump Mapping)
倘若拥有从3维World space到2维Texture space的一个映射关系,那么只需要将每个点的颜色信息即漫反射系数存储在2维的Texture之上,每次利用光照模型进行计算的时候根据映射关系就能查到这个点的漫反射系数是多少,所有点计算完之后,结果就像最左边的screen space之中,整个Texture被贴在了模型之上。

有了Texture,有了映射关系,对渲染结果会有一个非常大提升,因为很多fancy的效果都可以通过texture的设计得到(当然这属于美术的活儿了,咱们用就行了)。可以看这样一个有点丑的例子。
计算机图形学七:纹理映射(Texture Mapping)及Mipmap技术与凹凸映射(Bump Mapping)
可以看到在利用texture渲染之后,这个独眼怪物丑的更有特点了。

好了,相信到这大家都对纹理映射了有了一个大概的了解,那么有了一张Texture之后,这种映射关系究竟是如何表示的呢?这就要从纹理坐标(UV)说起了。在纹理空间之内任意一个二维坐标都在[0,1]之内。如下图是一个可视化纹理坐标的结果:
计算机图形学七:纹理映射(Texture Mapping)及Mipmap技术与凹凸映射(Bump Mapping)
横轴和纵轴的最大值都为1,为什么整幅texture图可视化之后是红色和绿色呢?可以将(u,v)坐标的两点想象成red和green就能明白了。一幅Texture上的任意一点都可以用一个(u,v)坐标来表示(0<=u<=1,0<=v<=1),因此只需要在三维world space中每个顶点的信息之中存储下该顶点在texture space的(u,v)坐标信息,自然而然的就直接的得到了这种映射关系。至于一个顶点所对应在纹理空间的坐标是怎么得到的,这就并不是程序员们关心的了,美术大大们会帮我们最好的(当然读者有兴趣也可以自行搜集资料)。

有一种特殊的纹理称为tile,这种纹理的特征是重复拼接之后上下左右都是连续的,因此这种纹理可以复制很多张贴在墙面或地板上。
计算机图形学七:纹理映射(Texture Mapping)及Mipmap技术与凹凸映射(Bump Mapping)
一个具体的重复利用这种贴图的例子如下:
计算机图形学七:纹理映射(Texture Mapping)及Mipmap技术与凹凸映射(Bump Mapping)
最后给出一个纹理坐标使用的伪代码供参考:
计算机图形学七:纹理映射(Texture Mapping)及Mipmap技术与凹凸映射(Bump Mapping)
简而言之就是对每个光栅化的屏幕坐标算出它的uv坐标(利用三角形顶点重心坐标插值),再利用这个uv坐标去查询texture上的颜色,把这个颜色信息当作漫反射系数Kd。

好了在理解了纹理映射的基础之后,考虑如果纹理精度特别小(reslution低)或者纹理精度特别大(reslution大)会分别引起什么问题呢?

2 纹理过小和纹理过大的问题及解决方案

2.1 纹理过小引发的问题

纹理过小的问题相对容易理解,想想我们把一张100x100的纹理贴图应用在一500x500的屏幕之上必然会导致走样失真,因为屏幕空间的几个像素点对应在纹理贴图的坐标上都是集中在一个像素大小之内。那么如果仅仅是使用对应(u,v)坐标在texture贴图下最近的那个像素点,往往会造成严重的走样。
计算机图形学七:纹理映射(Texture Mapping)及Mipmap技术与凹凸映射(Bump Mapping)
如图中红色点是屏幕空间下一像素所对应在texture空间中的点,会去选择离他最近的那个橙色框起来的点。

这种方法是不可取的,接下来会介绍利用双线性插值的方法缓解这种走样现象。

2.2 双线性插值(Bilinear Interpolation)

我们依然取上图的点作为例子,解释双线性插值。
第一步,取出离红色点最近的4个黑色顶点,分别算出,该红色点在水平及竖直方向偏移的比率s,t,图示如下:
计算机图形学七:纹理映射(Texture Mapping)及Mipmap技术与凹凸映射(Bump Mapping)
接着先利用s,可以线性插值出如下图所示的u0,u1点的颜色值
计算机图形学七:纹理映射(Texture Mapping)及Mipmap技术与凹凸映射(Bump Mapping)
那么下一步相信读者也能猜到了,利用比例t,颜色值u0,u1插值出红色点的颜色值
f(x,y)=lerp(t,u0,u1) f(x,y) = lerp(t,u_0,u_1)
如此这样利用两次线性插值,考虑到了所有4个点的颜色值,能够很好的缓解走样失真现象,并且计算速度较高。

(tips:还有一种插值方法叫做双三次插值(Bicubic),是利用三次方程来进行两次插值,效果可能更好,但是计算速度很低不在这里具体讨论了)

最后以一张闫老师课上的例子看看这3种方法效果的对比计算机图形学七:纹理映射(Texture Mapping)及Mipmap技术与凹凸映射(Bump Mapping)
可能会看不太清。理解个意思就好

2.3 纹理过大引发的问题

可能对于我们的第一直觉来说,纹理小确实会引发问题,但是纹理大那不是更好吗,为什么会引发问题呢?但事实是纹理过大所引发的走样甚至会更加严重。
想象一张很大的地板,在上面铺满了重复的方格贴图,我们所期望看到的结果应该是这样的:
计算机图形学七:纹理映射(Texture Mapping)及Mipmap技术与凹凸映射(Bump Mapping)
嗯,非常符合透视关系,不错,当然这只是一个参考。再来看看利用在第一章所提到的计算纹理颜色的伪代码来计算的结果呢:
计算机图形学七:纹理映射(Texture Mapping)及Mipmap技术与凹凸映射(Bump Mapping)
近处锯齿!远处摩尔纹!非常严重的走样现象,为什么会导致这样的一个现象呢?这里作者尝试给出自己的两种解释:

1 如开头所说,地板上铺满了重复的方格贴图,根据近大远小,远处的一张完整的贴图可能在屏幕空间中仅仅是几个像素的大小,那么必然屏幕空间的一个像素对应了纹理贴图上的一片范围的点,这其实就是纹理过大所导致的,直观来说想用一个点采样的结果代替纹理空间一片范围的颜色信息,必然会导致严重失真!(从信号的角度来说就是,采样频率过低无法还原信号原貌)

2 换一种想法,考虑离相机很远的一个三角形面,假设该三角形面真正在纹理贴图上对应的一片区域有10个像素点。但是由于透视的关系,距离很远的三角形面投影到*面时可能只有1个或2个像素点的大小(远远小于10个像素的原来大小),那么这1个或2个像素采样texture的结果就要代表原来这个三角形面10个像素点的颜色信息,自然会导致失真!

(tips:可能有读者一开始会疑惑(包括我也是)为什么1个屏幕空间像素点覆盖多个纹理空间像素就是纹理过大呢,想象一下纹理贴图大小500x500,屏幕空间100x100,将屏幕空间的像素点均匀分布在纹理空间之中,那么1个屏幕空间像素点所占的平均大小就是5x5=25个纹理空间像素,因此这就是纹理过大所导致的结果)
计算机图形学七:纹理映射(Texture Mapping)及Mipmap技术与凹凸映射(Bump Mapping)
这种现象被形象的成为屏幕像素在texture空间的footprint。如上图所示一个屏幕空间的蓝色像素点离相机越远,对应在texture空间的范围也就越大。其实也就是越来越欠采样,那么一种直观的解决方法就是Supersampling,如果一个像素点不足以代表一个区域的颜色信息,那么便把一个像素细分为更多个小的采样点不就可以解决这个问题了吗?对,确实是这样,可以看看如下图512x超采样的结果计算机图形学七:纹理映射(Texture Mapping)及Mipmap技术与凹凸映射(Bump Mapping)
效果虽称不上完美但也极大缓解了走样现象,但问题是什么?计算量太大了,一个像素点被分为了512x512个采样点,计算量几乎多出了25万倍!这显然不是所希望看到的,并且随着屏幕空间的点离相机距离更远,更多的texels(纹理空间的像素)会在屏幕像素的一个footprint里面,会要更高的超采样频率。

那么另外一种想法,如果不去超采样,仅仅是求出每个屏幕像素所对应的footprint里所有texels的颜色均值呢?这也就是接下来所要介绍的著名的Mipmap技术了!

2.3 Mipmap

待补…