GLantern 加入滤镜,并发布到了 NPM

GLantern 于今天调试加入了基本的滤镜支持。同时,0.1.0 版也作为第一个 release 发布到了 NPM 上

滤镜是高级弹幕很可能会用到的东西,毕竟 BiliBili 的高级弹幕 API 都给出了两个可用滤镜:模糊(blur)和发光(glow)。所以在完成了初步的绘图功能后,首要任务就是加入滤镜。这工作其实在 GLantern 推到 GitHub 之前就开始在做了(所以在第一个 commit 中可以发现完成了一半的滤镜功能),但是一直有很头痛的地方,就是这个:

出现了明显的“波纹”

可以看到,在对一个简单的椭圆进行模糊的时候却出现了许多的“波纹”。基本 shader 我是遵循 Pixi 的结构,但是 Pixi 的表现正常,而我的就不正常。这么明显的事故肯定是不能推的。于是我找了一下高斯模糊的 shader。许多线索都将我引向了这篇文章。于是我就按照文章中所讲述的原理写了一个 shader 测试,结果照样是有波纹。这下子就能确定不是算法上的问题了——从结构上看,二者采用的都是快速的两遍(2-pass)模糊,先 X 方向或 Y 方向,然后是另一个。这是基于统计学的一个常用定理,二维正态分布函数可以分解成两个一维正态分布函数的乘积:

A Gaussian function with a distribution of 2σ is equivalent with the product of two Gaussian functions with a distribution of σ.

然而,仔细读了文章之后发现,作者提到,他的图片结果都是连续模糊了9次的:

9-tap Gaussian blur applied nine times with discrete sampling (left) and linear sampling (right). Click for the full resolution of the image. Note that there is no visible difference between the two techniques even after several passes.

也就是说,不是一次成型,而是九次成型。作者专门提到了使用的是 linear sampling 而不是 discrete sampling,设置 r 为模糊半径的话,从算法上来看也只是取了 9 个点(而不是 r*r 个点),想想看,在靠近椭圆边界的地方必然会造成距离边界 krk 是整数)的像素处形成一个“峰”或“谷”,也就形成了“波纹”。

发现了这一点之后,将循环上了9次,果然:

模糊的效果得到了极大的改善

浏览了另外的一些资料,认为 Pixi 的 shader 思想应该是来自这里,不仅是预先计算了系数,还利用小的数值几乎解除了模糊操作对图像尺寸这个信息的依赖(采用 0.004 作为一个基础倍数)。这样做减少了一些除法,不过也降低了质量。以下是同样条件下的超级快速 shader 的应用结果:

质量降低时可以看到一些矩形分块

解决了模糊的问题之后,发光也就顺理成章地出来了(调整了发光颜色,提高区分度):

发光的效果测试

写这篇文章之前不久发现了一个问题,模糊半径越大,亮度就越低,超过 60 就很弱了——这是线性采样+循环带来的恶果吗?似乎 Pixi 在模糊半径 160 时都挺正常的,这个原因还有待探究。不过都已经 publish 出去了……

也有狂人做了真正的高质量模糊 shader(不缩水的正态分布):这里。各位可以试试调整一下其模糊半径(mSize),值高一点渲染速度马上就下去了。

分享到 评论