跳转到内容

Karis UE4 PBR — 把电影质感塞进游戏的 33 毫秒

是什么

这是 Epic 公司图形工程师 Brian Karis 在 SIGGRAPH 2013 公开的一份工程笔记,讲清楚一件事:迪士尼 2012 年发表的”几乎能复刻所有真实材质”的着色方案,每帧要在电影渲染器里跑几小时;UE4 怎么把它砍到 33 毫秒还让画面看起来像电影。

日常类比:米其林餐厅一道菜要熬 8 小时高汤;Karis 的工作是把这道菜搬进茶餐厅——12 块钱、3 分钟上桌,但你吃下去依然觉得”这是高汤味”。

最终方案就是后来 UE4 / UE5 / Unity / Frostbite / godot / three.js 默认材质球的祖宗。

为什么重要

不理解这篇笔记,下面这些你天天看到的东西都没法解释:

  • 为什么 2014 年之后游戏画面突然有”电影质感”——金属反射环境、皮肤次表面、湿地面反光
  • 为什么 Unity / Unreal 材质面板就那 4 个滑条(BaseColor / Metallic / Roughness / Specular)
  • 为什么 Substance 里调好的材质球直接拖进引擎就能用——大家算法是同一套
  • 为什么 WebGL 的 three.js MeshStandardMaterial 看起来像缩水版 UE——它就是这套的移植

核心要点

Karis 的方案分三层简化,每一层都是”砍掉理论上对、但跑不动的东西”:

  1. 材质参数:从 10+ 砍到 4。Disney BRDF 给美术 anisotropic / clearcoat / sheen 等十几个滑条;Karis 留下 BaseColor、Metallic、Roughness、Specular 四个。理由:90% 场景用不上其他参数,砍掉换性能。

  2. BRDF 公式:选最便宜的近似。微表面 BRDF 三件套 D × G × F

    • D(法线分布) 用 GGX,参数 α = roughness²
    • G(几何遮挡) 用 Schlick-GGX 近似
    • F(菲涅尔) 用 Schlick 近似 F0 + (1-F0)(1-cos θ)⁵
    • Diffuse 用最便宜的 Lambertian(Disney diffuse 太贵)
  3. 环境光积分:split-sum 拆成两张查表。这是全文最聪明的一招(下面专门讲)。

4 个滑条具体含义(你打开 UE 材质球看到的就是这套):

  • BaseColor:颜色。金属时是反射颜色,非金属时是漫反射颜色
  • Metallic:金属度 0..1。0 表示电介质(塑料、木头),1 表示金属
  • Roughness:粗糙度 0..1。0 是镜面,1 是完全粗糙
  • Specular:电介质的反射强度,默认 0.5(对应 F0 ≈ 0.04),平时不动它

实践案例

案例 1:split-sum 是怎么把”算不动的积分”拆成”两个查表”

环境光贡献的数学形式是个 半球积分(想象你站在场地中央,要把头顶半边天上每一点的光按权重加起来——每个像素都得做一遍这个事),实时引擎跑不起。Karis 的近似是把它拆成两个独立项的乘积

L_o(v) ≈ [预过滤环境贴图(roughness)] × [BRDF 积分 LUT(roughness, NoV)]
  • 第一项:把环境贴图按粗糙度烤成多级 mipmap。粗糙的物体取模糊的 mip,光滑的取清晰的 mip。离线烤一次,运行时一次贴图采样
  • 第二项:把剩下的 BRDF 部分预计算成一张 256×256 的 2D 查找表,横轴是粗糙度、纵轴是视角与法线夹角的余弦。全引擎共用一张图

运行时只剩两次贴图采样——从”算不动”变成”几乎免费”。

案例 2:roughness² 不是物理量,是给美术调的

GGX 公式里粗糙度参数 α 直接进分母平方,导致美术拖滑条时”前 30% 滑条没变化、后 30% 突然全变模糊”。Karis 让美术看到的 roughness 是 0..1 的线性条,内部 α = roughness²。这样滑条感知线性——拖到一半看起来就是一半粗糙。

教训:感知线性 > 物理线性。给人用的参数得照人的直觉来。

案例 3:Importance sampling 烤环境贴图

预过滤环境贴图本身也是积分,要离线算。Karis 用 Hammersley 低差异序列 取 1024 个采样方向,用 GGX 重要性采样压方差。1024 听起来多,但只跑一次,烤完游戏跑一辈子。

类比:你要测一个房间的平均光强。完全随机选 1024 个点测一遍是”蒙特卡洛”;按”光强变化大的地方多选、平的地方少选”测就是”重要性采样”——同样精度只要 1/10 的样本。Hammersley 序列是一种确定性的伪随机点,比真随机分布更均匀,方差更小。

案例 4:直接光与 IBL 的 G 项参数微调

同一个 Schlick-GGX,直接光照明k = (α+1)² / 8IBL 间接光k = α / 2。看着像魔数,其实是 Karis 拟合实际多次散射数据得出的。这种”两套场景两个参数”的工程做法,文献里读着诡异,实战中救命。

踩过的坑

  1. 高粗糙度下能量不守恒:白色粗糙金属球比真实物体暗一截。原因:微表面之间的多次反射没算。Heitz 2016 才补了多散射项;当年 Karis 直接接受了这个误差。

  2. 没有各向异性:拉丝金属、头发那种有方向的高光做不出。默认模型只支持各向同性 GGX,要拉丝得改 shader。

  3. Metallic 是切换不是物理量:金属和电介质用一个 0..1 的 Metallic 参数切,中间值是美术过渡,不对应任何真实材质。新人容易被”半金属”骗,以为可以做半金属合金。

  4. split-sum 有近似误差:环境光和 BRDF 数学上不完全可分,拆开会引入误差;但视觉上人眼看不出来——这是 Karis 反复强调的”工程哲学”。

适用 vs 不适用场景

适用

  • 任何想要电影质感的实时渲染——游戏、VR、实时电影预览、WebGL 数字孪生
  • 工业建模工具(Substance / Marmoset)→ 实时引擎的标准管线
  • 移动端 PBR(参数少、查表小,省带宽)

不适用

  • 离线渲染(Arnold / RenderMan / Cycles)——直接上完整 Disney BRDF 或多层 BRDF
  • 需要严格物理准确的科研可视化、光学仿真——用 Mitsuba / pbrt
  • 卡通渲染 / NPR——物理拟真不是目标

历史小故事(可跳过)

  • 2012:迪士尼 Burley 发表 Physically-Based Shading at Disney,给出 principled BRDF——10+ 参数能复刻几乎所有真实材质。但只在 RenderMan 离线渲染器里跑得动。
  • 2013:Karis 在 SIGGRAPH course 公开 UE4 的简化方案,第一次证明 PBR 可以塞进游戏机一帧。这份笔记 v1 上线。
  • 2014:v2 修订发布,是后来流传最广的版本(标题里的 “2014” 由此而来)。
  • 2016:Heitz 给出多散射补正,修了能量不守恒。
  • 2018:Lagarde 在 Frostbite 给出能量守恒、清漆、布料的工业完整版。

之后十年所有游戏引擎的 PBR 论文都是在 Karis 的地基上往上加层

学到什么

  1. 工程是把理论砍到能跑且看不出差距——Karis 砍掉 Disney 60% 的参数,画面几乎不退化。砍得准比加得多更难。
  2. 查表(LUT)是实时图形的万能解药:积分跑不动?离线烤一张表。这招在 SSAO、阴影、雾、IBL 反复出现。
  3. 感知线性 > 物理线性:roughness² 不物理,但好用。给人用的参数得照人的直觉来。
  4. split-sum 思想——把复杂积分拆成几个独立可预计算的项——是图形学反复使用的通用招式(之后还出现在 Lambertian SH、球谐光照、Gaussian Splatting 等场合)。
  5. 简单足够好:Lambertian diffuse 数学上不准,但配上 GGX specular 视觉上看不出。整体观感比”每一项都最准”更重要。
  6. 工程笔记体值得学:Karis 22 页论文写得像内部备忘录——为什么选这个、为什么砍那个、误差容忍范围是什么。这种”决策日志”比”理论推导”更稀缺。

延伸阅读

  • 论文 PDF:Real Shading in Unreal Engine 4(22 页,工程笔记体,可读性极高)
  • SIGGRAPH 2013 整套 course:Physically Based Shading in Theory and Practice(包含 Disney 原始 talk 和 Brent Burley 的演讲)
  • 自己写一遍:LearnOpenGL — IBL Specular(一步步实现 split-sum,含完整 GLSL 代码)
  • 现代延续:Heitz 2016 多散射补正、Lagarde 2018 Frostbite Moving Frostbite to PBR 笔记(200+ 页工业全集)
  • 实现参考:Google Filament 引擎源码——一份开源、注释充分的现代 PBR 实现,几乎是 Karis 思想的扩展版
  • 中文导读:知乎 / 博客圈大量 “UE4 PBR 详解”,但强烈建议先读原文再看二手——原文写得最清楚

关联

  • 3d-gaussian-splatting —— 同样是”把复杂渲染近似成可查表 / 可预计算”的代表作;都用了”离线烤一次、运行时查”的工程哲学
  • ampere-architecture-2020 —— GGX 重要性采样和 LUT 查表都吃 GPU 显存带宽,硬件演进直接影响 PBR 复杂度上限
  • attention —— 数学上完全无关,但都体现了”把复杂连续运算拆成可批处理张量积”的思路

反思

这篇笔记最值得学的不是公式,是 Karis 做工程决策的取舍方式

  • 看到一个理论上完整的方案(Disney BRDF),先问”哪些参数 90% 场景用不到”——砍
  • 看到一个跑不动的积分(半球积分)——拆成”离线能算的 + 运行时查表”
  • 看到一个不直观的参数(α)——给美术换上感知线性的 roughness
  • 看到一个能量不守恒的近似——衡量误差是否人眼可见,可见就修,不可见就接受

这套思路对任何”理论可行 / 工程不可行”的转化任务都通用。不只是图形:从 LLM 蒸馏、数据库索引、网络协议优化,到任何”把研究成果搬进生产”的场合,都是同一招。

读这种”工业落地论文”和读”理论论文”思路应当不一样:

  • 理论论文要看为什么这是对的——证明、定理、推论
  • 工业论文要看为什么这样砍是合理的——决策依据、误差容忍、性能预算

Karis 这一篇是工业论文教科书级范例:每个简化都伴有原因、量化代价、视觉效果对照图。这种写作方式比公式本身更值得复刻——不只是渲染工程师,做任何”研究 → 产品”的人都该读一遍。

反向链接