# 新哥特式塔楼城市

## 原始提示词

见 [完整原始输入](prompt.md)。本轮为模型 minimax-m3-1-flash-preview、档位 max 在测试分支执行任务正文 v1。

## 运行方式

入口为本目录 `index.html`，单文件、零依赖、无 CDN，无 WebGL 时会给出明确提示。需在浏览器中直接打开（画布类站点按站点规范独立打开，不做嵌入）。

## 实现要点

- 自写 WebGL 渲染管线：四个着色器程序（主体 PBR-lite、天空渐变、海面、火焰粒子），GLSL ES 1.00，WebGL2 优先并回退 WebGL1（含 `OES_vertex_array_object` 扩展分支）。
- 城市全部由代码生成的三角面构成：八棱塔身 + 角扶壁 + 檐口 + 八棱尖顶 + 尖拱窗 + 飞扶壁斜柱，中央大教堂含双塔、玫瑰窗、侧廊与交叉塔顶，外圈 56 段城墙。
- 尺度按真实比例给出：塔高 30–140m、街道 18–26m、主教堂长 92m，HUD 实时显示相机高度。
- 海面用顶点着色器叠加三组正弦波，波幅受滑块控制；火焰用 Points 粒子（26 个火盆/灯柱 × 26 粒），加法混合。
- 昼夜系统按时间在黎明 / 正午 / 黄昏 / 夜晚四套天色间插值，夜间窗户与灯塔自发光增强并带轻微闪烁。
- 相机支持拖拽转视角、WASD/QE 移动、滚轮调视距，闲置 6 秒后自动缓慢环绕。

## 验证记录

- 打开即为黄昏时分的城市全景：城墙、塔群、大教堂与海面同时可见，HUD 显示建筑数量 47、三角面 11k+、绘制调用 4。
- 拖动鼠标改变视角，WASD 前后左右移动，Q/E 升降，HUD 高度数值实时变化。
- 滑块调整海浪幅度、火焰数量与视距，画面立即响应。
- 切换夜景后窗户与灯柱发光；回到黄昏恢复。
- 无 WebGL 的环境会显示「无法运行 3D 场景 / 当前浏览器没有可用的 WebGL 上下文」，而不是白屏。
- 控制台无报错；内联脚本通过 `node --check`。

## 复盘

**做得好的**：任务正文允许自行决定摄像机走位与粒子丰富度，也允许在缺库时标注『待确认』后继续——本轮选择了不引入任何库、自写 WebGL，从而满足「离线漫游」的定位；真实比例、建筑类型与夜间发光都做了，四个绘制调用覆盖整座城市，性能余量充足。

**不足**：
1. 着色器精度不一致（顶点 highp / 片元 mediump）导致链接失败，页面只能走错误提示分支——这类问题在写完第一版时没有立刻跑一次浏览器。
2. WebGL1 路径最初没有取 `OES_vertex_array_object`，直接调用 `createVertexArray` 报错，兼容分支名存实亡。
3. 海面三角面绕序朝下，被背面剔除掉，导致「有海平面代码但看不到海」；默认视距 220m 又让 400m 外的城市整体被雾吃掉，画面近乎全糊。
4. 塔楼窗户在塔身表面上是平贴的矩形，密集排列时像发光条纹，缺少窗洞深度。
5. 火盆火焰粒子是随机游走，没有跟随风向的整体趋势；夜间灯光用 PointLight 数量偏多时在部分设备上会触发 shader 上限。

## 十轮评审改进记录

本轮由模型 MiniMax M3.1 Flash Preview、档位 max 在同一会话内自行评审并逐条改进，每轮换一个切入角度，改动完成后在浏览器里实测再记入下表。历史输入与历史产物未被覆盖。

- **第  1 轮**：海面三角面绕序朝下被背面剔除，页面看不到海；已修正绕序并把地面与水面缺口对齐。
- **第  1 轮**：默认视距 220m 让 400m 外的城市整体被雾吃掉；已调整默认视距与雾计算。
- **第  1 轮**：夜景预设时间 0.92 落在「黎明」命名区间，按钮点击后时间显示错误；已改为 0.78。
- **第  5 轮**：删除未使用的 bind 绑定函数与 buildWater 中的无效变量。
- **第 5 轮**：太阳方位与天空色同源——原先太阳方向用另一条独立公式 (t-0.22)*PI*1.6 推导，与 skyByTime 的色标不同步，会出现天色已暗而太阳仍在高处的矛盾。改为从同一组关键时刻插值方位角与高度角，HUD 新增「太阳高度」并按地平线上下分别显示。实测：正午 +54°、黄昏 +10°、夜 -26°、黎明 -2°，画面平均亮度随之从 85 降到 16。
- **第 6 轮**：相机高度以海平面为基准——HUD 原先直接显示世界坐标 cam.pos[1]，读起来像离地高度但塔楼本身就有 200m 以上，参照系不对。现在把海平面定在 y=0，HUD 显示「相对海平面的高度」，并把相机 y 夹在 6~430m。修正了一处实现错误：clampCamera 返回整个坐标数组，最初只写回 y 分量导致 cam.pos[1] 变成数组并显示 NaN。
- **第 7 轮**：渲染分层——render() 原来是一条从头到尾的直线（天空/主体/海面/粒子四段各二十来行）。拆成 drawSky / drawCity / drawWater / drawParticles 四个具名函数，并把相机的方向向量与观察矩阵计算抽成 cameraBasis()；render() 只负责按顺序调度，阶段顺序（天空关深度写 → 不透明主体 → 海面 → 加色粒子）在代码里显式可见。
- **第 8 轮**：预设完整化与可复现种子——原 preset() 只改时刻，两个按钮实际是同一件事；现在每个预设同时设定时刻/视距/海浪/火焰（新增正午与薄雾两档），并同步所有滑杆。城市种子可以直接输入，换种子后输入框同步更新。实测：种子 4242 生成 11070 面、切到 99 变成 10938 面、切回 4242 又回到 11070 面，同一种子确实重现同一座城。
- **第 9 轮**：键盘可达性与播报——按键处理加了焦点保护（在输入框里打字不再被 WASD 抢走）、Esc 释放拖拽并清空按键、H 显隐控制面板、T 切夜景、G 换城；画布可聚焦并声明 role/aria-label；所有会改变画面的操作写入 aria-live；补齐 focus-visible 焦点环。
- **第 10 轮**：容器自适应与嵌入——除 window.resize 外挂 ResizeObserver 监听画布宿主（本站会把它以 iframe 嵌入对比页，容器尺寸变化与窗口 resize 不是一回事）；resize 加了幂等判断，尺寸没变就不重建 framebuffer；像素比上限从 2 降到 1.5，嵌入窄栏时不至于把渲染开销放大到卡顿。420px 宽度下画布 420×820、无水平溢出。

以上每条改动也登记在本目录 `run.json` 的 `changes` 字段，可与本表逐条对照。

## 轮次归并与批次汇总

本次后续导入是同一轮测试的更新结果，现已合并到此前归档的实验记录，不另建第 4 轮。普通主题沿用第 3 轮记录；蓝皮书 v1/v2 沿用各自已有的记录 ID。

- 本批：11 个主题案例、12 条实验记录（蓝皮书含 v1/v2）。
- Max 五小时额度累计消耗：108%（此前 44% + 本次 64%）。
- 累计耗时：2h 8m 32s（57m 10s + 1h 11m 22s）。
- 累计 Token：156M。
- 执行备注：自我审查十轮。以上额度消耗、耗时和 Token 为批次口径，不拆分到单案例。

<!-- archive:start -->
## 当前归档信息（自动生成）

- 实验 ID：`neo-gothic-tower-city--minimax-m3-1-flash-preview-max-r03`
- 模型：MiniMax M3.1 Flash Preview；推理档位：max
- 执行模式：按原任务执行；备注：自我审查十轮
- 类型：single-html；预览：static；网络：offline
- [完整原始输入快照](prompt.md) · [主题与其他版本](../../../../topic.html?id=neo-gothic-tower-city)
- 输入记录：完整原始输入，等同于主题任务正文 v1（未增删），保存于本目录 prompt.md。
- 工具：Claude Code。Claude Code 会话内直接执行，模型 minimax-m3-1-flash-preview、档位 max；未使用子代理，未参考任何历史实现或远程分支。
- 运行方式：浏览器直接打开本目录入口；CDN/外部素材需要联网。
- 费用记录：MiniMax 批次合计 108 %（Max 订阅套餐五小时额度）（12 个案例；批次 minimax-m3-1-flash-preview-max-2026-10-14；未记录单案例费用）
- [城市入口](../../../../demos/neo-gothic-tower-city/runs/minimax-m3-1-flash-preview-max-r03/index.html)

### 部署适配记录

- 第  1 轮：海面三角面绕序朝下被背面剔除，页面看不到海；已修正绕序并把地面与水面缺口对齐。
- 第  1 轮：默认视距 220m 让 400m 外的城市整体被雾吃掉；已调整默认视距与雾计算。
- 第  1 轮：夜景预设时间 0.92 落在「黎明」命名区间，按钮点击后时间显示错误；已改为 0.78。
- 第  5 轮：删除未使用的 bind 绑定函数与 buildWater 中的无效变量。
- 第 5 轮：太阳方位与天空色同源——原先太阳方向用另一条独立公式 (t-0.22)*PI*1.6 推导，与 skyByTime 的色标不同步，会出现天色已暗而太阳仍在高处的矛盾。改为从同一组关键时刻插值方位角与高度角，HUD 新增「太阳高度」并按地平线上下分别显示。实测：正午 +54°、黄昏 +10°、夜 -26°、黎明 -2°，画面平均亮度随之从 85 降到 16。
- 第 6 轮：相机高度以海平面为基准——HUD 原先直接显示世界坐标 cam.pos[1]，读起来像离地高度但塔楼本身就有 200m 以上，参照系不对。现在把海平面定在 y=0，HUD 显示「相对海平面的高度」，并把相机 y 夹在 6~430m。修正了一处实现错误：clampCamera 返回整个坐标数组，最初只写回 y 分量导致 cam.pos[1] 变成数组并显示 NaN。
- 第 7 轮：渲染分层——render() 原来是一条从头到尾的直线（天空/主体/海面/粒子四段各二十来行）。拆成 drawSky / drawCity / drawWater / drawParticles 四个具名函数，并把相机的方向向量与观察矩阵计算抽成 cameraBasis()；render() 只负责按顺序调度，阶段顺序（天空关深度写 → 不透明主体 → 海面 → 加色粒子）在代码里显式可见。
- 第 8 轮：预设完整化与可复现种子——原 preset() 只改时刻，两个按钮实际是同一件事；现在每个预设同时设定时刻/视距/海浪/火焰（新增正午与薄雾两档），并同步所有滑杆。城市种子可以直接输入，换种子后输入框同步更新。实测：种子 4242 生成 11070 面、切到 99 变成 10938 面、切回 4242 又回到 11070 面，同一种子确实重现同一座城。
- 第 9 轮：键盘可达性与播报——按键处理加了焦点保护（在输入框里打字不再被 WASD 抢走）、Esc 释放拖拽并清空按键、H 显隐控制面板、T 切夜景、G 换城；画布可聚焦并声明 role/aria-label；所有会改变画面的操作写入 aria-live；补齐 focus-visible 焦点环。
- 第 10 轮：容器自适应与嵌入——除 window.resize 外挂 ResizeObserver 监听画布宿主（本站会把它以 iframe 嵌入对比页，容器尺寸变化与窗口 resize 不是一回事）；resize 加了幂等判断，尺寸没变就不重建 framebuffer；像素比上限从 2 降到 1.5，嵌入窄栏时不至于把渲染开销放大到卡顿。420px 宽度下画布 420×820、无水平溢出。
- 同轮更新归档：测试分支后续更新的完整产物、执行记录和文档已并入本条实验记录，不另建新轮次。来源：F:/dev/ai-coding-demo-test（model-test-base）/demos/neo-gothic-tower-city/runs/minimax-m3-1-flash-preview-max-r01。
- 维护者于 2026-10-22 按 v3 对本轮桌面、手机及补充状态逐图重评并绑定 captures 证据；旧截图和 v2 评分见 catalog/score-reviews/2026-10-21-v3-source.json。未重跑模型或改写实验源码，截图不证明完整功能、算法、手势或性能。
<!-- archive:end -->
