“这份讲座讲的是 EA SEED 的 ORCA,全称 Online Radiance Cache Acceleration。它要解的题很具体:游戏里的路径追踪如今靠 1 spp 加重降噪撑着,而硬件价格与供应让这条路走不远,于是必须往每像素不到一条路径的方向走。ORCA 的答案是每帧从零重建一张多尺度体素辐射缓存:只认真追踪 6.25% 的稀疏路径,其余像素通过「把路径的尾巴接到缓存上」推出来。讲者反复强调一条反直觉的设计纪律:它刻意不依赖时序历史,没有历史缓冲、没有延迟,代价全在每帧重建缓存上。”
- 先立动机:PT 在游戏里的现状是 1 spp 加重度降噪,而消费级显卡价格上行、供应紧张、老硬件留存更久,因此必须把高端功能往下搬,落到 << 1 spp。
- 看清借用关系:ORCA 的两条主线来自离线渲染,路径复用(VCM 与 MLT 的共同原则)与路径滤波;它在实时侧的具体形式是把辐射缓存当作路径的尾巴。
- 看懂关键取舍:SHARC 那类方案受限于覆盖与延迟,ORCA 不去预热缓存,而是把每个顶点同时存进所有 LOD,用有界的存储增长换覆盖率。
- 看懂工程实现:GPU 哈希表(两次哈希 + 线性探测)是底座,只取首次命中避免穿墙,聚合走 16.16 定点,路径预算靠全屏概率归一化收口。
- 带走实测与边界:Xbox Series X 上 PT 部分 14.2 ms 的基线被压到 5.0 ms,镜面修正后回到 7.26 ms;边界含许可、尚未发货的状态口径、两套平台计时不可混用等六条。
01开场:ORCA 要解的是「把高端功能往下搬」这件事
先把这份材料的身份交代清楚。它是 SIGGRAPH 2026《Advances in Real-Time Rendering in Games》课程里的一场讲座,讲者是 EA SEED 的 Jon Greenberg,题目是《Speeding up Path Tracing via ORCA(Online Radiance Cache Acceleration)》。名字里的三个词各自有分量:Online 指缓存每帧在线重建,Radiance Cache 指缓存里存的是辐亮度,Acceleration 指它服务于加速而非替代路径追踪。
要理解它为什么值得做,得先看清它假设的起点。第 2 页用最朴素的方式交代路径追踪:它被用在视觉特效与动画电影里,做法是模拟光在世界中的弹射,具体方式是追踪顶点之间的光线,把顶点连成路径。同一页附了一句最关键的性质:慢且噪,需要大量路径才能收敛。这句话是整个实时渲染领域几十年工作的起点,也是这份讲座要处理的那笔账。
第 3 页把镜头切到游戏,四行要点构成了这十年的现状:每像素一条采样(1 SPP)、重度依赖降噪、代价昂贵、瞄准高端 GPU、只在一小批游戏里发货。把五行连起来读,会看到一个自我强化的结构:因为贵,所以只敢在高端卡上做;因为只在高端卡上做,就没有动力去把成本压下来。ORCA 的定位,正是去打断这个循环。
讲者在进入技术之前先做了一次预期管理。第 6 页只写了三行:「但首先,先绕个路」「是时候讲个故事了」,标题是《Speeding up Real Time Path Tracing》。接下来的第 7 页到第 15 页都在讲他做过什么、碰过什么壁。把这段当成铺垫会更准确:它的作用在于交代这些取舍是从哪些具体的失败里长出来的,而非说清某个算法。对读者来说,这段路程值得跟着走,因为 ORCA 里几个最不寻常的设计(只取首次命中、不预热缓存、每帧重建)都能在这里找到原因。
02硬件现实:当摩尔定律不再兜底,采样率只能往下走
第 4 页是整份讲座的动机页,它的标题很直白:《摩尔定律被扔进了太阳》(Moore's Law was fired into the Sun)。页面上列了一串与算法无关的事实,但正是它们决定了算法该往哪走:GPU 在变快,可是消费级成本一直在涨;RTX 5090 现在的价格比发布时更高(当时 4000 美元以上,而官方建议价是 2000 美元);供应链紧张,硬件常常对消费者与供应商都不可得;消费者因此把旧硬件留得更久。
这四条事实合起来推出一个结论,讲者写得很直接:需要找到办法把高端功能往下缩放到更大范围的硬件上,而具体到路径追踪,这句话等价于每像素远低于一条采样(<< 1 SPP)。同一页还附了一句括号说明:G-buffer 仍然是光栅化的,且直接光照与路径其余部分的处理方式不同。这条限定很重要,它说明 ORCA 的工作位置是既有的「光栅化 G-buffer + 部分路径追踪」结构,并不打算把整条管线都交给路径追踪。
把这一章与上一章并排看,可以看出这份讲座的问题定义相当克制。它没有声称要做出比现有方案更好的画面,也没有要去替代降噪。它接受了一个给定的前提:要实现的功能已经确定,需要改变的是这些功能的成本曲线。这条定位会在后面反复出现,比如第 50 页那节讲集成的部分,讲者把「最大的单项非 PT 开销」直接点名为重建射线的追踪成本,而不是去讨论画面质量。
03从离线 PT 借来的两个概念:路径复用与路径滤波
第 5 页回答了一个自然的问题:离线渲染早就把收敛时间压下来了,它靠的是什么。讲者列了两组手段。第一组是重要性采样与缓存,代表性方法是顶点连接与合并(VCM)与梅特罗波利斯光传输(MLT);讲者对这两者的点评是:它们的关键原则都是路径复用,也就是想办法把更亮的路径分享出去。第二组是路径滤波,做法是把邻近相似片段上的顶点做平均,以此降低整条路径的噪声。
这一页的信息量在于它把「实时侧要做什么」提前定了性。如果离线渲染的收敛加速主要来自复用与滤波,那么在实时侧做同一件事,可用的自由度就只有两处:决定哪些路径值得真追,以及决定如何把它们的贡献扩散出去。这两个自由度恰好对应后面章节里的稀疏路径挑选(第 13 章)与体素聚合(第 11 章)。换句话说,ORCA 的骨架在这一页就已经给出了,后面四十多页填的是「在 GPU 上、每帧、无历史」这三个约束下如何把骨架立起来。
还有一个细节值得记下:讲者在这里没有给出任何性能承诺,也没有说实时侧能追平离线。这种克制在后文得到了呼应,第 36 页在讲缓存未命中时用的措辞是「通常非常低,低于 1% 到 2%」,而不是「不会发生」。整份材料对代价的登记方式是「把每一项开销逐条列出来」,这在第 14 章的账单里会看得很清楚。
04作者路线与 SEED 的定位:去碰团队不敢冒险的想法
第 7 页到第 9 页是一段自述,篇幅不大但解释了这套东西为什么长成这样。讲者 2000 年 7 月加入 Midway Games 芝加哥,第一部发货作品是 PS2、XBox 与 Gamecube 上的《NFL Blitz 20-02》;工作室 2010 年 4 月变成 Netherrealm,他负责《真人快打》与《不义联盟》系列的渲染,长期专注于高性能、高帧率的游戏。
第 8 页交代了 2020 年 5 月加入 EA-SEED 之后的位置。他用的描述很值得注意:图形研究员,工作是探索游戏团队无法承担风险的那些想法。同一页补了一句他为什么愿意待在这里:可以接触到大量不同的游戏与引擎技术。这两句合起来,界定了这份材料的性质:它的性质是一份允许失败的研究过程记录,而非产品交付记录。这也解释了为什么讲座里会花整页去讲一个「碰了壁的方案」,以及为什么最后一页的结论会是「尚未发货」。
第 9 页引出了后面一切的起点,Project RayMachine。它的底子是纹理空间着色(TSS)引擎,构建在 Halcyon 框架之上,而这个框架是 SEED 用来做 Pica Pica 演示的。页面列出的特征里有两类信息。一类是能力范围:通过 TSS 查找实现了大量光追相关特性,包括折射、次表面散射、反射与全局光照。另一类是工程风格:激进地 GPU 驱动,几乎所有事情都在 GPU 侧完成(成百上千个 EI 串联),连纹理空间的内存管理都用 JIT 分配器与 LRU 淘汰来做。直接光照用的是 ReSTIR 与 Surface Cones。
把第 9 页当作背景而不是正题会更准确,讲者在第 10 页也承认「RayMachine 里有很多有意思的东西,但大部分与本题无关」。不过其中有一条直接长出了 ORCA:那套引擎用 texel 当作光照与着色的缓存,于是缓存压力成为一个必须解决的问题。第 5 章要讲的 64 倍过着色,就从这里来。
把这段自述与第 2 章那条动机连起来看,会发现它交代的不只是履历。讲者强调自己长期在做高性能、高帧率的游戏,这句话解释了整份材料为什么把「守住预算」当成一条不可让步的纪律:在这类产品里,帧时间是硬约束,任何一项特性都必须先证明自己能塞进预算,才谈得上画质。也正因为这条纪律,ORCA 的收口动作是「把路径数装回预算里」(见第 13 章),而不是「把质量调到满意为止」。
还有一条隐含信息值得记下。SEED 的定位是研究团队,工作是碰那些产品团队无法承担风险的想法,这意味着这里的技术不需要立刻可发货,但必须能被别的团队接走。第 26 页那次从纹理空间到屏幕空间的转向,以及第 48 至 49 页讨论的两条实现路线(单体 RayGen 与多 dispatch),都是这条定位的直接体现:研究阶段先证明机制成立,落地时再适配目标引擎的结构。
05Project RayMachine 的教训:texel 当缓存,以及 64 倍过着色
第 10 页交代了 RayMachine 里与本题相关的那一部分。做法是 8×8 的 texel 补丁(与 Baker 2016 的对象空间光照思路相近),把 texel 用作光照与着色缓存;访问一个 texel 时,如果补丁尚未分配,就走分配、初始化、光照的即时(JIT)流程,然后把 texel 取回为辐亮度。注意这里的结构:缓存是按需生成的,用不到就不分配,而非预先烘好。
第 11 页给出了这套结构的失效方式,它是整份讲座里第一个具体的失败案例。讲者列了三条因果:间接光线通常非常不相干,它们会打到世界里彼此无关的位置;于是大量只含单个 texel 的补丁想要被分配,形成对 texel 缓存的下游压力;结果是大量的 64 倍过着色。他补了一句很关键的限定:mip 偏置有帮助,但没有解决问题。
这条教训的价值不在 64 这个数字,而在它指出的结构性错配:缓存的空间划分依据与查询的空间分布不一致。纹理空间的分辨率是按屏幕可见性定的,而间接光线的命中点分布取决于场景几何与材质;两者一旦不一致,按需求分配的缓存就会大量浪费。第 12 页顺着这个判断去找灵感,第 13 页干脆换了空间划分的依据,而这一整条推理线索的起点就是这一页。
06从屏幕空间反射与体素方案找灵感
第 12 页记录了一次横向找灵感的过程,对象是屏幕空间反射(SSR)。讲者选它的理由写得很清楚:SSR 是完全软件的,因此是一个好的参照点;它经常把上一帧当作辐亮度缓存;于是自然浮现一个想法:能不能把「被照亮颜色」的米普贴图做出来,看起来可能有很大收益。他给出的推断链条很紧凑:这样做相关性更好,因为 mip 近似于采样聚合,也就近似于锥追踪。紧接着是转折:但是,它无法处理粗米普里的发散与不连续,并且纹理空间着色在 mip 上有同样的问题。
第 13 页把这次探索推进到空间划分本身,这是整份讲座里方向发生转折的一页。讲者列了三个候选:在多个体积纹理里放有方向的体素(引 McLaren 2015 与 Crassin 2011);干脆从纹理空间转向世界空间;各向异性体素看起来有潜力。他同时给出两条约束,它们决定了最后选了什么:他们并不真的想把场景几何体素化,但用体素来存东西很直接。结论是把体积纹理换成 GPU 哈希表。
这三条放在一起读,能看出一次很务实的取舍。放弃纹理空间,是因为上一章那条错配(缓存划分依据与查询分布不一致)。保留体素,是因为体素只是一个「存储容器」,不需要先有体素化的几何体才能用。引入哈希表,是因为世界空间的坐标是稀疏的:场景里真正被光线打到的位置只占极小一部分,用稠密三维纹理存会很浪费,而哈希表可以按需分配。第 21 页会进一步说明哈希表带来的一个新自由度:它的空间尺度可以逐档变化,这为第 10 章那条「存进所有 LOD」的思路铺好了路。
07GPU 哈希表:两次哈希、线性探测、校验和
第 14 页给出的是这套系统的底层数据结构,引用的是 Binder 2018 的《Fast Path Space Filtering by Jittered Spatial Hashing》。做法有五个步骤,每一步都有明确目的:把位置量化到体素尺度;把法线量化到 N 位;把「细节层级」(也就是到相机的对数距离)量化;哈希两次,一次得出映射地址,一次得出校验和;用「映射地址取模映射表大小」作为基础地址,把校验和存在该地址上,用线性探测解决碰撞,并用校验和来验证。
第 15 页紧接着列出了这套结构的三个代价,篇幅很短但都是硬约束。第一,哈希做得越好,内存相关性越差;第二,哈希表越大,访存性能越差;第三,占用率无法预测,因此需要留很多余量。这三条都属于后面所有规模决策的前提,而不只是实现细节:既然占用率不可预测,表的容量就不能按「理想装载率」来配;既然表越大访存越差,就不能无节制地多加档位。
伪码里还有两处细节值得单独点出,它们决定了这套结构在真实负载下的行为。第一处是循环的边界 SEARCH_LENGTH:线性探测不是无限次的,探测长度有一个上限,超过就放弃。这样一来,最坏情况下的访存次数是有界的,代价是长探测链上的项会被判为未命中,也就是「本该命中却查不到」。第二处是那个 UNALLOCATED 哨兵值:空槽与校验和不匹配这两种情况被区分开来,前者说明这个位置从未被写过,可以直接插入,后者说明发生了真实碰撞,必须继续探测。这两处合起来解释了第 15 页那条「占用率无法预测就要留余量」的来由:表越满,探测链越长,判为未命中的概率越高,而余量正是用来压住探测长度的。
这也解释了一个看起来矛盾的现象。ORCA 要让每个顶点存进所有 LOD,直觉上表会变得很大;而哈希表的三个代价都在提醒表越大越糟。第 19 页给出的限定词因此非常重要:存储的增长是有界的。把这两页并读,可以看出设计者是带着明确预算在做多尺度这件事的,并不是「档位越多越好」。
08哈希表的两个优化:法线不进主哈希,以及粗粒度空间分箱
第 16 页给出了两条优化,都来自 Binder 2021 的《Massively Parallel Path Space Filtering》,而且两条针对的是同一件事:缓存的内存布局与查询的空间局部性不匹配。第一条优化是不要在初次哈希里用上法线与体素尺寸,但要把它们放进校验和哈希。讲者解释道,这样做的效果是邻近的东西更可能出现在缓存里彼此靠近的位置。
第二条优化是粗粒度的空间分箱:把哈希内存切成 M 个箱(例如 17 个),先用「体素尺寸乘 N」(例如 N 取 32)把位置量化,把这个位置哈希到一个箱里,然后再在箱内做标准哈希加校验和。讲者给它的评价是松散地把邻近的东西聚在一起。
把这两条优化放在第 15 页那三条代价之后看,会发现它们并没有把代价消掉,只是把代价的分布改了。哈希表依然无法预测占用率,依然在变大时访存变差;优化做的是让「命中」这件事尽量发生在已经加载进缓存的那些行里。这也是一份诚实的工程记录该有的样子:它不承诺消除物理限制,只承诺把访问模式调整到与限制相容。
09体素辐射缓存:只存首次命中,以及为什么不能直接用 SHARC
第 17 页把前面几章积木搭成了第一个完整形态。步骤是:从表面追踪间接光线;把命中的位置与命中的方向做哈希;把命中处的辐亮度存进哈希表,也就是一个体素辐射缓存。紧接着讲者自己提出两个疑问并给了处理:基于相机的细节层级对任意光线并不理想,所以改为逐段追踪路径并查找顶点:查到就用该辐亮度并终止这条路径,查不到就继续追踪。最后是一条结构性的说明:因为缺少临界区,需要两个阶段,建缓存与读缓存。
两阶段这条约束还带来一个容易被忽略的后果:缓存无法在一帧内部被部分复用。既然读阶段开始时缓存必须已经建完,那么任何「先用一部分缓存、边建边用」的想法都不成立,整条管线因此被切成固定的几段,段与段之间只能用显式的缓冲交接。这也解释了为什么后面第 14 章那份账单会逐个列出「扫描与分配路径」「识别稀疏命中体素」「把顶点颜色存进体素」「清理体素计数」这些看起来零碎的步骤:它们各自对应一次段间交接,而每一次交接都要单独计入成本。
讲者在页末加了一句补充,指向英伟达的 SHARC,并评价它「用得有点激进」。第 18 页就是把这句话展开,回答「为什么不能直接用 SHARC」。他给出的原因有两条。第一条关于体素尺寸的取值困境:体素的尺寸要选得让屏幕面积覆盖率相对相机距离保持一致,而太小会导致体素太多,太大会导致视觉质量下降,那个「刚刚好」的区间非常依赖内容。第二条关于覆盖所需的时间:需要大量路径来填充,覆盖整个场景要很多帧,于是最好的情况是严重的照明延迟,最坏的情况是只能排除动态物体。
这一章实际上是整份讲座的分水岭。前面几章在讲「怎么做缓存」,这一章在讲「既有缓存方案的代价是什么」,而下一章给出的答案会同时回应两条障碍:用多尺度结构解决覆盖,用每帧重建解决延迟。值得注意的是,多尺度这个念头并非凭空而来,它正是第 12 页那条「mip 近似采样聚合」的等式在体素空间里的落地。
10ORCA 的关键取舍:不去预热缓存,而是把顶点存进所有 LOD
第 19 页是整份讲座里最关键的一页,它先用两句话否掉一个直觉方案,再给出真正的思路。讲者先问:能不能把缓存预热?紧接着自己回答:朝着静态光照去做并不理想。这句否定很短,但它定下了整个方案的立场。预热等价于把上一帧或更早的照明当作先验,而这正是第 18 页那条「照明延迟」的来源;既然目标是保住动态性,就不能靠时间累积去填缓存。
然后他重新定义了问题:最明显的问题是覆盖不足,于是回想米普贴图的经验,得到那条关键的设计决定:不要只在某一档细节层级上存每个顶点,而是把它存进所有 LOD。讲者对代价的表述同样关键:需要更多存储,但增长是有界的。
第 20 页把多档结构落到查询与聚合两个动作上。把命中点在各个体素尺度上分别做哈希,于是多个顶点可能哈希到同一个体素,那么就把辐亮度聚合起来。查询的方式也随之确定:要找某个辐亮度时,从最小的体素开始,逐档扫到更大的尺度。这一页还隐含了一个重要的事实:因为每个顶点都被写进了所有档,所以小体素查不到时,较大体素里几乎必然有它的某个「粗化版本」,覆盖率才得以保证。
把这一页与第 18 页的问题清单对照,可以看出两条障碍各自被什么解掉。覆盖那条:多档冗余让少量路径就能撑起较粗尺度的缓存,因此不需要很多帧。延迟那条:因为不预热、跨帧不存活,所以不存在「需要时间才能收敛」的过程。剩下的问题就交给第 11 章的降采样与第 13 章的采样预算处理。
11体素降采样:三步循环与 16.16 定点聚合
第 21 页到第 25 页是同一张页面的五连拍,用来逐帧动画式地展示降采样过程,页面上始终挂着同一个参数:尺寸范围从 8−A 到 8B,例如 A 取 −2、B 取 3。这个范围就是多档结构的档数定义,也是本篇公式 (3) 的来源。
降采样分三步,讲者把每一步的要点都列了出来。第一步是识别并分配所有尺寸上的所有体素:位置只用路径的首次命中;把每个体素关联到比它粗一档的「父」体素;体素辐亮度初始化为零;并建立最小体素的列表。第二步是用路径辐亮度填充列表里的体素:辐亮度要做曝光处理以免被截断,然后建立下一档更大体素的新列表。第三步是从新建立的列表开始,把辐亮度聚合进子体素,使用 16.16 定点数,再建立下一档更大体素的新列表,然后重复;全部完成后把辐亮度重新打包。
第 26 页结束这一段并做出转向,理由写得很干净:这套系统里没有任何东西是纹理空间着色专有的,于是他们问,为什么不把它搬到更标准的渲染引擎里。结论是一次转向(Pivot):把它整合进跑在屏幕空间里的标准间接光路径追踪器。这一句是整个方案的分界线:至此 ORCA 与 Project RayMachine 彻底解耦,成为一项可以搬进别的引擎的技术。
12两类像素:稀疏路径与重建像素
第 27 页是整份讲座的结构图,标题是《先有鸡还是先有蛋》,而答案是把像素分成两类。讲者先交代前提:这个层级结构意味着空间覆盖很广,少量种子路径就能得到很高的命中率。然后给出分类:把像素分成稀疏与重建两类。稀疏像素跑真正的路径,稀疏地分布在画面上;重建像素则借用离线 VCM 与 MLT 的想法,把辐射缓存当作路径的尾巴:把路径连接到輻射缓存上,用缓存里的信息推断辐亮度。
第 28 页到第 35 页是一段连续的管线演示,每页一句话,把整套流程按顺序走了一遍,可以当成执行顺序来读。第 28 页:追踪稀疏路径,找到它们的首次命中与輻亮度。第 29 页:建立不同尺寸体素的层级。第 30 页:为重建像素从 G-buffer 追踪射线,找到这些重建射线的首次命中。第 31 页到第 34 页是同一句话重复四遍,用来配合动画演示查找过程:在缓存里查找重建命中点,从最小体素开始,逐档尝试更大的尺寸直到命中。第 35 页收口:用找到的体素輻亮度近似重建路径。
第 36 页指出一个容易被忽略的性质,并顺手处理了它带来的问题:这里不需要任何时序状态或历史,每一帧都从零重建。紧跟着讲者自己问:那如果缓存未命中呢。回答分两句:通常命中率非常低,未命中率低于百分之一到百分之二;可以标记这些失败的路径重新追踪,也可以让它们「死掉」,交给降噪器去补。第 37 页补上一条设计约束的原因:为什么只取首次命中:为了减少聚合误差,避免穿过「薄墙」,并提到可以考虑把「到命中点的对数距离」也加进哈希。
把这两页连起来看,能看到一套完整的错误处理策略:主要的错误来源(未命中)被量化到 1% 到 2% 以下并交给降噪器,次要的错误来源(跨尺度聚合导致穿墙)通过「只取首次命中」从结构上排除。这两条一起解释了为什么 ORCA 敢承诺每帧重建:它没有把误差藏进时间里,而是把误差限在每帧的少数像素上。
把未命中的处置方式与整篇的立场对照,可以看出它并不是一个临时兜底。讲者给的两条路里,重新追踪是「把正确的值补出来」,交给降噪器则是「承认这个像素这帧拿不到可信值」。后一条之所以可行,是因为未命中率被压到了百分之一到二以下,误差的量级落在降噪器本来就擅长处理的范围内。更关键的是这条选择背后的次序:先保证时间维度上没有误差被搬运,再用空间维度的降噪去吸收残余。这与第 19 页否掉预热是同一条立场。
13采样率、镜面修正与预算再平衡
第 38 页讲的是「挑哪些像素去跑真路径」,也就是稀疏像素的选法。做法用了 FAST 噪声(引 Donnelly 2014):用它取一个 16 位无符号归一化的随机数,以概率作为阈值来检验这个随机数。讲者紧接着给出代价提醒:路径更少会更快,但重建不是免费的;并指出一个关键现象:当路径率降到很低时,均匀采样会失效,页面上给了具体的例子:1/16 的路径率等于 6.25% 的实际路径,随之而来的是高光可能被漏掉、体素伪影可能冒出来。
第 40 页给出了镜面修正的做法,它的思路是把「该像素本来会选哪个叶瓣」当成重要性线索。步骤是:先考虑需要更多路径的高光细节,去看该像素的 BSDF 采样本来会选中哪个叶瓣;如果是漫反射,就用原来的阈值(1/16);如果是镜面,就套一组启发式,按启发式的重要性评分额外采样路径,并把镜面 BSDF 的选择概率作为一个好的起点。
第 41 页给出了这组启发式的实现,值得逐行读,因为它把「重要性」具体化成了一个可算的量:先取起始吞吐的亮度上限(betaLum),再算菲涅耳因子(1 减去该亮度的四次方),然后把稀疏化评分定义为「漫反射时为 1,否则取最小粗糙度与菲涅耳因子中的较小者」;接着用粗糙度上限把它映射成一个调整量,再用 lerp 在基础阈值与「镜面稀疏化允许的最高比率」之间插值,得到最终阈值。讲者在第 42 页给出了这条修正的代价:同一镜头下稀疏路径从 82,944 涨到约 162,000 条(随视角变化),几乎是原来两倍;第 43 页又给了一个镜面更多的视角:约 330,000 条。
第 44 页处理了这条修正带来的新问题,它把「预算」这个词的含义讲清楚了。讲者先点出症状:路径数现在是视角相关的,而且是无界的。然后是那句态度鲜明的话:预算的意义就在于守住它,所以要重新平衡评分。做法是一套归一化三步:先为全屏计算启发式,得到逐像素概率;把全帧的逐像素概率加起来,除以预算;把这个结果作为逐像素概率的缩放因子。讲者给的效果评价是重新分配了路径,松散地匹配预算。
第 45 页给出了归一化之后的结果,并附了三个观察:平衡把路径数带回约 84,000 条;内存占用同样保持稳定;以及一条待办:可以考虑丢掉一部分重建射线来降低它的开销(引 Scherzin 2026)。把这三个观察与前面两页并读,可以看到这套设计的完整闭环:启发式决定路径该花在哪,归一化保证总量不越界,而重建射线成为下一步要压的成本项。
归一化还有一个容易被低估的成本,讲者在第 44 页的步骤里其实已经写出来了:第一步是为全屏计算启发式。这意味着第 41 页那组启发式不能只对稀疏像素算,而要对画面上每个像素都算一遍,否则无法得到全帧的概率之和。换句话说,挑选路径的成本是全网格的,而只有被选中的像素才真的产生路径追踪开销。这一条在工程上很重要:当路径率继续往下压时,挑选本身的开销占比会上升,它构成了一条不能靠降低采样率绕过的下限。
最后这一条正是第 14 章里那个「最大单项非 PT 开销」的伏笔。
14实测、集成与边界
第 46 页给出了一个方向性的备选方案,它的价值在于把「无历史」这条纪律的边界探了一次。讲者先把现状复述清楚:到目前为此,没有任何东西依赖时序感知,帧间不携带任何东西,没有历史缓冲,结果也没有延迟。然后是转折:但是,我们可以追踪逐像素的帧间方差,用它建立逐像素评分,并在方差置信度低时回退到启发式。他还给出了两边的代价对比:方差求值更贵,但能发现启发式发现不了的特征,因为启发式没有考虑法线变化与深度不连续。
第 48 页与第 49 页讨论「这套系统实用吗」,答案分两条实现路线。第一条是递归式,沿用早期 DXR 的想法;第二条是更快的实现,拆成许多个 dispatch,好处是对主机更友好。讲者举了两个参照实现:Codemasters 的 F1 2025 代码库,其做法是在一个庞大复杂的 RayGen 着色器里跑完整的间接光 PT 循环,并在 GDC 2026 的讲座里有详细描述;以及一个未具名的多游戏引擎,它拆成许多 dispatch,作为「参考」实现。
第 51 页给出了 F1 在 PS5 Pro 上的分项账单。页面把「扫描与分配路径」「SHARC 更新与解析」「原始 PT」「追踪重建射线」「识别稀疏命中体素」「把顶点颜色存进体素」「降采样与清理」「识别重建射线」「应用重建」「为降噪做准备」逐项列出,最后是 PT 总时与整帧总时;原始 PT 一项标注为 36.29,整帧总时标注为 52.45 ms,而 PT 总时一栏在不同配置下分别是 38.42、21.75 ms 与 14.31 ms。
第 52 页列出了三类潜在问题与各自的处置,是一页典型的工程清单。萤火虫污染缓存:在存储前钳制辐亮度(讲者自己加了个「booo」表示这是无奈之举),或在降采样时把概率因子进权重(w 取 p,颜色乘 w)。缓存仍然太稀疏:重光照上一帧路径的辐亮度(或只重光照第一个顶点),在存储时注入并乘以 1/(1+历史长度)。反射里能看到体素:用抖动查找来缓解(引 Binder 2018)。
第 53 页补上第四类问题,也就是体素伪影本身。更锐利的镜面高光在收敛与降噪之后可能把体素的形状显出来,处置方式是抖动,但讲者同时提醒抖动可能触发更多缓存未命中;做法是为每个像素计算抖动以获得最好效果,并把哈希量化的方式改掉:在查找时(但不包括插入时)用「位置除以尺度后取整,再加抖动偏移」来代替原来的量化。这一页有两个版本(抖动关、抖动开)的对照图,讲者用它们来说明伪影是否可见。
边界一:许可是非 CC,页脚逐页标注保留所有权利
本篇素材来自 SIGGRAPH 课程讲义,每一页页脚都标注了保留所有权利,讲义未给出任何 CC 声明。按分区既有的非 CC 口径处理:逐件登记来源、逐图署名(源 slide 号、版权、未修改声明)、保留残留风险标注。本篇 21 件图元全部按此办理,未裁切、未加中文标注、未重绘。需要说明的是,本篇走的是整页渲染而非原生图流,原因是该讲义的文字与矢量图示都在页内(见台账的载体判定),因此图注里的「未修改」指的是未对渲染结果做任何后期处理。
边界二:ORCA 尚未发货,所有读数都是集成中的阶段性数据
第 50 页明写这是一项进行中的工作,ORCA 尚未发货。这意味着本篇引用的全部性能读数都取自集成过程中,而不是线上产品的实测。进一步地,讲者提到体素降采样在集成阶段被按整合结论重新设计过,说明研究期的管线与引擎里的版本并不完全相同,因此第 39 至 45 页那套账单与第 51 页的 F1 账单之间并不构成「同一实现的两种平台实测」的关系。
边界三:两套平台的计时不可混用
本篇登记了两组计时,它们的口径不同,不能相加也不能对比。第一组取自 XBox Series X,镜头分辨率 1536×864,且只含 PT 部分,不含整帧;这一组的基线是 14.2 ms,五个方案读数分别是 5.035、6.488、10.639 与 7.26 ms。第二组取自 PS5 Pro,是 F1 的分项账单,页面同时给出原始 PT、PT 总时与整帧总时。两组之间没有共同的归一化基准,讲义也没有做跨平台换算,因此本篇在正文里始终分栏陈述。
边界四:PS5 Pro 分项表的多列差异,讲义未逐列说明
第 51 页在同一张表里并排给出了多组配置的读数,页面上可辨认的 PT 总时包括 38.42、21.75 ms 与 14.31 ms,整帧总时标注为 52.45 ms。讲义的文字备注没有逐列解释各列对应哪种配置(是否启用重建、是否启用 SHARC、是否只算 PT 等)。本篇因此只引用其中标注最清楚的两个端点,并如实登记未说明的部分,不做推测性归因。
边界五:预算的匹配是「松散」的,不是硬上限
第 44 页的归一化流程给出的效果评价是松散地匹配预算,第 45 页的实测也是「回到约 84,000 条」而不是精确的某个数。原因在于稀疏像素是通过概率阈值抽样选出来的:缩放因子只保证期望路径数落在预算附近,实际条数会围绕它波动。这一点在工程上很重要,因为哈希表容量与内存占用必须按波动后的上限来配,而不是按期望值配。
边界六:与 SHARC 并存是 F1 的选择,不是 ORCA 的必要条件
第 59 页的问答里,讲者对「F1 为什么同时用 SHARC 与 ORCA」给出的说明是:可能 ORCA 就够了,但两者在 F1 里解决的是不同问题。它们的分工是能量尺度上的:SHARC 补回被砍掉的路径尾巴所损失的能量,做 10 次弹射并聚合长期结果;ORCA 做 4 次弹射、结果是即时的。讲者甚至提到,理论上有可能干脆去掉 SHARC、把它的 10 次弹射并进 ORCA 的弹射上限,但这一点他们还没有试过。因此本篇把「两套缓存并存」登记为集成方的选择,而不是 ORCA 的组成部分。
wide。第 47 页的方差评分码列表按「代码不取图、正文说明」处理,如实登记在台账的未取图页清单里。
口径登记:本篇三处公式均由讲义参数推出并已在正文标注;第 51 页多列配置未被讲义逐列说明,按边界四登记;两组平台计时按边界三分栏处理;ORCA 尚未发货的状态口径见边界二。批次归属见 paper-pipeline/inbox/2026-siggraph-batch-01.job.md。
- 把一份研究过程记录读成一条可复核的推理链:纹理空间的缓存划分与间接光线的落点分布不一致(64 倍过着色是它的症状),因此换到世界空间;世界空间稀疏,于是用 GPU 哈希表按需分配。
- 把那条最反直觉的设计纪律讲清:ORCA 刻意不预热缓存,因为朝静态光照收敛会牺牲动态性;它改用把每个顶点存进所有 LOD 来买覆盖率,代价是有界的存储增长。
- 把两处机制接线点出来:缓存即路径的尾巴(查到就终止路径)与稀疏 / 重建两类像素的二分,前者来自离线 VCM 与 MLT 的路径复用,后者决定整条管线的成本结构。
- 把三处公式补成可复算的形式:稀疏路径数 1536×864×(1/16) = 82,944、预算再平衡的缩放因子、以及多尺度档数 A+B+1 = 6,三处均已标注为本篇推算并与讲义读数互验。
- 六条边界单列成节:非 CC 逐件登记、尚未发货的状态口径、两套平台计时不可混用、PS5 Pro 分项表未逐列说明、预算匹配是「松散」而非硬上限、SHARC 并存是集成方选择而非 ORCA 必需。