canvas无法真正渲染3d,需通过数学投影(正交/透视)、深度排序、法向量光照计算和requestanimationframe动画模拟动态光影3d效果。

Canvas 本身不支持真正的 3D 渲染,所谓“带投影的 3D 图形”,本质是用数学计算把三维点映射到二维画布上,并通过排序、缩放、着色等手段模拟出具有深度感和光影层次的视觉效果。关键不在画 3D,而在算得准、排得对、涂得真。
投影方式选正交还是透视
两种投影对应不同视觉目的:
- 正交投影:直接丢弃 z 坐标,(x, y, z) → (x, y),所有物体大小不变,适合结构示意、工程图或 UI 控件的立体感表达
- 透视投影:用 x′ = x × d / (z + d),y′ = y × d / (z + d) 计算(d 是观察距离,如 200),z 越大(越靠近观察者)投影越大,产生自然的“近大远小”效果
- 实际中常加裁剪限制,例如只处理 z ∈ [1, 100] 的点,避免 z 接近 -d 时坐标爆炸
深度排序保证遮挡关系
Canvas 没有深度缓冲,必须手动控制绘制顺序:
用户要生成可打印的中文字帖/练习纸、导出多页 A4 PDF 报告,或把 SVG 设计稿零误差还原到 Canvas 时使用。本技能是「Canvas 内容工厂闭环」的总控,编排:网格渲染引擎(13 种教育网格+拼音标注) → 多页 PDF 导出(A4 合成) → SVG 精准复刻(坐标误差<0.001px)。触发词:生成字帖、练习纸、导出 PDF、SVG 转 Canvas、印刷级还原、A4 报告、米字格田字格。
- 对每个面(如立方体的一个面),计算其四个顶点在投影前的平均 z 值,作为该面的“深度值”
- 按深度值从大到小排序(z 越大表示越靠前),然后逆序绘制——先画远处的面,再让近处的覆盖上去
- 若只画线框,可跳过排序;但填色或半透明时,不排序会导致穿帮(后画的面盖不住前面的)
光照模拟靠法向量点积
动态光影不是渲染引擎生成的,而是靠几何计算实时得出明暗系数:
- 每个面取两个边向量叉乘,得到单位法向量 n
- 设定光源方向(如 [0, -1, -1] 表示左上方来光),归一化后与 n 点乘,得漫反射强度 diffuse = max(0, n · light)
- 叠加环境光(如 0.2)防止背光全黑:finalLight = 0.2 + 0.8 × diffuse
- 用这个系数缩放基础颜色,比如蓝色面 base=[100,140,255],光照后变成 [100×l, 140×l, 255×l]
动画与交互要稳且可调
流畅旋转和用户控制直接影响体验真实感:
- 用 requestAnimationFrame 驱动循环,每帧微调旋转角(如 angleY += 0.01),避免 setInterval 掉帧
- 鼠标拖拽时记录起始位置,移动差值转为 yaw(水平)和 pitch(俯仰)角,实现自由视角转动
- 键盘支持 ± 调光源高度、←→↑↓ 微调旋转速度,让调试和演示更灵活
- 开启 ctx.imageSmoothingEnabled = false 可防止缩放失真,尤其适合像素风或硬边风格










