4.1 光照:逐顶点 vs 逐片元 本节摘要:实时光照没有硬件开关,只有你在着色器里写的点积与纹理。把光照放在顶点着色器,每顶点算一次,片元得到插值后的颜色,这是 Gouraud 路线,便宜,高光在低模上会漂。把法线插值后在片元着色器里点积,这是 Phong 类逐片元路线,贵在像素,高光形状跟着法线走。PBR 把逐片元再换成微表面公式加环境查找,不是第三条管线,是同一端上更贵的公式。选型看网格密度、灯光数量、是否已走法线贴图。
本节摘要:实时光照没有硬件开关,只有你在着色器里写的点积与纹理。把光照放在顶点着色器,每顶点算一次,片元得到插值后的颜色,这是 Gouraud 路线,便宜,高光在低模上会漂。把法线插值后在片元着色器里点积,这是 Phong 类逐片元路线,贵在像素,高光形状跟着法线走。PBR 把逐片元再换成微表面公式加环境查找,不是第三条管线,是同一端上更贵的公式。选型看网格密度、灯光数量、是否已走法线贴图。
阅读完本节,你应当能够:
兰伯特漫反射是法线点光方向。这个点积放 VS,每个顶点一次,大三角形内部的亮度是三个顶点结果的混合。平坦大面中间不会突然亮一块,高光尤其惨:高光本该是小小一簇,插值会把它抹成顶点之间的渐变,甚至整个消失。把点积放 FS,每个像素一次,高光跟着法线场走。法线贴图改变的是逐像素法线,放 VS 等于白贴。
次数对照很硬:网格 500 顶点、屏幕覆盖 10 万片元,逐顶点省了两个数量级的点积;同一网格特写占满屏,逐片元按像素付。所以没有全局“谁更好”,只有覆盖与网格谁更密。
逐顶点: 光公式 × 顶点数 → 颜色插值 逐片元: 法线插值 → 光公式 × 片元数
| 光源 | Uniform 典型载荷 | 逐顶点 | 逐片元 |
|---|---|---|---|
| 方向光 | 方向、颜色 | 低模可接受 | 默认推荐 |
| 点光 | 位置、颜色、衰减 | 衰减插值不物理 | 按像素距离衰减 |
| 聚光 | 位置、方向、锥角 | 锥边会块状 | 锥边可加平滑 |
| IBL 环境 | 立方体或球谐系数 | 球谐可放 VS | 镜面反射必须 FS 采立方体 |
材质系数:固有色可以是 Uniform 或贴图。贴图必须 FS 采样后再进光公式,于是整条光照自然落到片元。只做纯色低模演示,逐顶点够用,还能当教学对照。引擎标准材质几乎都是逐片元,因为它们默认你有贴图。
// 概念:同一兰伯特,搬家到 FS varying vec3 vN; uniform vec3 uL; uniform vec3 uC; void main() { float ndl = max(dot(normalize(vN), normalize(uL)), 0.0); gl_FragColor = vec4(uC * ndl, 1.0); }
VS 版本把 ndl 算完塞进 varying。代码更短,外观在细分不足时更假。我倾向产品查看器直接逐片元;后台表格里的小 3D 图标可以用逐顶点或甚至不光照只贴图。

前向渲染里 FS 循环所有灯,overdraw 意味着同一像素付多次全套灯光。灯多了,延迟着色把几何与材质写到 G-buffer,再在屏幕空间逐灯。这要求 MRT 与较重的带宽,WebGL2 更顺。对照:灯少于大约几个、场景简单,前向逐片元;灯很多、几何很重,才值得延迟。不要因为文章里延迟更“现代”就上。
烘焙:把光照印进贴图,运行时只采样。CPU 预计算,GPU 当固有色。动态灯不能进烘焙,或只能加一层便宜实时。展览里的静物,烘焙加一个方向光高光,往往比完整 PBR 更稳。PBR 需要能量守恒、IBL、多张贴图,移动端带宽先爆。路线是“要不要物理正确”,不是“会不会写 GGX”。
⚠️ 常见坑:VS 输出的法线没归一化,FS 又 normalize 得不够,大三角形上光照条带。插值会弄脏长度。逐片元路线也要在 FS 再 normalize。
💡 关键直觉:先定计算粒度,再定公式时髦程度。粒度错了,换 GGX 只是让错误更昂贵。
环境光不要用一句加常数糊弄完就算 PBR。最低对照:常数环境 vs 半球 vs 球谐 vs 立方体漫反射。产品查看器用 IBL cubemap 值得;内部工具用半球就够。引擎默认 PBR 材质会拉一整套贴图槽,按 4.3 节决定哪些槽真要填。
在电影级前瞻里是。在顶点很少、片元很多的粒子或远山批次里,仍可能是正确的减法。过时的是“只提供一种粒度还宣称通用”。你的查看器锁定逐片元完全合理;你的地图瓦片用烘焙加逐顶点补一盏太阳,也合理。对照写进设计,不要写进身份。
第一个光照立方体:同一份网格,切两份 program,一份 VS 出颜色,一份 FS 点积。转起来看高光。这比读定义记得快。然后再加法线贴图,看 VS 路线为什么立刻破产。
加环境光常数把暗面抬亮,简单,但亮部更容易饱和。半球光用天空色与地面色按法线 y 插值,便宜且比常数耐看。球谐把低频环境压成几个系数,VS 或 FS 都能评。立方体 IBL 才是产品查看器的常见终点。四级不是进化任务,是四档账单。内部工具停在半球,并不可耻。可耻的是四级叠在一起:常数加半球加 IBL 再加烘焙,能量爆炸,物体像发光塑料。减法:选一档环境,再加一盏方向光做主高光。
高光模型:Phong 的幂次、Blinn 的半程、GGX 的微表面,逐级更贵、对法线更敏感。低模大三角上 GGX 会露出插值法线的丑。先加细分或法线贴图,再换 GGX。否则“上了 PBR 更丑”会怪公式,其实是粒度不够。粒度仍是 4.1 的主题:公式时髦程度不得超过法线场的密度。
多光源前向的循环在移动端很痛。实务上限常常是一盏主方向光加三盏点光,其余烘焙。超过就考虑延迟或把点光改成贴花。贴花是屏幕或世界空间的投影纹理,粒度不是每盏灯跑全场景几何。对照又是:灯是全局循环,还是局部贴花。局部更像 3.4 的又一次离屏或投影网格。
材质实例:同公式不同 Uniform。引擎的材质克隆若共享 program,只改颜色,CPU 轻。若每次克隆编译新程序,变体爆炸。裸写时按关键字缓存 program,按对象存 Uniform 块。光照路线与封装轴在这里交汇:选逐片元 PBR 已经够贵,不要再按物体编译。
PBR 在伽马空间点积会脏。反照率 sRGB 解码到线性,计算完再转到显示。漏解码,暗面发灰。引擎有 outputEncoding 一类开关。裸写要自己在 FS 末尾 pow 或用 sRGB 附件。验收时放一张灰球与一张金属球在同一 IBL 下,对照参考图。没有参考图,调参会无止境。查看器的“高级感”来自 IBL 与线性,不来自再加三盏点光。点光多了,CPU Uniform 与 GPU 循环一起涨,4.7 会找上门。
环境叠了好几档、反照率没线性解码、粗糙度贴图当 sRGB、缺金属度对比。先减环境只留 IBL 加一盏方向光。灰球金属球对照。塑料感常是能量与颜色空间,不是缺 GGX。再加灯只会更亮更假。
逐顶点光照或法线没在 FS 归一化。换逐片元并 normalize。再不够就加法线贴图或细分。条带是粒度不足。换更时髦的高光模型只会让条带更明显。
前向循环按灯数乘片元。减灯、改贴花、烘焙。延迟要 MRT 与带宽,移动不一定赢。先限四盏。超出的灯不进实时,进烘焙或干脆关掉。产品能接受的灯数写进档位,不要按编辑器里能插多少灯来。
仓库放灰球金属球粗糙球三件套,同一 IBL。改着色器先看三球。三球不对不要改业务模型。灯预算:低档一方向光加半球,中档加两盏点光,高档才 IBL 全开。预算写进档位文件。美术插第五盏灯要走评审,评审看 4.7 账单。法线贴图缺失时程序用几何法线,不要用平贴图当法线。平贴图是错误数据。环境叠加禁止超过一档。代码里若已有 IBL 就关掉常数环境。能量重复比缺光更难看。
光照验收以三球为准,业务模型为辅。三球不过,禁止调业务贴图。环境只开一档。代码里检测 IBL 与常数环境同时开就告警。灯预算超了,多的灯在加载时丢掉并记日志,不要静默。丢掉比偷偷循环八盏好。移动档用半球加方向光,桌面高档才 IBL。法线贴图缺省是几何法线,不是平贴图。平贴图当法线会让整物变暗或变色,像光照公式错。先查槽,再查公式。线性解码漏了,暗面发灰,会被当成缺环境。用线性/伽马切换键对比。切换后明显改善,就是颜色空间,不是再加灯。加灯会更灰更亮,更假。
三球场景进仓库,CI 可截图对比灰度均值。均值漂了说明能量政策被破坏。破坏通常来自又加了一档环境或漏了线性解码。CI 比肉眼稳。肉眼会在业务模型上被贴图骗过。骗过之后上线,用户说塑料。塑料来自能量,不是来自缺一张细节贴图。先修能量,再谈槽。
现场对照时只改一个变量。同时改版本、封装、通道,归因会消失。消失之后只能靠感觉,感觉正是本书要拆掉的东西。把改动写成一条,测完再写下一条。一条一条,四组对照才用得上。用不上的对照表只是装饰。装饰填不满格子,格子空着就不要标完工。光照这一格改完,用三球验收,不要直接调产品贴图。
下一节对照阴影:把物体压到地上的假影,和从灯的相机渲染深度再比较的真一点的影。