Skip to content

从三角形到屏幕:GPU 渲染管线 ​

地球看起来是连续曲面,GPU 实际处理的却是有限的顶点(vertex)和三角形(triangle)。理解渲染管线,可以回答一个很实际的问题:改变颜色时为什么通常不用重建网格,而提高地球曲面的精细程度却需要更多几何?

本课矩阵链的基础见矩阵和参考系,裁剪空间与除以 w 的原因见齐次坐标与投影。可以先按“位置依次换一种表示”来理解。

同一个三角形从地理坐标经过顶点变换、光栅化到像素合成

顶点阶段决定东西在哪里 ​

应用把顶点位置、纹理坐标(texture coordinates)等 attribute 放入缓冲区。顶点着色器(vertex shader)对每个顶点执行程序,输出裁剪空间(clip space)位置 gl_Position。基本关系是:

text
clipPosition = projection × view × model × localPosition

这里的 model、view、projection 分别是模型、视图、投影矩阵(matrix),均为 4×4;localPosition 是 (x,y,z,1) 形式的齐次坐标(homogeneous coordinates)。model 将局部位置变成世界位置,view 改写为相机坐标,projection 将可见范围映入裁剪空间。矩阵作用顺序从右向左,不能任意交换。Cesium 还需要处理地球尺度的精度问题,常用高低位编码或相对眼睛坐标,不能简单把数百万米的 ECEF 坐标塞进 float 后假设近景仍精确。顶点着色器可以根据高度纹理抬升顶点,但不会凭空增加顶点;只有四个角的大网格,无论着色多细,边缘仍由原来的三角形决定。

光栅化把几何变成片元候选 ​

GPU 对裁剪后的三角形做透视除法(perspective divide,把 x、y、z 除以 w)、视口变换(viewport transform)和光栅化(rasterization),找出它覆盖的像素样本。顶点间的 varying 通常按透视校正插值(perspective-correct interpolation)传递,送入片元着色器(fragment shader)。插值是在已知顶点值之间估计中间值;透视校正使这种估计与远近关系一致。片元并不等于最终像素:同一个像素可能先后接收地球、天气层、轨迹等多个片元,深度测试、丢弃和混合共同决定结果。

片元着色器适合采样标量、映射颜色、画隐式等值线。它能改变表面外观,却不能让原来的三角形轮廓向外鼓起。要改变轮廓,应改顶点位置或几何细分。这正是“画面更细腻”与“几何更精细”的区别。

一帧还有 CPU 的工作 ​

CPU 准备场景、剔除对象、更新 uniform、组织命令;GPU 异步执行绘制。提交一条命令所花的 JavaScript 时间,不等于 GPU 执行时间。小三角形很多时可能受顶点与提交成本限制;半透明全屏层很多时则可能受片元填充与带宽限制。先判断瓶颈所在,再决定减少对象还是减少像素工作。

对象如何被画出EXPERIMENT 05 · CESIUM 1.145.0

对象如何被画出

比较 Entity 与 Primitive,查看几何与材质的边界。

在浏览器本地运行 · 无需账号 · 鼠标拖动旋转,滚轮缩放

底图:Natural Earth II(公共领域);其余几何和地形为教学合成。 数据说明
查看运行源码

此代码直接读取实际运行的实验模块。按 kind 分支定位当前实验,也可查看数据公式和 GLSL。

读取源码…

观察任务 ​

旋转几何实验,分别观察轮廓和内部颜色。假设一个大三角形的片元颜色变化非常平滑,它是否已经成为弯曲表面?再思考为什么移动相机通常只需改变矩阵,而不是重新下载顶点。

展开答案

平滑颜色只说明片元计算连续,三角形仍是平面。相机改变的是世界到视图、投影的变换,已有顶点缓冲可以复用。地球的 LOD 可能随视角另行选择网格,但这是场景系统的策略,不是每次相机移动都必须重建所有几何。

不要把 Fragment Shader 理解成“每个屏幕像素恰好执行一次”,也不要把 JavaScript 循环与 GPU 并行执行逐项对应。两者处理粒度和调度方式不同。

阅读依据:Khronos WebGL 2 规范、Cesium 1.145 Scene 源码。

气象学 · 数学基础 · 地理可视化