图片边缘出现黑点的问题分析和解决(纹理过滤)
最近在项目中遇到UI图片非透明区域边缘出现黑色杂点的问题,经过分析和纹理过滤有关,并提出解决方案,需要美术制图时特别注意。本文是此问题的分析与解决方案。
问题
- 我的项目使用的FairyGUI,然而此问题与UI框架无关。
上图为出现问题的图片,一个绿色的环形图片,周围为透明。
通过修改渲染所用shader,并在片源着色中修改out颜色的alpha = 1,可以获得排除透明通道的颜色图。
对比两张图片可以看到,有一些#00000000的色块在和绿色部分相交的地方产生这些黑块。
那么可以想见就是这些相交的区域产生了这些黑色杂点。为什么会产生这些杂点呢?下面将会分析
纹理过滤(texture filter)
我们的纹理是要贴到三维图形表面的,而三维图形上的pixel中心和纹理上的texel中心并不一至(pixel不一定对应texture上的采样中心texel),大小也不一定一至。当纹理大于三维图形表面时,导至一个像素被映射到许多纹理像素上;当维理小于三维图形表面时,许多个像素都映射到同一纹理。
纹理过滤一般常见的有四种:
1. Nearest Point Sampling(最近点采样)
最近点采样取最接近的图片上的像素点进行采样。
2. Bilinear(双线性过滤)
双线性过滤以pixel对应的纹理坐标为中心,采该纹理坐标周围4个texel的像素,再取平均,以平均值作为采样值。
3. Trilinear(三线性过滤)
三线性过滤以双线性过滤为基础。会对pixel大小与图片像素大小最接近的两层Mipmap level分别进行双线性过滤,然后再对两层得到的结果进行线性插值。
4. Anisotropic Filtering(各向异性过滤)
各向同性的过滤在采样的时候,是对正方形区域里行采样。各向异性过滤把纹理与屏幕空间的角度这个因素考虑时去。简单地说,它会考滤一个pixel(x:y=1:1)对应到纹理空间中在u和v方向上u和v的比例关系,当u:v不是1:1时,将会按比例在各方向上采样不同数量的点来计算最终的结果(这时采样就有可能是长方形区域)。
问题分析
通过了解纹理过滤的定义,我们可以很清楚的意识到,几种过滤方式中后三种都使用了插值运算。也就是说插值出的像素点都会是一个新的颜色。只要使用过插值运算,就有可能会产生图片上本来不存在的颜色。
如过存在插值,那么下面2*2的图片被渲染为2倍大小的话就会出现类似这种情况(实际上并不是完全相同,具体结果间上面不通纹理过滤的算法)
a | b |
白色不透明 | 红色透明 |
白色不透明 | 红色透明 |
==变为===>>>>>>
a | b | c | d |
白色不透明 | 粉色半透明 | 粉色半透明 | 红色透明 |
白色不透明 | 粉色半透明 | 粉色半透明 | 红色透明 |
白色不透明 | 粉色半透明 | 粉色半透明 | 红色透明 |
白色不透明 | 粉色半透明 | 粉色半透明 | 红色透明 |
其中粉色半透明就是新产生的颜色。
也就是说黑色透明色 #00000000 和绿色不透明色#00ff00ff之间在插值过程中产生了灰色半透明,就是这些边缘的杂点。
问题确认
看一下图片的导入设置中,Filter Mode中的选项,选择了Bilinear 即在纹理过滤是使用的模式是 双线性过滤 ,使用了插值。
改成Point ,即使用 最近点采样 。再看效果
黑点消失了,但是边缘变得不整齐了。这也是最近点采样的缺点。
问题解决
很简单,美术作图的时候尽量避免使用橡皮擦。保证一个透明过度关系应该为:
绿色不透明 -> 绿色半透明 -> 绿色透明 -> 黑色透明
这样插值就不会插出灰色半透明之类的情况。