UNIT 07渲染约 21 分钟 · 核心线
光追为什么真,又为什么慢?
上一课最后,左边的球怎么也映不出画面外的红方块,右边的一直映得出。红魔 v4 把机甲从 EEVEE 换到 Cycles,怪反光没了;可 Cycles 渲广州片,1080p 一帧要 16 秒 C20 C04。这一课讲右边那一半:它为什么真,又为什么慢。
07 · 1让光线自己去找
右边做的,正是上一课第一节那个「太慢」的办法:每个像素从相机射一条线,在整个场景里找它先碰到谁。碰到镜面,就按反射的方向拐个弯,接着在整个场景里找。画面外的红方块、被挡住的东西,只要在场景里,就找得到。这叫光线追踪(ray tracing)。
影子也这样算:线碰到地面上一点,再从这一点朝灯射一条线,中途被球挡住了,这一点就在影子里。游戏算影子是另一种省法:先从灯的位置拍一张深度图(阴影贴图),再拿画面上每一点和它比;光线追踪不拍图,直接问场景。
注意方向:真实世界里光是从灯出发、弹进眼睛的;算的时候反过来,从相机出发往回找。从灯出发的光绝大多数根本不进相机,算了白算。

- 来自材料
- 路径追踪的做法(pbr-book):在不反光的漫反射面上随机挑方向弹(朝正上方附近挑得多一些),每碰到一次还直接朝灯看一眼;金属按镜面反射,斜着看时反射更强(菲涅尔效应)。噪点曲线是页面现测的:右边地面上白框那一块,和另算的 8192 次采样参考图比。
- 本实验的设定
- 几何全是简单形状,金属是理想镜面,没有贴图;「反弹次数」只数漫反射之后又弹了几次;降噪是一个按法线和深度加权的简单滤波。
- 它能证明
- 路径追踪不受画面限制;噪点随采样数怎么降(07 · 3 讲规律,那里有实测数);暗场里一次反弹就看得见颜色渗透。随机数带种子:同样的设置,每次算出同一张图。
- 它不能证明
- Cycles、Lumen 的具体实现和速度;右上角的秒数只说明这台设备算这个小场景有多快。
07 · 2影子里的光从哪来?
看球的影子:它不是全黑的。灯照不到那里,可地面、白墙、红方块被照亮以后,又把光弹了过去。一个点能收到的光,来自它头顶的四面八方。要算清楚,就得把四面八方都问一遍:无穷多条线。
路径追踪的办法是随机:随便挑一个方向弹出去,一路跟着它。碰到灯或天空,就把那里的光带回来;碰到墙或地面,就按那个面的颜色打个折,再随机弹一次;弹到上限为止。这一条路径带回的光,就是这一次的答案,叫一次采样。那每个像素只随机追一条,不就行了?
每个像素只随机追一条路径(每像素 1 次采样)。右边的画面会是什么样?
- 整体暗一点,但干净
- 满屏黑白噪点
- 和 1024 次一样,只是快
满屏噪点。实验台 A-3 的 02 已经打开,把采样拨到 1 次看看。影子边上的一个像素,随机挑一个方向:碰巧碰到灯,它就亮;没碰到,就黑。旁边真实亮度一样的像素,各碰各的运气,一个亮一个黑,这就是噪点(noise)。不是分辨率不够,是一次随机的答案太不准。
一格里多采几次取平均,和上一课「一格里多射几条光」是同一个动作:那边为了把斜边抹平,这边为了把随机抹平(路径追踪每次采样还会在格子里挪一点位置,两件事一起做了)。下面把噪点单独拿出来:影子边上 64 个真实亮度一样的像素,每个随机采样几次取平均。
- 来自材料
- 用随机采样求平均的办法叫蒙特卡洛法,它的误差按 1/√N 缩小(pbr-book 第 2 章);一次非 0 即 1 的采样,散布(标准差:大家离平均值典型地差多远)是 √(p(1−p)),p 是真实亮度。
- 本实验的设定
- 每次采样只有「照到」和「没照到」两种结果,真实亮度 p 是照到的比例;64 个像素各用一串独立的、带种子的随机数。
- 它能证明
- 噪点就是随机采样的散布,采样变 4 倍、散布减半;实测点贴着理论线走。
- 它不能证明
- 真实场景里每个像素的 p 各不相同,一次采样也不止 0 和 1 两种值;规律一样,数不一样。
07 · 3多采几次,噪点降得多快?
采样多了取平均,答案就越来越接近真值,噪点越来越少。问题是:少得多快。
每像素采样从 64 次加到 256 次,花在采样上的那部分时间变成 4 倍。噪点(像素之间相差的程度)会少多少?
- 少四分之三:采样是 4 倍
- 少一半
- 几乎不变:64 次已经够了
少一半。这里说的噪点,就是每个像素离它真实值差多少的典型值(标准差),也就是误差。它跟着采样数的平方根往下走:误差 ∝ 1/√N(∝ 读作「成正比」)。采样变 4 倍,√4 = 2,噪点只减半。为什么是平方根:N 次随机的偏差有正有负,加在一起会互相抵掉一部分,合起来只按 √N 增长,而次数按 N 增长,平均下来就是 √N ÷ N = 1/√N。A-5 里一格一格拨,每往右两格(4 倍)点就往下掉一半;A-3 的噪点曲线是右边画面上现测的:在一台 Apple M4 上换 5 个种子,16 次与 256 次的噪点之比平均 3.93,64 次与 256 次之比 1.98。
猜「少四分之三」的,把噪点当成了和采样数成反比;那得 16 倍的采样。猜「几乎不变」的,看的是单张缩小后的样子:缩小时相邻像素被平均,噪点确实看不太出来,广州片 2K 下 128 和 256 次缩到同样大小,肉眼几乎分不出 C04;放大看、连续播放时,它才露出来。
一分多钟的导览:为什么噪点减半要四倍采样。它会带着上面两个实验台走一遍,你随时可以接手。
时间这边要分两段说。一帧里随采样变的那一段,采样变 4 倍它就变 4 倍;可每帧还有一段不随采样变的固定开销(把场景摆好交给显卡、画完取回来存文件……),采样开多少它都一样。所以整帧的时间不到 4 倍。第 8 课会在 4080 上把这两段量出来:广州片 2K 从 128 次采样加到 256 次,随采样变的那段翻了一倍,整帧却只从 27 秒到 46 秒,是 1.7 倍。
反过来也一样:少采一半,噪点只多四成(√2 ≈ 1.41)。MotionLib 的片子把每像素采样从 64 降到 32,两张图逐像素比,平均只差不到 1/255,只在湿地面的反光处多了一点颗粒;每帧省了约 40% 的时间,不是一半,省掉的只是随采样变的那一段 C04。噪点和时间不是一比一的买卖:前面便宜,越往后越贵。
07 · 4弹几次才够?
一条路径每在漫反射的表面上弹一次,带回的光就乘上那个表面反射的比例(实验台里的白墙设的是约八成;红方块的红光约八成,绿光和蓝光只剩几个百分点)。弹得越多,带回的越少,可每一次都要多找一次、多算一次。所以渲染器都给反弹次数设个上限:红魔的渲染脚本给 Cycles 设的是总共最多弹 4 次。
这里的次数,数的是「碰到漫反射面以后,还要不要接着弹」:0 次=地面只收灯直接照过来的光,然后进相机;1 次=地面还收从别的面弹过来一次的光。所以 0 次时,灯照不到的地方几乎全黑。
暗场,只开一盏灯;红方块拖进画面,放在球旁边。把反弹从 0 次调到 1 次,球的影子里会多出什么?
- 什么也不多:影子本来就是灯照不到的地方
- 变亮一点,还带点红
- 变亮一点,纯灰色
变亮一点,还带点红。0 次时影子几乎全黑;1 次后,被灯照亮的地面、白墙和红方块把光弹进了影子,红方块弹过去的光带着它的红色。A-3 的 03 已经打开,照题目摆好(环境选「暗场」,01 把方块拖进画面,或者点这里一步摆好),在 0 次和 1 次之间切换。页面实测:1 次反弹时,影子里的红绿比(红通道 ÷ 绿通道,大于 1 就偏红)是 2.50。光在场景里弹来弹去、互相照亮,叫全局光照;弹过去的光带着颜色,叫颜色渗透(color bleeding),是全局光照最好认的标志。
为什么要在暗场里看?亮天空下,偏蓝的天光从四面八方灌进影子,把红方块那一点贡献淹掉了:同样 1 次反弹,影子里的红绿比只有 0.94。
07 · 5降噪:用少量采样猜出干净的画面
既然噪点贵,能不能少采样,再把画面「洗」干净?这就是降噪(denoising):看相邻的像素是不是同一块表面(朝向一样、离相机一样远),是就互相借一点、取平均。广州片的渲染脚本在显卡上用 OptiX 降噪器,喂给它的除了带噪点的画面,还有每个像素处表面本身的底色(不受光照影响)和朝向。
它便宜,也有代价:它分不清噪点和细节。A-3 的 04 里,把采样拨到 16、打开降噪,影子的边和倒影的边都被抹软了。还有一件单看一帧看不出来的事:每一帧的随机采样不同,降噪猜出的斑块也不同,连起来播就会闪、会爬。
广州片的动态分镜就撞上了这两样:每像素只采 16 次、再降噪,放大看,陶塑人物的脸和胡须糊成了色块,一股塑料感;树叶和水面一动起来就在「爬」C03。采样少,降噪就得抹得狠,细节跟着噪点一起被抹掉;每帧抹法不同,连起来就爬。所以广州片正式开渲前,要把连续 4 帧放大两倍,一帧一帧地比,看降噪的斑块有没有在跳 C04。
07 · 6所以它慢在哪
把账连起来:随采样变的那一段 = 像素数 × 每像素采样数 × 每条路径弹几次,每一次都要在整个场景里找;再加上每帧那段固定开销,才是一帧。上一课说的「装箱」在这里出场:一条线先问一层层的箱子,只在碰到的箱子里试三角形,找一次比挨个试少得多。即便这样,广州片 1080p · 128 次采样,一帧也有 1920 × 803 × 128 ≈ 2 亿条路径,每条弹好几次,4080 上一帧 16 秒。游戏每个像素通常只着色一次(光栅化),反射、影子、间接光靠上一课那些「作弊」补。第 8 课把这笔账算到分钟。
带走一个问题
红魔 v4 的机甲换成 Cycles 后,半分辨率约 1 秒一帧 C20。假如最后 10 秒的暗场特写噪点太多,要再减一半,渲染时间大概变成几倍?如果不想多花时间,还有哪条路,它会让画面付出什么?
不看上面,写下这一课撞上的几堵墙,再展开对照
- 光栅化只知道画面里有的 → 让光线在整个场景里自己找(光线追踪),反射、影子都不受画面限制。
- 影子里的光来自四面八方 → 随机挑方向(路径追踪);每像素只追一条 → 满屏噪点。
- 采样翻倍噪点减半?→ 误差 ∝ 1/√N,减半要四倍采样。
- 反弹越多越好?→ 每弹一次带回的越少,代价不少;暗场里一次反弹就看得出颜色渗透。
- 降噪免费?→ 它分不清噪点和细节:抹细节,帧与帧之间的斑块会跳。