all lessons / computer_graphics / 08 · shading & PBR lesson 8 / 15

Local shading & physically-based reflectance局部着色与基于物理的反射模型

Lesson 07 handed us the reflectance integral — a full hemisphere of incoming light, weighted by the BRDF, gathered at every point. Evaluating that per pixel, sixty times a second, is hopeless. So real-time rendering makes a bargain: replace the integral with a sum over a handful of point and directional lights, throw away the recursive indirect term, and evaluate one BRDF per light. This lesson builds that bargain from the cheapest hacks (Lambert, Phong) up to the microfacet model behind modern PBR — where the same material looks right under any light, on any artist's screen. 第 07 课把反射积分交到我们手上——整个半球的入射光,按 BRDF 加权,在每个点上收集起来。要每秒六十次、逐像素地去算它,是没指望的。于是实时渲染做了一笔交易:把积分换成对少数几个点光源与方向光的求和,丢掉递归的间接项,每个光源只求一次 BRDF。这一课就从最廉价的招式(朗伯、Phong)一路搭到现代 PBR 背后的微表面模型——在那里,同一种材质在任何光照下、在任何美术的屏幕上都显得正确。

The plan本课计划

Five moves. (1) Make the local-shading approximation explicit: replace over the hemisphere with a sum over lights, drop the recursive bounce, and add a constant ambient fudge to stand in for it — stating exactly what we throw away. (2) The cheapest real BRDF, Lambertian diffuse albedo/π, and where that 1/π came from. (3) The empirical specular hacks — Phong and Blinn–Phong with the halfway vector — cheap, tunable, and not energy-conserving. (4) The microfacet BRDF: surface as a field of tiny mirrors, Cook–Torrance D·F·G / (4(n·l)(n·v)), reading each of D, F, G. (5) The metalness–roughness workflow that turns all this into two intuitive sliders and makes PBR consistent across lights and artists. Lesson 09 then feeds those sliders from textures; lesson 12 tries to win back the indirect term we drop here.

五步走。(1) 把局部着色近似挑明:用对光源的求和替换半球上的 ,丢掉递归的弹跳,再加一个常数环境光凑数来顶替它——并说清我们究竟丢了什么。(2) 最廉价的真实 BRDF,朗伯漫反射 albedo/π,以及那个 1/π 从何而来。(3) 经验镜面招式——带半程向量的 Phong 与 Blinn–Phong——廉价、可调,且不守能量。(4) 微表面 BRDF:把表面看成一片微小镜面,Cook–Torrance D·F·G / (4(n·l)(n·v)),逐一读 DFG。(5) 把这一切化为两个直觉滑块、并让 PBR 在不同光照与不同美术间保持一致的金属度–粗糙度工作流。随后第 09 课用纹理去喂这些滑块;第 12 课则试图赢回我们在这里丢掉的间接项。

1 · The local-shading approximation — a sum, not an integral局部着色近似——是求和,不是积分

Lesson 07 ended on the reflection integral: outgoing radiance is the BRDF times incoming radiance times the cosine, integrated over the whole hemisphere of directions ωi. That integral is the honest physics, and it is ruinously expensive — every direction's incoming light is itself the outgoing light of some other surface (the recursion from lesson 01). To render a frame in milliseconds we approximate it twice, and it is worth being brutally explicit about both cuts.

第 07 课收束在反射积分上:出射辐射亮度等于 BRDF 乘入射辐射亮度乘余弦,在整个方向半球 ωi 上积分。那个积分是诚实的物理,也贵得要命——每个方向的入射光本身又是别的某个表面的出射光(第 01 课的那个递归)。要在毫秒内画完一帧,我们把它近似两次,而把这两刀都毫不含糊地摊开来说,是值得的。

Cut one: discretize the integral into a sum. In most scenes nearly all the light comes from a few compact sources — the sun, a lamp, a window. So replace the continuous integral over all directions with a sum over a handful of point and directional lights, each contributing from exactly one direction l. Cut two: drop the recursion. Ignore that surfaces relight each other; a red wall no longer bleeds red onto the floor beside it. To avoid the un-lit parts going pure black, add a single constant ambient term as a crude stand-in for all that missing indirect light. What remains is the local-shading model:

第一刀:把积分离散成求和。大多数场景里,几乎所有的光都来自少数几个紧凑的光源——太阳、一盏灯、一扇窗。于是把“对所有方向的连续积分”换成“对少数几个光源与方向光的求和”,每个光源都恰好从一个方向 l 贡献。第二刀:丢掉递归。无视表面之间互相照亮这件事;一面红墙不再把红色渗到它旁边的地板上。为了不让没被直射到的部分变成纯黑,加一个单独的常数环境光项,粗糙地顶替所有缺失的间接光。剩下的,就是局部着色模型:

Lo(v) ≈ ka·Lambient  +  Σlights fr(l, v) · Llight · max(0, n·l)

Read it against lesson 07's integral: the ∫dωi became a finite Σ; each light supplies its own Llight and direction l; the cosine i·n) survives as max(0, n·l) (clamped, because light arriving from behind the surface contributes nothing); and the recursive Li collapsed into the flat ka·Lambient. Everything the rest of the lesson does — Lambert, Phong, GGX — is a choice of the one factor left: the BRDF fr(l, v) evaluated per light.

拿它对照第 07 课的积分:∫dωi 变成了有限的 Σ;每个光源提供自己的 Llight 与方向 l;余弦 i·n) 幸存为 max(0, n·l)(要截断,因为从表面背后来的光毫无贡献);而递归的 Li 坍缩成了扁平的 ka·Lambient。本课余下所做的一切——朗伯、Phong、GGX——都只是在挑那唯一剩下的因子:逐光源求值的 BRDF fr(l, v)

What we just threw away我们刚刚丢掉了什么

Be honest about the bill. Dropping the recursion means no indirect bounces (color bleeding, light spilling around a corner), no soft shadows from area lights, no ambient occlusion in creases — the flat ambient term cannot reproduce any of these because it is direction-blind and constant everywhere. This is precisely the debt lesson 12 (probes, lightmaps, screen-space GI) and lesson 11 (path tracing) work to repay. Local shading is not "wrong"; it is a deliberately cheap approximation whose seams we spend later lessons hiding.

要诚实地看这张账单。丢掉递归意味着:没有间接弹跳(颜色渗染、光绕过墙角洒出来),没有面光源带来的软阴影,凹缝里没有环境光遮蔽——那个扁平的环境光项一样都复现不了,因为它对方向盲目、处处恒定。这正是第 12 课(探针、光照贴图、屏幕空间 GI)与第 11 课(路径追踪)努力偿还的欠债。局部着色并非“错”;它是一个刻意廉价的近似,其接缝要靠后面的课来遮掩。

2 · Diffuse — the Lambertian BRDF and its 1/π漫反射——朗伯 BRDF 及其 1/π

The simplest surface scatters incoming light equally in all outgoing directions — matte chalk, unfinished wood, paper. This is Lambertian diffuse. Because the outgoing radiance does not depend on where you stand, its BRDF is a constant in direction; the only free parameter is albedo (the fraction of light re-emitted, per color channel). So fr = albedo / π, and the shaded color is just that constant times the cosine:

最简单的表面把入射光均匀地散向所有出射方向——哑光粉笔、未上漆的木头、纸。这就是朗伯漫反射。因为出射辐射亮度与你站在哪儿无关,它的 BRDF 在方向上是个常数;唯一的自由参数是反照率 albedo(被重新发出的光的比例,逐颜色通道)。于是 fr = albedo / π,着色后的颜色就是这个常数乘以余弦:

Lo = (albedo / π) · Llight · max(0, n·l)

The mystery is the 1/π. It is not a fudge — it is exactly the normalizer that makes a diffuse surface energy-conserving. A BRDF must not reflect more light than arrives. Integrate a constant BRDF fr = c times the cosine over the hemisphere and you get c·∫Ω cosθ dω = c·π (that hemisphere-cosine integral evaluates to π, the fact lesson 07 established). For the total reflected fraction to be at most albedo ≤ 1, the constant must be albedo/π. The π is the price of spreading light over a whole hemisphere.

谜团在那个 1/π。它不是凑出来的——它恰好是让漫反射表面守能量的归一化因子。BRDF 反射出去的光不能多于射进来的光。把一个常数 BRDF fr = c 乘余弦、在半球上积分,得到 c·∫Ω cosθ dω = c·π(那个半球余弦积分等于 π,正是第 07 课确立的事实)。要让反射的总比例至多为 albedo ≤ 1,这个常数就必须是 albedo/π。这个 π,就是把光摊到整个半球的代价。

Note what diffuse shading is not: it has no dependence on the view direction v. Chalk looks the same brightness from every angle, given a fixed light. All the angle-dependent glint — the moving highlight that tells you a surface is glossy — lives in the specular term, which is where the next three sections go.

注意漫反射着色是什么:它完全不依赖视线方向 v。给定固定光源,粉笔从任何角度看亮度都一样。所有随角度变化的闪光——那个会移动、告诉你表面有光泽的高光——都住在镜面项里,而那正是接下来三节要去的地方。

3 · Empirical specular — Phong, Blinn–Phong, and the halfway vector经验镜面——Phong、Blinn–Phong 与半程向量

A glossy surface sends a bright highlight toward you only when your eye sits near the mirror-reflection of the light. The oldest model, Phong (1975), builds the ideal reflection direction r = reflect(−l, n) and measures how close the view v is to it, raised to a shininess power s: (r·v)s. Large s → the term falls off fast away from the mirror direction → a tight, sharp highlight. Small s → a broad, soft one.

有光泽的表面只有当你的眼睛靠近“光的镜面反射方向”时,才会朝你送出一块明亮的高光。最古老的模型 Phong(1975)先构造理想反射方向 r = reflect(−l, n),再量视线 v 离它有多近,取光泽度s(r·v)ss 大 → 该项一离开镜面方向就迅速衰减 → 高光紧而锐;s 小 → 高光宽而软。

Blinn–Phong (1977) is a cheaper, better-behaved variant. Instead of reflecting a vector, it forms the halfway vector h = normalize(l + v) — the direction that would bisect light and eye — and asks how aligned the surface normal is with it: (n·h)s. The highlight is brightest exactly when n = h, i.e. when the surface is oriented to mirror the light into your eye. It avoids the (occasionally negative) reflect computation, and the halfway vector reappears verbatim inside the physically-based model in section 4 — which is the real reason to prefer it.

Blinn–Phong(1977)是更廉价、更乖巧的变体。它不去反射向量,而是构造半程向量 h = normalize(l + v)——那个平分“光”与“眼”的方向——再问表面法线与它有多对齐:(n·h)s。高光恰在 n = h 时最亮,也就是表面朝向正好把光镜射进你眼睛的时候。它避开了(偶尔会取负的)reflect 计算,而这个半程向量会在第 4 节的基于物理模型里一字不差地重现——这才是偏爱它的真正理由。

A hack, not physics是招式,不是物理

Phong and Blinn–Phong predate physically-based rendering and were tuned by eye, not derived from a surface model. They are not energy-conserving: crank up the shininess and the light color and a surface can reflect more energy than it receives, which is why old renders needed per-material fiddling to not look "wrong" under new lighting. There is also no principled link between the shininess exponent s and any real material property. They survive because they are dirt-cheap and, with hand-tuning, convincing — but they do not generalize. Section 4 fixes exactly this.

Phong 与 Blinn–Phong 早于基于物理的渲染,是靠肉眼调出来的,而非从表面模型推导而来。它们不守能量:把光泽度和光色调高,表面就能反射出比它接收到的更多的能量——这正是老式渲染需要逐材质地手工微调、才不至于在新光照下显得“不对”的原因。光泽度指数 s 与任何真实材质属性之间也没有有原则的联系。它们能存活,是因为极其便宜、且经手工调校后颇能唬人——但它们不会推广。第 4 节修的正是这一点。

4 · The microfacet BRDF — a field of tiny mirrors微表面 BRDF——一片微小的镜子

Physically-based rendering starts from one idea: a real surface, magnified, is not smooth — it is a landscape of microscopic facets, each a perfect mirror. What we see as a "rough" or "glossy" material is the statistics of how those facets are tilted. A highlight is broad when the facets point every which way (rough) and tight when they mostly agree with the macroscopic normal (smooth). The Cook–Torrance BRDF turns that picture into three factors over a normalization:

基于物理的渲染从一个念头出发:一个真实表面,放大来看并不光滑——它是一片显微尺度的小面构成的地貌,每个小面都是一面完美的镜子。我们看到的“粗糙”或“有光泽”材质,其实是这些小面如何倾斜的统计。当小面朝向四面八方(粗糙)时高光就宽,当它们大体与宏观法线一致(光滑)时高光就紧。Cook–Torrance BRDF 把这幅图景化为归一化之上的三个因子:

fr(l, v) = ( D(h) · F(v, h) · G(l, v) ) / ( 4 · (n·l) · (n·v) )

Only the facets whose own normal equals the halfway vector h mirror light from l into v — so h from section 3 returns as the axis the whole model turns on. Each factor answers a distinct physical question:

只有那些自身法线恰等于半程向量 h 的小面,才会把光从 l 镜射进 v——所以第 3 节的 h 又回来了,成了整个模型绕之旋转的轴。每个因子回答一个各自独立的物理问题:

The 4·(n·l)·(n·v) denominator is a geometric normalizer that falls out of the microfacet derivation (converting between facet-space and macroscopic-space solid angles). The upshot: three physically-motivated factors, each clamped and energy-aware, replacing Phong's single tunable exponent. That is why the widget below can show a highlight that tightens and grows a tail as you drag roughness, with no per-material hand-tuning.

4·(n·l)·(n·v) 这个分母是微表面推导中自然掉出来的几何归一化因子(在小面空间与宏观空间的立体角之间换算)。要点是:三个有物理动机、各自被截断且顾及能量的因子,取代了 Phong 那个单一可调指数。这正是为什么下面的控件能在你拖动粗糙度时,展示一个会收紧并长出尾巴的高光,而无需任何逐材质的手工调校。

A lit sphere: Blinn–Phong vs GGX — drag roughness, watch the highlight一颗被照亮的球:Blinn–Phong vs GGX——拖动粗糙度,看高光
The sphere is shaded pixel-by-pixel: for each point inside the disk the surface normal is n = normalize(x, y, √(R²−x²−y²)) in view space, the eye is fixed at v = (0,0,1), and the light direction l sweeps the x–z plane with the angle slider. Blinn–Phong uses (n·h)s with shininess mapped from roughness; GGX evaluates the full Cook–Torrance D·F·G/(4(n·l)(n·v)) with Schlick Fresnel (F0=0.04). Low roughness → a tight bright highlight; high roughness → a broad soft one. Compare the two: GGX keeps a faint tail of glow that Phong cuts off.这颗球是逐像素着色的:对圆盘内每个点,视空间里的表面法线是 n = normalize(x, y, √(R²−x²−y²)),眼睛固定在 v = (0,0,1),光方向 l 随角度滑块在 x–z 平面内扫动。Blinn–Phong(n·h)s,光泽度由粗糙度映射而来;GGX 求完整的 Cook–Torrance D·F·G/(4(n·l)(n·v)),配 Schlick 菲涅尔(F0=0.04)。粗糙度低 → 高光紧而亮;粗糙度高 → 高光宽而软。对比二者:GGX 会留一条 Phong 直接切掉的微弱辉光尾巴
Model
GGX
Roughness α
0.25
Blinn–Phong s
Rel. highlight size
Show the core JS查看核心 JS
// Per pixel in the disk: view-space normal from screen (x,y); eye v=(0,0,1).
var nz = Math.sqrt(R*R - x*x - y*y);          // z of the hemisphere
var n = normalize(x, y, nz);
var ndl = Math.max(0, dot(n, l));             // clamped cosine (Lambert)
var diffuse = albedo / Math.PI * ndl;         // §2

// halfway vector, shared by both models (§3)
var h = normalize(l[0]+v[0], l[1]+v[1], l[2]+v[2]);
var ndh = Math.max(0, dot(n, h));

var spec;
if (mode === 'phong') {                        // Blinn–Phong: s from roughness
  var s = 2 / (alpha*alpha) - 2;
  spec = Math.pow(ndh, s) * ndl;
} else {                                        // GGX Cook–Torrance (§4)
  var a2 = alpha*alpha;
  var d  = a2 / (Math.PI * Math.pow(ndh*ndh*(a2-1)+1, 2));   // GGX NDF
  var hv = Math.max(0, dot(h, v));
  var F  = 0.04 + 0.96 * Math.pow(1-hv, 5);                  // Schlick, F0=0.04
  var k  = alpha*alpha/2;                                    // Smith-ish geometry
  var G  = (ndl/(ndl*(1-k)+k)) * (ndv/(ndv*(1-k)+k));
  spec = d * F * G / (4*ndl*ndv + 1e-4);
}
var L = ka*ambient + (diffuse + spec) * Llight;   // §1: ambient + sum over lights

5 · The metalness–roughness workflow — two sliders that generalize金属度–粗糙度工作流——两个能推广的滑块

The microfacet BRDF is principled but has fiddly inputs (F0 as an RGB, separate diffuse and specular colors). The metalness–roughness workflow — the industry standard since around 2014 — compresses them into two intuitive parameters plus a base color, and this compression is the whole reason PBR is authorable at scale:

微表面 BRDF 有原则,但输入琐碎(作为 RGB 的 F0、各自独立的漫反射色与镜面色)。金属度–粗糙度工作流——自约 2014 年起的行业标准——把它们压成两个直觉参数外加一个基色,而正是这份压缩,让 PBR 能被大规模地创作:

ParameterDielectric (metallic = 0)Metal (metallic = 1)
Diffuse albedo= base colorblack (no diffuse)
Specular F0≈ 0.04, colorless= base color (tinted)
Highlight shapefrom roughness → αfrom roughness → α
Exampleplastic, wood, skin, stonegold, copper, steel, aluminum
参数电介质(金属度 = 0)金属(金属度 = 1)
漫反射反照率= 基色黑(无漫反射)
镜面 F0≈ 0.04,无色= 基色(带色)
高光形状由粗糙度 → α由粗糙度 → α
例子塑料、木头、皮肤、石头金、铜、钢、铝

The energy is split: light either reflects specularly (governed by F) or scatters diffusely, and a proper PBR shader scales the diffuse term by (1 − F) so the two never sum to more than the incoming light. This is the payoff of the honest 1/π and the energy-aware D·F·G: because every term conserves energy, an artist sets metallic and roughness once and the material reads correctly under a candle, at noon, or in a dark reflection — and looks the same in every studio's engine. That consistency across lights and artists is the entire point of PBR, and it is what empirical Phong could never promise.

能量是被分配的:光要么镜面反射(受 F 支配),要么漫散射,而一个正规的 PBR 着色器会把漫反射项乘以 (1 − F),好让两者之和永远不超过入射光。这正是那个诚实的 1/π 与顾及能量的 D·F·G 的回报:因为每一项都守能量,美术只需一次设定金属度与粗糙度,这材质就能在烛光下、正午里、或幽暗的反射中都读起来正确——并且在每一家工作室的引擎里看起来都一样。这种跨光照、跨美术的一致性,正是 PBR 的全部意义所在,也是经验性的 Phong 永远无法承诺的。

Where this points next接下来指向何处

We now have a per-pixel shading recipe: an ambient stand-in plus a sum over lights, each contributing one BRDF evaluation, with the microfacet model and its metalness–roughness controls giving physically consistent results. But so far every material parameter — albedo, metallic, roughness, the normal itself — has been a single constant across the whole surface. Real objects vary point to point: scratches, dirt, wood grain, painted logos. Lesson 09 supplies those variations as textures — albedo maps, normal maps, metallic/roughness maps — sampled per fragment to feed exactly the sliders we built here. And the whole flat ka·Lambient shortcut is the debt lesson 12 sets out to repay with real indirect light.

现在我们有了一套逐像素的着色配方:一个环境光顶替项,加上对光源的求和,每个光源贡献一次 BRDF 求值,而微表面模型及其金属度–粗糙度控件给出物理一致的结果。但到目前为止,每一个材质参数——反照率、金属度、粗糙度、乃至法线本身——都是横跨整个表面的单个常数。真实物体逐点不同:划痕、污渍、木纹、印上去的标志。第 09 课把这些变化作为纹理供给——反照率贴图、法线贴图、金属度/粗糙度贴图——逐片元采样,去喂我们在这里搭起来的那些滑块。而那个扁平的 ka·Lambient 捷径,正是第 12 课要用真实间接光偿还的欠债。

Takeaway要点

Local shading approximates lesson 07's hemisphere integral twice: replace ∫dωi with a sum over point/directional lights, and drop the recursive indirect term, patching the gap with a constant ambient — giving up indirect bounces and soft shadows (lesson 12's job). What's left is one BRDF per light. Lambertian diffuse is albedo/π — the 1/π is the energy-conservation normalizer — and is view-independent. Phong / Blinn–Phong add a specular (r·v)s or (n·h)s via the halfway vector h: cheap, tunable, but not energy-conserving. The Cook–Torrance microfacet BRDF D·F·G/(4(n·l)(n·v)) models tiny mirrors: D (GGX, roughness α) shapes the highlight, F (Schlick, F0) raises reflectivity at grazing angles, G (Smith) accounts for masking. The metalness–roughness workflow exposes it as two sliders: dielectrics get F0≈0.04 and a diffuse base color, metals get F0= base color and no diffuse — which is why PBR stays consistent across every light and every artist.

局部着色把第 07 课的半球积分近似两次:用对点/方向光的求和替换 ∫dωi,并丢掉递归的间接项,用常数环境光补缺——放弃了间接弹跳与软阴影(那是第 12 课的活儿)。剩下的是每个光源一次 BRDF。朗伯漫反射是 albedo/π——那个 1/π 是守能量的归一化因子——且与视线无关。Phong / Blinn–Phong 借半程向量 h 加上镜面项 (r·v)s(n·h)s:廉价、可调,但守能量。Cook–Torrance 微表面 BRDF D·F·G/(4(n·l)(n·v)) 建模微小镜面:D(GGX,粗糙度 α)塑造高光形状,F(Schlick,F0)在掠射角抬高反射率,G(Smith)计入遮蔽。金属度–粗糙度工作流把它暴露为两个滑块:电介质取 F0≈0.04 与漫反射基色,金属取 F0= 基色且无漫反射——这就是 PBR 为何在每种光照、每位美术手中都保持一致。

Interview prompts面试题