1.3 顶点与片元:一张图里的两条数据流


1.3 顶点与片元:一张图里的两条数据流

本节摘要:一张 Shader Graph 在 GPU 上分两段执行:先对每个顶点跑一遍顶点着色器,再把三角形内部的每个像素交给片元着色器处理。本节用 1.2 节的渐变图做实验,讲清两个阶段各自执行多少次、拿到什么数据、为什么 UV 动画和水面波动的连法完全不同。

先看一个现象

把 1.2 节的渐变图打开,做一个小实验:把 Lerp 的 T 输入从 UV 的 G 换成一个 Time 节点的输出(暂时不管 Time 是什么,就当它是一个不断变大的数)。保存后预览球从上到下整体变色,而不是出现"扫描线"式的局部变化。这个现象背后就是本节要讲的结构:你的图被完整执行了两遍——一遍按顶点,一遍按像素,两遍看到的"世界"不一样。

顶点阶段:跑在三角形角上

GPU 渲染一个模型,最先拿到的是顶点数组:每个顶点有位置、法线、UV 坐标、顶点色这些属性。顶点着色器的任务是对每个顶点执行一次你的图(或图中被标记进顶点阶段的部分),输出"这个顶点最终投影到屏幕哪个位置、携带哪些插值数据"。一个立方体只有二十来个顶点,一颗几万面的角色模型可能有几万个顶点——所以顶点阶段的执行次数在几千到几十万这个量级。

在 Shader Graph 里,主栈里有一个独立的"顶点"(Vertex)块,里面是 Position(顶点位置)、Normal(法线)这类输入端口。你连进去的任何计算只影响顶点,片元阶段的图可以完全不知道它。第 6 章的水面案例就是把正弦波接进 Position 端口让网格起伏,就是这个端口的用法。

片元阶段:跑在每个像素上

三角形被投影到屏幕后,GPU 把它覆盖到的每个像素都喂给片元着色器执行一遍。注意执行次数的量级变了:一个占屏四分之一的角色,在 1080p 下可能就是五十万个像素各自执行一遍你的图。这就是为什么两个输入完全一样的节点,放的位置不同、成本可能差出三个数量级——顶点阶段算一次省几万次,片元阶段算一次就是几万次乘法。

1.2 节的渐变色就是片元阶段干的事:UV 坐标在每个像素上不一样(左下角是零,右上角是一),Split 出来的 G 值逐像素变化,Lerp 按各自的 G 值混出不同颜色。数据从顶点流到片元的桥梁叫"插值":顶点阶段的 UV 值只有顶点上有,但三角形内部的像素拿到的是相邻顶点 UV 的线性插值结果——这是 GPU 光栅化阶段免费帮你做的。

图:从顶点数组到屏幕像素的两段执行流

图:从顶点数组到屏幕像素的两段执行流

用渐变图验证两阶段

三个实验,每个都只动一根线。实验一:把 UV 节点直接接到 Vertex 块的 Position 端口——类型不匹配会报错,说明顶点块对输入类型有明确约束,也提醒你两个块的"接口"不同。实验二:把 Tiling And Offset(暂时只用它的默认参数)接在 UV 和 Split 之间,渐变条纹数量变化,这发生在片元阶段。实验三:新增一个"Position"节点(世界空间),拆出它的 Y 通道接到 Lerp 的 T 上——渐变变成"按世界高度"而不是"按模型表面",因为 Position 节点给的是每个像素对应的世界坐标。三个实验做完,你对"数据从哪来、在哪算"就有了手感。

插值的数字例子

插值值得用具体数字走一遍。假设一个三角形三个顶点的 UV 值分别是:顶点甲为 0.2、顶点乙为 0.8、顶点丙为 0.5。三角形正中心那个像素拿到的插值结果是三点数值的平均,约 0.5;靠近甲的像素得到的值偏向 0.2,靠近乙的偏向 0.8。GPU 在光栅化阶段对三角形覆盖的每个像素都做了这样一次"按距离加权平均",权重由像素在三角形内的相对位置决定。你在片元阶段拿到的每一个 UV、每一个顶点色,都是这种加权平均的结果——这就是为什么渐变永远平滑,不会在两个顶点之间出现断层。

两个阶段的数量级算术

把 1.3 节开头提到的量级落成算术。一颗三万顶点的角色模型,顶点阶段每帧执行三万次你的顶点逻辑;它站在画面里占四分之一的屏幕,在 1080p 分辨率下覆盖约五十一万个像素,片元阶段就要执行五十一万次你的片元逻辑——后者是前者的一百七十倍。再考虑深度测试剔除的部分(被前景挡住的像素通常不会进入片元着色),实际执行数会少一些,但数量级不变。这张算术表解释了两件事:顶点阶段的复杂逻辑没有想象中贵;片元阶段哪怕多一次乘法,乘上五十万也值得掂量。

数量级示例 优化抓手
顶点阶段执行次数 数千至数十万 控制模型面数
片元阶段执行次数 数十万至数百万 控制屏占比与过绘制
单次执行成本 由节点链决定 第 8 章的成本分层

过绘制:片元成本里的隐形大头

一个像素被反复着色的次数叫过绘制(Overdraw):一辆车挡着后面的车,被挡部分的像素如果也执行了着色计算,就是浪费。半透明物体是重灾区——它们不能靠深度测试提前剔除,每个覆盖像素都要完整执行。排查办法是场景视图着色模式切到过绘制可视化:画面越亮红的区域,像素被画的次数越多。UI 后面的半透明面板、叠加的透明光效聚集处,通常红得发光。

两条流的"汇率":从顶点到像素的数据预算

顶点阶段往片元阶段传数据不是免费的——每个顶点输出的每一组属性(UV、顶点色、自定义插值)都会在三角形之间逐像素插值并占用带宽。这引出一个预算意识:顶点阶段往下游传多少数据,等于给每个像素发多少"行李"。实践含义有两条:能靠 UV 与贴图表达的数据,优先走贴图采样而不是顶点色传递;顶点阶段算好的结果如果逐像素都需要(比如按高度的渐变系数),提前在顶点算好再插值,比片元阶段逐像素重算便宜——插值是 GPU 免费的,逐像素重算不是。这个"把计算上提到顶点"的思路,是两条流水线之间最重要的省钱通道。

问题:为什么有的效果必须放在顶点阶段,放片元阶段就错了?

判断标准是"变化的空间尺度"。水面起伏改变的是几何形状本身——片元阶段只决定像素颜色,动不了几何,物理上就放不进去。而菲涅尔边缘光逐像素依赖视角,顶点插值的精度不够细腻,放在片元才平滑。一句话总结:动形状的进顶点,动颜色的进片元,两者都要的(描边的宽度与颜色)就两头各放一半——5.2 节的描边正是这样拆的。

本节要点回顾

  • 两段执行:顶点阶段按顶点跑,片元阶段按像素跑,量级差三个数量级。
  • 插值免费:顶点属性到像素的插值是 GPU 自动做的,UV 渐变就是这么来的。
  • 块即接口:Vertex 块管变形,Fragment 块管颜色,连错块效果与成本都不对。
  • 位置即成本:同样的计算放在顶点阶段便宜,放在片元阶段昂贵,第 8 章会量化。

理解了两条数据流,下一节我们看这张图保存之后到底变成了什么——读一点生成的 HLSL 代码,把"图"和"程序"在脑子里对上号。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U