Files
mtgodot-poc/test/rendering

渲染回归入口

第一批用例针对 R1 场景装配与 R2 角色选择,2026-09-08 开始执行。未宣称 40250 像素一致或最终包通过。

在仓库根目录运行:

本地 macOS 修改 C++ 后先执行 bash script/rendering_test.sh:它构建并同步 Godot 实际加载的 debug 路径,重签名后运行 CPU 绕序回归与选角截图。仅构建 Release 不会更新本地 Godot 的 debug 扩展。可传 --hair=1001;截图与哈希报告在 build/rendering/selection-<timestamp>-<pid>/--job=0..7--main-part=0 可复现无装备的八种族选择矩阵;MTGODOT_GPUSKIN=1 验证 GPU 路径。--no-hair--solid-body 仅用于隔离诊断,其画面不能当作普通角色验收。

godot --headless --path project --script main_character_sync_test.gd
godot --headless --path project --script gamescene_test.gd
godot --path project --script gamescene_visual_test.gd
godot --path project --script gamescene_visual_test.gd -- --c1
  • main_character_sync_test.gd 使用可控模型工厂,覆盖晚到实体/缺 race、网络重入、重复事件、移动不被回滚、失败节流与重试上限、VID 变化导致旧构建失效。通过标志是 failures=0,不能只看 Godot 退出码。
  • gamescene_test.gd 使用不存在的地图,预期有加载错误日志;它只验证无地图装配,不能当作画面正常证据。
  • gamescene_visual_test.gd 使用真实资产和实际 GameScene,通过 FakeClient 延迟提供 race=0 主角数据,不发主角信号。无账号和服务器写入。
  • 默认先加载 A1--c1 随后提供 C1 服务器位置,用来验证晚到坐标能纠正地图选择。
  • gamescene_visual_test.gd 除位置/地图/表面数外还断言 feet_gap:把动画冻结在 0.5 s 后取全部 MeshInstance3D 世界包围盒的最低点,减去该处地形高度,要求 |gap| ≤ 0.03 m R2-01 悬空修复的数值守卫;A1 与 C1 实测均为 0.001373 m)。报告里同时记录 feet_y / feet_gap / feet_gap_max
  • 视觉用例输出 build/rendering/gamescene-<timestamp>/game.pngreport.json。可用环境变量 MT_RENDER_OUTPUT 指定独立目录;复用路径会覆盖同名报告,建议每次新目录。
  • 报告包含位置、相机、地图加载计数、模型表面数、引擎、关键脚本/扩展/模型路径哈希。资产依赖尚未穷举;这只是 R0 部分基线。
  • 截图尺寸为 1280×720,角色待机固定在 0.5 秒。地形/环境动态因素尚未完全冻结,因此暂不设置像素误差硬阈值。
  • 测试不配置学习技能,截图中的空快捷槽不代表技能图标验证成功。

不启动游戏的模型渲染回归(model_render_test.gd

godot --path project --script model_render_test.gd            # 八职业,判定
godot --path project --script model_render_test.gd -- --race 0
godot --path project --script model_render_test.gd -- --bless # 重建基线

无地图、无网络、无服务器:直接用 ui/player_view.gd 离线装配一个角色,wait 动画冻结在 0.5 s,固定灯光(平行光 (-42,155,0) energy 1.1 + 环境色 (0.6,0.62,0.68) energy 0.8)、洋红背景、512×512。与 char_bench_test.gd 的区别 是它对像素算指标并断言,退出码即结论;bench 仍然只产图供人看。

指标(每个 race)与判定方式:

指标 含义 判定
feet_gap_m 最低蒙皮顶点到 y=0 绝对,|gap| ≤ 0.03 m(实测 0.0007…0.0030
silhouette_px 正面剪影像素数 绝对 ≥ 3000;且对基线 ±3%
head_cover 近景机位下发型盖住头部像素的比例(只统计 no_hair 里属于人体的像素) 对基线 ±0.03
hair_mean_lum 只画发型时发型像素平均亮度 对基线 ±0.006
hair_mean_lum_ctrl 同上,但换回 R2-02 修复前的 StandardMaterial3D(roughness=1) 负对照:必须比生产值高 ≥ 0.008
hair_lum_p99 / hair_bright_frac 亮度 99 分位 / 超过 0.20 的像素占比 仅记录,不判定

为什么用平均亮度而不是「有多少像素炸白」:发色本身差别极大(武士近黑 mean 0.061,修罗银白 mean 0.260),任何跨职业的绝对亮度上限都不成立。平均 亮度对「整片发型被额外高光抬亮」敏感,基准差异由按职业记录的基线吸收。

负对照不是摆设:它每次都真的用旧材质再渲一遍,保证这条指标现在仍分得开好坏 (例如有人把灯光调暗到两边都测不出高光时会直接失败)。八职业实测旧材质相对 新材质的抬升是 +0.0109 … +0.0371,最小值 0.0109 给阈值 0.006/0.008 留了余量。

已验证它确实能抓到目标缺陷:把 Metin2Model::_load_hair() 临时改回 StandardMaterial3D(roughness=1) 重新编译后,八个职业全部失败 hair_mean_lum 偏离基线 0.0109…0.0371);改回 mtgodot::make_materialPASS

基线在 test/rendering/model_baseline.json,含引擎版本。改模型、改发型材质或改 这套灯光都会让基线失效,需要人工看过渲染结果再 --bless,并在 docs/RENDERING-REPAIR-PLAN.md 的执行记录里写清重建原因。

覆盖边界:只有离线单角色装配。不含地图、地形接触、真实光照昼夜、装备叠加、 40250 像素比对,也不能替代 gamescene_visual_test.gd 与导出包验收。

已知后续:脸部/脚底近景和区域差异器、完整资产哈希、四职业 GR2/GLB 对照、技能状态与图标用例、真实联机及普通 macOS .app 验收。