AI 协作日志

学生 Web 实验作品平台

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

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

> AI工具:Doubao(豆包),通过对话协助完成矩阵构造、动画时间推进、鼠标坐标映射与截图实现;所有数值结论均在本机运行核验后写入。

---

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

- **我自己的预测**:正方形初始中心 C=(0.50,0.25)。我认为“绕世界原点旋转”应该先把图形放到 C(即 T(C)),再绕原点旋转,所以矩阵写成 `R(θ)·T(C)`;“绕自身中心旋转”应该先转自身再放回去,即 `T(C)·R(θ)`。我预计两种写法的效果不一样:前者图形位置会绕原点转(公转),后者图形只在原地转(自转)。
- **AI工具与原始提示词**:Doubao。关键提示词(原文):「列向量约定下,为什么绕世界原点公转要写 M=R·T(C),绕自身中心自转要写 M=T(C)·R?请用 (0.5,0.25) 在45°下给出两者的计算结果,并说明右边先执行的含义。」
- **AI的建议摘要**:确认列向量约定下右侧矩阵先作用于顶点;`R·T(C)` 先平移后旋转,等价于把位于 C 的图形整体绕世界原点旋转(公转,位置和朝向都变);`T(C)·R` 先旋转再平移,等价于图形绕自身局部原点旋转后放回 C(自转,位置不变)。并给出公式:`R·T` 的平移列为 `R·[cx,cy]`。
- **本人计算、运行与证据**:我用公共库 `mat4` 实测(Node 运行 `js/transforms.js`):
  - 输入 P=(1,0,0,1),C=(0.5,0.25),θ=45°:
    - `R(θ)·T(C)·P = (0.884, 1.237, 0, 1)`(位置明显离开 C,绕原点转动)
    - `T(C)·R(θ)·P = (1.207, 0.957, 0, 1)`(仍围绕 C 附近,方向转)
  - 两者数值不同 → 证明次序不等价。
  - 5 项数值测试(对照实验指导书表格)全部 PASS:
    | 测试 | 输入 | 实测输出 | 结论 |
    |---|---|---|---|
    | 平移 | P=(1,0,0,1),T(0.3,-0.2,0) | (1.3,-0.2,0,1) | 正确 |
    | 均匀缩放 | P=(1,0,0,1),S(2,2,2) | (2,0,0,1) | 正确 |
    | Z轴旋转 | P=(1,0,0,1),Rz(90°) | (0,1,0,1) | 正确 |
    | Y轴旋转 | P=(1,0,0,1),Ry(90°) | (0,0,-1,1) | 正确 |
    | 方向向量 | v=(1,0,0,0),T(0.3,-0.2,0) | (1,0,0,0) | 正确(w=0 平移不变)|
- **采纳或修正结论**:采纳 AI 关于次序的说明,并亲手运行验证数值吻合。Task B 页面中 `pivot=world` 走 `R·T`、`pivot=self` 走 `T·R`,运行截图和矩阵面板均确认正方形公转与自转轨迹不同。

---

## 协作2:故障诊断与修复(Task C/D / 4分)

- **错误现象与重现步骤**:Task D 中我最初实现鼠标添加时,点击画布左上角(约 200,150 像素),对象却出现在接近画布中心的位置,且所有对象都堆在世界原点附近。另外在 Task C 中暂停后点“继续”,三角形有一次明显的瞬间跳跃。
- **我先检查了什么**:先检查 `addFromMouse`,发现我一开始写成 `const cx=e.clientX, cy=e.clientY`,直接把浏览器像素坐标当世界坐标用,没有经过 `getBoundingClientRect()` 换算;又检查了 `tick()`,发现如果暂停时没有更新 `lastMs`,恢复瞬间 `dt` 会等于暂停时长,导致 `elapsed` 突跳。
- **AI工具与原始提示词**:Doubao。关键提示词(原文):「点击Canvas添加对象位置不准,我把 clientX/clientY 当世界坐标用了,应该怎么换算?另外 rAF 暂停后再继续,为什么对象会突然跳一大段?」
- **AI建议的候选原因与测试**:AI 给出排查清单:① 是否用 `rect=canvas.getBoundingClientRect()` 减掉 `left/top`;② 世界 X 范围是否按宽高比 `canvas.width/canvas.height` 缩放;③ Y 轴是否翻转(`cy=1-2v`);④ 暂停时是否仍每帧更新 `lastMs`,恢复时 `dt` 是否过大。我逐项对号入座。
- **实测结果、修复与复测**:
  - 鼠标:改为 `u=(clientX-rect.left)/rect.width`、`v=(clientY-rect.top)/rect.height`,再 `cx=(2u-1)*aspect`、`cy=1-2v`。复测默认 800×600:中心(400,300)→(0,0);左上(200,150)→(-0.667,+0.5),与验收数据一致。
  - 暂停:改为每帧都更新 `lastMs`,仅在 `running && globalSpeed>0` 时才把 `dt×speed` 累加进 `elapsed`,并对 `dt` 限制上限 0.1s。复测:暂停 2 秒后继续,画面不再突跳;切后台再回来也不会瞬间跳到很远。
- **真正原因、修改位置和复测结论**:真正原因是“直接用像素当世界坐标”和“暂停期间未推进时间基准”。修改位置为 `js/app.js` 的 `addFromMouse()` 与 `tick()`。复测结论:两者均已修复,多对象各自独立运动、位置准确。

---

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

- **我考虑的两种实现**:① **对象数据组织与 Model 更新**:方案 A——每帧在 CPU 上重新生成新的顶点数组并 `bufferData` 上传;方案 B——把四种局部顶点只上传一次到 GPU Buffer,每帧仅更新每个对象的 `uModel` 矩阵 Uniform。② **PNG 截图**:方案甲——`canvas.toBlob('image/png')` + `URL.createObjectURL` 触发下载;方案乙——用 `canvas.captureStream()` 取帧再编码(复杂)。
- **AI工具与原始提示词**:Doubao。关键提示词(原文):「多对象动画中,每帧重新上传顶点和只更新 Model 矩阵 uniform,哪个更合适?PNG 截图用 toBlob 还是 captureStream?」
- **对比、取舍与原因**:方案 A 每帧都重建/上传 Buffer,浪费带宽与 GC,且一旦顶点在动画中被“改”了,就无法再回到局部原点做旋转缩放;方案 B 顶点一次性 `STATIC_DRAW`,动画只改矩阵,代码更清晰、可维护性更好、性能更高。截图方面,`toBlob` 简单可靠,配合 WebGL 的 `preserveDrawingBuffer:true` 可避免空白;`captureStream` 需要额外解码/合成帧,成本高、没必要。因此我采用方案 B + 方案甲。
- **实际实现及核验**:`js/graphics.js` 构造时一次性上传四种网格;`js/transforms.js` 的 `modelFor()` 每帧根据 `elapsed` 算 Model 矩阵;`savePNG()` 先 `draw()` 再 `toBlob` 触发下载。核验:无头浏览器运行 Task E,画布 800×600 下点击添加 4 种形状后保存 PNG,得到 `实验3_画布_*.png`,PNG 签名有效、像素尺寸 800×600、内含 33 种非背景色(确实含图形、非空白);把画布改为 960×540 后再截图,圆仍为圆、中心点击仍落在 (0,0)。

---

## 其他使用AI的记录(如有)

- 无其他独立记录;上述三次协作覆盖矩阵次序、Bug Hunt、方案比较三方面,均附本人运行结果。