AI 协作日志

学生 Web 实验作品平台

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

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

AI工具:TraeCode(内置 Kimi-K3 模型),用于矩阵推导讨论、代码补全与排错。

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

- **我的目标和预先预测:**
  设正方形中心 `C=(0.50,0.25,0)`,旋转 90°。我预测:`Rz(90°)·T(C)` 是先把局部点平移到 C,再绕**世界原点**公转,中心会转到 `(-0.25,0.50)`,且正方形自身姿态不变(边仍平行坐标轴);`T(C)·Rz(90°)` 是先旋转后平移,中心**仍在 (0.50,0.25)**,只是绕自身中心自转,边不再平行坐标轴。
- **关键提示词(原文):**
  > "列向量约定下,M=Rz(90°)·T(0.5,0.25,0) 和 M=T(0.5,0.25,0)·Rz(90°) 分别作用于局部原点 (0,0,0,1),结果分别是什么?右侧矩阵是否真的先执行?"
- **AI建议摘要:**
  列向量右乘先执行。对局部原点(即物体中心):`Rz·T` 中 T 先把中心送到 (0.5,0.25),Rz 再把它绕世界原点转到 (-0.25,0.5);`T·Rz` 中 Rz 作用在原点上仍是原点,T 再平移,中心保持在 (0.5,0.25)。并建议在 Task B 页面用 `matrix-output` 的实际矩阵数值核对。
- **我的计算或测试方法:**
  手算两组矩阵的平移列(第12/13元素):`Rz·T` 的第13列 = Rz·(0.5,0.25,0,1) ≈ (-0.25,0.5,0,1);`T·Rz` 的第13列仍为 (0.5,0.25,0,1)。然后在 task-b.html 选"正方形/旋转",切换"世界原点/对象自身中心",对比画面中正方形中心轨迹与姿态,并核对页面显示的 Model 矩阵。
- **实际结果与证据:**
  运行结果与手算一致:world 模式下正方形绕画布中心公转且姿态不随角度翻转;self 模式下中心不动、原地自转。页面下方矩阵数值与预测相同。
- **采纳、拒绝或修正的结论:**
  采纳。由此确定 [app.js](js/app.js) B-05 与 [transforms.js](js/transforms.js) C-04 的乘法次序:`world → R·T(C)`,`self → T(C)·R`。修正了我最初"旋转会改变自身姿态所以 world 模式下边会转"的直觉错误——`Rz·T(C)` 对局部点确实是先平移再旋转,局部边会被一起转,但观察时中心和边一起绕原点旋转,看起来像公转加自转叠加,与纯自转的对比在 Task C 动画中非常明显。

## 协作2:故障诊断与修复(Task C/D,Bug Hunt)

- **错误现象、触发步骤:**
  在 Task C 中暂停动画约 2 秒后点"继续",三角形和正方形瞬间跳到一个新位置,而不是从暂停时的姿态平滑继续。
- **我先检查了什么:**
  确认 `tick()` 里只有一个 rAF 循环(排除重复循环);确认 `this.lastMs=ms` 每帧都更新。发现最初实现(我自己写的占位版本)在暂停时把 `lastMs` 停更,导致恢复后第一帧 `dt` 包含了暂停期间的全部墙钟时间,所有对象 `elapsed` 一次性累加约 2 秒,产生突跳。
- **关键提示词(原文):**
  > "requestAnimationFrame 动画暂停后继续,对象位置瞬间跳变。我的 elapsed 累加逻辑是 dt=(ms-lastMs)/1000,暂停时 lastMs 不更新。可能的原因有哪些?逐项怎么排查?"
- **AI列出的候选原因:**
  ① dt 未截断,暂停/切后台时间被一次性累加;② 存在多个 rAF 循环叠加;③ 暂停时仍在累加 elapsed;④ 每帧用"当前位置累加位移"导致漂移放大。
- **我逐项测试的结果:**
  ① 在暂停前后打印 dt,恢复后首帧 dt≈2.0s —— **命中**;② 打印 frameHandle 只有一个,排除;③ 暂停时 elapsed 未变,排除;④ 位移由初始中心和周期函数直接计算,无累加,排除。
- **真正原因、修改位置和复测结论:**
  真正原因是 dt 未做上限截断。修复在 [app.js](js/app.js) `tick()`:`dt=Math.min(Math.max((ms-this.lastMs)/1000,0),0.1)`,并且 `lastMs` 每帧无条件更新、暂停时不向 `elapsed` 累加。复测:暂停 5 秒后继续,所有对象从暂停姿态平滑继续,无突跳;切后台 30 秒返回也无跳跃。

## 协作3:方案比较与实现核验(Task E,截图实现)

- **待解决的具体设计问题:**
  PNG 截图如何可靠拿到当前 WebGL 画面,避免保存出全黑/空白图。
- **关键提示词(原文):**
  > "WebGL2 canvas.toBlob 保存 PNG 有时是黑图。方案A:getContext 时 preserveDrawingBuffer:true,截图前主动重绘再 toBlob;方案B:不开 preserve,在 rAF 回调里绘制完成后同步 toDataURL。两者在时间成本、正确性、可维护性上怎么取舍?"
- **两种方案及取舍依据:**
  - 方案A(preserveDrawingBuffer + 截图前重绘):实现简单,截图可在任意事件回调里进行;代价是浏览器不能做缓冲交换优化,性能略降,但对本实验规模无感。
  - 方案B(绘制后同一帧内同步导出):不需要 preserve,性能最好;但 toDataURL 是同步大图编码会卡帧,且截图逻辑必须耦合进渲染循环,事件驱动(按钮点击)时不灵活。
- **我最终采用的实现:**
  方案A。框架的 [graphics.js](js/graphics.js) 已在 `getContext('webgl2',{preserveDrawingBuffer:true})`,在 [app.js](js/app.js) `savePNG()` 中先 `this.draw()` 主动重绘当前帧,再 `canvas.toBlob(...,'image/png')`,用临时 `<a download>` 触发下载并 `revokeObjectURL` 清理。
- **测试与反思:**
  在 800×600 与改后的 960×540 两种尺寸下保存 PNG,打开检查:图像尺寸与 Canvas 内部像素一致,包含设置的背景色与全部对象,无黑图。反思:若未来场景对象很多、帧率敏感,应改方案B并去掉 preserve 以降低开销;本实验以正确性和代码简单为先。

## 其他使用AI的记录

- 工具:TraeCode(Kimi-K3)。提示词:"根据实验指导书完成实验,js中的代码按要求补充即可,不要全部重写"。处理说明:由 AI 在保留框架结构的前提下补齐 18 处 TODO;本人逐项核对矩阵数值(平移/缩放/Rz/Ry/w=0 向量五项全部通过)、三角波端点 0→1→0 连续性、鼠标映射(800×600 中心点击≈(0,0),(200,150)≈(-0.667,0.5))、清屏后对象计数归零与 PNG 输出。