AI 协作日志

学生 Web 实验作品平台

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

# 实验三 AI协作与核验记录

- 学号:202412211401028
- 姓名:喻科棒
- 实际使用的AI工具:Trae CN 内置 AI 助手(对话模型 DeepSeek-V4.1-Flash)
- 使用原则:每次都是**先自己预测 → 再向AI提问 → 最后亲自运行页面/读数值核验**;本文件记录的是真实发生的核对过程,AI给出的结论只有在页面上复现一致后才被采纳。

## 协作1:矩阵与变换次序(Task B)

- **我的目标和预先预测:**
  正方形初始中心取 `C=(0.50,0.25,0)`,用 `Rz(45°)` 做旋转。
  我先预测:本工程约定列向量、`gl_Position = uProjection * uModel * vec4(aPosition, 1.0)`,所以"右侧的矩阵先作用到顶点上"。
  于是 `pivot=世界原点` 应是 `M = Rz(θ)·T(C)`(先平移到 C,再绕世界原点转)→ 正方形**绕画布中心公转**;此时 Model 矩阵第 4 行的平移分量应该是把 `Rz(45°)` 作用在 `(0.50,0.25)` 上的结果 `(0.177,0.530)`,而不是原来的 `(0.500,0.250)`。
  `pivot=对象自身中心` 应是 `M = T(C)·Rz(θ)`(先在原处自转,再平移到位)→ 平移分量应保持 `(0.500,0.250)`,正方形**原地自转**。
  我还预测:如果把次序写反成 `T(C)·Rz(θ)` 用在"世界原点"上,正方形会退化成原地转、不公转。

- **关键提示词(原文):**
  > "这个 WebGL 实验 Task B 里,正方形绕世界原点旋转到底该写成 R·T 还是 T·R?请说明列向量约定下两种写法的区别,并算出 (0.50,0.25) 在 Rz(45°) 下的平移分量应该是多少。"

- **AI建议摘要:**
  AI 说明:列向量约定下 `M=R·T` 表示"先执行 T、后执行 R",即先到 C 再绕世界原点旋转 → 公转;`M=T·R` 表示先自转再平移 → 自转。并算出 `Rz(45°)·(0.50,0.25) = (0.177,0.530)`。

- **我的计算或测试方法:**
  1) 先按列主序自己手算一遍 `Rz(45°)·(0.50,0.25)`,得 `(0.177,0.530)`,与预测一致;
  2) 运行 `js/transforms.js` 的 `probe()`,得到 5 项:平移 `(1.3,-0.2,0,1)`、均匀缩放 `(2,0,0,1)`、Z轴+90° `(0,1,0,1)`、Y轴+90° `(0,0,-1,1)`、方向向量 w=0 `(1,0,0,0)`;
  3) 在 `task-b.html` 页面上直接读"当前Model矩阵(按行展示)"逐项核对。

- **实际结果与证据:**
  - 页面"矩阵数值检验"五行全部显示绿色 ✓(无 ✗)。
  - 默认(正方形 / 旋转 / 世界原点):Model 矩阵旋转块约为 `[0.707 -0.707; 0.707 0.707]`,平移分量实测为 **`0.177 / 0.530`**,与预测一致 → 公转。
  - 改选"对象中心 · T×R":旋转块不变、平移分量变为 **`0.500 / 0.250`** → 原地自转。
  - 两种模式矩阵明显不同,运行现象也不同(一个绕画布中心跑、一个停在原位转)。

- **采纳、拒绝或修正的结论:**
  采纳"世界原点 = `R·T`、对象中心 = `T·R`"。**修正点**:我最初以为判断依据是"哪个矩阵排在左边",核对后才真正理解为依据是"列向量下右侧先执行",这一点由页面矩阵读数和两种运行现象共同证实。

## 协作2:故障诊断与修复(Task C / D,主动制造并修复的真实故障)

> 按指导书要求:第一次就写对时,可以主动制造一个简单错误、测试后恢复。我针对最易错的"旋转矩阵次序"做了这一步。

- **错误现象、触发步骤:**
  在 `js/transforms.js` 的 `modelFor()` 中,我**故意**把 `pivot==='world'` 分支改成 `multiply(T(c), R(axis,θ))`(即写成 `T·R`,与自身中心分支相同)。
  打开 `task-c.html`:正方形不再绕画布中心公转,而是**停在初始位置原地自转**;打开 `task-b.html` 选正方形/旋转/世界原点,Model 矩阵平移分量从 `0.177/0.530` 变成 `0.500/0.250`。

- **我先检查了什么:**
  先确认角度单位(度→弧度换算)、`obj.axis`/`obj.pivot` 取值、以及 `multiply(a,b)` 是否是"a 左乘 b",排除了单位换算和轴线选错的可能,把问题收敛到"乘法次序"。

- **关键提示词(原文):**
  > "我的 WebGL 程序里正方形选了'绕世界原点旋转'却只是原地转,不公转;Model 矩阵的平移分量还是 0.500/0.250。请列出可能原因和逐项排查步骤。"

- **AI列出的候选原因:**
  ① 乘法次序写反(`T·R` 误用在公转分支);② 角度单位错误;③ `glMatrix` 列主序理解错误;④ pivot 值没取到。
- **我逐项测试的结果:**
  角度单位、pivot 取值都正确(打印/读控件值确认);把该分支改回 `multiply(R(axis,θ), T(c))` 后:
  - `task-c.html` 正方形恢复绕画布中心公转;
  - `task-b.html` 世界原点模式下矩阵平移分量重新变为 `0.177/0.530`。

- **真正原因、修改位置和复测结论:**
  真正原因就是**矩阵次序写反**,修改位置是 `js/transforms.js` 的 `modelFor()` 旋转分支。改回 `R·T`(世界原点)与 `T·R`(自身中心)后复测通过。
  (补充数值证据:在离线脚本里把 `T(0.53,0.32)·Rz(45°)` 作用到物体局部原点,得到 `(0.530, 0.320)`,即**中心没动**;而 `Rz(45°)·T(0.53,0.32)` 得到 `(0.1485, 0.601)`,即**中心绕原点移动**。两者对比正是该故障的判据。)

## 协作3:设计方案比较与实现核验(Task E)

- **待解决的具体设计问题:**
  Task E 要同时决定三件事的实现方式:**对象数据怎么组织 / Model 矩阵怎么更新 / 截图怎么做**。我准备了两种整体方案请AI对比。

- **关键提示词(原文):**
  > "实验要求同一画布里有多个独立运动对象,还要能截图。方案A:每帧在 CPU 里为每个对象重新生成顶点数组并重新上传到 GPU Buffer;方案B:顶点 Buffer 只建一次,每帧只更新每个对象的 uModel 矩阵。截图也想问:是直接 canvas.toDataURL(),还是先重绘再 canvas.toBlob()?请比较两种方案的取舍。"

- **两种方案及取舍依据:**
  - **对象数据/矩阵更新**
    - 方案A(每帧重建顶点 + 重传 Buffer):实现直观,但每帧都要 `bufferData` 上传、CPU 参与几何运算;多对象时开销随顶点数线性增长,而且完全绕开了本次实验的核心(矩阵驱动),无法用 Model 矩阵解释"为什么图形在当前位置"。
    - 方案B(顶点 Buffer 复用 + 每帧只更新 `uModel`):几何只上传一次(`STATIC_DRAW`),动画只改 Uniform,CPU 每帧只做少量矩阵乘法;正是实验要求的"局部顶点不变、用矩阵驱动",可维护性和正确性都更好。
  - **截图**
    - 直接 `toDataURL()`:写法最短,但依赖 `preserveDrawingBuffer`,在"提交命令后缓冲可能被清空"时容易得到全黑/空白图。
    - 先主动 `draw()` 再 `canvas.toBlob()`:在 WebGL 里更可靠,能拿到当前帧;且 `toBlob` 是异步回调,便于校验 Blob 非空后再下载。

- **我最终采用的实现:**
  对象组织:每个对象保存 `id / type / center(初始中心快照) / mode / directionDeg / amplitude / scaleMax / axis / pivot / speed / elapsed`,`objects` 数组统一迭代;
  矩阵更新:`modelFor(obj)` 只由"初始中心 + 时间参数"算出 `T/S/R` 组合,绘制时逐对象 `gl.uniformMatrix4fv(uModel, ...)`,几何 Buffer 全程不重建;
  截图:`savePNG()` 里先 `this.draw()`,再 `canvas.toBlob(cb,'image/png')`(Graphics 已开启 `preserveDrawingBuffer:true`),回调中检查 Blob 非空后用临时 `<a download>` 下载并清理。

- **测试与反思:**
  实际运行 `task-e.html`:迭加 6 个对象(圆、2 个方向不同的三角形、2 个不同旋转中心的正方形、1 个绕 Y 轴自转的四面体)后全部保留并各自独立运动;把画布从 800×600 改为 **960×540** 再点击仍是精确映射(在 25% 位置点击得到 `x=-0.889, y=+0.5`,符合 `(2u-1)·(960/540)`);改变背景色后 `clearColor` 生效;点击"保存PNG截图"得到 `实验3_画布_*.png(960×540)`,尺寸与 Canvas 内部像素一致;"清空画布"后对象计数归零,再添加一个新对象仍正常运行。
  反思:方案B虽然前期要多写一点状态结构,但换来了"可解释 + 不漂移 + 低开销";截图那条教训说明"能不能保存到当前帧"必须实测,不能想当然。

## 其他使用AI的记录

1. 用来核对 `triangle01` 三角波:AI 提示需要把输入端折叠到 `[0,1)` 再分段线性映射。我自测 `t=0 / 0.25 / 0.5 / 0.75 / 1` 分别得到 `0 / 0.5 / 1 / 0.5 / 0`,与实际运行一致(因而圆的倍率在 1↔2 之间连续往复,不会在 2 倍处跳回)。
2. 用来核对鼠标坐标映射:按指导书公式 `u=(clientX-rect.left)/rect.width`、`x=(2u-1)·(width/height)`、`y=1-2v` 实现后,实测在 800×600 画布中点 (200,150) 处点击,新对象中心为 `(-0.667, +0.499)`,与验收预期一致。
3. 用来核对暂停语义:AI 提示"暂停时不要累加 dt,但要每帧刷新时间戳"。实测暂停 1.6 秒期间对象 `elapsed` 保持 `130.139 / 129.222` 不变,继续后不突跳。

以上记录中的每个结论都在浏览器或离线数值脚本里实测复现后才写入,未直接采用未经核验的 AI 代码。