从鼠标拖动到屏幕像素,一帧画面要在 CPU、GPU、显示器之间接力跑完。这篇用大白话讲清整条链路,外加一个很多人都会踩的「变换累加」误区。
一、一句话结论
在 WebGL 里,「动」几乎从不靠改几何,而靠改矩阵。
轨迹那几千个点一次性进了显存后基本不动;旋转、平移、改原点,都只是在每帧那张「合成矩阵」上做文章;GPU 再用成千个核心,把原始点并行重算成屏幕坐标。
记住三件事:
- 几何不动:原始坐标进显存后是固定参照,平移 / 旋转 / 改原点默认都不碰它。
- 每帧一张矩阵:所有「动」折进
投影 × 视图 × 模型这一张矩阵,才 16 个数。 - GPU 并行重算:顶点着色器拿原始点 × 矩阵,几千点同时算。
二、一帧是怎么画出来的
从你松手前的最后一动,到像素点亮,要走 10 道接力、跨 4 个地盘:
渲染管线 10 步接力总览
软硬件分工,一张表看懂:
| 干什么 | 谁在干 | 软 / 硬 |
|---|---|---|
| 算旋转角、出矩阵 | CPU · 你的 JS | 软件 |
| 翻译命令、传矩阵 | 浏览器 GPU 进程 + 驱动(ANGLE) | 软件 / 桥接 |
| 转点、连线、上色 | GPU 着色器核心 | 硬件 · 可编程 |
| 光栅化 | GPU 固定电路 | 硬件 · 固定功能 |
| 交换缓冲、扫描输出 | 显示控制器 + 显示器 | 硬件 |
为什么旋转这么顺?几何不重传,每帧只换一张 16 个数的矩阵,GPU 几千核心并行重算所有点。改动极小、并行极大 —— 60 帧/秒轻轻松松。
三、「旋转」到底改了什么?一个常见误区
很多人以为:每帧把上一帧转好的点,再加一点转动,像滚雪球一样累上去。
这是错的。真这么干(P_n = P_(n-1) + Δ),浮点误差会一帧帧累积,轨迹慢慢变形、漂移。
实际是:每一帧都拿没动过的原始坐标当输入,乘上累加来的总矩阵,从头算出这一帧的位置:
屏幕坐标 = M总 × P原始
累加的是矩阵(或它背后的角度),点永远从原始重算。
不变 / 累加 / 重算 三者关系
这样做的好处连成一串:不漂移(误差不累积)、可逆(矩阵清回单位阵即还原)、便宜(几何常驻显卡)、可组合(三件事乘成一张矩阵)。
补充:累加「角度」再每帧重建矩阵最稳;若直接累乘矩阵
M = ΔR · M,连乘久了浮点会让旋转矩阵慢慢「不正交」,工程上常用四元数或定期重新正交化来兜底。
四、改「原点」呢?也只是矩阵
让用户改模型的原点 / 初始位置,渲染里还是一张矩阵吗?是的。「把原点相对模型挪一下」正是平移矩阵的定义本身。
更进一步:根本没有「绝对的原始坐标」,那串数字本来就是相对某个原点才有意义。改原点 = 换个坐标系描述同一堆几何。于是有两种做法,屏幕上一模一样:
- (a) 加一个矩阵:原始坐标不变,偏移叠进模型矩阵 —— 实时首选。
- (b) 重写顶点数据:把每个点按新原点重算 —— 只在精度(大世界 / GIS)、导出、烘焙时才需要。
原点改变 = 模型矩阵里多出来的一个因子
渲染器从头到尾只看最终那张合成矩阵,所以两种做法跑出来的像素完全相同。
五、速查(背下这几条就够)
- 原始几何只进一次显存,之后基本不动。
屏幕坐标 = 合成矩阵 × 原始坐标,每帧从原始重算,不是在上一帧结果上累加。- 累加在角度 / 矩阵上;在点坐标上累加会漂移。
- 平移、旋转、改原点 —— 全是矩阵。
- 「加矩阵」vs「重写数据」产生相同像素;只有精度 / 导出 / 烘焙才真改数据。
- CPU 算矩阵(串行 · 软件),GPU 转点上色(并行 · 硬件),显示控制器扫描输出。