本文已参与[新人创作礼]活动,一起开启掘金创作之路
转载请注明出处.
图形学的数学基础(三十二):纹理应用(上)
在计算机图形学中,纹理贴图是使用图像、函数或其他数据源来改变物体表面外观的技术。例如,可以将一幅砖墙的彩色图像应用到一个多边形上,而不用对砖墙的几何形状进行确表示。通过这种方式将图像和物体表面结合起来,可以在建模、存储空间和速度方面节省很多资源。当然纹理不仅仅用于改变物体表面漫反射颜色,纹理本质上是一个数据集,可以用来存储任何我们需要的顶点数据,因此在方方面面得到了广泛的应用.本文将介绍MaterialMap、AlphaMap、BumpMap、NormalMap、ReliefMap、DisplacementMap、ParallaxMap、TexturedLight、ShadowMap、EnvironmentMap。 我们将以上纹理贴图的应用划分为6个大类:
- 控制着色信息
- 控制片元透明度
- 改变顶点法线
- 改变表面结构
- 阴影贴图
- 环境贴图
控制着色信息
根据可知,物体表面着色信息有以下因子来控制:
符号 | 解释 |
---|---|
物体表面环境光吸收率 | |
环境光强度 | |
到达物体表面的光线 | |
漫反射系数 | |
着色点法线 | |
光线单位矢量 | |
高光系数 | |
高光衰减因子 |
可以看到有许多可以调节的参数来控制着色点的着色表现。虽然可以赋予顶点更多的属性来改变这些参数,但是要做到亚三角形的细节,就需要使用各种纹理,对每个片元的着色参数进行调节,这些纹理映射方法统称为材质映射()。
最简单的就是漫反射映射,将纹理采样得到的值直接用于项,很直观也很简单,这里不做过多赘述。
此外还可以改变镜面反射系数,如粗糙度(决定高光衰减)和高管反射系数。
控制片元透明度
纹理都是矩形的,但当我们要实现各种贴画()或者镂空()效果时,往往不想让纹理贴满整个表面,也就是说一些地方的透明度为0,这时候就登场了。
所以不需要把片元当作半透明进行混合,而是进行透明度测试 (Alpha Test),将透明度小于阈值的 texel 认为是完全透明,直接抛弃片元,否则为完全不透明,测试完成后,再用 z-buffer 算法进行对所有完全不透明片元进行混合。透明度测试的伪代码如下:
if(texture.a < alphaThreshold) discard
但是透明度测试在使用时会存在问题:如下图,第0级纹理连续四个texel的透明度为 [0.0, 1.0, 1.0, 0.0],第1级纹理就为 [0.5, 0.5],假设我们设定alphaThreshold为0.75,可知第0级纹理有1.5/4通过测试,但是在第一级纹理中,所有的纹素的值都变成了0.5, 0.5 < 0.75所以,所有像素都被抛弃。
于是在不同的纹理等级中,经过透明度测试留下来的像素占比也不一样,因为高等级纹理是对低等级纹理的范围平均,因此随着levelD的增大,纹素值会趋于平均化,之前在阈值之上的值,被平均化以后很有可能到阈值线之下,因此被抛弃的像素就越来越多:
我们来看一个例子,如下图所示,当相机距离树较近时,看起来一切正常:
当相机拉远后,树叶消失了一部分(由于mimmap均值化后,导致被抛弃像素占比增加):
当相机拉远到相当一段距离后,发现树叶消失的更多:
一般解决办法有两个:
- 手动调节每级mipmap透明度,或者在shader中根据纹理等级d对透明度缩放
- 限制d的最大值
但这两种方法都只能是近似,不能很好解决问题,出现这个 问题的关键在于:不同 ,用同样的透明度阈值会得到不同的 Coverage(代表测试留存的像素比例)
Castano提出了一种:保证coverage一致的情况下,自适应确定透明度阈值,并对原透明度缩放调整的方法。
经过这种特殊处理后,我们发现远处的树木表现恢复正常了。
另外在对RGBA值进行线性插值时,要注意把alpha分量预乘到RGB分量,再进行插值:
比较实际的情况是:希望插值的结果偏向于不透明那边颜色的色调,所以通常而言,预乘后插值会比较合理。