all lessons / computer_graphics / 12 · real-time GI lesson 12 / 15

Real-time global illumination — faking the integral in 16 milliseconds实时全局光照——在 16 毫秒内伪造那个积分

Lesson 11 solved the rendering equation correctly with Monte Carlo path tracing — and paid for it with hundreds to thousands of rays per pixel. A game has about 16 ms per frame; it cannot converge an integral in that time. So real-time rendering does not solve the equation, it fakes each term cheaply. This lesson is the catalogue of those fakes — shadow maps, ambient occlusion, baked light, probes, screen-space reflections — and the three verbs that generate them all: precompute, approximate, amortize. 第 11 课用蒙特卡洛路径追踪正确地解出了渲染方程——代价是每像素几百到几千条光线。而游戏每帧只有约 16 毫秒;它没法在这点时间里让一个积分收敛。所以实时渲染不去解那个方程,而是把每一项便宜地伪造出来。这一课就是这些“伪造术”的目录——阴影贴图、环境光遮蔽、烘焙光照、探针、屏幕空间反射——以及生成它们的三个动词:预计算近似摊销

The plan本课计划

Five moves. (1) The budget wall: restate why a per-frame Monte Carlo integral is impossible, and name the three escape routes — precompute (bake), approximate (screen-space), amortize (temporal) — each buying one term of the rendering equation cheaply. (2) Shadows via shadow mapping: render depth from the light's view, then compare; meet acne and peter-panning and the bias that trades between them, then PCF and cascades. (3) Ambient occlusion: the "how open is the sky here" term, faked in screen space by SSAO. (4) Precomputed indirect light: radiosity, lightmaps (static only), and irradiance probes with spherical harmonics (for dynamic objects). (5) Screen-space and the new era: SSR and its on-screen-only limit, then hardware ray tracing + ML denoising + DDGI closing the gap to true dynamic GI (bridge to lesson 15). The widget dissects the single most instructive bug in the whole set: shadow-map bias.

五步走。(1) 预算之墙:重申为何逐帧的蒙特卡洛积分不可行,并点出三条逃生路线——预计算(烘焙)、近似(屏幕空间)、摊销(时序)——每条都便宜地买下渲染方程的某一项。(2) 用阴影贴图做阴影:从光源视角渲染深度,再比较;见识阴影痤疮彼得潘,以及在二者之间权衡的偏置,然后是 PCF 与级联。(3) 环境光遮蔽:那个“这里天空有多开阔”的项,由 SSAO 在屏幕空间伪造。(4) 预计算间接光:辐射度、光照贴图(仅限静态)、以及带球谐的辐照度探针(用于动态物体)。(5) 屏幕空间与新纪元:SSR 及其“只有屏幕上才有”的局限,然后是硬件光线追踪 + ML 去噪 + DDGI 逼近真正的动态 GI(通往第 15 课的桥)。小控件将解剖整套方法里最有教益的那个 bug:阴影贴图偏置。

1 · The budget wall — you cannot converge an integral per frame预算之墙——你没法逐帧让积分收敛

Recall the constraint from lesson 01: a real-time frame must finish in roughly 16.7 ms at 60 FPS (and half that, ~8 ms, at 120 FPS). Now recall lesson 11: path tracing estimates the recursive integral ∫ fr Lii·n) dωi by averaging random light paths, and its noise falls only as 1/√N — to halve the noise you must quadruple the rays. A clean offline frame might spend hundreds to thousands of paths per pixel. Multiply that out: a full-HD frame is about 2 × 10⁶ pixels, so even 256 paths each is ~5 × 10⁸ ray-scene queries. There is no budget for that in 16 ms on commodity hardware; the integral simply cannot converge in time.

回忆第 01 课的约束:实时一帧在 60 FPS 下必须在大约 16.7 毫秒 内画完(120 FPS 下则减半,~8 毫秒)。再回忆第 11 课:路径追踪通过对随机光路取平均来估计递归积分 ∫ fr Lii·n) dωi,其噪声只以 1/√N 下降——要把噪声减半,就得把光线数翻四倍。一帧干净的离线画面每像素可能要花几百到几千条路径。乘出来看:一帧全高清约有 2 × 10⁶ 个像素,即便每像素只 256 条路径也是 ~5 × 10⁸ 次光线—场景查询。在普通硬件上、16 毫秒内根本没有这个预算;那个积分就是来不及收敛。

So real-time rendering gives up on solving the equation and instead spends its milliseconds buying the look of each term as cheaply as possible. There are exactly three ways to make an expensive term affordable, and every technique in this lesson is one of them:

于是实时渲染放弃了去解那个方程,转而把它有限的毫秒花在尽可能便宜地买下每一项的观感上。让一个昂贵的项变得负担得起,恰好只有三种办法,而本课的每一种技术都是其中之一:

Precompute · 预计算
bake it once, read it back (lightmaps, probes) — costs memory & assumes static烘焙一次、回读(光照贴图、探针)——耗内存、且假设静态
Approximate · 近似
substitute a cheap stand-in from data already on screen (SSAO, SSR)用屏幕上已有的数据换一个便宜的替身(SSAO、SSR)
Amortize · 摊销
spread the cost across frames — reuse last frame, converge over time (TAA, DDGI)把开销摊到多帧上——复用上一帧、随时间收敛(TAA、DDGI)

Keep this triple in view: it is the whole strategy. When you meet a new real-time trick, ask which verb it is. A shadow map is precompute the light's visibility (§2). SSAO is approximate the ambient occlusion term from the depth buffer (§3). Lightmaps are precompute the indirect integral (§4). Temporal accumulation in modern GI is amortize across frames (§5). None of them solve the rendering equation; each one buys one factor of it at a price the frame can pay.

把这三元组一直摆在眼前:它就是全部策略。当你遇到一个新的实时技巧,就问它是哪个动词。阴影贴图是把光源的可见性预计算下来(§2)。SSAO 是从深度缓冲近似出环境光遮蔽项(§3)。光照贴图是把间接光积分预计算好(§4)。现代 GI 里的时序累积是跨帧摊销(§5)。它们都不解渲染方程;每一个都以帧付得起的价格买下方程的某一个因子。

2 · Shadows via shadow mapping — a depth test from the light's eye用阴影贴图做阴影——从光源之眼做一次深度测试

A shadow is pure visibility toward a light: point x is lit if the light can see it, and shadowed if some closer surface blocks the view. Path tracing answers this with a shadow ray. Rasterization cannot shoot arbitrary rays cheaply — but it is superb at one thing: rendering depth from a viewpoint. So flip the viewpoint. Shadow mapping is a two-pass idea:

阴影本质上就是朝向光源的可见性:如果光源能看到点 x,它就被照亮;如果有更近的表面挡住了视线,它就在阴影里。路径追踪用一条阴影光线来回答。光栅化没法便宜地发射任意光线——但它极擅长一件事:从某个视点渲染深度。那就把视点翻过去。阴影贴图是一个两遍(two-pass)的想法:

lit(x)  ⟺  dlight(x)  ≤  zmap[ project(x) ]  +  bias

The whole subtlety hides in that + bias. The shadow map is a finite grid: each texel stores one depth for a little patch of surface, but the real surface under that texel slopes and spans a range of depths. So the stored value is a quantized, slightly-wrong estimate of the true depth. Compare a point against it naively and half the lit surface reports d(x) > zmap purely from that quantization error — the surface shadows itself in stripes. This is shadow acne.

全部的微妙都藏在那个 + 偏置 里。阴影贴图是一张有限的网格:每个纹素为一小片表面只存一个深度,可那纹素底下真实的表面是倾斜的、跨着一段深度范围。所以存下来的值是对真实深度的、量化过的、略有偏差的估计。若拿一个点朴素地去和它比,光照表面里会有一半仅仅因为这点量化误差就报出 d(x) > zmap——表面把自己投成了一条条纹的阴影。这就是阴影痤疮(shadow acne)

The fix is to nudge the comparison: add a small bias so a point must be meaningfully farther than the stored depth before it counts as shadowed. Too little bias and the acne stripes remain. But push the bias too high and you overshoot the other way: real contact shadows lift off, and an object's shadow visibly detaches from its base as if the object were floating — peter-panning (the shadow, like Peter Pan's, comes unstuck from the feet). Bias is a single knob trading one artifact against the other, and the widget below lets you feel exactly where the safe middle is.

解法是把比较推一下:加一个小小的偏置,让一个点必须明显地比存储深度更远,才算作被遮蔽。偏置太小,痤疮条纹还在。可偏置推得太高就矫枉过正到另一头:真实的接触阴影被抬起,物体的阴影会明显地从它的底部脱开,仿佛物体悬浮着——彼得潘(peter-panning)(阴影像彼得潘那样,从脚上松脱了)。偏置这一个旋钮在两种瑕疵之间权衡,下面的控件让你精确地感受到那个安全的中间地带在哪。

Slope-scaled bias, briefly斜率缩放偏置,简述

A constant bias is a compromise: surfaces nearly edge-on to the light need a large bias (their depth changes fast across one texel), while surfaces facing the light need almost none. The standard refinement is slope-scaled bias — scale the bias by how grazing the light angle is (roughly by tan of the angle between the surface normal and the light). This shrinks the peter-panning you pay on flat faces while still killing acne on steep ones. The widget uses a single flat receiver, so a constant bias suffices to show the trade cleanly.

常数偏移是一种折中:几乎与光线平行(掠射)的表面需要偏移(它们的深度在一个纹素内变化很快),而正对光的表面几乎不需要。标准的改进是斜率缩放偏移(slope-scaled bias)——按光照角度有多掠射来缩放偏移(大致按表面法线与光线夹角的 tan)。这在平面上减少了你要付出的彼得潘现象,同时仍能消灭陡面上的痤疮。本控件只用了一个平整的接收面,所以一个常数偏移就足以干净地展示这个取舍。

Two more pieces make shadow maps usable in practice. PCF (percentage-closer filtering) softens the hard, aliased shadow edge: instead of one depth comparison, sample the map at several neighboring texels, run the test on each, and average the pass/fail results. A point half-inside the penumbra comes back "60% lit," giving a soft edge instead of a jagged binary one. And cascaded shadow maps (CSM) handle large scenes: a single map spread over a whole outdoor level would give each texel a huge footprint (chunky shadows). CSM slices the view frustum by distance into a few cascades and gives each its own map — high resolution near the camera where you look, coarse far away where you don't.

还有两件事让阴影贴图在实践中可用。PCF(百分比渐近滤波)柔化那道生硬、走样的阴影边缘:不做单次深度比较,而是在邻近的几个纹素上采样该图、对每个都跑一遍测试、再把通过/未通过的结果平均。一个半陷在半影里的点会返回“照亮 60%”,从而得到柔和的边缘,而不是锯齿状的非黑即白。而级联阴影贴图(CSM)应对大场景:单张图铺满整个户外关卡,会让每个纹素覆盖一大片(块状阴影)。CSM 按距离把视锥切成几段级联、各给一张自己的图——相机近处(你看的地方)高分辨率,远处(你不看的地方)粗糙。

Shadow-map bias — acne vs peter-panning阴影贴图偏置——痤疮 vs 彼得潘
Side view. The light is up-left; the horizontal line is a flat receiver (the ground); the box is an occluder. The scene is baked into a coarse shadow map of only a handful of light-space depth texels (each stores one depth), so the lit ground has quantization error. For every point on the ground we compute its true light-space depth and compare to the stored texel depth + bias. Too little bias → the lit ground breaks into striped shadow acne (self-shadowing). Correct bias → a clean shadow anchored at the box's base. Too much bias → the shadow detaches from the box (peter-panning). Drag the bias.侧视图。光源在左上;水平线是一块平坦的接收面(地面);方块是遮挡物。场景被烘焙进一张粗糙的阴影贴图,只有寥寥几个光源空间深度纹素(每个存一个深度),所以被照亮的地面带有量化误差。对地面上的每个点,我们算出它真实的光源空间深度,与存储的纹素深度 + 偏置 相比。偏置太小 → 被照亮的地面裂成条纹状的阴影痤疮(自遮蔽)。偏置合适 → 一道锚在方块底部的干净阴影。偏置太大 → 阴影从方块脱开(彼得潘)。拖动偏置。
Bias
Artifact
Shadow-map texels
Lit ground misclassified
Show the core JS查看核心 JS
// Depth-compare-plus-bias, run per receiver point x.
// zMapAt(u): the coarse map's stored (quantized) light-space depth for the
//            texel that direction u falls in — one depth per texel.
// dLight(x): x's TRUE distance from the light along that same direction.
function isLit(x, bias){
  var u      = lightTexel(x);      // which shadow-map texel x projects to
  var stored = zMapAt(u);          // quantized nearest depth in that texel
  var d      = dLight(x);          // x's true light-space depth
  return d <= stored + bias;       // lit if not meaningfully behind the map
}
// bias too small  -> quantization makes lit ground read d > stored -> ACNE
// bias too large  -> points just past the occluder pass the test -> PETER-PANNING

3 · Ambient occlusion — how open is the sky here?环境光遮蔽——这里的天空有多开阔?

Split the incoming light Li into direct (from named light sources — handled by shadow maps) and ambient (a soft, everywhere glow standing in for all the indirect bounces we cannot afford). A crude but common ambient model just adds a constant to every surface. The problem: that constant floods creases, contact points, and cavities that should be darker because surrounding geometry blocks most of the sky. Ambient occlusion is the missing factor — for each point, what fraction of the hemisphere above it is open versus occluded by nearby surfaces. A corner where two walls meet is occluded, so it should be darker; an exposed flat is fully open.

把入射光 Li 拆成直接光(来自具名光源——由阴影贴图处理)与环境光(一层柔和、无处不在的辉光,替代所有我们负担不起的间接反弹)。一个粗糙但常见的环境光模型只是给每个表面加一个常数。问题在于:这个常数会漫进那些本该更暗的折缝、接触点与凹陷——因为周围的几何挡住了大部分天空。环境光遮蔽就是那个缺失的因子——对每个点,问它上方半球有多大比例是开阔的,又有多少被邻近表面遮蔽。两面墙相交的角落是被遮蔽的,故应更暗;一块暴露的平面则完全开阔。

Computing true occlusion means casting many rays over the hemisphere per point — again, too expensive per frame. SSAO (screen-space ambient occlusion) is the classic approximate trick: it never touches the real geometry. It reads the depth buffer — which the rasterizer already produced — and, around each pixel, samples a few nearby depth values. If many neighbors are closer to the camera than the surface here, this pixel sits in a pocket surrounded by geometry, so darken it; if neighbors fall away, it is open, so leave it bright. It is a purely 2.5-D estimate from data already on screen — which is exactly why it is cheap, and exactly why it has the same blind spot as everything screen-space (§5): it can only account for occluders the camera can see.

计算真实遮蔽意味着对每个点在半球上投出许多光线——同样,逐帧太贵。SSAO(屏幕空间环境光遮蔽)是经典的近似技巧:它根本不碰真实几何。它读取深度缓冲——那是光栅器已经产出的——在每个像素周围采样几个邻近的深度值。若许多邻居比此处表面更靠近相机,说明这个像素坐在被几何环绕的凹坑里,就把它压暗;若邻居纷纷退远,它就是开阔的,保持明亮。这是一个纯粹 2.5 维的、从屏幕已有数据得来的估计——这正是它便宜的原因,也正是它和一切屏幕空间方法(§5)有同样盲点的原因:它只能顾及相机看得见的遮挡物。

4 · Precomputed indirect light — radiosity, lightmaps, and probes预计算间接光——辐射度、光照贴图与探针

The hardest term is the recursive one: light that bounces off other surfaces before reaching x — indirect illumination, the soft color-bleeding fill that makes a scene look real. If the geometry and lights do not move, we can pay for it once, offline, and read the result back for free every frame. That is the precompute route, and it has a lineage.

最难的项是那个递归项:在到达 x 之前先从别的表面反弹过的光——间接光照,那种柔和的、渗色的补光,正是它让场景显得真实。如果几何与光源都不动,我们就可以一次性、离线地为它付账,之后每一帧免费回读结果。这就是预计算路线,它有一条谱系。

Radiosity is the classical method for the diffuse case. Break every surface into small patches; the light leaving patch i is its emission plus the light it receives from every other patch j, weighted by a form factor Fij that encodes how much of j's hemisphere i occupies (their mutual geometry — size, distance, orientation). That is a big linear system of patch radiosities; solve it once and you have full diffuse interreflection — soft, physically-based fill light. It is view-independent (diffuse light looks the same from every angle), which is exactly what makes the solution reusable.

辐射度(radiosity)是处理漫反射情形的经典方法。把每个表面拆成小面片;离开面片 i 的光,等于它自身的发射加上它从其他每个面片 j 收到的光,后者按一个形状因子 Fij 加权——该因子编码了 i 占据 j 半球的比例(它们相互的几何:大小、距离、朝向)。这是一个关于各面片辐射度的大型线性系统;解一次,你就得到完整的漫反射互反射——柔和的、基于物理的补光。它与视角无关(漫射光从任何角度看都一样),而这正是解可以复用的原因。

Lightmaps are how that precomputed result ships in a game. Bake the indirect (and often the static direct) illumination into a texture unwrapped over the static geometry; at runtime, shading a wall is just a texture fetch — nearly free, and gorgeous. The iron catch is in the word static: a lightmap is correct only for the geometry and lights it was baked with. Move a wall, open a door, or swing a light and the baked shadows and bounce lie. And crucially, lightmaps live on static surfaces — they say nothing about how to light a moving character walking through that baked room.

光照贴图(lightmaps)是那份预计算结果在游戏里的交付形式。把间接光(常常还有静态直接光)烘焙进一张在静态几何上展开的纹理;运行时,给一面墙着色只是一次纹理取样——几乎免费,而且极美。铁一般的坑就在静态二字:光照贴图只对烘焙时所用的几何与光源才正确。挪一面墙、开一扇门、或转动一盏灯,烘焙好的阴影与反弹就都在撒谎。而且关键在于,光照贴图长在静态表面上——它对如何照亮一个穿过那间烘焙房间的移动角色只字未提。

That gap is filled by irradiance probes (light probes). Scatter sample points through the volume of the scene; at each probe, precompute the light arriving from every direction and store it compactly. A dynamic object then reads the nearest probes and interpolates — so a character darkens as it walks into shade and picks up a red glow beside a red wall, all without per-frame bounce computation. The compact storage is the elegant part: incoming light over a sphere is a smooth low-frequency function, so we expand it in spherical harmonics (SH) and keep only the first few coefficients (often just 9, for the diffuse "L2" band). Those few numbers reconstruct the soft directional irradiance well enough for diffuse shading. Probes are precompute for the light field in space; lightmaps are precompute for it on surfaces — the pair covers static rooms and the dynamic actors moving through them.

这个空缺由辐照度探针(光照探针)填补。在场景体积里撒下采样点;在每个探针处,预计算从各个方向到来的光并紧凑地存下。动态物体随后读取最近的几个探针并插值——于是角色走进阴影会变暗、走到红墙边会染上一层红晕,全程无需逐帧计算反弹。紧凑存储是其精巧之处:球面上的入射光是一个平滑的低频函数,所以我们用球谐(SH)把它展开,只保留前几个系数(漫反射常常只用 9 个,即“L2”band)。这寥寥几个数就能重建出足够好的、柔和的方向性辐照度,用于漫反射着色。探针是对空间中光场的预计算;光照贴图是对表面上光场的预计算——二者搭配,覆盖了静态房间和穿行其中的动态角色。

TechniqueBuys which termVerbHandles dynamic?
Shadow mapdirect-light visibilityprecompute (per frame)yes (rebuilt each frame)
SSAOambient occlusionapproximate (screen-space)yes, but on-screen only
Lightmapindirect on surfacesprecompute (offline bake)no — static geometry & lights
Light probe + SHindirect on moving objectsprecompute (offline bake)dynamic objects, static lights
SSRglossy/mirror reflectionapproximate (screen-space)yes, but on-screen only
技术买下哪一项动词能处理动态吗
阴影贴图直接光可见性预计算(每帧)能(每帧重建)
SSAO环境光遮蔽近似(屏幕空间)能,但仅限屏幕上
光照贴图表面上的间接光预计算(离线烘焙)否——静态几何与光源
光照探针 + SH移动物体上的间接光预计算(离线烘焙)动态物体,静态光源
SSR光泽/镜面反射近似(屏幕空间)能,但仅限屏幕上

5 · Screen-space, and the new era屏幕空间,以及新纪元

SSR (screen-space reflections) fakes the mirror/glossy term the same way SSAO fakes occlusion: reuse the frame you already rendered. From a reflective pixel, reflect the view ray about the surface normal and march that ray through the depth buffer, stepping until it crosses a stored depth — a hit. Sample the already-shaded color there and blend it in as the reflection. When it works, you get gorgeous wet floors and glossy tabletops for the price of a screen-space raymarch, no extra scene traversal.

SSR(屏幕空间反射)伪造镜面/光泽项的方式,和 SSAO 伪造遮蔽如出一辙:复用你已经渲染好的这一帧。从一个反射像素出发,把视线光线关于表面法线反射,然后让那条光线在深度缓冲里步进,一步步走到它越过某个存储深度处——即一次命中。取那里已经着色好的颜色,作为反射混合进来。当它奏效时,你能以一次屏幕空间光线步进的代价换来极美的湿地板和光泽桌面,无需额外的场景遍历。

The screen-space blind spot屏幕空间的盲点

SSR, SSAO, and every screen-space method share one fundamental limitation: they can only use what is currently on screen. The depth buffer holds the visible surfaces and nothing else — so a reflection of the sky, of an object just off the edge of the frame, or of a surface hidden behind the reflector simply has no data to draw from. SSR characteristically fails at screen edges (reflections fade out) and cannot reflect anything the camera itself cannot see. It approximates the reflection integral using a 2-D shadow of the 3-D world; whatever that shadow omits, the reflection omits too.

SSR、SSAO 以及每一种屏幕空间方法都共享一个根本局限:它们只能用当前屏幕上有的东西。深度缓冲只保存可见表面、别无其他——所以对天空、对刚好落在画面边缘之外的物体、或对藏在反射体背后的表面的反射,根本没有数据可取。SSR 的典型失败发生在屏幕边缘(反射逐渐淡出),并且无法反射任何相机自己看不到的东西。它用三维世界的一张二维"影子"来近似反射积分;那张影子漏掉什么,反射就跟着漏掉什么。

This blind spot is precisely what the new era removes. Hardware ray tracing (lesson 10's BVH, now in silicon) lets a real-time renderer shoot a modest number of true rays per pixel into the actual scene — off-screen geometry included — for shadows, reflections, and one or two bounces of GI. The catch returns us to §1: a modest ray count is noisy. The unlock is pairing it with two of our three verbs — amortize across frames (reuse and accumulate samples over time) and ML denoising (a neural network trained to reconstruct a clean image from a sparse, noisy one). Schemes like DDGI (dynamic diffuse GI) go further: keep the light-probe idea from §4 but update the probes every frame with a few traced rays each, so the baked indirect light of lightmaps becomes fully dynamic — lights and geometry can move. This is the direct bridge to lesson 15, where real-time and offline rendering, split back in lesson 01, finally begin to converge.

这个盲点,正是新纪元所消除的。硬件光线追踪(第 10 课的 BVH,如今做进了芯片)让实时渲染器能向真实场景——包括屏幕外的几何——每像素射出为数不多的真光线,用于阴影、反射、以及一两次反弹的 GI。而问题又把我们带回 §1:光线数不多就有噪声。破局之道是把它与我们三个动词中的两个配对——跨帧摊销(随时间复用并累积样本)与 ML 去噪(一个被训练来从稀疏、含噪的图重建出干净图的神经网络)。像 DDGI(动态漫反射 GI) 这样的方案更进一步:保留 §4 的光照探针思想,但每帧用少量追踪光线更新探针,于是光照贴图那份烘焙好的间接光变得完全动态——光源与几何都能移动。这正是通往第 15 课的桥,在那里,第 01 课分开的实时与离线渲染,终于开始融合。

Where this points next接下来指向何处

We now have the real-time toolbox as a coherent set rather than a bag of tricks: every technique buys one term of the rendering equation by precomputing, approximating, or amortizing it, and the physically-correct path tracer of lesson 11 is the ground truth they all approximate. But everything so far has assumed a frozen scene — one instant, held still while we shade it. Real content moves: characters walk, cameras pan, cloth billows. Lesson 13 leaves shading for animation and motion — how positions change over time, interpolation and easing, skeletal rigs and skinning, and why motion introduces its own sampling problem in time (the temporal cousin of the aliasing from lesson 06). The lighting we just learned to fake will now have to hold up while the whole scene is in motion.

现在我们把实时工具箱看成一套连贯的体系、而非一袋子技巧:每种技术都通过预计算近似摊销买下渲染方程的某一项,而第 11 课那个物理正确的路径追踪器,是它们共同逼近的“地面真值”。但到目前为止,一切都假设场景是冻结的——一个瞬间,被定住让我们着色。真实内容会动:角色行走、相机横摇、布料翻卷。第 13 课离开着色,转向动画与运动——位置如何随时间变化、插值与缓动、骨骼绑定与蒙皮,以及为什么运动会在时间上引入它自己的采样问题(第 06 课那种走样在时间上的表亲)。我们刚学会伪造的光照,现在得在整个场景运动时依然站得住脚。

Takeaway要点

Real-time rendering cannot converge a Monte Carlo integral per frame — path tracing's 1/√N noise needs far more rays than 16 ms allows. So it never solves the rendering equation; it buys each term cheaply by one of three verbs: precompute (bake), approximate (screen-space), amortize (temporal). Shadow maps precompute the light's visibility as a depth buffer from the light's view, then test d(x) ≤ zmap + bias; too little bias gives acne (self-shadowing from quantization), too much gives peter-panning (the shadow detaches) — PCF softens edges, cascades handle scale. SSAO approximates ambient occlusion from the depth buffer. Lightmaps bake indirect light onto static surfaces; light probes + spherical harmonics bake it into space so dynamic objects can read and interpolate it. SSR reflects by marching the depth buffer — but every screen-space method is blind to what is off-screen. Hardware ray tracing + ML denoising + DDGI lift that limit and begin converging real-time toward offline (lesson 15).

实时渲染没法逐帧让蒙特卡洛积分收敛——路径追踪的 1/√N 噪声所需的光线数远超 16 毫秒 所能允许。所以它从不解渲染方程;它用三个动词之一便宜地买下每一项:预计算(烘焙)、近似(屏幕空间)、摊销(时序)。阴影贴图把光源的可见性预计算成从光源视角看的深度缓冲,再测试 d(x) ≤ zmap + 偏置;偏置太小得痤疮(量化引起的自遮蔽),太大得彼得潘(阴影脱开)——PCF 柔化边缘,级联应对尺度。SSAO 从深度缓冲近似环境光遮蔽。光照贴图把间接光烘焙到静态表面上;光照探针 + 球谐把它烘焙进空间,好让动态物体读取并插值。SSR 通过步进深度缓冲来反射——但每种屏幕空间方法都对屏幕外的东西视而不见。硬件光线追踪 + ML 去噪 + DDGI 抬掉那道限制,开始让实时向离线收敛(第 15 课)。

Interview prompts面试题