聊一聊 ThreeJS 反锯齿

© Young 2019-03-05 17:20
Welcome to My GitHub

概述

之所以会折腾这个问题主要是因为自己的魔方微信小游戏在Android机型中存在莫名其妙的锯齿问题;

那怕是已经在创建WebGLRenderer渲染器时设置了反锯齿参数。

曾经有人问我遇到锯齿问题怎么办?

其实除了设置反锯齿参数之外,我也不知道该怎么办…

因此就趁着这个机会稍微了解了一下。

为什么会有锯齿?

锯齿的出现和光栅器的工作方式有关,顶点坐标理论上可以取任意值,但是片元不行,因为它们受限于显示器的分辨率,所以光栅器必须以某种方式来决定每个片元最终的屏幕坐标;

在上图中每个像素中心包含一个采样点,它会被用来决定这个三角形是否覆盖了某个像素点,图中红色的采样点被三角形所覆盖,在每个被覆盖的像素处都会生成一个片元;虽然三角形边缘的一些部分也遮住了某些屏幕像素,但是这些像素的采样点并没有被三角形内部所覆盖,所以它们不会被片元着色器影响,最终渲染出来的效果如下:

由于显示器的分辨率限制,有些边缘的像素能被渲染出来,有些则不会,造成的结果就是渲染的图像具有不平滑的边缘,这也就是锯齿出现的原因了。

超采样抗锯齿(SSAA)

最开始大家使用一种叫超采样抗锯齿(Super Sample Anti-aliasing)的技术来解决这个问题,它会使用比正常分辨率更高的分辨率来渲染场景,当图像输出在帧缓冲中更新时,分辨率会被下采样(Downsample)至正常的分辨率,这些额外的分辨率会被用来防止锯齿的产生,但是因为要绘制更多的片元,因此会带来很大的性能开销。

在 ThreeJS 框架中有对应的例子实现webgl_postprocessing_ssaa

不过这个例子中并不是采用的使用更高分辨率的方式来实现的,而是通过多次渲染场景,每次渲染时对摄像机进行轻微的抖动偏移,从而得到额外的颜色信息,最终会根据这些额外的颜色信息来进行防锯齿处理。

多重采样抗锯齿(MSAA)

在超采样抗锯齿技术基础上诞生了更为现代的技术,即多重采样抗锯齿(Multisample Anti-aliasing)

如图所示多重采样所做的正是将单一的采样点变为多个采样点,我们不再使用像素中心的单一采样点,取而代之的是以特定图案排列的4个采样点,我们将用这些子采样点来决定像素的覆盖度;当然这也意味着颜色缓存区的大小会随着子采样点的增加而增加;

采样点的数量并不是固定的,更多的采样点能带来更精确的覆盖率,而最终的像素颜色将由片元本身的颜色和覆盖率共同决定。

多重采样需要硬件的支持而且多采样点也会带来一定的性能问题,另外还不能和延迟渲染很好的配合。

后处理抗锯齿(PPAA)

鉴于多重采样的不足,各种后处理抗锯齿技术(Post Process Antialiasing)发展了起来,比如FXAASMAA

快速近似抗锯齿(FXAA)是基于边缘检测的抗锯齿算法,依赖于像素信息的变化,可以参考 mattdesl/glsl-fxaa 的实现:

  • 先是根据片元位置获取其周围片元的位置;

示意图如下:

  • 然后根据位置获取颜色值;

  • 然后根据颜色值计算亮度;

同时计算最大亮度和最小亮度,边缘变化越明显则局部差异越大(luma是标准灰度向量,亮度通过颜色向量和标准灰度向量的点乘计算得到)。

  • 最后就是很玄学的抗锯齿处理的颜色计算了。

说实话有点看不明白rgbArgbB计算过程。

作者提供了 在线DEMO 对比优化前后的效果。

在 ThreeJS 框架中对于FXAASMAA都是对应的例子实现:

但是当我依葫芦画瓢在微信小游戏中使用FXAASMAA时却出现了很尴尬的情况:

虽然解决了魔方外部边缘的细小锯齿,但是却给魔方内部边缘带来了更严重的锯齿。

这里我只能甩锅给微信小游戏的渲染引擎了!

参考资料

关于“聊一聊 ThreeJS 反锯齿”我的2个想法

发表评论

电子邮件地址不会被公开。 必填项已用*标注