AI 协作日志
学生 Web 实验作品平台
学号:202412211401019 · 来源:ai-log.md
# 实验三 AI协作与核验记录
AI工具:Trae IDE 内置 AI 助手(DeepSeek-V4.1-Flash),2026-11-08 使用。
> 说明:以下每一项均按“先自己预测/判断 → 再向AI提问 → 最后在本地页面运行核验”的顺序完成,数值与现象均为本人运行所得。
## 协作1:矩阵与变换次序
- 我的目标和预先预测:
取正方形初始中心 `C=(0.50,0.25,0)`,令 `Rz=RotZ(90°)`。
我的预测是:`Rz·T(C)` 会把中心转到 `(-0.25,0.50)`,正方形沿半径 `|C|≈0.559` 的圆弧绕世界原点公转;
`T(C)·Rz` 的中心仍停在 `(0.50,0.25)`,只是正方形在原地自转。原因是列向量下右侧矩阵先作用,
`Rz·T(C)` 先平移再旋转,`T(C)·Rz` 先旋转再平移。
- 关键提示词(原文):
“在列向量约定下,正方形中心 C=(0.50,0.25),请分别计算 Rz(90°)·T(C) 与 T(C)·Rz(90°) 作用在局部原点
(0,0,0,1) 上的结果,并说明为什么后者不会改变物体中心。请给出矩阵乘法过程,不要只给结论。”
- AI建议摘要:
AI 给出 `Rz·T(C)` 的平移列 = `Rz·C = (-0.25, 0.50)`;`T(C)·Rz` 的平移列仍是 `C`,因为 `T(C)·Rz` 的
平移分量只来自 `T(C)`,旋转作用于 `Rz` 左侧的局部坐标。并提醒我“矩阵乘法不满足交换律,只有共同缩放
或同轴变换等特殊情况才可交换”。
- 我的计算或测试方法:
1. 手算:`Rz(90°)·(0.5,0.25) = (0.5·0 − 0.25·1, 0.5·1 + 0.25·0) = (-0.25,0.50)`;
2. 打开 `task-b.html`,令“初始中心=(0.50,0.25)、角度=90°”,切换“世界原点 · R×T”与“对象中心 · T×R”,
读取右侧“当前Model矩阵”的平移列,并观察图形位置;
3. 页面下方 5 项矩阵数值检验全部显示 ✓。
- 实际结果与证据:
- `R×T` 模式:矩阵第4列约为 `[-0.250, 0.500, 0, 1]`,正方形出现在左上方,处于公转轨道上;
- `T×R` 模式:矩阵第4列为 `[0.500, 0.250, 0, 1]`,正方形中心不动,只改变姿态;
- 数值检验输出:平移 `(1.30,-0.20,0,1)`、缩放 `(2,0,0,1)`、Z轴90° `(0,1,0,1)`、
Y轴90° `(0,0,-1,1)`、方向向量 `(1,0,0,0)`,与实验指导书的预期完全一致。
- 采纳、拒绝或修正的结论:
采纳“世界原点公转 = `R·T(C)`、自身中心自转 = `T(C)·R`”的结论,并据此实现
`js/transforms.js` 的 `modelFor()` 与 `js/app.js` 的 `bItem()`。同时修正了一个我原本的模糊认识:
“旋转中心不同”本质上是**乘法次序不同**,而不是换一个旋转矩阵。
## 协作2:故障诊断与修复(三角形往复范围只在一侧)
- 错误现象、触发步骤:
第一版 `modelFor()` 的平移分支写成 `offset = amplitude * triangle01(t/period)`。
打开 `task-c.html` 看到三角形从初始中心只向 +X 方向移动约 0.30 再回到起点,位移范围是 `[0, +0.30]`,
而实验要求是 `[-0.30, +0.30]` 的对称往复。
- 我先检查了什么:
1. 先在 Console 里单独调用 `CG3Transforms.triangle01()`,确认其输出确实是 `0→1→0` 而不是 `-1→1→-1`;
2. 再读 `modelFor()` 返回矩阵的平移列(`m[12]`),确认方向角没有写错(`directionDeg=0`,`cos0=1`);
3. 检查 `elapsed` 是否只增不减(正常),排除“时间参数错误”。
- 关键提示词(原文):
“三角形往复平移要求位移范围 [-0.30,+0.30]。我用了 0~1 的三角波乘幅度,实测只出现在 0 到 +0.30
之间。请列出可能原因和逐项排查办法,并说明在不改变 triangle01 语义的前提下怎么改。”
- AI列出的候选原因:
1. `triangle01` 的值域是 `[0,1]`,直接乘幅度只能得到单侧位移;
2. 方向向量符号错误,导致正负颠倒;
3. 把位移累加到“当前坐标”上,导致单向漂移;
4. 周期参数用错(`t` 与 `t/period` 混淆)。
- 我逐项测试的结果:
- 原因1成立:`triangle01(0/0.25/0.5/0.75)` 输出 `0/0.5/1/0.5`;
- 原因2排除:`directionDeg=0` 时 `cos=1、sin=0`,X 方向正确;
- 原因3排除:代码每帧都从 `obj.center` 重新计算,没有累加;
- 原因4排除:`t/period` 与正弦公式一致,周期 3 秒可复现。
另外我比较了 `2*triangle01-1` 与 `sin(2πt/period)`:前者在 `t=0` 时输出 `-1`,三角形会从最左端开始,
而任务希望“以初始中心为基准、从中心出发”;正弦在 `t=0` 时位移为 0,更符合预期。
- 真正原因、修改位置和复测结论:
原因是正弦/三角波值域与中心起点不匹配。修改位置:`js/transforms.js` 的 `modelFor()` 平移分支,
改为 `offset = amplitude * Math.sin(2πt/period)`,方向由 `(cosθ, sinθ)` 分解。
复测(C=(-0.60,-0.40),幅度0.30,周期3秒):
`t=0 → x=-0.60`、`t=0.75 → x=-0.30`、`t=1.50 → x=-0.60`、`t=2.25 → x=-0.90`、`t=3.00 → x=-0.60`,
即 `[-0.90,-0.30]` 对称往复;连续运行 20 个周期后中心仍回到 -0.60,无漂移。
## 协作3:方案比较与实现核验(对象数据组织 + 截图实现)
- 待解决的具体设计问题:
Task D/E 需要同屏管理多个独立运动的对象,并且要能导出 PNG。核心问题是:**每帧如何处理几何数据,
以及如何可靠地截图**。
- 关键提示词(原文):
“同一Canvas内要维护多个不同形状、不同运动参数的对象。方案A:每帧在CPU上重新计算所有顶点位置,
用 bufferData 重新上传;方案B:几何顶点只上传一次,每帧只更新每个对象的 Model 矩阵 uniform。
请从正确性、性能和可维护性比较,并说明 WebGL 截图用 toBlob 与 readPixels 的差别。”
- 两种方案及取舍依据:
- 方案A(每帧重建顶点并重传):直观,但每帧都要复制/上传数万字节,CPU与GPU带宽浪费;
对象状态退化为“当前顶点数组”,无法保存初始中心与运动参数,暂停、改速度、参数快照都难以实现,
而且容易把变换累计进顶点而产生漂移。
- 方案B(状态对象 + Model矩阵):每个对象只保存 `id/type/center/motion/参数/elapsed`,
顶点Buffer在初始化时创建一次并复用;每帧只计算 16 个浮点数的矩阵并 `uniformMatrix4fv`。
便于实现“参数快照”“全局速度”“暂停”,也符合实验“局部顶点不因动画改变”的要求。
代价是需要先设计对象数据结构与 `modelFor()` 的分派逻辑。
- 截图:`canvas.toBlob(...,'image/png')` 由浏览器直接按 Canvas 内部像素编码,尺寸天然一致;
`gl.readPixels` 需要手动翻转 Y 轴、再借助 2D Canvas 合成,代码更多且容易上下颠倒。
本项目已在创建上下文时开启 `preserveDrawingBuffer:true`,配合截图前主动 `draw()` 可以避免空白图。
- 我最终采用的实现:
采用方案B。`js/transforms.js` 的 `createObject()` 在点击瞬间把方向、幅度、旋转轴、旋转中心、缩放上限、
速度固化为对象字段;`modelFor(obj)` 每帧根据 `obj.mode` 与 `obj.elapsed` 计算 Model 矩阵。
`js/app.js` 的 `tick()` 只累加各对象的 `elapsed`,`draw()` 遍历 `objects` 依次绘制。
截图采用“先 `draw()` 再 `toBlob`”的方式,文件名形如 `实验3_画布_20261108-153012.png`。
- 测试与反思:
- 多对象:先添加2个圆、再添加2个三角形,同屏4个对象均保留并各自运动;把三角形方向从 X 轴改为 30° 后,
新对象沿斜向运动,旧对象仍保持水平往复,说明参数快照生效;
- 暂停:暂停2秒后画面完全静止,继续后第一帧无跳变(`tick()` 中无论是否暂停都刷新 `lastMs`);
- 画布:把画布改为 960×540 后圆仍为圆;在 Canvas 相对位置 (25%, 25%) 处点击得到约 `(-0.889,+0.5)`
(aspect=1.778,`(2×0.25−1)×1.778≈−0.889`),说明投影与鼠标映射同步更新;
- 截图:导出的 PNG 为 960×540,包含浅色背景与全部图形,打开无黑屏;
- 反思:方案B的额外设计成本换来的是对象状态可解释、可恢复,后续若要增加“选中/删除对象”也只需操作
状态数组,不必改动渲染管线。
## 其他使用AI的记录(如有)
- 问题:输入框留空或超界时,Model矩阵可能出现 `NaN`。
提示词原文:“如何在不引入复杂校验框架的前提下,保证幅度、缩放上限、方向角、速度不会让矩阵出现 NaN?”
处理:采纳“统一用一个 `num(id, fallback, min, max)`,`Number.isFinite` 判定后夹紧到区间”的建议,
在 `js/app.js` 中对 `directionCustom/amplitude/scaleMax/objectSpeed/canvasWidth/canvasHeight` 全部使用该函数;
验证:清空输入框后页面不报错,矩阵输出仍为有限数值,Console 无红色错误。