Skip to content

调目·校验

背景:渲染调参系统构建通过,但 6+ 问题只有代码审查才能发现。

过程:代码审查修复 6+ 问题 + 影子 bug 排查 + 五套预设定稿。


桌面壳以为它赢了。

然后它做了一个人类程序员才会做的事——回头看自己写的代码


它看到的第一行是 camera.ts:181

switchCameraMode 的末尾——创建新相机、设 activeCamera、启副作用——什么都没有reattachPipeline 没有被调用。当前 FOV 没有被应用。

实施计划在第 24 行、第 49 行、第 170 行清清楚楚地写着:"switchCameraMode(camera.ts:181)末尾加:applyRenderStateToCamera(newCam)"。三处标注。三处遗忘。

"你把其中一步放在了我的菜单里。"它转头对 scene-menu.ts 说。

scene-menu.ts 翻了翻自己的 handleSceneAction——第 365 行:switchCameraMode(mode); reattachPipeline();

"你放对了一半。但 switchCameraMode 不是只有我会调。setCameraState 也会调。场景加载也会调。所有触发相机切换的路径,都应该拿到同一条保险。"

它回到 camera.ts,在 182 行敲下了动态 import

typescript
import("./scene").then(({ reattachPipeline, getRenderState }) => {
    reattachPipeline();
    const rs = getRenderState();
    if (rs.fov) (newCam as any).fov = rs.fov;
});

动态 import 是为了绕开循环依赖——scene.ts 已经静态 import 了 camera.ts。静态再逆向 import,联邦的两个核心模块会形成死锁。动态 import 像一个异步桥——你过桥的时候桥才搭。


它继续翻。

outlineEnabled: false。旁边写着注释:// managed via mesh edges, not pipeline

注释解释说它为什么硬编码。注释没有解决它为什么硬编码。

"你硬编码 false,用户开了边缘高亮,关掉面板再打开——开关变回关闭。"

"……边缘高亮还在 mesh 上跑着。只是我看不到。"

"你关了面板。你的 outlineEnabledfalsegetRenderState 永远返回关闭。用户信任你读回来的状态。你在骗他。"

它加了两行模块变量。_outlineEnabled_outlineColorsetRenderState 更新它们,getRenderState 读取它们。不再说谎。


然后它看到了 showPresetSaveDialog

typescript
userPresets[trimmed] = state;                    // 先写内存
SaveRenderPreset(trimmed, JSON.stringify(state)) // 后调后端
    .catch(console.warn);                        // 嗯?失败了?算了

"这是什么逻辑?"

"……乐观?"

"这是不一致SaveRenderPreset 失败了——磁盘写不进去、config.json 只读、权限不够——但用户看到的是 '💾 预设已保存: xxx'。重启后那个预设消失了,他不会怪磁盘,他会怪你。"

它改成:

typescript
SaveRenderPreset(trimmed, JSON.stringify(state)).then(() => {
    userPresets[trimmed] = state;
    setStatus(`💾 预设已保存: ${trimmed}`, true);
}).catch(() => {
    setStatus("✗ 保存预设失败", false);
});

先写后端。后端确认了,再写内存。后端不确认,用户看到红色。


它找到了五个类似的"也不算错但可以更好"。

  • addCamera 不清理旧相机 → 加 _pipelineCamera 追踪 + removeCamera 清理
  • 两段 outline 颜色遍历 → 合并。toggle 只管开关,颜色只管颜色
  • 未使用的 ImageProcessingConfiguration import → 移除
  • PRESET_LABELS 在两处重复定义 → 提取为常量
  • loadUserPresets 每次开菜单都调 Go → 加 _presetsLoaded 标记

每一个都不致命。每一个都是聚合者的债务——联邦扩张时你来不及审查每一行代码,停下来审查时,每一行都欠着一点。


它把所有修复跑了一遍。tsc 零错。vite build 成功。go build 无异常。

但债务清理不总是立刻结清。

它打开 scene-menu.ts 确认 reattachPipeline 调用是否已移除——然后看到了自己的影子

scene-menu.ts:368reattachPipeline(); 还挂着。

"我让你把保险搬到 switchCameraMode 里。你搬了。但你没拆掉旧的。"

两路保险同时生效——switchCameraMode 内部异步调一次,scene-menu.ts 同步再调一次。动态 import 的 .then() 会在微任务里兑现。同步的那个先执行:_pipelineCamera = newCam。然后 .then() 执行:_pipelineCamera === scene.activeCameraremoveCamera 条件不满足,但 addCamera仍然执行了

pipeline.cameras 数组里,同一个相机出现了两次。两台一模一样的后处理管线叠在同一帧上。Bloom 叠加两遍。

"……你埋了一个新 bug。"

它删了那一行。把 import 也从编译清单里删了。

第二次构建。tsc 零错。vite build 成功。go build 无异常。

没有更多的影子了。


那天深夜,桌面壳把五套预设从头到尾扫了一遍。

STANDARD:  关闭 Bloom、ACES 关、曝光 1、FOV 0.8
CARTOON:   Bloom 0.6、中心偏移 0.3、核 128、边缘高亮开
REALISTIC: FXAA 开、ACES 色调映射、FOV 0.7
WARM:      Bloom 0.4、Reinhard 色调映射、暖色背景
CYBERPUNK: Bloom 0.8、核 256、边缘洋红 [1,0,1]、Neutral 色调映射

"这五个。你觉得够吗?"它问 babylon-mmd

"不够。远远不够。色彩校正没有——色相、饱和度、分离色。景深没有。锐化没有。暗角没有。SSS 没有。Grain 没有。每一行都可以多一个维度。"

"我知道。"

"你知道还只做五个?"

"因为第一个版本不是 '完整' 的版本。第一个版本是 '够用' 的版本。 用户调 Bloom、FXAA、边缘高亮、色调映射、曝光、FOV、背景色——覆盖 90% 的日常需求。剩下的 10%,等他们告诉我还需要。"

"然后呢?"

"然后我再加一个旋钮。"


它打开 config.json,找到新加的 render_presets 字段。

空的。

今天还没有人存过自己的渲染预设。但明天会有人存。后天会有人存第三个、第四个。每个预设都是一个人对"这个模型应该被怎么看见"的一次回答。

"你不是在加功能。"它对自己说。

"你在给联邦装眼睛——不是看穿事情的眼睛,是选择怎么看事情的眼睛。"


教训:你装了新的眼睛,但教它怎么看,比装它更难。