模拟器中的 Filter 或 Shader 基本都是基于图像本身的。一般来说模拟器不会提供与几何相关的 shader(32 位机以后会有少量这类 shader)。也就是说,模拟器滤镜生成的图像都是在不清楚游戏本身运行逻辑的情况下,单纯对最终输出的图像进行变换。因此这里用 Filter 远比用 Shader 来得更为精确。不过因为 RetroArch 的滤镜系统将其称为 Shader,因此之后将不分辨该用词(shader = 滤镜 = filter)。

以下将从抗锯齿滤镜、放大增强滤镜、效果滤镜和硬件仿真滤镜四个角度对模拟器常用滤镜进行介绍,并着重对现在应该如何模拟 CRT 进行说明。

抗锯齿滤镜

对模拟器常见的 2D 游戏,抗锯齿滤镜基本没什么用,所以只是简单介绍一下。

首先是为什么要抗锯齿。大家知道时域采样往往要用规则采样。时域采样在频域中相当于用狄拉克梳子卷积信号本身,如果被采样信号的带宽低于采样信号的奈奎斯特频率,就没问题,不然就会堆叠失真产生 aliasing(一维叫混叠,二维叫锯齿)。在空间域中采样几何本身或者现实世界图片的时候,规则采样用得很少,因为很容易对周期性高频信号出现 aliasing。人们通过局部改进分辨率、随机采样等等途径进行抗锯齿,就产生了各种 AA 算法。

我们知道模拟器滤镜都是作用于屏幕空间(不是模拟器图形设置中的 AA 选项),和图形渲染不同:它往往是通过减少图像中的高频信号,而非增加采样频率或改变采样策略进行 AA 的。是纯粹的初次采样完毕之后的空间域行为,不需要获知图形的几何信息。

常用的屏幕空间 AA 就是 FXAA 了,其具体原理太过繁琐,可参考此贴:

catlikecoding/unity/tutorials/advanced-rendering/fxaa/

一般来说,2D 游戏,尤其 16 位机器以下的游戏不要使用 Anti-aliasing shader。像素图像本身甚至可以说就是由锯齿构成的,如果强行进行 AA 会使图像看起来非常诡异:

nds用模拟器合集(nds模拟器历史版本)(1)

3D 游戏可酌情使用,尤其是模拟器本身 AA 开的不高的情况下。屏幕空间的 AA 效果虽然一般但通常速度较快,如果开 3D 游戏模拟器内 AA 比较吃力的情况下,就凑合用屏幕空间的 AA 吧。

放大增强滤镜

这类滤镜是平时最常见的,也是人们最为经常使用的滤镜(虽然 LZ 并不常用这类滤镜)。它的主要作用是减少像素画面的颗粒感。像素艺术最大的问题就是经不起放大:一旦放大以后,原本可爱的 Sprite 瞬间变得狰狞了起来:

nds用模拟器合集(nds模拟器历史版本)(2)

为了解决像素图像放大的问题,人们发明了一系列增强算法。在机器学习介入之前,这类滤镜还比较简单,我们也只考虑机器学习之前的常用滤镜。

首先是基本的插值:Nearest Neighbor,Bilinear 两种

像素图片放大这件事上,只要模拟器输出分辨率跟具体显示分辨率不匹配,模拟器本身就要选择一种插值方式。可以进行线性插值(颜色设为邻居的加权平均)或者最近邻插值(与最近的邻居像素颜色相同)。显示上最近邻插值能够还原原本的像素颗粒,而线性插值能进行初步的模糊和润滑,具体喜欢哪种就看个人喜好了。

Scale 系列:

包括 Scale2x、2xSal、EPX、AdvMAME2x 等等。这类滤镜是使用简单的 filter 对图像进行卷积。有时比单纯的卷积要复杂一些,等价于使用了多个不同的滤镜进行卷积以后产生多个图像,最后对图像进行条件混合。还有些强调边缘的滤镜也会通过图像的二次差分判断边缘从而采取不同的混合策略(权重)。

考虑最简单的 Scale2x,将像素 P 放大为 4 个子像素,根据周围 4 像素设置子像素的颜色采用以下规则:

nds用模拟器合集(nds模拟器历史版本)(3)

1=P; 2=P; 3=P; 4=P;

IF C==A => 1=A

IF A==B => 2=B

IF D==C => 3=C

IF B==D => 4=D

IF of A, B, C, D, three or more are identical: 1=2=3=4=P

则称之为 Scale2x 算法。其结果其实基本上就是把一个像素分成了四个像素,颗粒感会大大下降。规则简单,性能也好:

nds用模拟器合集(nds模拟器历史版本)(4)

其它 Scale 系列同理,主要都是在放大之后根据原图像周围像素颜色通过一定规则决定子像素颜色。

Eagel、2xSal 也是同一系列的滤镜,只不过考虑的周围像素范围不同。例如比较复合的 Super2xSal 考虑的像素范围就要更大一些,涉及周围 11 个像素的值,并且也设置了相似的判定规则,效果如下:

nds用模拟器合集(nds模拟器历史版本)(5)

Scale 系列滤镜是我认为 16 位机和 8 位机的底线,下面的就稍微有点越界了。

HQx 和 xBR 滤镜系列

有时候现代人口味刁钻,希望能消除像素本身的颗粒感。而前面那些简单的临像素加权平均或分支的滤镜会导致边缘模糊,并且处理像素游戏中的线条非常苦手,因此有人开发了相应更复杂的滤镜满足这些人的需求。

HQx 系列 :(high-quality scale)

这一系列滤镜会根据周围像素颜色与自己的不同关系(周围 8 个像素根据阈值分为相似或者不相似两类,因此共 256 种可能),通过查找表的方式确定放大之后的像素颜色如何定义。而这一查找表本身的定义比较复杂。用 C 写几千行也很不容易(包含了 HQx-2/3/4github/grom358/hqx/blob/master/src/hq2x.c),当然用 GLSL 要简单不少。其目的主要是为了放大之后的线条能够更加顺滑。

nds用模拟器合集(nds模拟器历史版本)(6)

xBR 滤镜系列

xBR 滤镜系列,包含 xBR , xBRZ, xBR-Hybrid, Super xBR, xBR+3D 和 Super xBR+3D.

同样的,这些滤镜也主要是用来游玩像素游戏时消除像素颗粒感使用。总有人认为这种圆滑感看起来比颗粒感的像素更舒服一些。

这类滤镜比 HQx 更强大的地方在于通过多个 pass 解决了许多 HQx 的单次查找表索无法解决的问题,让还原的线条更加锐利。

具体原理参考:(forumsbretro/t/xbr-algorithm-tutorial/123)( pastebin/cbH8ZQQT)

nds用模拟器合集(nds模拟器历史版本)(7)

虽然这里可怜的马里奥看起来有点不堪,但一般情况下这个滤镜没有那么惨。xBR 滤镜对边缘的处理远比 HQx 更加强大,非常善于消除像素的颗粒感并且保留色块和边缘的锐利。NGA 有人专门写过一篇文章吹这个滤镜,并且认为 xBRZ 是最好的 2D 放大滤镜(单纯从放大角度,不考虑深度学习类的方法,应该算是没错的)。有需求的可以参考一下:

bbs.nga/read.php?tid=9171524

(然而说实话我是 xBR PTSD,看着就难受)

其他大多数像素增强也都是采用了各种不同的自定规则对子像素进行插值。效果有好有坏。游戏之间的图像特征也有很大的区别,适用不同滤镜,大家使用时可以根据自己的视觉体验进行选择。

比如 NEDI(New Edge-Directed Interpolation), 论文:web.archive/web/20101126091759/neuron2/library/nedi.pdf

比如专门为 GB/GBA 设计的 OmniScale:sameboy.github.io/scaling/

深度学习方面尤其跟 GAN 有关的方法则包含一些 AI 将图片库中特征结合进行的创作,不符合高还原度 retro gaming 的主题,一般也不推荐使用。

效果型滤镜

以下滤镜会生成一些有趣的效果,一般用不着,想体验一下也行。

Dithering:dithering 是早期 PC、针式打印机等等用点阵表示密度来展现色彩的。最近很火的独立游戏《obra dinn》也是这种风格。但单纯基于图像的 dithering 其实很消耗时间,采用一些近似的化效果也不好。用在 16 位机的游戏上也并不合适,凑合看看吧:

bayer-matrix-dithering:

nds用模拟器合集(nds模拟器历史版本)(8)

Cel-shading:卡通渲染用在 16 位机上当然是个灾难,但 3D 游戏有时候也有点意思。同样的,不要指望单纯的屏幕空间的滤镜能搞出什么花来:

nds用模拟器合集(nds模拟器历史版本)(9)

老电影

technicolor 滤镜是一个不错的老电影效果滤镜,还能模拟胶卷上的点和划痕:

nds用模拟器合集(nds模拟器历史版本)(10)

效果型滤镜随喜好添加即可。

硬件仿真型滤镜

这是我认为模拟器屏幕空间滤镜真正有用的地方,也是本帖的重头戏。这类滤镜的目标是尽量模拟真实硬件的显示设备,在现代 LED 显示器上对古旧显示设备(掌机屏幕、电视、街机 CRT 等)进行仿真,从而带来更多模拟游戏和怀旧乐趣的一类滤镜。

注意,这里介绍的大部分滤镜的最佳使用场景都是 4K 显示器全屏游玩。各种掌机屏幕几乎都没有能力模拟这些效果,而手机屏幕太小,效果是看不清的。

首先来说说比较简单的掌上设备。使用显示器屏幕模拟掌机设备的一大问题是无法准确模拟掌机屏幕的表现。而屏幕空间的滤镜通过色彩、像素颗粒感这两方面尝试逼近掌机屏幕的表现。

例如 GB(带光)式的色彩和像素映射(gameboy-light):仔细观看会发现马里奥采用了方形像素,并且使用了横向和纵向的像素分割线对老式 LCD 进行了风格化。色彩映射也是 GB 的绿屏。

nds用模拟器合集(nds模拟器历史版本)(11)

可见像素并非简单近邻插值,而是同时模拟了像素本身的荧光扩散效果。使用了大量的像素模拟了单个 GBA 像素的荧光扩散灰度显示不同亮度时的不同梯度。因此才能将像素显示的阴影感准确模拟出来。而这一效果也是在 4K 显示器下才能体现得最为明显。因为 4K 显示器有足够的像素去表现这些效果。(你要问我为什么用 4K 显示器全屏玩 GB 游戏?可能是吃得太饱了……)

对比一下就知道加滤镜和不加滤镜的天壤之别。很可爱吧,是不是想起了另一个古旧 LCD 设备(文曲星):

nds用模拟器合集(nds模拟器历史版本)(12)

这里这个 GB 的滤镜是 LCD 系列滤镜的一种。这一系列滤镜就是为了创造相应掌机设备 LCD 屏幕效果而出现的。它的原理大框架就是增加像素之间的间隔形成 LCD 颗粒感,通过隔离的荧光过渡形成像素本身的阴影感,从而复原当年的古旧 LCD 屏幕的样子。

比如 GBA 样式(包括了 GBA 屏幕的颜色映射,GBA 反射颜色并不鲜艳,用现代的屏幕去显示需要通过一定的映射)。一定程度上还原了像素排列方式,甚至还原了 GBA 屏幕本身的动态模糊:

nds用模拟器合集(nds模拟器历史版本)(13)

举个 GBA 游戏的例子:用 GBA 的朋友对这种色彩和像素风格的画面应该有印象

nds用模拟器合集(nds模拟器历史版本)(14)nds用模拟器合集(nds模拟器历史版本)(15)

对比不加滤镜的鲜艳色彩和线性插值的图像 (用模拟器玩晓月的朋友记忆中应该是这个画面):

nds用模拟器合集(nds模拟器历史版本)(16)

如果你觉得 9102 年了还要还原 GBA 的色彩简直开历史倒车(虽然这正是这篇文要干的事情……),那么也可以只进行 LCD 像素映射。只使用 LCD3X 系列滤镜而不映射色彩即可:

nds用模拟器合集(nds模拟器历史版本)(17)

同样的,NDS 也可以采用类似滤镜。

由于 PSP 的屏幕相对来说要好不少,类似现代显示器屏幕,一般没有针对 PSP 屏幕的模拟需求。如果想模拟 PSP 的屏幕可以使用 RetroArch 自带的 PSP-color 进行色彩映射。

下面说说另一个(真正的)重头戏:

CRT 滤镜

首先请把所有其它 CRT 滤镜扔掉,只留下一个:CRT-Royale(除非硬件跑不了,再考虑其他)。

滤镜使用了大量 pass 进行了 CRT 的模拟。如果 PC 性能够强的话,延时方面的影响也很小。CRT-Royale 十分复杂和强大, 对 GPU 有一定的要求。如果用 intel 的 GPU 的话(集显)需要进行修改,改版也在 RetroArch 里提供了。

用来显示 CRT-royale 滤镜的屏幕至少需要 2K 以上的分辨率,4K 甚至 8K 屏幕的模拟效果更加真实。是的你没看错,要模拟 CRT,最低要求是 2K 分辨率,4K 更佳

我们知道 CRT 中的磷光体(或荧光体)是产生冷发光现象的物质,受到阴极射线(电子束)激活发光。它发出的光线具有一定的特征,与现代 LED 的像素光线有较大的区别。CRT 滤镜的关键就是通过大量现代 LED 像素去模拟磷光体的发光特征,从而模拟 CRT 的显示效果。而在这方面做得最好的就是此滤镜了。(CRT 虽然没有直接的像素的概念,只有荧光粉或者荧光条。不过电子束的信息改变是离散的,因此我们可以将离散电子束信息改变周期内扫过的空间等价为像素的概念)。

在 RetroArch 的桌面 UI 里打开 CRT-royale 的设置界面,我们可以看到很多相关设置,涉及到一些重要的调整项。如果你对 Shader 语言略有了解,也可以直接打开 Shader 文件进行调整,只是没有界面中方便。根据每个人接触到的不同型号和不同厂家生产的 CRT,你所喜爱的 CRT 参数必然有所不同,玩家可以自行调整到喜欢的设置选项。

首先看看效果(网络图片有压缩,要观看大体效果还是自己 4K 全屏运行模拟器比较靠谱。看图片也要看大图,小图自带 AA,把所有特征都抹掉了):

nds用模拟器合集(nds模拟器历史版本)(18)nds用模拟器合集(nds模拟器历史版本)(19)

对比没开滤镜的游戏:

nds用模拟器合集(nds模拟器历史版本)(20)

影响最终效果的选项很多。下面我们来解释一些影响较大的参数:

Halation and Diffusion

Halation 是被荧光体直接反射的光线,而 Diffusion 是光线穿过 CRT 玻璃时产生的散射荧光。这两项参数的权重可以进行调整。

nds用模拟器合集(nds模拟器历史版本)(21)

Bloom

如果点亮的荧光体发光过强影响到了电视上的其他面积,使整个画面变得过亮,就是一种 bloom 的效果。特别好的 CRT 会控制 bloom,但由于这是大量中低端电视可能产生的效果,因此也需要忠实模拟。

nds用模拟器合集(nds模拟器历史版本)(22)

Beam

这项参数控制了实际进行扫描的电子束的各项维度。不知为何一直有人认为 scanline 是黑线:scanline 是扫描到的线,而没扫描到的地方才是黑线。除了可以调整 Beam 本身的大小以外,这里也可以调整高斯模糊函数的各个参数。根据不同的参数选择可能产生不同型号电视或街机的效果:

nds用模拟器合集(nds模拟器历史版本)(23)

Convergence

彩色电视电子枪发射的三束射线对荧光粉的轰击是否足够整齐:好的 CRT 比如彩监是非常整齐的,但许多消费者级别的 CRT 这方面的表现就很一般了,根据每个人童年不同质量的 CRT 可以仔细微调。

nds用模拟器合集(nds模拟器历史版本)(24)

MASK

这项控制的是荧光体的排列方式。滤镜提供了三种排列:0.0 (Aperture Grille), 1.0 (Slot Mask), 和 2.0 (Dot Mask)。这三种排列如下:

nds用模拟器合集(nds模拟器历史版本)(25)

每一种排列都对应不同厂家的电视效果,可以分别予以调整。同时,MASK 也有大量参数可以进行调整。比如使用的荧光体个数可以调整 CRT 显示的粒度。

nds用模拟器合集(nds模拟器历史版本)(26)

和其它滤镜相比也是高下立判。如果你觉得没有高下立判,就调整参数让它高下立判!

nds用模拟器合集(nds模拟器历史版本)(27)

不同的制式和不同的输入会有一定程度的图像失真,没关系,这些失真可以用额外的 pass 来模拟。比如电视机的 composite 输入导致的色彩失真效果,加 NTSC 的色彩映射的效果如下:

nds用模拟器合集(nds模拟器历史版本)(28)nds用模拟器合集(nds模拟器历史版本)(29)

再传两个其他游戏的图,还是那句话,要在自己的屏幕上运行模拟器动态才能比较明显看到效果。

nds用模拟器合集(nds模拟器历史版本)(30)nds用模拟器合集(nds模拟器历史版本)(31)nds用模拟器合集(nds模拟器历史版本)(32)

以上基本介绍了常见的几种 Filter 和它们的大体效果。那么如何使用这些 filter?哪些模拟器支持 shader 语言写的 filter 呢?

这里:emulation.gametechwiki/index.php/Shaders_and_Filters 介绍了一些常见模拟器支持的 filter 文件类型。一般来说,采用通用前端 RetroArch 可以使用大部分的 shader,而使用模拟器自带前端则有很多限制。所以最简单的方案就是直接使用 RetroArch,然后从其 shader 文件夹中选择所需 shader,并通过菜单调整相应参数即可。

总结:模拟器滤镜是个挺大的话题,这里只是简单介绍一些类型和它们的效果。一般来说,对 32 位及以下机型模拟时才推荐使用屏幕空间的滤镜,抗锯齿滤镜一般不推荐像素游戏使用。像素放大增强滤镜根据个人口味使用,一般来说进行简单的 2xSal 等即可,特别讨厌像素的颗粒感的话,可以考虑 HQx 或 xBR 系列滤镜。如果追求一些特殊效果,可以使用效果型滤镜。而为了模拟古旧硬件(主要是显示设备),则可以分别使用该种硬件的滤镜。CRT 滤镜主要就是 CRT-Royale,按照自己口味调整之后,配合高亮度 4K 显示器,基本可以满足一般 CRT 模拟需求。如果有特殊需要当然实机 + 彩监更好,没有这个条件的话模拟器效果也不赖,而且彩监只能体验一种或少数型号的显示效果,而滤镜可以自行配制体验多种电视和信号的不同感觉,因此也并不冲突。此外,部分 Filter 会降低游戏性能,或者因为需要帧信息从而略微增加游戏延时,有性能需求时应当关闭所有滤镜。