解决UI黑边问题,Alpha Bleeding技术简介
问题描述
前段时间美术那边反馈部分UI有一圈黑边,而且同一个UI元素在不同位置,黑边的宽度还不一样,大概在1到2个像素,非常不美观。具体效果如下图:
其中涉及到2张图片,如下:
我们通过PhotoShop具体放大看下图片的边缘,可以看到2张图片的边缘都是没有黑边的,其中黄色块的边缘有1圈透明像素。如下:
问题分析
1. 检查图集
一般我们使用UI图片都会打成图集再使用,这里也不例外,所以第一步当然要检查下生成后的图集是否有问题。具体来说就是检查图集的padding是否过小。
这是最简单最常见的错误,padding过小,也就是图片之间的距离过小,会导致渲染时出现采样问题。一般来说UI元素都是采用线性的,所以采样时会将附近的像素进行混合,如果padding不够大,就会混合到旁边图片的像素,导致出现一些黑边问题。
一般来说,padding设置成2个像素基本就没问题了。
2. 采样问题
上面的问题很容易解决,我相信大部分开发者都不太会在这里栽跟头,我们也不是。考虑到黑边时隐时现,估计会是采样问题。
后来在OpenGL的论坛里找到一篇回答:Blending and alpha - black border,里面有提到类似的问题,以及给出了解决方案。
简单来说,问题出在这2个非常简单的颜色块上,就是因为它们太简单了,其边缘没有任何的渐变或者羽化,我们可以叫它硬边缘。上面说到UI渲染一般都是线性采样(不然很容易有锯齿),那么当采样到这些硬边缘时,以白色块为例,白色是(1,1,1,1)
,边缘是完全透明的(0,0,0,0)
,那么线性采样后,就变成了(0.5,0.5,0.5,0.5)
,也就是灰色,这就是出现黑边的原因。
为什么其他UI元素没这个问题?其实所有图片或多或少都有这个问题,只是很多UI图片元素,其边缘不规则,而且有一定的过渡,所以一般看不出来。
解决方案
方案很多种,这里先讲一些不太现实的方案,最后讲一个非常简单的方案,大家没兴趣的可以直接看最后的方案4。
1. 用点采样
既然线性采样会有问题,那么改成点采样是不是OK了?是的!
不过这也有很多限制,一般采样模式是和图片绑定的,一张图片只能有1个采样模式,如果你出问题的图片打成图集了,那么就意味着同个图集里所有图片都要用点采样。那为啥大家都不用点采样呢?当然就是因为它的效果不好,锯齿很严重,所以选择用这个方案,你最好把出问题的图片独立抽出来,减少影响面。
2. 用AlphaTest
在渲染时,一般有2种Alpha的渲染模式:AlphaBlend和AlphaTest。
如果你所有UI的图片,其Alpha是二元的,也就是说,要么是全透明,要么完全不透明,不存在半透明的图片。那么你可以考虑用AlphaTest,也就是把低于某个Alpha值的像素在渲染时直接丢弃掉,这样通过线性采样得到的边缘灰色像素,由于其Alpha值不会大于0.5,那么通过AlphaTest就可以把那些讨厌的黑边丢弃了。
不过没有半透明这个限制对大部分游戏来说,都不太能接受,除非你的UI是极简风格的,或许可以考虑。
3. 不用图集
这个Bug的根本原因就是采样硬边缘导致的,这些硬边缘就是因为打成图集时,图片和图片之间加上了一些透明像素做padding导致的。这样的话,我们不打成图集不就可以了?确实,对于硬边缘的图片可以考虑这个方案,像上面的灰色底图,如果采用单张图片,那么就不会有黑边问题,不过黄色块还是会有问题,因为它图片本身就有一圈透明像素!
很多时候,美术出图时,会考虑通过添加透明边缘把图片做成一样大,方便后续对齐,所以如果要采用这个方案,你也要留意下有没有这种图片存在。
4. 修改边缘像素
这是效果最好的方案(当然也有一些缺点,后面会讲到),上面讲到线性采样后,2个颜色混合成灰色了,那么如果边缘的颜色不是(0,0,0,0)
,而是(1,1,1,0)
呢?这样的话,混合出来的颜色就是(1,1,1,0.5)
,也就是一个半透明的白色!当然重点不是白色,而是它和边缘的颜色是一致的,所以玩家看不出边缘有黑边。
具体来说,就是我们需要把所有图片边缘旁边的完全透明像素(颜色值为(0,0,0,0)
),将其RGB值改成和边缘一致,其Alpha值保持为0。这样线性采样后,混合出来的颜色就和边缘的颜色是一样的,只不过变成半透明而已!这个技术就叫做Alpha Bleeding。
有意思的是,不管RGB值怎么改,由于Alpha是0,那么在图片上都是看不到这个像素的,所以用了AlphaBleeding技术的图片和没用AlphaBleeding技术的图片肉眼看起来是一模一样的。
TexturePacker里打开AlphaBleeding
如果你是用TexturePacker来生成图集,那么恭喜你,TexturePacker自身就支持了AlphaBleeding,不过是叫ReduceBorderArtifacts。下面是官网的介绍:
ReduceBorderArtifacts
Transparent pixels get color of nearest solid pixel. These color values can reduce artifacts around sprites and remove dark halos at transparent borders. This feature is also known as “Alpha bleeding”.
不同版本的TexturePacker里,这个设置的位置可能会不同,不过在很早的版本就开始支持了。
下面是使用打开这个选项后生成的图集的效果,可以看到黑边完全消失了,看起来干净多了。如下:
Alpha Bleeding的缺点
对我来说,这些缺点影响比较小,不过大家还是要根据自身情况多注意下。
Alpha Bleeding带来的最大改变是,图片自身新增了不少的颜色值,而新增的这些颜色值会导致一些问题:
1. 部分对颜色数量敏感的压缩算法效果会变差
好吧,我知道的类似算法就是PNG8,PNG8是通过采用调色板来压缩图片的,8就是意味着8位调色板,也就是256色,所以如果你图片里包含的颜色数量低于256,那么采用PNG8是无损的,相反,颜色数量越多,颜色差异越大,最后压缩的效果就越差(这里指图片看起来有点糊)。
明显,采用Alpha Bleeding会导致最后的效果更差。
当然,由于UI图集一般包含大量不同的图片,所以都不建议采用PNG8压缩。
2. 文件大小变大
这个和压缩算法有关,一般的图片压缩算法,如PNG、JPEG等,都是对数据压缩的,所以数据越单一,越规律,压缩效果越好。同样大小的PNG图片,如果一张是纯色,一张是噪点,你会发现2张图片的文件大小差了非常多。
Alpha Bleeding会给图片增加了很多新的颜色值,这会增加文件层面上数据的复杂度,从而导致压缩效果变差。
我用自己游戏里最大的一张图集做过测试,会变大10%左右。当然这个不同图片结果不同,这个数据仅供参考。
3. 打图集时间变长
这个很好理解,因为打完图集后需要扫描整个图片来进行Alpha Bleeding。我自己没仔细研究怎么实现高效的Alpha Bleeding算法,不过估计复杂度最快都要 O ( n 2 ) O(n^2) O(n2)。就算采用Texture Packer来打图集,打开Alpha Bleeding后也能明显感知到打图集的时间至少多了1倍以上。
也是因为这点,想在运行时做Alpha Bleeding也就不太现实了。