← 02 实验索引

三角形解剖

四段动画,拆的是 draw(3) 这一行背后发生的事:顶点函数跑了几次、坐标被怎么换、 哪些设置会让三角形消失,以及光栅化如何确定覆盖区域。顶点、坐标与粗网格扫描是 JavaScript 示意动画;剔除实验使用真实 WebGPU 渲染。

① draw(3):顶点函数跑三次

@vertex fn vs(@builtin(vertex_index) i : u32) -> @builtin(position) vec4f { let p = array<vec2f, 3>( vec2f( 0.0, 0.5), vec2f(-0.5, -0.5), vec2f( 0.5, -0.5)); return vec4f(p[i], 0.0, 1.0); }
vertex_index返回的 vec4f

同一个函数被调用三次,唯一的区别是 vertex_index。 这里逐次点亮是为了讲解,不代表 GPU 逐次执行;三个调用没有先后依赖。

② 同一个点,两套坐标:Y 朝上还是朝下

规范里的视口变换(画布按 280×280 算): framebufferCoords(n) = (0.5 × (n.x + 1) × width, 0.5 × (−n.y + 1) × height)
顶点NDC (x, y)帧缓冲 (x, y)

两张图里三角形的位置一模一样,变的只是编号方式:NDC 原点在正中、Y 朝上、范围 −1~1; 帧缓冲原点在左上、Y 朝下、单位是像素。公式里那个 −n.y 就是翻转。

③ 顺序写反了,三角形会不会消失

实测:等 WebGPU 初始化……

左边是按规范的面积公式算出来的判定,右边是同样配置下真跑一遍的结果, 还顺带把画出来的像素数数了一遍(渲染到一张 64×64 的离屏纹理,拷回内存数非透明像素)。 两边对不上,就是我的理解错了。

④ 光栅化示意:三角形覆盖哪些格子

@fragment fn fs() -> @location(0) vec4f { return vec4f(1.0, 0.45, 0.2, 1.0); }

这里用 JavaScript 检查哪些格子的中心落在三角形内。 计数器:0 个覆盖格子(28×28 粗网格)。这不是 GPU 片段调用次数的实测,也不能按画布尺寸直接推算调用次数。

这里的片段函数每次返回相同的颜色,所以是纯色。换成按 @builtin(position) 计算颜色,就能得到位置渐变;下一篇再看顶点颜色的插值。