本节摘要:管线上只有着色器站位接受代码,其余环节靠状态开关配置。本节盘点可编程阶段的完整家族与各自职责,演示固定功能(深度测试、混合、面剔除)的配置手感,并给出一套"效果该放哪一站"的判断方法——这是后续所有章节选型的底层依据。
别以为管线里能写代码的地方只有一个顶点着色器加一个片元着色器。现代 GPU 的可编程家族远比这丰富,只是大多数教程从中间两个讲起,让人误以为其余阶段不存在。把家族全貌铺开,你对"一个效果该在哪站做"的判断会准得多。
按数据流顺序,可编程阶段全家福是这样的:
顶点着色器——必选。每顶点一次,负责位置变换与属性准备。它是唯一"必须存在"的着色器,哪怕只做一句矩阵乘法。
曲面细分控制与求值着色器——可选。成对出现,把低模面片在 GPU 上拆成更密的网格,适合角色边缘、地形细节这类"近看需要更多三角形"的场景。OpenGL 4.0 与 DirectX 11 时代加入。
几何着色器——可选。输入完整图元(比如整个三角形),能产出零个、一个或多个新图元。法线可视化、点精灵扩展这类小活计是它的传统地盘。缺点是硬件实现上并不划算,很多引擎刻意绕开它。
片元着色器——必选。每片元一次,负责最终颜色。效果创作的绝对主力。
计算着色器——脱离图形数据流独立存在。不接收顶点也不输出颜色,就是把 GPU 当大规模并行协处理器用:粒子模拟、后处理的排序模糊、甚至通用数值计算。

支线虽多,日常百分之九十的效果仍然只靠主干双工位完成。教程把篇幅花在主干上,支线在需要处点名即可。
图元装配、光栅化、深度测试、混合这些站写不进代码,但都有状态开关,而且这些开关组合出的行为差异极大。动手感受一下,深度测试关与开的区别:
// 固定功能状态:深度测试的开关与函数选择 glDisable(GL_DEPTH_TEST); // 关闭:后画的永远盖住先画的,与远近无关 glEnable(GL_DEPTH_TEST); // 开启:逐片元比较深度缓冲,近者胜出 glDepthFunc(GL_LESS); // 比较规则可换:LESS / LEQUAL / ALWAYS 等
把这句开关放进渲染循环试一下:场景里先画一面墙再画墙前的角色,关闭深度测试时角色会"沉"到墙后面去——因为它后提交,被无脑盖上了。深度测试开着,输出合并站逐片元查深度缓冲,墙挡住的片元直接淘汰。这一站你一行着色器代码都写不了,但配置错了画面立刻不对。
透明混合同理。混合站按固定公式组合源颜色与目标颜色:
// 经典半透明配置:src alpha 作为权重 glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // 公式:最终色 = 片元色 x 片元alpha + 已有色 x (1 - 片元alpha)
注意混合的输入之一是片元着色器的输出 alpha——固定功能站与可编程站并非井水不犯河水,前者消费后者的产出。面剔除(丢弃背向三角形)也是同类配置:开与不开,片元数量可能差掉近一半,这直接连着第 9 章要谈的片元开销。
把固定功能的常用开关列成一张随身清单,配置管线状态时逐项过一遍:深度测试开还是关、比较函数选哪种;混合模式用哪种权重;剔除背面还是正面、还是都不剔;光栅化要不要线框模式。清单的价值在于把"画面不对"这类模糊问题拆成可逐项验证的具体问题——九成渲染异常都能在这四项里找到被写错的那一行。
💡 关键直觉:固定功能站可以理解为"出厂焊死的专用电路",快,但不接受改造;着色器是"通用计算单元",慢一点,但什么都能算。管线设计者的思路是:高频且成熟的操作(光栅化、深度比较)焊死换速度,变化无穷的操作(变换、着色)开放可编程换表达力。
固定功能状态还有一个容易被忽略的工程维度:状态切换本身有成本。每次改变混合模式、深度规则,驱动都要向 GPU 提交状态变更指令,频繁切换会在 CPU 侧堆积开销。这就是"按材质分组排序绘制"的原因——同类状态的物体连着画,切换次数最少。
// 反面模式:两个物体交替用不同混合状态,每帧切换四次 draw(a); setBlendAdd(); draw(b); setBlendAlpha(); draw(c); setBlendAdd(); draw(d); // 正面模式:按状态分组,切换一次 setBlendAdd(); draw(b); draw(d); setBlendAlpha(); draw(a); draw(c);
验证固定功能配置是否生效,有个朴素的办法——极端值实验:把混合权重推到 0 或 1、把深度函数改成 NEVER、把剔除模式换成正面剔除,画面的剧烈变化证明状态通了线;毫无反应则说明配置没送达(绑错对象、被引擎覆盖、写在了错误的渲染阶段)。这套"故意做错来验证通路"的手法,在着色器调试里同样好使。
工程里每天要做的决策其实是这个:拿到一个效果描述,把它放到哪一站?给三条经验法则。
效果随顶点变化、且表现为"形状改变"的,放顶点着色器。旗帜随风摆动、草地踩踏下陷、缝补动画里的顶点位移,本质都是"按某种规则移动顶点位置"。
效果随像素变化、且表现为"颜色改变"的,放片元着色器。调色、描边、水面波纹的光泽、扫描线,本质都是"按某种规则改写颜色"。
效果需要跨图元知识或多次全屏迭代的,考虑计算着色器或后处理链——这类超出单站能力,第 7 章的后处理会展开。
举一个边界案例:卡通渲染里的描边。做法之一是顶点阶段沿法线把模型向外扩张一圈画纯色背面(形状操作,VS);另一做法是片元阶段检测深度或法线突变上墨线(颜色操作,FS)。同一个视觉目标,两站各有实现——判断标准不是"想要什么效果",而是"这个效果在数据流里表现为形状还是颜色"。
管线地图和可编程边界都清楚了,下一章正式学习给这些工位写代码的语言——GLSL。