AI 协作日志

学生 Web 实验作品平台

学号:202412211401007 · 来源:ai-log.md

# 实验五 · AI 协作记录(ai-log.md)

> 说明:以下三项按“先自己预测 → 再问 AI → 自己选测试 → 记录实际结果 → 改正结论”的顺序完成。
> 所有数值均来自本机实测(Chromium,WebGL2),不是凭肉眼判断;完整数据见 `README.md` 第 4 节。
> AI 负责给假设、给最小测试和讨论方案;**最终选择测试、判断结果、修改代码由本人完成**。

---

## 协作 1 · 概念核验(3 分,对应 B / C)

### 1.1 我先写下的预测(提问之前)

| 改变哪一项 | 画面 | 模型世界中心读数 | 判断依据 |
|---|---|---|---|
| 只改 M(按 D 平移模型) | 物体移动 | 应该变化(0 → 0.1) | 世界坐标被 M 改了 |
| 只改 V(切换观察预设) | 方向改变 | 应该**不变**(0.00) | 相机换了,物体没动 |
| 只改 P(正交 ↔ 透视) | 近大远小关系改变 | 应该不变 | 只换投影,M/V 原样 |
| 只改 FOV | 物体在画面中占比变化 | 不变 | 属于 P,不是 Model 缩放 |

### 1.2 问 AI 的提示词(按指导书 8.1 改写)

> 在 WebGL2 右手坐标系、列向量和 P·V·M 的顺序下,我想比较“移动物体”与“移动相机”、“正交”与“透视”。请给我三组能区分原因的控制变量实验,指出每组应改变 M/V/P 中的哪一项,说明可以打印哪些数值进行核验;不要直接生成整套代码。

### 1.3 AI 给出的建议(摘录)

1. **M 组**:保持 eye/target 与投影不动,只让 `tx` 增加 0.1;打印 `stat-world`、`stat-camera`,并直接读 `state.tx`。若世界中心变了而 eye 不变,就是 M 的作用。
2. **V 组**:保持 M 全零(tx/ty/tz/rx/ry/scale),依次切换正/侧/顶/斜上四个 eye;打印 eye、target 与 `stat-world`。世界中心必须恒为原点,变化只发生在 V。
3. **P 组**:固定同一个 M、V 与两个等尺寸、不同深度的立方体;分别用正交 `ortho(-w/2,w/2,-h/2,h/2,near,far)` 与透视 `perspective(fov,aspect,near,far)`,把两立方体 8 个角的**裁剪坐标**算出来再除以 w,比较 NDC 宽度比。另建议打印 `aspect = canvas.width/canvas.height`,因为它进 P 不进 M。
4. AI 还提示:FOV 变化会同时改变所有深度的成像,宽度比 `近/远` 保持不变;Model 缩放虽然也让物体变大,但世界坐标与深度关系一起变,所以两者不能等价。

### 1.4 我实际选择的测试与结果

| 组 | 我做的操作 | 实际数值 | 是否符合预测 |
|---|---|---|---|
| M | 按 D 键一次 | `tx 0 → 0.1`,eye 仍为 `(0,1.8,8.5)`,世界中心 x 变为 0.10 | 是 |
| V | 依次点正面 / 侧面 / 顶部 / 斜上方 | eye 四组变化,`stat-world` 四次均为 `(0.00, 0.00, 0.00)` | 是 |
| P | 透视 50° ↔ 正交(同 M、同 V) | 近/远立方体 NDC 宽度比:透视 **1.332**,正交 **1.000**;切换后 `tx/ry/ex/ez` 仍为 0/0/0/8.5 | 是 |
| P(FOV) | 透视 50° → 80° | 近立方体宽度 0.1021 → 0.0567,远立方体 0.0766 → 0.0426,**比值仍是 1.332**;世界中心不变 | 是 |

补充核验:`near=8` 时模型前缘 `(0,0,1)` 的 `z_ndc = -1.145 < -1`,被近平面裁掉,但对象仍存在于世界中(把 near 调回 0.1 立即重新出现)——这不是“删除对象”。

### 1.5 我的结论(人工判断)

- “画面变了”至少对应 M、V、P 三种完全不同的原因,**必须用状态读数 + 控制变量**区分,不能只看图。
- AI 建议里“打印 NDC 宽度比”最有用:正交的比值恒为 1,透视的比值等于两物体到眼点距离之比(实测 11.5/8.5 ≈ 1.35,与 1.332 同量级,差异来自离轴位置的透视形变)。
- FOV 与 Model 缩放都对“表观大小”有影响,但 FOV 不改变世界坐标、不改变近远比值;Model 缩放改变世界坐标且不改变近远比值——两者机制不同,不能互相替代。

---

## 协作 2 · 真实故障诊断(4 分,对应 C / D)

### 故障一:Task E 页面空白,状态栏停留在“正在初始化模型…”

**Console 现象**:`Uncaught SyntaxError: Unexpected token ')'`(task-e.js),随后 `CG5App is not defined`。

**我先提出的三种假设**

1. 整个 `task-e.js` 的 IIFE 存在语法错误,导致里面的 `ex.extra` 和页面启动代码都没执行;
2. `CG5Start("e")` 内部抛错(例如 WebGL2 上下文创建失败);
3. `ui.js` 生成的控件缺失,`app.js` 取不到 `model-select` 而抛错。

**每项最小测试**

1. 在 Console 执行 `Object.keys(CG5Exercises)`:结果只有 `normalize/eyePreset/view/projection/drag/wheel/key`,**没有 `extra`**——说明 `ex.extra` 的赋值根本没跑到,更像语法错误而不是运行期异常。
2. 执行 `node --check js/task-e.js`:报 `SyntaxError: Unexpected token ')'`,定位到 `readPivot()` 的 `Math.max(.6, Math.min(4, num("e-r", DEF.r))` 少写了一个右括号。
3. 手动执行 `CG5Start("e")`:成功返回“茶壶造型 · 858 顶点 / 1512 三角面”,排除假设 2、3。

**根因与修复**:`readPivot()` 中 `Math.max(...)` 括号不配对,解析阶段就失败。补上 `)` 后刷新页面,`CG5App` 正常,状态栏显示模型信息。

### 故障二:Task E 的绕点标记“设了 35° 却几乎不动”

**Console 现象**:无报错,矩阵数值全为有限值,因此不是 NaN/着色器问题。

**AI 提示的三种假设**

1. 角度单位重复换算:`CG5.R(axis, angle)` 内部已经 `angle*π/180`,如果传入的已经是弧度,就会变成 35° 的 1/57;
2. 矩阵链顺序错(`T(P)/Ry/T(-P)` 的乘法方向或 `chain` 顺序不对);
3. 标记的最终位置被 `T(q)` 覆盖,或轨道半径被读成 0。

**我选的最小测试(一次只验一个假设)**

- 取 `P=(-1.5,0.1,0.2)`、`q=(0.7,0.25,0.2)`,直接算 `T(P)·Ry(90°)·T(-P)·q`:
  - 手算期望:绕 P 转 90° 后 `(P.x + (q.z−P.z), q.y, P.z − (q.x−P.x)) = (-1.50, 0.25, -2.00)`;
  - 实测得到:`(0.70, 0.25, 0.14)`,几乎等于 q 本身 → 说明实际转角约为 0.6°,正好是 `35°×(π/180)`,**确认是重复换算弧度**。
- 再用 `θ=0°` 与 `θ=180°` 两组验证:0° 应保持 q,180° 应关于 P 对称翻到 `(-3.70, 0.25, 0.20)`;修复后两者均吻合,排除假设 2、3。

**根因与修复**:`ex.extra` 里写成了 `const theta = s.orbit * RADD; CG5.R("Y", theta)`,而 `CG5.R` 接收的是**角度制**。改为直接把 `thetaDeg` 传给 `CG5.R("Y", thetaDeg)`,子标记自转改为 `thetaDeg*2`;修复后 `θ=90°` 实测 `(-1.50, 0.25, -2.00)` 与手算一致。

### 我记录下来的排查方法

- 先看 **Console 第一行错误**:语法错误与运行期错误要分开处理(`node --check` 一秒定位语法问题)。
- 数值不对但无报错时,**不要改代码试运气**,先用一个能手工算的 θ(90°/180°/0°)比对 `M·q` 的坐标。
- 单位、矩阵链顺序、`T(q)` 位置三者必须分别验证,一次只动一个变量。

---

## 协作 3 · 工作台方案比较(3 分,对应 E)

### 3.1 问 AI 的提示词(按指导书 8.3 改写)

> 我已完成 OBJ 导入、四视角、正交/透视以及鼠标键盘 T/R/S。请给出两个能在 40 分钟内复用现有 WebGL2 模块的观察台拓展设计,对比功能、矩阵链、交互参数和验证难度;不要引入 Three.js、光照、纹理或复杂模型编辑器。

### 3.2 AI 给出的两个方案(摘录并对照)

| 比较项 | 方案 1:绕点观察 + 父子部件 | 方案 2:正交/透视双画布同步对照 |
|---|---|---|
| 功能 | 参考点 P、绕点运动的父标记、继承父变换的子标记、轨迹环、P 与半径可输入 | 同一 M/V,左右两个 canvas 仅 P 不同;拖动左画布时右画布同步 |
| 矩阵链 | `M_orbit = T(P)·Ry(θ)·T(-P)·T(q)·S`;子标记 `M_orbit·T(q)·Ry(2θ)·T(o)·S` | 两条链只差 P:`P_ortho·V·M·N` 与 `P_persp·V·M·N`,共享 M/V 缓存 |
| 交互参数 | P.x/P.y/P.z、轨道半径 r、θ(复用已有的“标记绕Y旋转”滑杆) | 两套 canvas 尺寸/aspect、两套 near/far/FOV,需处理双画布指针同步与焦点 |
| 验证难度 | 低:θ=90° 时 `M_orbit·q` 有唯一手算值可比对;P 变化时标记绕 P 而非原点 | 中:要保证两画布 aspect 各自正确、拖动同步无延迟,还要防止两套状态互相串改 |
| 40 分钟可行性 | 高:只加一个 `extra()` 返回绘制对象,不改框架渲染循环 | 低:要改 `engine.js`/`app.js` 支持第二画布,框架改动面大 |

AI 另外提醒:方案 1 容易退化成“只是多画几个方块”,必须让 P 可输入、并给出 `M_orbit·q` 数值读出,否则无法证明绕点矩阵真的生效。

### 3.3 我的选择与理由(**最终由我决定**)

**采用方案 1**,并在其基础上自己加了两处:

1. **实时读出 `M_orbit·q`**:把矩阵作用后的坐标直接写到面板,θ=90° 时与手算 `(-1.50, 0.25, -2.00)` 对照,避免“看起来在转”但实际绕原点的假实现。
2. **额外做 Challenge 2(右键轨道相机)**:不改第二画布就能复用 V 的基向量,代价小;实测拖动后 eye 从 `(0,1.8,8.5)` 变为 `(-5.81,3.13,5.65)`,到 target 距离仍为 8.68,世界中心不变。

**不采用方案 2 的原因**:40 分钟内要改框架的双画布渲染与事件分流,风险和验证成本都高于收益;本实验 C 已经用“同一画布切换 P + NDC 宽度比”完成了正交/透视对照,双画布属于加分项而非必要项。后续若时间允许,可在 Challenge 1 中再做。

### 3.4 AI 协作的边界

- AI 提供的三组控制变量、故障假设、方案比较都只作为**候选**;是否采用、参数取多少、结论是否成立,由我用实测数值判断。
- 本次两处真实故障(语法错误、弧度重复换算)都是我先复现、再由 AI 给假设、最后由我写最小测试定位,AI 没有直接重写整个文件。