GPU 干两件事:① 把顶点流处理成裁剪空间顶点;② 据此画像素。 本课整段都在 ① 里面。
本节点在骨架 ① 的第 1 环——也是整条管线的入口。
在顶点被搬进裁剪空间之前,它得先存在。而顶点并不自动存在——它们只是躺在顶点缓冲里的一串字节, 等着输入装配按规则把它们捞出来。规则就两个数字:步长 stride 和起始偏移 offset。 本段只增加一样东西:取址规则。
左边 12 个 float、右边只有 3 个顶点:一块内存要经过"取址规则"才变成顶点。规则不对,读出来的就不是你想要的东西。
缓冲里真实布局是每顶点 12 字节(x、y 加一个无关的第三个 float)。 只有 12 才读对;8 和 16 会把别人的字节当成坐标,三角形立刻变形。
工程上有一点值得注意:stride 烘在管线里,所以换步长 = 换一条管线。 这里预建了三条,切换时只是换一条来用——这也是热重建要处理的那类失效。
点 8 或 16,看三角形怎么塌掉。同一个缓冲、同一份数据,只有取址规则变了。
| idx | 值 | 属于 |
|---|
| # | x | y |
|---|
| 项 | 值 | |
|---|---|---|
知道顶点来自缓冲、由 stride / offset 决定怎么读;也知道这份规则一旦写错,读出来的就是一堆错的坐标。
本节点在骨架 ① 的第 2 环——顶点已经读进来了,但它们还在你自己的坐标系里。
上一段只把顶点从缓冲里读了出来,它们的坐标还是你随手写的数值。GPU 不认这套坐标,它只认裁剪空间。 中间那次搬运就是顶点着色器干的活——而搬运的旋钮,是一个矩阵。 本段只增加一样东西:一个矩阵。
左边 3 个格子进、右边 3 个格子出:顶点着色器被调用了 3 次,一次只处理一个顶点。它不是「一次算完整个三角形」。
拖动滑块移动、旋转、缩放这个三角形。颜色不跟着三角形走——因为颜色由裁剪空间坐标算出,它钉在画布上;三角形动,颜色从它身上扫过去。
// 每个顶点调用一次 @vertex fn vs(p : vec2f) -> VSOut { let clip = u_matrix * vec4f(p, 0, 1); o.pos = clip; o.color = clip * 0.5 + 0.5; return o; }
m = projection(400, 300); m = translate(m, x, y); m = rotate(m, angle); m = scale(m, sx, sy);
| # | x | y |
|---|
| # | x | y |
|---|
| # | r | g |
|---|
buildMatrix() 算出——所以你可以互相验算。
知道顶点着色器是「每顶点调用一次」的函数,输出裁剪空间坐标;也知道矩阵就是改这个输出的旋钮。
本节点在骨架 ① 的第 3 环——顶点已经在裁剪空间里,但还没变成屏幕坐标。
上一段把顶点搬进了裁剪空间。可裁剪空间是个 [-1, 1] 的立方体:落在外面的顶点,本来就不该出现在屏幕上。 这一环是固定功能阶段——没有 shader,硬件自己干两件事:把越界的切掉,把切出来的新顶点插在边界上。 本段只增加一样东西:越界判定与切割。
左边 3 个格子进、右边出来的是 4 个格子——多出来的两个是在边界上插值得到的新顶点。
把平移 x 推到两端,或者把缩放拉到 3 以上。左边是裁剪前的完整几何——框外那部分用条纹标了出来,它就是要被切掉的; 右边是裁剪后的真实结果,硬件已经把它切掉了,切口是直的。 两个窗格用的是同一套矩阵、同一个 shader,唯一区别是左边给了一张放宽的矩阵把越界部分也画出来。
| # | x | y |
|---|
| # | x | y |
|---|
| # | max|xy| | 判定 |
|---|
知道裁剪空间是 [-1, 1] 的立方体、越界部分由固定功能切掉、并在边界上插出新顶点 ——所以顶点数在管线里不是恒定的,「进来几个、出去几个」并不相等。
本节点在骨架 ① 的最后一环——顶点已经是合法的 NDC 了,但它还不是像素。
裁剪之后,顶点落在 [-1, 1] 的方框里。可屏幕上没有「NDC」,只有像素。把这个方框搬到屏幕上的哪一块、搬多大, 由一个叫视口的矩形决定——这一步同样是固定功能,也没有 shader。 本段只增加一样东西:视口。
同一个 NDC,右边的像素坐标会随着视口变——而左边的 NDC 一个数都没动。
把宽与高调成不相等的两个数:三角形会被拉扁或拉长。NDC 没变,变的是 1 个 NDC 单位等于多少像素——水平和垂直两个比例不等,形状就失真。
先只拖视口 x / y:三角形整体在屏幕上平移,形状不变。再只拖视口宽:它在横向上被拉伸。 NDC 全程一个数都没动。
| # | x | y |
|---|
| # | sx | sy |
|---|
| 项 | 值 | |
|---|---|---|
知道 NDC 到像素是视口说了算、不是矩阵说了算;也知道视口不等比时画面会被拉伸。 到这里,一个顶点从缓冲一路到屏幕的整条路就走完了。
跑不起来先看设备要求:活例由真 GPU 出画面 —— 手机需 Android 12+ 且 Chrome 121+,或 iOS / iPadOS 26+;电脑需 Chrome / Edge 113+ 或 Safari 26+。Android 上取不到适配器多是设备不支持 Vulkan 1.1+,会自动改走兼容模式(需 Chrome 146+)。不满足时画面区会写出原因与自查步骤,同一页的示意图与数值表照常可读。