分块着色
Tiled Shading
分块着色把屏幕划分为瓦片(如 32×32 像素),先为每个瓦片建立可能影响它的光源列表,着色时每个像素在一次着色器调用中只遍历所在瓦片的列表。它可用于延迟着色(tiled deferred),也可用于前向着色(tiled forward,又称 Forward+)。配合 z-prepass 得到的瓦片深度范围可进一步剔除光源,但深度不连续会削弱效果,于是出现 HalfZ、2.5D 剔除等改进。
动机(20.3 节)
基础延迟着色对每个光源单独调用着色器并重复读 G-buffer,数百上千个光源时很昂贵;在一次调用中评估多个光源更高效。早期探索有 Zioma(渲染光源体积,逐像素存光源信息,用深度剥离处理重叠,受限于每像素重叠光源数)和 Trebilco(z-prepass 后渲染光源体积,逐像素存光源 ID,用位移与混合技巧不用多次剥离就能存 4 个光源)。分块着色由 Balestra 与 Engstad 于 2008 年在《神秘海域:德雷克船长的宝藏》中首次提出,随后用于 Frostbite、PhyreEngine 等。
这里的瓦片是开发者选择的结构,与移动 GPU 的分块渲染或硬件内部瓦片关系不大。对光源体积做分块"渲染"相当于对场景做低分辨率渲染,可在 CPU 或 GPU 计算着色器中完成。
光源与瓦片的相交测试
瓦片的屏幕边界构成一个不对称视锥,用它测试每个光源的球形影响范围。按瓦片而非逐像素存储是保守的(光源未必覆盖整个瓦片),换来大幅降低的处理、存储与带宽。
常规的球-视锥测试假设视锥宽大、球较小;瓦片视锥细长且不对称,会产生大量假阳性(球同时与底面和右侧平面相交而被误报)。改进是在平面测试后再加一个球-盒测试(盒包围视锥),两者交集显著减少误报。聚光灯用球包围效果不好,可用层次剔除、光栅化和代理几何优化。
Tiled deferred 的好处
- 每个像素的 G-buffer 总共最多读一次,而不是每个光源一次。
- 输出图像只写一次,不用逐光源累加。
- 着色器可把渲染方程的公共项提出来只算一次。
- 瓦片内所有片元遍历同一列表,warp 执行一致。
- 不透明物体完成后,透明物体可用同样的光源列表做前向着色。
- 所有光源在一遍中计算,帧缓冲精度可以低(传统逐光源累加在 8 位通道上会产生色带);不过现代系统为色调映射通常需要高精度输出,这点意义不大。
Tiled forward / Forward+
先做几何 z-prepass(避免最终 pass 过度绘制并支持深度剔除),计算着色器按瓦片分类光源,再做第二遍几何前向着色,按片元屏幕位置取光源列表。《教团:1886》使用了它。Pettineo 的开源测试中:无抗锯齿时随光源数增至 1024,延迟在许多 GPU 上占优;抗锯齿级别提高时前向更好。
深度范围剔除
对每个瓦片用归约操作求不透明物体的 与 (例如 Harada 等用计算着色器和 UAV 同时做视锥剔除与归约),剔除不与该范围重叠的光源;只看到天空的空瓦片可直接跳过。是否使用最小、最大或两者取决于场景。延迟着色的 G-buffer 中已有深度,同样可用。
flowchart LR A["z-prepass 或 G-buffer 深度"] --> B["每瓦片归约 zmin, zmax"] B --> C["瓦片视锥 + 深度范围 测试每个光源"] C --> D["生成瓦片光源列表"] D --> E["着色: 遍历本瓦片列表"]
透明物体:深度范围来自不透明表面,透明需单独处理。Neubelt 与 Pettineo 在不透明 z-prepass 缓冲上再渲染透明表面,取透明表面的 、以不透明的 封住远端,另做一次光源分类生成新列表,再只让透明表面走类似 tiled forward 的渲染。
深度不连续问题:一个瓦片里近处角色背后是远山, 范围巨大,几乎无法剔除光源。这种瓦片在场景中占比可能很高,森林、高草、植被场景更甚。
- HalfZ / 双峰簇:在 与 中点分一次,把相交光源归为近半、远半或全范围。直接解决"一近一远"两物体的情形,但不能解决光源两者都不碰或多于两个深度层的情况。
- 2.5D 剔除(Harada 等):把深度范围沿深度方向分成 个单元(他们用 ),按像素深度生成 位几何掩码,再为每个与瓦片视锥相交的光源生成光源掩码,两者按位与为 0 则该光源不影响瓦片内任何几何。
- 在某款 GPU 上(Stewart 与 Thomas),光源超过 512 个后 HalfZ 开始胜过基础 tiled deferred,超过 2300 个后 2.5D 剔除占优,但优势不大。
- 逐像素裁剪(Mikkelsen):16×16 瓦片先用光源屏幕包围矩形和几何深度范围剔除,再让 64 个计算线程每个检查 4 个像素的世界位置是否在光源体积内,没有任何像素在内就剔除——只保留保证至少影响一个像素的光源。他发现对其场景再做 z 轴剔除反而降低总体性能。
按瓦片特化着色器
光源合并成列表后,单个延迟着色器要处理所有材质和光源类型,会很复杂。可在每个像素存一个特性位掩码(每位代表材质在该像素使用的一项着色特性),瓦片内按位或得到所需的最小特性集合,按位与得到所有像素都用到的特性(无需 if 判断),选用对应的特化着色器。这不仅减少指令,还因为不必为最坏路径分配寄存器而提高占用率。也可跟踪其他属性,例如《Split/Second》按 4×4 瓦片分类是否完全或部分处于阴影、是否含需要抗锯齿的多边形边等。
GEA 的视角:Forward+(卷 II 12.5.12.4)
- 前向渲染把光源列表交给管线,每个光源用球或锥体限定影响范围;但每个片元仍要遍历全部光源才能知道哪些影响它。先剔除与视锥不相交的光源能改善一些,但列表仍可能很长(卷 II 12.5.12.4)。
- Forward+ 把屏幕划分成规则网格,预先测试每个格子受哪些光源影响,结果存进与网格同尺寸的二维缓冲;前向着色时像素着色器找到片元所在格子,只遍历该格的光源。GEA 认为这让"几乎不限数量的光源"也能高效渲染。
- 术语分歧(原书问题,12.5.12.4):GEA 说 Forward+ "也叫分块渲染 (tiled rendering) 或分簇前向渲染 (clustered forward rendering)",而它描述的是只在屏幕上二维划分的网格。RTR4(本页与 分簇着色)把二者区分开:分块着色只划分屏幕瓦片(可加深度范围剔除),分簇着色还沿深度方向切分成三维簇。按 GEA 自己的描述,它讲的是分块前向;"分簇"另有所指。另外 "tiled rendering" 一词也常指移动 GPU 的分块渲染架构,与这里的光源分块是不同概念(见 分簇着色 的「移动端」)。出处:Forward+ 即分块前向,Harada, McKee, Yang, "Forward+: Bringing Deferred Lighting to the Next Level", Eurographics 2012 短文;分簇着色是 Olsson, Billeter, Assarsson, "Clustered Deferred and Forward Shading", HPG 2012 提出的另一种方法。
出处
- realtime_rendering_4ed.pdf 第 20 章 Efficient Shading
- Game_Engine_Architecture_4th_Edtion_Volume_2_-_Jason_Gregory.pdf
- Jason Gregory《Game Engine Architecture》第 4 版(2026)卷 II 第 12 章 12.5.12.4 节