☰
Claude-of-Duty像素级回归测试指南:用baseline与imagediff打造位图一致的确定性截图门
2026/9/27 1:40:02 网站建设 项目流程

Claude-of-Duty像素级回归测试指南:用baseline与imagediff打造位图一致的确定性截图门

【免费下载链接】Claude-of-DutyA Call of Duty-quality FPS in Three.js, built from a single prompt.项目地址: https://gitcode.com/gh_mirrors/cl/Claude-of-Duty

Claude-of-Duty 是一个用 Three.js 从零构建的《使命召唤》级浏览器 FPS,它附带一套专业的像素级回归测试体系:baseline.mjs负责可复现地拍摄 11 张固定镜头截图,imagediff.mjs则逐像素比对两组截图,任何一像素变动都会让测试门失败(非零退出码)。这套工具曾在一次性能优化中被用来强制执行"零视觉变化"——优化后的构建与优化前参考图在所有镜头上逐位一致(bit-identical)。本指南带你快速上手这套截图门。

1. 为什么需要"像素级"的回归门?

这个项目最有意思的部分不是游戏本身,而是围绕它的测试框架(harness)。项目 README 明确记录了两个关键教训:

  • 中位数帧时间会掩盖真实问题:静态相机基准测出 94 fps,但实际游玩只有 12–17 fps,根因是 34+ 个 WebGL 着色器在帧中途惰性编译导致 728–1236 ms 的卡顿。
  • 截图曾经不可复现:shotset.mjs在同一个页面里连拍 11 个镜头,粒子年龄、贴花缓冲、曝光状态会"泄漏"到后续镜头——两次完全相同的运行有 10/11 张截图不一致。

也就是说,如果截图本身不稳定,"视觉回归"就无从谈起。于是有了 tools/baseline.mjs 与 tools/imagediff.mjs 这对组合。

2. 回归工具链一览

工具用途是否用于回归门
tools/capture.mjsGPU 驱动的无头 Chromium 拍摄单个命名镜头快速验证用
tools/shotset.mjs单会话拍全部 11 个镜头,供人工/评审快速浏览❌ 不可复现
tools/baseline.mjs可复现采集:每个镜头独立页面 + 固定帧预算✅ 生成基线
tools/imagediff.mjs逐像素比对,有像素变动则退出码非零✅ 回归门

11 个镜头全部定义在 src/dev/shots.js 中,涵盖环境光照(hero/interior/detail/sunset/night)、武器视角(weapon/ads/muzzle)与战斗特效(combat/impacts/hud)三大类,每个镜头都是冻结输入后的固定相机位姿 + 固定时刻,保证每次评审看到的构图完全一致。

3. baseline.mjs:确定性截图的三根支柱

打开 tools/baseline.mjs 的文件头注释,作者把可复现性拆解为三条设计,理解它们就能理解整套截图门:

  1. 隔离(ISOLATION)——每个镜头开一个全新页面。避免粒子年龄、贴花缓冲、动画相位、自动曝光状态在镜头之间泄漏;
  2. 固定帧预算(FIXED FRAME BUDGET)——镜头在已知的帧索引处应用,随后精确推进settle(默认 90)帧,使 TAA 抖动相位、曝光自适应等时间累积器总从同一相位收敛;
  3. 时间重置(TEMPORAL RESET)——开拍前请求渲染器丢弃 TAA 历史并重置曝光,从已知相位开始累积。

更深层的确定性来自lockstep 模式。引擎自身的 rAF 循环会在测试框架做各种等待往返时继续推进帧数,导致"快门按下时的帧索引"每次漂移 10–20 帧,所有与帧索引锁相的效果(TAA 抖动、噪声旋转frame % 64、曝光自适应)都会解算出不同结果。修复方案写在 src/dev/shots.js 的注释里:lockstep 下引擎从不自己调度帧,帧只发生在测试框架显式调用的__PUMP__(n)内——精确推进 n 帧,快门时刻的帧索引成为常量,截图期间仿真完全静止。

配套的还有固定步长时钟:capture 模式下每帧 dt 被强制为精确的 1000/60 毫秒,与启动路径无关(见 src/dev/shots.js)。再加上 ARCHITECTURE.md 的硬性规则——游戏逻辑与视觉中禁止使用Math.random(),必须用确定性随机源 src/core/rng.js——采集可复现性在代码层面被制度性地兜住。

4. imagediff.mjs:逐像素的回归门

tools/imagediff.mjs 用 pngjs 逐字节读取两组 PNG,按通道取最大差值统计:

  • tol(默认 0):每通道 0–255 差值容差,低于它的像素视为未变化;
  • 输出指标:每张截图的changedPct(变化像素百分比)、maxDelta(最大通道差)、meanDelta(平均通道差);
  • 退出码即门禁:tools/imagediff.mjs 中,只要所有镜头changedPct为 0(或整体在明确论证过的 epsilon 内:变化 <0.05% 且maxDelta ≤ max(2, tol))才返回 0,否则退出码为 1——可直接接入 CI 流水线;
  • --write-diff:把发生变化的像素以亮品红色高亮、原图压暗输出.diff.png,方便肉眼定位差异区域。

项目 README 记录了它的实战价值:一次大规模优化被约束为"零视觉变化",且这一约束由imagediff.mjs强制执行而非口头声明——交付构建在全部 11 张镜头上与优化前参考位图一致。

5. 快速上手:三步建立你自己的截图门

环境要求:Node.js + 依赖中的playwright、pngjs、vite(见 package.json)。

第一步:安装依赖

git clone https://gitcode.com/gh_mirrors/cl/Claude-of-Duty cd Claude-of-Duty npm install npx playwright install chromium

第二步:采集基线

node tools/baseline.mjs --out=shots/base --port=8080

工具会自动拉起 vite(若端口未占用)、以 1920×1080 视口、deviceScaleFactor=1、sRGB 色彩配置启动无头 Chromium,逐一采集 11 张 PNG 并写出report.json。常用参数:

  • --shots=hero,detail只拍子集,迭代更快;
  • --settle=90调整收敛帧数;
  • --query=prewarm=0向所有镜头 URL 追加查询参数,可做 A/B 对照(例如对比预热的开/关)。

第三步:改动代码后做像素比对

# 重新拍一组(例如优化后的构建) node tools/baseline.mjs --out=shots/opt --port=8080 # 逐像素回归门 node tools/imagediff.mjs --a=shots/base --b=shots/opt [--tol=1] [--write-diff]

退出码为 0 即"视觉无变化"通过门禁;为 1 时终端 JSON 会指出worst(最差镜头)与每张镜头的统计行,加--write-diff后还能直接看高亮差异图。

6. 实践要点与常见陷阱 🎯

  • 不要拿shotset.mjs的结果做回归比对。它快,但同页连拍导致时间状态泄漏,两次运行 10/11 张不同。它只适合人工评审。
  • 锁相帧索引是复现性的核心。TAA 抖动相位、GTAO/SSR/接触阴影噪声旋转都按frame % 64走,帧索引差 1 帧,输出就差一截。这正是 src/dev/shots.js 中 lockstep 模式存在的原因。
  • 预热(pre-warm)也会"看起来"改变画面:它消耗约 1.4 s 墙钟时间,若系统动画依赖performance.now()而非引擎时钟,启动时长变化就会平移一切输出。项目修复方法是让相关子系统改用引擎时钟,配合 src/core/prewarm.js 的着色器预热实现"可证明的像素中性"。
  • 跨机器位图一致有前提:同一 GPU 后端与驱动下可 bit-identical;换显卡驱动后可能出现系统性微小差异,此时应重新采集基线,并用--tol明确论证 epsilon。
  • 报告即诊断:tools/baseline.mjs 会把页面pageerror与 console error 收进report.json并影响退出码——截图失败不会静默通过。

7. 延伸阅读

  • 回归门背景与设计权衡:README.md 中 "Tooling" 与 "Performance" 章节
  • 引擎契约与确定性硬规则(禁用Math.random()):ARCHITECTURE.md
  • 镜头定义与 lockstep 实现:src/dev/shots.js
  • 单镜头快速截图:tools/capture.mjs
  • 确定性随机源:src/core/rng.js
  • 着色器预热的像素中性约束:src/core/prewarm.js

一句话总结:先让截图可复现(baseline 的隔离 + 锁相 + 固定帧预算),再谈逐像素比对(imagediff 的门禁语义)——这就是 Claude-of-Duty 把"优化不许改画面"从口号变成可执行退出码的完整方法。

【免费下载链接】Claude-of-DutyA Call of Duty-quality FPS in Three.js, built from a single prompt.项目地址: https://gitcode.com/gh_mirrors/cl/Claude-of-Duty

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询