本文已参与[新人创作礼]活动,一起开启掘金创作之路
转载请注明出处.
图形学的数学基础(三十一):纹理映射(下)
纹理采样()
屏幕上每个单元我们称之为像素(),与之相对应,纹理上的一个单元我们称为纹素()。理想情况下,我们都希望每个像素和每个纹素是一一对应的。即纹理图片分辨率和物体一致,这样渲染的结果不会失真。只需要每个texel逐个读取即可。但现实情况是,纹理往往会过大或过小。针对这两种不同的情况,需要分别来处理。
纹理过小,一个小范围内的像素将对应同一个纹素,需要放大(),是一个上采样()的过程。纹理过大,一个像素覆盖很多个纹素,需要缩小(),是一个下采样()的过程。
纹理过小
想象将一张很小的图贴到物体上,如果使用着色点的uv取最近的纹素,物体上一个区域内的着色点将对应纹理上同一个纹素,这必然会造成失真(马赛克),为此我们需要根据当前着色点uv,取周围最近4个顶点(纹理网格顶点),通过加权平均计算当前着色的色值,缓解这种走样现象。 下面介绍双线性插值,实现不同的上采样纹理插值。
(1) 计算点相对于周围四个点的位置,方法是+0.5后保留小数,用范围为的表示 (2) 选取计算权重函数f,有多种函数可选择,计算横向和纵向插值的权重 (3)先后对横向和纵向两个方向进行插值(共三次,横向两次,纵向一次)。得到最终纹理值。
纹理过大
纹理过大,导致一个pixel覆盖了多个texel,会使生成的图像产生更明显的失真。近处产生锯齿,远处摩尔纹。为什么远处会产生摩尔纹呢?这种现象是光栅化的算法导致的。我们知道,一个三角形有顶点坐标和纹理坐标,纹理坐标范围是。光栅化的过程就是把三角形在屏幕上以一个个像素的形式显示出来,插值计算三角形内部每个像素的顶点的数据,包括常见的深度值与纹理坐标。如果这个三角形距离camera近,也就是说在屏幕上占了较多的像素,那么相邻两个像素的纹理坐标是接近的,这样通过纹理坐标获得纹理贴图上的纹素值也是接近的,这样这俩个像素看起来比较平滑,视觉上不突兀,同时gpu读取也快速,因为大部分纹素读取是在cache中。而如果这个三角形距离camera较远,也就是在屏幕上只占了很少的像素,这种情况就是一个小物体应用了一个大纹理,光栅化后,相邻两个像素的纹理坐标会差别好大,读出来的纹素也会差别很大,会很突兀,尤其是camera移动时特别明显,产生闪烁,火花现像,除此之外,gpu读取性能也很低效,因为两个相邻的像素所对应的纹素,一个可能在cache中,另一个还没有加载到cache中。
这种现象被形象的称为屏幕像素在texture空间的footprint。一个屏幕空间的蓝色像素点离相机越远,对应在texture空间的范围也就越大。其实也就是越来越欠采样,那么一种直观的解决方法就是Supersampling,如果一个像素点不足以代表一个区域的颜色信息,那么便把一个像素细分为更多个小的采样点不就可以解决这个问题了吗?但问题是这种超采样的方式造成很大的性能问题。我们可以换种思路,不做超采样,而是求出每个像素对应footPrint里所有texel的颜色均值,而这种技术正是Mipmapping。
基本思想就是,建立一系列不同尺寸的多级纹理,纹理采样时,计算对应的细节等级,再利用三线性插值()计算。
level 0代表的是原始texture,也是精度最高的纹理,随着level的提升,每提升一级将4个相邻像素点求均值合为一个像素点,因此越高的level也就代表了更大的footprint的区域查询。接下来要做的就是根据屏幕像素的footprint大小选定不同level的texture,再进行点查询即可,而这其实就相当于在原始texture上进行了区域查询。
计算像素点的
在屏幕空间中取当前像素点的右方和上方的两个相邻像素点(4个全取也可以,一个像素的大小),分别查询得到这3个点对应在Texture space的坐标,计算出当前像素点与右方像素点和上方像素点在Texture space的距离,近似得到footPrint,二者取最大值,计算公式如图中所示,那么level D就是这个距离的log2值 (D = log2L) 。
但是这里计算出的D值是一个带小数的值,而我们事先定义的levelD都是整数,这里可以通过函数分别取当前D值的上一层和下一层。分别对这两层纹理做双线性插值,最后在层与层之间再做一次线性插值,插值权重根据D值的小数部分来计算。
三线性插值
总结
在通过纹理采样取得纹理值后,并不一定直接将纹理值作为颜色使用,还有很多其他用途,例如中存储顶点法线,第一个pass存储相机在光照空间下的深度纹理等等,在中当作高度偏移值等等。
总结纹理映射的几个步骤。
1) 投影映射:将三维物体坐标转化为二维参数空间坐标,实时渲染中,坐标通常是保存在顶点信息中。
2) 变换函数:将坐标经过处理变换后,根据实际的纹理尺寸,转化为纹理空间坐标,此时也可能有小数。
3) 纹理采样:依据纹理空间坐标,对纹理进行采样,要处理放大和缩小两个情况,其中缩小的情况更为复杂,牵涉到各向异性过滤的算法。
4) 纹理转换:通过采样得到纹理值后,往往不能直接使用,还需要进行相应转换才能使用。