距离上次更新 AetherEngine 的立项文章才过了两周,但感觉像是过了很久——大概是这两周发生的事情太多了。

白天上班,晚上回家带娃,等娃睡了差不多也就十点了,属于自己的时间被压缩得所剩无几。KongEngine 那会儿周末还能泡一整天在代码里,现在连完整的一个小时都是奢望。

但偏偏就是这种时候,我做了一件看起来挺疯狂的事:在两周内,让 AI 把 AetherEngine 从只有一个三角形的骨架,推到了拥有延迟渲染、阴影、IBL、SSAO、SSR 和编辑器基建的完整引擎。

为什么在这个时间点做这件事

说实话,AetherEngine 的立项多少有点"不务正业"。UE 渲染那块工作上的事情已经够忙了,回家还要带娃,哪有时间从零开始写一个引擎?

但有两个契机让我决定试试。

第一个契机是去年年底家里迎来了小宝宝。 joy 是真的 joy,累也是真的累。以前周末两天可以完整做自己的事,现在周末是全天候带娃模式。我算了一下,一周下来属于自己的整块时间,加起来可能不到五个小时。五个小时,连 KongEngine 一个功能的零头都写不完。

第二个契机是 AI 编程能力的爆发。年初开始尝试用 AI 写一些工作里的脚本和工具代码,发现效果出奇的好。不是那种"生成个 hello world"的好,是真的能处理复杂逻辑、跨文件引用、甚至 debug 的好。一个想法突然冒出来:如果 AI 能写业务代码,那它能不能写渲染引擎?

答案是能,但有个前提——你得设计一套让 AI 能稳定发挥的系统。

从"写代码"到"描述需求"

KongEngine 的每一行 C++ 都是我手敲的。延迟渲染、级联阴影、SSR、水体效果,从算法查资料到 shader 调参,全流程自己啃。那时候写一篇文章,背后往往是两三个周末的埋头苦干,调一个 shadow acne 能调一晚上。

AetherEngine 完全不一样。从 5 月 24 日立项到现在,GBuffer Pass、Lighting Pass、Shadow Map、IBL、SSAO、SSR,甚至统一启动器和编辑器骨架——我一行代码都没看过。不是不会看,是不需要看。

我的工作变成了这样:晚上十点半,娃睡了,我打开电脑,花十分钟写一个需求描述,“Phase 2 需要 SSAO,参考 KongEngine 的实现,但要适配我们的 deferred 管线”。然后 AI 去写提案、拆任务、生成代码。第二天早上我花五分钟验收,看看效果对不对,帧率是否达标。对的话就归档,不对的话写两句修改意见,AI 继续改。

整个流程里,我只做决策和验收。技术细节、代码实现、调试修 bug,全是 AI 的事。

让 AI 写引擎,需要专门设计

当然不是说扔给 AI 一句"给我写个渲染引擎"就能出好东西。AI 不是万能的,你扔给它一个模糊需求,它要么给你一团浆糊,要么给你一个从某个教程里背下来的 OpenGL demo。

要让 AI 稳定产出高质量的引擎代码,需要专门设计的协作界面。AetherEngine 从第一天开始就不是为"人类手写"设计的,而是为"AI 生成"优化的。

模块必须小到能塞进上下文

AI 的上下文窗口是有限的。你让它读一个 2000 行的文件,再让它改其中某个功能,它大概率会漏掉依赖关系。AetherEngine 的约束是每个模块控制在 500 行以内,一个文件就是一个完整的概念单元。

比如 GBuffer Pass 就 400 来行,包含 WGSL shader、pipeline 创建、uniform 绑定、execute 方法。AI 生成时上下文完整,不会出现"忘了改 shader 里的 binding 编号"这种低级错误。

接口必须简单到不需要解释

KongEngine 的 C++ 模板元编程,AI 看了直接懵。AetherEngine 用 hecs 做 ECS——没有 derive 宏,没有复杂的 trait 约束,就是简单的 world.spawnquery。AI 生成 ECS 代码的通过率,比让它写 C++ 的 CRTP 模式高了一个数量级。

RenderGraph 的 Pass trait 也一样:prepare() 准备资源,execute() 录制命令。没有生命周期体操,没有关联类型,AI 不会在这里翻车。

着色器必须内联

wgpu 用 WGSL,语法比 GLSL 简洁。但 AetherEngine 更进一步,着色器代码直接内联在 Rust 文件里,用 r#"..."# 包裹。

为什么?因为 AI 在生成 Rust 代码时,如果 shader 是外部文件,它经常忘了同步修改 shader 里的 binding 编号或者结构体布局。内联之后,shader 和 Rust 的 bind group 布局在同一个上下文里,AI 出错的概率大幅降低。

还有个意外收获:Windows 上 include_str! 偶尔会产生"幽灵文件",Rust 编译器读到了缓存但文件实际不在磁盘。内联彻底规避了这个问题。

OpenSpec 作为 AI 的长期记忆

AI 没有跨会话的记忆。今天让它实现了 GBuffer Pass,明天让它实现 Lighting Pass,它不会记得昨天 GBuffer 的纹理格式是什么。

AetherEngine 用 OpenSpec 工作流解决这个问题:Proposal 写清楚为什么要做这个功能,Design 定义接口和验收标准,Tasks 拆成 AI 能独立完成的子任务,完成后 Archive 归档。每个新功能开始前,AI 先读相关 archive,了解现有架构,不会凭空造一套不兼容的接口。

这套方法论的产出

从 5 月 24 日立项到现在,AetherEngine 的进展:

阶段 内容 状态
Phase 0 引擎骨架(窗口、三角形、egui) 已完成
Phase 1 Deferred PBR + Shadow Map + IBL 已完成
Phase 2 SSAO + SSR + Tonemap 已完成
Phase 3 ECS 编辑器 + Picking + Gizmo + Save/Load 基本完成

两周半,一个包含延迟渲染、阴影、环境光照、屏幕空间反射、编辑器骨架的引擎。如果是我手写,同样的功能量大概需要三个月——而且是在有完整周末的前提下。现在一周就五个小时,没有 AI 的话这个项目根本立不起来。

wgpu 全栈升级(0.19 → 29.0)这种繁琐但关键的工作,AI 花了两天完成,所有测试通过,运行时冒烟通过。让我自己来做,估计先花半天查 changelog,再花一天改编译错误,第三天才能跑起来。

编辑器界面

上图是 AetherEngine 的编辑器界面——带场景层级面板、Inspector、Transform Gizmo,全部 AI 实现。下图是 IBL 环境光照效果,同样是 AI 从提案到代码一条龙完成。

IBL 环境光照

踩过的坑

当然不是说 AI 万能,有几个坑是必须要人把关的。

AI 会过度设计。有一次让它实现一个配置加载,它给我整了一套完整的资源热重载系统,带文件监听和自动刷新。我只需要读个 JSON 而已。后来我在设计阶段加了"最小可行方案"的约束,这种情况才减少。

AI 不知道 runtime 的坑。wgpu 不支持多个 fragment entry point per pipeline 这个限制,AI 第一次写 GBuffer MRT 时直接写了四个 fs_main,编译报错才知道改。这类框架特有的限制,必须靠人提前写在"已知陷阱"文档里。

AI 会遗忘跨模块的约定。比如 normal 编码,GBuffer 里要 *0.5+0.5,Lighting 里要 *2.0-1.0。AI 第一次实现时漏了这一步,画面全黑。后来我把这类约定写进上下文文档,AI 生成前先读,问题就少了。

写在最后

这个转变对我个人来说挺有意思的。以前周末的时间花在调 shader、追内存泄漏、写模板元编程。现在周末的时间——好吧,现在其实没什么周末时间了——但晚上那仅有的一个小时,花在设计接口、审 AI 的提案、思考架构约束上。

某种意义上,我从"工匠"变成了"建筑师"。AI 是我的施工队,而且是一支不需要睡觉、不会抱怨、能瞬间理解需求的施工队。我的工作就是确保蓝图是对的,验收标准是清晰的,施工规范是明确的。

AetherEngine 的终极目标也不是"写一个渲染引擎"——KongEngine 已经证明了我能写出来。AetherEngine 的目标是验证一种可能性:一个白天上班晚上带娃的普通人,借助 AI,能否在几周内设计并交付一个现代渲染引擎。

目前看来,答案是肯定的。

技术之路道阻且长,但好的工具能让这条路走得更快一些。而 AI,可能是我们这代人遇到的最好的工具。


本文对应 AetherEngine 仓库:https://github.com/ruochenhua/AetherEngine