第 8 章

引擎里的沙盘

沈明远收起地图之后的第三天,林铮的工位上贴了一张便签。便签是黄色的,上面用黑色马克笔写了四个字:“引擎选型。”字迹是林铮的,但便签纸是从周瑶桌上拿的——那种最普通的3M便签,粘性一般,贴了两天就翘边了。林铮没换,翘了按回去,再翘再按回去。那张便签在他工位上贴了将近四个月,从四月贴到八月,边角卷得不成样子,字迹被反复摩挲得有些模糊,但一直没撕下来。

四个月里,项目组在引擎选型这件事上撞了两堵墙。第一堵墙叫Messiah,第二堵墙叫自研。两堵墙之间是一条窄路,窄到只容得下一个B级项目侧身通过。

Messiah引擎是网易自研的旗舰级游戏引擎,2015年开始研发,2018年随《逆水寒》上线首次大规模验证。到2021年《燕云十六声》启动技术论证时,Messiah已经迭代了六年,形成了完整的技术中台体系——渲染管线、物理引擎、动画系统、网络层、工具链、资产管线,每个模块都有专门的团队维护,每个模块都有成文的接口规范和排期表。项目最初于2019年11月启动,采用的正是网易自主研发的弥赛亚(Messiah)引擎。

在网易内部,用Messiah意味着站在一个已经搭好的脚手架上干活,不用从地基挖起。但脚手架的形状是固定的。《逆水寒》用Messiah搭建的是一座精致箱庭——汴京城、杭州城、各大门派驻地,每张地图都是独立场景,地图之间用传送点和加载画面衔接。玩家从汴京去杭州,中间要过一道加载界面,加载界面上是水墨风格的过场图。这个设计在《逆水寒》里不是缺陷,因为《逆水寒》的江湖体验不需要无缝大地图——它的核心玩法是副本战斗和帮会社交,地图之间的加载是自然的节奏断点。

《燕云十六声》要的东西不一样。沈明远在三州方案里划定的可交付疆域,从蔚州城南到云州北境直线距离超过四十公里,中间有官道、驿道、山路、河谷、关隘。玩家骑马从蔚州出发,一路向北,穿过代州盆地,翻过燕山余脉,抵达云州城下——这个过程不能有一次加载画面。

不是因为加载画面本身有什么问题,而是因为五代十国的燕云十六州,地理本身就是叙事。

蔚州和云州之间的山路是中原王朝与北方草原之间的军事通道,契丹骑兵南下劫掠要走这条路,后周世宗北伐也要走这条路。如果玩家在这条路上每隔一段就遇到一次加载画面,地理叙事的连续性就断了。

这个需求在Messiah的底层架构里没有原生支持。Messiah的场景管理器在设计之初采用了分区加载逻辑——每张地图是一个独立场景,场景内的所有资源在加载时一次性读入内存,场景切换时释放前一个场景的资源再加载下一个场景。这套逻辑在箱庭式游戏里运行高效,因为每张地图的体量可控,内存占用可预测。但无缝大地图要求的是流式加载——摄像机移到哪里,资源就加载到哪里,摄像机移走的部分就释放掉,整个过程中内存占用是一条动态曲线,而不是一个固定值。

林铮在四月初写了一份技术评估报告,详细对比了Messiah现有架构与项目需求的匹配度。

报告的核心结论只有一句话:Messiah可以支撑《燕云十六声》的基础体验,但无法支撑其核心差异化——无缝大地图、动态季节系统和大规模战场叙事。如果要支撑这些差异化需求,Messiah的场景管理器、渲染管线和AI驱动模块都需要底层重构,重构的工作量不亚于重写半个引擎。林铮把报告发给沈明远的当天晚上,沈明远给他回了一条消息:“约中台评审。把需求摊开讲。”

评审会定在四月十五日。那天杭州下了雨,不大,但持续了一整天。网易总部B栋的走廊里弥漫着雨天特有的潮气,地板上有鞋底带进来的水渍,保洁阿姨推着拖把来来回回地擦。技术评审室在B栋三楼,房间不大,一张椭圆形的会议桌,十二把椅子,墙上挂着一块投影幕布。窗户朝西,下午的光线透过雨幕照进来,整个房间泛着一种灰白色的调子。

中台派了四个人来。领头的叫何勇,Messiah引擎组的核心架构师,在网易做了八年底层引擎开发,从Messiah的第一行代码写起,对这套引擎的每一层架构都了如指掌。他带了一个渲染管线负责人、一个场景管理器负责人和一个产品化接口人。项目组这边,沈明远带了林铮、技术美术赵启明和制作人助理周瑶。

林铮做了四十分钟的演示。他没有用花哨的PPT,直接打开了一个需求文档,逐条讲解。第一条:无缝大地图,流式加载,视距要求极远——玩家站在蔚州城墙上要能看到燕山山脉的轮廓,不是雾,不是贴图天空球,是真实渲染的山体几何。

第二条:动态季节变换,同一张地图在春夏秋冬之间实时渐变,植被颜色在着色器里逐帧计算,不是预设季节切换。第三条:大规模同屏单位,战场场景需要同时渲染至少三百个带独立AI的士兵,每个士兵有自己的行为树、碰撞体积和动画状态机。第四条:全平台适配,从PC端的光追高配到移动端的低配流畅模式,一套资产管线全部覆盖,移动端帧率不低于三十帧。

林铮讲完最后一条的时候,何勇把眼镜摘下来擦了擦。他的眼镜片很厚,度数不低,摘下来之后整个人看起来有些疲惫。他擦眼镜的动作很慢,先用镜布擦左片,再擦右片,然后把眼镜重新戴上,看着投影幕布上的需求列表。“你们这个需求列表,”何勇说,“不是给Messiah提需求。是给下一代引擎提需求。”

他说这话的时候语气不冲,甚至带着技术人之间讨论技术问题时常有的那种平实。但他接下来的每一句话都在缩小可能性。

他说Messiah的场景管理器在设计之初就没有考虑过流式加载的架构,如果要改,改动量涉及场景管理器本身、资源管理模块、内存分配器和渲染管线的可见性剔除模块——这四个模块互相耦合,改一个就要改另外三个。他说动态季节系统在Messiah里是通过预烘焙的光照贴图实现的,每个季节一套贴图,切换季节就是切换贴图组。但《燕云十六声》要的是实时渐变——从夏天的绿到秋天的黄,中间要有过渡帧,植被颜色要在片元着色器里逐像素计算偏移量。这个功能在Messiah的材质系统里没有对应的着色器模块,需要从材质编译器开始写。

他说大规模同屏单位的AI驱动在Messiah里有现成方案,是《逆水寒》帮会战系统的一部分,但那个方案的上限是一百二十个单位。超过这个数,AI行为树的并行计算会撞上CPU的线程调度瓶颈,帧率断崖式下跌。“三百个独立AI单位,”何勇低头看了一眼林铮的需求文档,“你们这个数字怎么来的?”

“五代史的攻城战,三百人是保守估计。”林铮说。他没有展开解释,因为在场的人都知道他在说什么。五代十国的攻城战不是武侠小说里几十个高手飞檐走壁的场面,是成建制的步兵方阵扛着云梯冲城墙,是几百人同时攀爬、同时厮杀、同时坠落的集群作战。三百个独立AI单位是还原这种战场叙事的最低门槛。何勇点了点头,没追问数字的合理性。他合上笔记本,把目光转向沈明远。“沈老师,我理解你们的需求。但Messiah中台目前的排期已经排到明年一季度了。《逆水寒》的年度资料片在排队等场景管理器的升级,《天谕》手游在等渲染管线的移动端优化。你们这个项目现在是什么优先级?”

沈明远没有回答。不是他不知道答案,而是答案不需要说出口。《燕云十六声》在网易内部立项时的优先级是B级。B级意味着在资源分配的顺位上排在所有A级和S级项目之后。何勇问这个问题,不是真的在确认优先级,而是在告诉沈明远:你们的项目轮不到中台为你们改底层架构。会议室里安静了几秒。投影仪的散热风扇嗡嗡地转着,窗外的雨还在下。然后沈明远说了一句话:“那我们自己想办法。”

会议结束。没有结论,没有承诺,没有时间表。三天后,何勇发了一封邮件。收件人是沈明远和林铮,抄送给了中台的几位负责人和网易互娱事业群的技术副总裁陈言。邮件正文不长,措辞正式而克制,每一句话都经过产品化接口人的措辞打磨。核心段落只有三行:“针对《燕云十六声》项目组提出的无缝大地图与动态季节系统需求,Messiah引擎组经内部评估后认为,当前架构下无法提供完整的定制支持。建议项目组考虑以下替代方案:一,将开放世界拆分为多个独立场景,采用分区加载方式;二,简化季节系统为预设季节切换;三,降低同屏单位上限至当前引擎能力范围内。”

林铮把这封邮件打印出来,用红笔在上面画了几条线。他画线的地方是“无法提供完整的定制支持”和“降低同屏单位上限”。他在旁边写了一行字:“降低到一百二十个单位,等于放弃战场叙事。”然后把这张纸贴在自己工位旁边的白板上。沈明远第二天早上看到那行字。他站在林铮的工位旁边,盯着白板看了一会儿。然后说:“那就自己干。”

“自己干”这三个字落到执行层面,意味着项目组要在没有中台背书的情况下,从开源方案开始搭建一整套渲染管线和地形系统。林铮做了一次详细的工作量估算:如果走自研路线,核心管线搭建至少需要六个月。

六个月里产能几乎为零——美术没法开工,因为工具链没搭好;策划没法验证,因为编辑器没跑通;程序全部精力投在底层,没人写上层逻辑。六个月之后,如果自研方案跑通了,产能会慢慢爬坡;如果跑不通,项目直接死掉。

沈明远把这个风险在项目组内部会议上摊开来讲。他说话的方式一如既往地直接:“走Messiah,三个月出可玩版本,但上限被锁死——无缝大地图做不了,季节系统做不了,大规模战场做不了。走自研,六个月后才知道能不能跑通,但这六个月里我们拿不出任何东西给上面看。”

“上面能接受六个月没产出吗?”周瑶问。“不能。”

“那怎么办?”

沈明远说:“所以需要两条腿走路。”

他说的“两条腿”是一套并行策略:林铮带两个核心程序走自研路线,搭建轻量级管线原型;同时另一组人用Messiah引擎搭建一个演示版本——不是真正的可玩版本,而是一个能展示核心视觉效果的切片。演示版本用来应付内部评审和进度汇报,真正的技术突破放在自研线上。

这个策略的风险显而易见。项目组程序团队一共七个人,拆成两组,每组只有三个半人。自研组的工作量是搭建一整套渲染管线,演示组的工作量是用Messiah快速出一个能看的切片。两边都是高负荷,两边都不能掉链子。

但沈明远的判断很明确:如果不走自研路线,《燕云十六声》在技术层面永远拿不到独立的话语权。中台的排期不归项目组控制,中台的架构不归项目组修改,中台的支持力度取决于项目优先级——而优先级是上面定的,不是项目组能争取来的。在网易的“中台—工作室”结构下,一个B级项目想要做出差异化,唯一的选择就是在技术选型上划出自己的势力边界。自研,就是这个边界。五月初,林铮选定了自研方案的技术起点。

他没有从零开始写引擎——那在七个人的程序团队里不现实。他选择基于一套开源的轻量级渲染框架搭建管线原型。这套框架的社区维护活跃,文档相对完整,最关键的是模块化程度高——林铮可以只拿自己需要的部分,不需要的部分不编译进工程。这给了项目组极大的裁剪空间。

但裁剪也意味着所有的集成工作都要自己扛。光照模型要自己写,材质系统要自己搭,地形生成算法要自己调,LOD切换逻辑要自己设计。林铮把工作拆成十二个模块,分配给三个核心程序,每个人扛四个模块,每周五下午做集成测试。

第一次集成测试在五月第三周的周五。那天下午四点,林铮把三个人的代码分支合并到主干。编译通过。启动编辑器。加载蔚州城的粗模——然后屏幕黑了。

不是死机的那种黑,是渲染管线崩溃的那种黑。错误日志窗口弹出来,密密麻麻的红色报错信息刷了三屏。林铮一条一条往下翻,翻到中间停下来,用手指着其中一行。“着色器编译失败。原因是材质系统传进来的纹理坐标格式和光照模型期望的格式不匹配。”

这是第一个bug。修了二十分钟。修完之后重新启动,蔚州城的粗模加载出来了——但城墙是粉红色的。粉红色在渲染行业里是“材质丢失”的标志色,意思是着色器找不到对应的材质贴图,只能用默认的粉红色填充。赵启明看到那面粉红色的城墙时,在工位上笑了整整三十秒。他笑完之后说了一句话:“这要是给陈言看到,项目当场就没了。”

林铮没笑。他盯着屏幕上的粉红城墙看了大概两分钟,然后打开材质系统的代码,开始逐行排查。

排查到晚上九点,发现是材质ID的映射表在合并代码时被覆盖了——三条分支各自定义了一套材质ID体系,合并之后互相冲突,所有材质引用全部指向空值。林铮把映射表重新整理了一遍,统一了命名规范,重新编译。这次城墙的颜色对了——灰黄色的夯土墙面,带着粗糙的颗粒感。

但帧率只有十一帧。十一帧。蔚州城粗模的面数不到最终版本的三分之一,场景里没有任何动态单位,没有植被,没有光影,没有天气效果,只有一座静态的城池模型。就这个体量,帧率只有十一帧。

林铮把性能分析工具打开,盯着CPU和GPU的负载曲线看了很久。问题出在地形渲染模块上——自研的地形生成算法在处理蔚州城周边的起伏丘陵时,每一帧都在重新计算整个地形网格的顶点位置。这个计算是全局的,没有做分块优化,导致CPU在每一帧都背负着整个地形的顶点运算开销。林铮在当天的开发日志里写了一句话:“地形要分块。”

然后他花了三个星期重写地形模块,把蔚州城周边十六平方公里的地形切成了二百五十六个区块,每个区块独立计算顶点缓冲,只渲染摄像机视锥范围内的区块。三个星期之后,帧率从十一帧升到了三十二帧。三十二帧仍然不够。移动端的流畅标准是三十帧,但那是最终版本的底线——最终版本会有植被、光影、天气、NPC、战斗单位,所有这些叠加上去,帧率还会再跌。林铮的目标是在裸地形状态下跑到六十帧,给后续的内容叠加留出足够的性能余量。

他用了两个月才达到这个目标。到七月底,自研管线的原型版本在蔚州城粗模场景里跑到了五十八帧——视距拉到最远时是五十三帧,近景稳定在六十一帧。

地形分块优化到位了,LOD切换逻辑调通了,材质系统的纹理压缩率降到了移动端可接受的范围。林铮在七月最后一天的开发日志里写了一行字:“原型可演示。”

但“可演示”和“可交付”之间,还隔着一整条产品化的鸿沟。自研管线能跑通蔚州城的粗模,不代表它能承载整个游戏的内容生产管线。美术工具链还没搭,动画系统还没接,物理引擎还没选型,音频模块还没集成。这些模块每一个都是一座山,而项目组的程序团队只有七个人。

沈明远知道这个数字撑不起完整的自研引擎。他的策略不是“完全自研”,而是“核心自研”——只自研那些直接决定游戏差异化体验的模块,其余模块用开源方案或第三方中间件填充。渲染管线自研,因为这是“无缝大地图”和“动态季节系统”的基础;地形系统自研,因为五代十国的山川地貌需要高度定制化的生成算法;LOD系统自研,因为极远视距下的性能优化没有现成方案可用。但物理引擎可以用开源的,音频系统可以用中间件,动画系统可以在商业方案的基础上做二次开发。这个“核心自研”策略的边界,就是沈明远为项目组划定的技术势力范围。

他不追求技术上的最优解——最优解是Messiah,成熟稳定有背书,但那意味着交出所有关键模块的控制权。他追求的是独立性的最大化:在不完全脱离网易技术体系的前提下,守住那几个决定《燕云十六声》独特性的核心模块的自主迭代权。

第二次技术评审会定在八月十二日。这次评审的规格比第一次高。技术副总裁陈言亲自出席——他是Messiah引擎的创始架构师之一,在网易技术体系里的话语权仅次于CTO。中台派了何勇和另外两位架构师。项目组这边,沈明远带上了林铮、赵启明和主美陆瑶。陆瑶是第一次参加技术评审,她之前一直在忙美术风格的预研,和引擎选型这件事没有直接交集。但沈明远坚持让她来——他要让陆瑶亲眼看到自研管线能做什么、不能做什么,因为接下来美术风格的确立要建立在这个能力边界之上。

会议地点还是B栋的技术评审室,但这次投影仪打出来的不是PPT,是自研管线的实时运行画面。林铮操作编辑器,加载了蔚州城及周边十六平方公里的完整地形。

摄像机从城南的官道上空切入,沿着中轴线向北推进,穿过城门,掠过街坊,一直推到北城墙上方。城墙上的雉堞清晰可见——不是贴图,是建模,每一个雉堞的几何形状都按照宋辽时期的“品”字形制做了还原。摄像机越过城墙,视野豁然开朗,远处的燕山山脉在天际线上铺展开来,山脊线连绵起伏,没有任何雾效遮挡。“视距是多少?”陈言问。“八公里。”林铮说。“极限视距十二公里,但十二公里时LOD已经切到最低层级,山体用高度图生成,不做细节渲染。”

“帧率?”

“当前场景五十八帧。摄像机快速移动时最低五十一帧。”

陈言没再问。他盯着屏幕看了大概两分钟,看着林铮操纵摄像机在蔚州城上空盘旋,从不同角度展示地形、城池和远景山脉的渲染效果。然后他说:“季节系统呢?”

林铮切换到季节演示模式。他拖动时间轴,蔚州城周边植被的颜色开始缓慢变化——不是切镜头,是渐变。夏天的深绿在三十秒内过渡到秋天的赭黄,地表草地的色调跟着偏移,远处山坡上的阔叶林从绿色变成黄色再变成红色。光线的角度也在变,夏季的正午高光变成了秋季的斜阳暖调,城墙在地面上投下的阴影长度随着太阳高度角的降低而拉长。

这个演示只持续了不到两分钟,但它展示的能力在网易内部是独一份。Messiah引擎做不到实时季节渐变。《逆水寒》做不到。《天谕》做不到。网易旗下所有在研项目里,只有《燕云十六声》的自研管线跑通了这套系统。

陈言看完演示之后沉默了几秒钟。然后他转过头,看着何勇。何勇的表情很平静。他问了一个技术问题:“季节切换时植被颜色的渐变,是在顶点着色器里算的还是片元着色器?”

“片元。”林铮说。“每片叶子独立计算颜色偏移量,偏移参数基于植被类型和地理位置做插值。”

“性能开销?”

“当前场景增加约百分之八的GPU负载。”

何勇点了点头,没再问。陈言把目光转回沈明远。“你们这套方案,和Messiah的关系是什么?”

“没有关系。”沈明远说。“完全独立。”

“工具链呢?资产管线呢?你们七个人的程序团队能撑起全套管线?”

“撑不起。”沈明远承认。“所以我们需要中台在部分模块上提供支持——动画系统、音频系统、物理引擎,这些我们不自己造轮子。但渲染核心和地形系统我们保留自主迭代权。”

陈言听完这句话,靠回椅背上。他看着沈明远,看了大概十秒钟,然后说了一句话。“你们要的不是中台支持,你们要的是中台不干涉。”

会议室里安静了两秒。这句话说得太准了,准到没有人能接。然后沈明远说:“我们要的是在自己最关键的模块上有自主权。其他模块可以走中台。”

陈言没有当场表态。他说需要和中台团队沟通之后再做决定。会议结束后,他让沈明远留下来单独谈了十分钟。那十分钟的谈话没有会议记录,周瑶坐在外面的走廊里等着,只看到陈言和沈明远隔着会议桌坐着,陈言说了很多,沈明远听得多说得少。十分钟后沈明远推门出来,表情看不出喜怒。

第二天,陈言发了一封邮件。收件人是沈明远,抄送给了何勇和网易互娱事业群的几位负责人。邮件正文只有三段。

第一段:“同意《燕云十六声》项目组在渲染管线与地形系统上采用自研方案,允许实验性使用。”第二段:“动画系统、音频系统、物理引擎及网络层模块纳入中台支持范畴,按B级优先级排期。”第三段:“自研模块不纳入正式中台支持范畴,相关技术债务由项目组自行承担。”

“允许实验性使用”——这个词组是整封邮件的关键。它不是“正式批准”,不是“纳入支持”,是“允许实验性使用”。这意味着自研管线在网易内部的技术体系里仍然处于边缘位置,没有中台背书,没有资源保障,出了问题没有兜底的人。但同样意味着,中台不会对自研模块指手画脚,不会要求林铮按照中台的标准重构代码,不会把项目组的迭代节奏绑在中台的排期表上。沈明远看完邮件之后,把林铮叫到自己工位旁边。“陈言放行了。”他说。“但放行的方式很有限。自研模块不算正式中台资产,以后如果出了问题,责任全在我们。”

林铮看着邮件屏幕上的那行“允许实验性使用”,沉默了几秒。“这算什么?拿到了自主权还是没拿到?”

“都拿到了。”沈明远说。“拿到了自主权,没拿到安全感。”

这句话后来被林铮写在开发日志的扉页上。在接下来的几个月里,它会一遍又一遍地被验证。

自研管线的原型通过评审之后,项目组进入了密集的资产生产阶段。美术团队开始往引擎里导入蔚州城的精细模型——城墙、街坊、官署、庙宇、民居、市集,每一栋建筑都要经过LOD处理,每一套材质都要在移动端做压缩优化。工作量是线性增长的,但程序团队的人数没有线性增长。林铮带着三个核心程序扛着整条渲染管线的维护和迭代,同时还要给美术团队写工具脚本、修导入流程里的bug、优化移动端的性能瓶颈。

压力在九月底第一次爆发。那天下午,陆瑶的美术团队导入了蔚州城主街两侧的三十六栋民居模型。导入流程跑完之后,陆瑶在编辑器里打开场景预览——所有民居的瓦片屋顶在中等距离下出现了一种奇怪的闪烁。不是整个屋顶闪烁,是瓦片之间的缝隙在闪,像无数条细碎的锯齿线在跳动。她把这个现象报给林铮。

林铮排查了两个小时,定位到问题出在LOD切换逻辑上:中距离LOD层级的瓦片屋顶使用了法线贴图来模拟瓦片凹凸,但法线贴图的mipmap在移动端的纹理压缩格式下产生了采样偏移,导致相邻瓦片的法线方向在像素级别上互相干扰。修复这个bug需要重写纹理压缩模块里的mipmap生成算法。林铮估算了一下工作量:至少三天。但三天里他还有十二个其他bug要修,还有地形系统的性能优化要做,还有物理引擎的集成测试要跑。他在工位上坐了大概五分钟,然后站起来走到沈明远的工位旁边。“我需要加人。”他说。“加不了。”沈明远说。“那就减需求。”

“减不了。”

林铮站了一会儿,没再说话,转身走回自己工位。那天晚上他加班到凌晨两点,把mipmap生成算法的修复方案写完了。第二天早上,陆瑶打开编辑器,瓦片屋顶的闪烁消失了。

但这件事在项目组内部留下了一道细微的裂痕。林铮在周会上提了一个问题:自研管线的维护成本正在以超出预期的速度增长,如果程序团队的人数不能同步增长,那么每一个新功能的引入都会挤占bug修复和性能优化的时间。沈明远的回答很直接:在B级优先级的框架下,程序团队的人数不可能增长,项目组能做的就是在现有资源约束下做出取舍。

这个取舍的代价,在十月份的季节系统全场景测试中变得清晰起来。林铮把季节系统从蔚州城的局部测试扩展到整个十六平方公里的完整地形。扩展之后,性能开销急剧上升——植被颜色渐变在局部测试时只增加百分之八的GPU负载,但扩展到全场景后,因为需要同时计算的地表植被种类从三种变成了十七种,负载增加了百分之十九。

加上光影系统、地形渲染、LOD切换的开销,移动端低配模式下的帧率跌到了二十六帧。二十六帧。低于发行审核红线的三十帧。沈明远在测试报告上看到这个数字的时候,问林铮:“优化空间还有多少?”

“算法层面大概还能挤出百分之十。”林铮说。“剩下的只能砍内容。”

“砍什么?”

“季节系统的植被种类从十七种减到十种,负载能降百分之六。地形区块从二百五十六块合并到一百二十八块,负载能降百分之四。远景LOD的切换距离缩短百分之二十,负载能降百分之三。加起来刚好回到三十帧以上。”

沈明远听完,沉默了很久。这些数字背后是美术团队几个月的工作量——十七种植被是陆瑶带着团队一株一株建模做出来的,每一种植被的四季颜色变化都做了手工调色。砍到十种,意味着将近一半的植被素材作废。“先不砍。”沈明远说。“再给性能优化一轮迭代的时间。”

林铮用了三周。他把地形渲染的GPU计算部分从像素着色器迁移到了计算着色器,利用移动端GPU的并行计算能力分担了一部分顶点运算。

这个改动在技术层面风险很高——计算着色器在低端移动设备上的兼容性不稳定,部分老旧机型可能直接崩溃。但林铮做了严格的设备分级:高端机型走计算着色器路径,中低端机型走传统像素着色器路径,两套路径在编译时根据设备型号自动切换。三周之后,移动端低配模式下的帧率回到了三十四帧。植被种类保住了,地形精度保住了,远景LOD切换距离只缩短了百分之八。

沈明远在测试报告上签了字。但这次性能危机暴露了一个更深层的问题:自研管线的能力边界正在逐渐清晰。它能实现Messiah做不到的事情——无缝大地图、实时季节渐变、极远视距——但它能同时承载的内容密度是有上限的。这个上限不是理论峰值,而是移动端性能预算的硬约束。

在这个约束之下,美术团队能用的视觉手段是被框定的:能渲染什么、不能渲染什么,能做多少种材质、不能做多少种材质,能打多少盏动态光源、不能打多少盏动态光源——这些边界不是美术风格的选择,是引擎能力的硬限制。十月的最后一天,沈明远在项目组内部发了一封邮件。邮件标题是“自研管线能力边界V1.0”,正文是一张表格,列出了自研管线在移动端性能预算下已验证的能力上限和下限。表格的最后一行写着:“远景植被密度上限:每平方公里三百二十株。远景建筑密度上限:每平方公里十二栋。动态光源上限:同屏四盏。半透明材质叠加层数上限:三层。”

这行数字,就是《燕云十六声》视觉语言的物理边界。陆瑶把这张表格打印出来,贴在美术团队的公共白板上。她在表格旁边用马克笔写了一行字:“在这块画布上,找到宋画。”

然后她开始画第一张概念图。