《索尼克06》的画面到底有多割裂?危机之城有反射、粒子特效和戏剧化光照,而城堡镇平坦得让人难受。同一个游戏,两个区域像是两个世代的作品。

打开网易新闻 查看精彩图片

在推进《马拉松重编译》项目的过程中,有人深挖了这款游戏的核心渲染系统,包括着色器、阴影和光照。结论指向一个方向:大量被砍掉的内容其实还留在游戏里,只是出于性能原因没有启用。

打开网易新闻 查看精彩图片

从有限的测试版影像里能看到不少正式版中不存在的效果:王国谷有完整的凹凸贴图,城堡镇有更复杂的几何结构和植物,新城的碰撞数据则暗示开发过程中曾有过完全不同的建筑布局。测试版城堡镇拥有正常的光照和阴影。

打开网易新闻 查看精彩图片

即便已经砍掉了这些视觉效果,游戏在真实硬件上依然难以达到目标帧率。在赶工压力下,这些特性成为第一批被舍弃的对象,并不令人意外。

这些效果是怎么被关掉的?答案藏在大量不同的着色器变体里。游戏中的大多数物体使用地形着色器或广告牌着色器渲染(尽管名字叫广告牌,实际并不是广告牌效果)。两者都有众多变体,按特性等级编号从00到07,数字越高特性越完整。

游戏里使用最多的着色器是广告牌02。它不是最差的一档,但绝对属于最差之列。这意味着游戏中最常用的着色器只有逐顶点光照,不支持级联阴影贴图,也不支持点光源。大量物体看起来灰暗单调,原因就在这里——大部分场景只用了最基础的反照率贴图和环境光照来渲染。

换着色器也没有想象中容易。不同变体之间的输入输出布局不一致,直接替换往往导致各种技术手段以不同方式崩掉。此外,每个材质的贴图定义在物体的 .xno 文件中,有些 .xno 可能根本没有引用法线贴图和凹凸贴图,即便这些贴图还躺在游戏文件里,光换着色器并不够。

阴影贴图同样问题缠身。游戏设定最多使用4级级联,每级负责相机视锥的一段,让不同距离下解析阴影的像素量保持相对一致。但级联阴影显然也挨了临上线前的性能砍刀。

级联层数由 GenerateCSMLevels 和 GenerateCSMObjects 定义,前者一般对应地形和静态元素,后者对应其他物体。大多数区域使用完整的4级级联,但 twn_a 和少数测试关卡把前者降到3级、后者降到2级,阴影渲染距离因此大幅缩短。多人模式在所有场景都使用缩减后的级联数。

即便阴影正常渲染,也容易出现严重的阴影痤疮——这类技术常见的瑕疵。通常的解决办法是给阴影贴图采样加一个很小的 epsilon 值,但开发团队没有这么做。

阴影偶尔还会出现定义错误。物体可以设置自己是否参与阴影贴图,有时会设错,导致索尼克在危机之城的滑雪板没有影子,艾莉丝的裙子在某段过场动画里也丢了阴影。这类问题不好修,往往需要直接改动物体本身,或者在渲染代码里写复杂的钩子去查找和修正。

点光源是另一个遗憾。游戏理论上有一套相当灵活的光照系统:每个物体有几个固定的环境光,外加若干点光源,用于火把之类的小细节,不投射阴影,渲染开销也不高。

但开发团队把每个物体的点光源数量限制在2个。也就是说,任意时刻只有2个光源在着色器输入里有位置被渲染出来。这种限制在正向渲染器里很常见,只要设计师在关卡设计时心里有数就没问题。问题是,他们没有。

王国谷这类关卡比较守规矩,而烈焰核心和尘土沙漠则彻底超限,试图为每个物体渲染8个以上光源。渲染器因此混乱,光源互相争抢优先级,有些会半随机地出现或消失。在烈焰核心,只有路径左侧的光源能正常渲染。

开发团队曾尝试为点光源做通用优化,把光源有效分配给附近物体,但这个检查出了岔子。推测原因是限制在开发后期被大幅收紧,同时许多逐像素光照物体被切换成逐顶点光照,而后者会大幅改变点光源的影响范围。结果是正式版里点光源几乎不工作,即便在遵守限制的关卡里也是如此。

在重编译版本中,可以打补丁去掉大部分破坏点光源的优化逻辑,让点光源渲染比原版稳定得多,但仍然受限于2个光源的上限。负责叠加点光源贡献的光照计算在最终着色器字节码里全部展开内联,识别和修改都很棘手,因为不同着色器里长得不一样。

《索尼克06》其实有实时平面反射,危机之城里的效果就是。这部分整体表现相当不错,能为场景增加不少真实感。正式版里反射分辨率被严重限制在帧缓冲的四分之一,但这很容易打补丁修好。真正被坑的是多人模式。

为了不在多人模式里把昂贵的平面反射渲染两遍,开发团队干脆把它们全部移除,换成静态立方体贴图。问题是这些立方体贴图效果相当一般,王国谷从惊艳变成了阴郁的沼泽。补回来?由于渲染代码的运作方式,这并不容易。

游戏用 Lua 脚本构建渲染图,不同模式有不同的脚本。单人模式和多人模式分别有 render_gamemode.lua 和 render_gamemode_multi.lua。前者里能看到关键的 RenderReflection() 调用,后者里则完全缺席。强行在多人模式启用实时反射,得到的只会是一片黑色虚空。

也不能直接把多人脚本换成单人脚本。多人渲染器有专门创建分屏的步骤,本质上需要两个帧缓冲,渲染图必须能同时渲染两个帧缓冲才能正常工作。即便给多人渲染器加上 RenderReflection,它也不是为分屏环境设计的,同样不会按预期工作。

有些问题已经得到部分解决,但要让画面达到测试版影像里的水准,还需要弄清楚着色器该怎么改。部分修复超出了重编译项目的范围,但无论修不修得成,这些挖掘都在加深对这款游戏底层运作方式的理解,也会直接转化为后续的改进和补丁。

这款游戏还有很多东西可以挖,开发团队没被给够时间把它做完,确实令人遗憾。角色着色器的离谱程度、缺失的遮挡剔除,都还没展开。用一句轻松的话收尾:祝这款傻乎乎的游戏二十周年快乐。