Unity-APV体积探针
APV(Adaptive Probe Volumes,自适应探针体积,常简称”体积探针”)是 Unity 6 正式化的烘焙 GI 方案,URP 和 HDRP 都支持,这篇以 URP 为主。它是 Unity 放弃 Enlighten 之后选定的”烘焙 + 探针”路线的终点形态:把 lightmap 和 Light Probe 两套东西收敛成一套——按场景几何密度自动放置探针,烘焙成球谐系数存进 3D 纹理,动态、静态物体统一采样,还支持大世界的按块流送。
这篇先讲怎么用,再拆内部原理(为什么它叫”自适应”、数据存成什么样、烘焙做了什么),最后简短对比 UE4 的 Volumetric Lightmap。它接在 Base-渲染方案演进总览 之后——APV 就是那条”烘焙漫反射 IBL”主线的终点。
1. APV 是什么:定位
一句话:APV 是”自动放置的烘焙体积探针”。
对比旧方案,它的三个核心不同:
| Light Probe Groups | APV | |
|---|---|---|
| 探针位置 | 美术手动摆 | 按几何密度自动生成,无需手摆 |
| 密度 | 均匀 / 手动 | 多级自适应:复杂区域密、空旷区域疏 |
| 采样对象 | 动态物体(静态走 lightmap) | 动态 + 静态统一,逐像素采样 |
| 存储 | 内存里的探针数组 | 3D 纹理图集 + 按块流送 |
| 大世界 | 不适合 | 面向开放世界设计 |
逐像素采样是关键差异:旧方案按 GameObject 取探针插值,相邻物体之间会接光不一致、出现色块;APV 逐像素插值,物体间过渡更平滑。
2. 怎么用(URP 流程)
2.1 开启(在 URP Asset 里,不是 HDRP 的 Frame Settings)
- Project Settings > Quality > Rendering,双击激活的 URP Asset 在 Inspector 里打开。
- Lighting > Light Probe Lighting > Light Probe System:设为 Adaptive Probe Volumes。
- 同一面板可勾 Enable Lighting Scenarios(多套光照)和 Enable Lighting Scenario Blending(运行时混合)。
- 大世界流送:Lighting > Enable Disk Streaming(磁盘 → CPU),需要时再开 Enable GPU Streaming(CPU → GPU,依赖 Disk Streaming)。
URP 没有 HDRP 那套 Camera 级 Frame Settings,APV 一开就对整个管线生效,Lit 材质自动采样。
2.2 放体积
GameObject > Light > Adaptive Probe Volume,Inspector 里选 Mode:
- Global:自动扩到整个场景 / Baking Set 里所有参与烘焙的物体,最常用。
- Scene:按单个场景的范围。
- Local:手动拉一个盒子,只覆盖指定区域。
配合用的小体积:Probe Adjustment Volume(局部把探针标为无效)、带 Probe Volumes Options Override 的 Volume(改某区域的运行时采样方式,不影响烘焙)。
2.3 让物体参与 / 收光
- 光源:Mode 设为 Mixed 或 Baked,才会进 APV 烘焙。
- 静态物体:Mesh Renderer > Lighting 勾 Contribute Global Illumination。
- 收光的物体:Mesh Renderer 的 Receive Global Illumination 设为 Light Probes(动态物体本来就收探针光)。
2.4 烘焙
Window > Rendering > Lighting > Scene页签 > Mixed Lighting,勾选 Baked Global Illumination。- 切到 Adaptive Probe Volumes 页签:
| 分组 | 关键参数 | 作用 |
|---|---|---|
| Baking | Baking Mode(Single Scene / Baking Set)、Bake 开关 | 单场景或多场景进一个烘焙集 |
| Probe Placement | Min / Max Probe Spacing、Probe Positions、Renderer Filter | 探针间距范围;Recalculate 重算探针位置;Layer Mask / Min Renderer Size 过滤参与物体 |
| Sky Occlusion | Samples、Bounces、Albedo Override、Sky Direction | 天空可见性采样 |
| Probe Invalidity | Dilation(Search Radius / Validity Threshold / Iterations)、Virtual Offset | 修复无效探针、漏光 |
| Disk Usage | Scenario Size、Baking Set Size | 烘焙数据磁盘占用 |
- 点 Generate Lighting(或从下拉里选 Bake Probe Volumes,只烘焙 APV 不烘焙 lightmap)。场景里一个 APV 都没有时,Unity 会提示自动建一个。
2.5 调优与调试
Window > Analysis > Rendering Debugger > Probe Volume 页签可以可视化:Probes / Bricks / Cells 的布局、Validity(哪些探针无效,比如长在几何里)、SH(逐探针看光照)、Sky Occlusion。不需要真烘焙,光看摆放就能预览自适应密度,这是调整”哪密哪疏”的主要手段。
3. 内部原理
3.1 整体架构
APV 的数据由几个组件分工:
| 组件 | 职责 |
|---|---|
ProbeReferenceVolume |
单例,管理 CPU/GPU 数据、流送、绑定、生命周期 |
ProbeBrickPool |
管理 GPU 上的 3D 纹理图集,存探针数据(L0/L1/L2/Validity) |
ProbeBrickIndex |
层级空间查找:世界坐标 → 定位到哪个 brick |
ProbeVolumeBakingSet |
烘焙集,ScriptableObject,含配置、场景列表、流送资源引用 |
烘焙产物落成 .apv Streaming Asset,运行时按块从磁盘读,再上传 GPU 纹理。
3.2 自适应密度:brick 与 subdivision 层级
“自适应”是 APV 名字的核心,也是它和旧探针的本质区别。
- 整个体积被切成 3D 的 brick(砖块),每块 brick 是 4×4×4 = 64 个探针。
- brick 有多级 subdivision:Level 0 最疏,越靠近几何,subdivision 越高、探针间距越小。默认探针间距档位是 1 / 3 / 9 / 27 米(对应 Min/Max Probe Spacing 范围),烘焙时按”到几何表面的距离”自动选档。
- 结果就是:墙角、走廊、家具堆里探针密,空旷大厅探针稀——同样的体积,数据量却被几何复杂度”调配”了。
可以针对某个 Adaptive Probe Volume 做 Subdivision Override / Override Probe Spacing,配合 Renderer Filter(Layer Mask、Min Renderer Size)决定哪些几何参与密度计算;Fill Empty Spaces 可以让空旷区域也补上(按 Max Probe Spacing)的砖块。
3.3 存储:3D 纹理图集 + chunk 流送
探针数据最终不是存在内存数组里,而是打包进 GPU 3D 纹理图集:
ProbeBrickPool管理几张 3D 纹理,按波段分开存(L0_L1Rx、L1G_L1Ry、Validity等)。- 空间查找靠 间接索引纹理(
ProbeBrickIndex):运行时用世界坐标查索引纹理 → 拿到 brick 在纹理图集里的位置 → 采样。
流送是这套存储方式带出来的能力:
- chunk(cell)= 一组 brick,是流送的最小单位,大小约等于最大的 brick。
- 运行时相机移动时,只加载视锥范围内的 chunk:
DiskStreamingRequest用AsyncReadManager从磁盘读.apv数据 → 进双缓冲的CellStreamingScratchBuffer暂存 → 由ProbeVolumeUploadData.compute上传到 GPU 纹理。 - 流送要在 URP Asset 里开启(见 2.1),这就是它能支撑开放世界的原因——不需要整张地图的探针数据常驻显存。
3.4 每个探针存什么
| 数据 | 内容 | 用途 |
|---|---|---|
| SH 系数 | L0/L1(4 系数)或 L2(9 系数) | 漫反射辐照度:运行时按法线算间接漫反射 |
| Validity | 探针有效性(长在几何内部的探针无效) | 插值时当权重,避免把光照”穿过墙”传过去 |
| Sky Occlusion | 天空可见性 + 主遮蔽方向 | 让探针知道天光被什么挡了,支持运行时更新 |
注意:APV 本身存的是漫反射。镜面反射在 URP 里仍然走 Reflection Probe——APV 不替代它。
3.5 烘焙管线:五个 pass
烘焙由 ProbeGIBaking 驱动,底层用统一的光追后端(DXR / Vulkan),过程是:
- Subdivision & Placement:按几何距离生成层级探针位置——这就是 3.2 的自适应密度。
- Light Transport:光线传输,算出每个探针的 SH 系数(L0/L1/L2)。
- Virtual Offset:光追把”长在几何内部”的探针推到表面之外——否则这些探针会得到黑的辐照度,造成黑斑。
- Sky Occlusion:算每个探针的天空可见性与遮蔽方向。
- Dilation:把有效区域的数据向无效区域扩散填充,填补暗斑和漏光。
3.6 运行时怎么采样
逐像素进行:
1 | |
因为采样是”三线性 + validity 权重”,所以 APV 有天然的平滑过渡,也比旧探针更不容易漏光。
3.7 Lighting Scenarios(多套光照)
先要在 URP Asset 里开启 Enable Lighting Scenarios(要混合再开 Blending),面板才会出现 Scenarios 分组。APV 把烘焙数据拆成:
- 共享数据:subdivision 和探针位置(所有光照方案共用)。
- Scenario 数据:探针的光照值(每套光照一份)。
白天/夜晚多套 scenario 可以在运行时切换甚至混合。前提是烘焙时选 Don't Recalculate(探针位置必须保持稳定),否则场景间无法共享/混合。一个注意点:scenario 只影响烘焙间接漫反射,不影响 baked Reflection Probe——光照变化大时,镜面要配 Realtime Reflection Probe。
4. 常见坑
- 漏光 / 暗斑:先看 Rendering Debugger 的 Validity,再调 Dilation(Search Radius、Validity Threshold、Iterations)和 Virtual Offset(Geometry Bias、Ray Origin Bias)。
- 探针不能手动摆:位置完全自动生成,只能靠 Subdivision Override、Renderer Filter 和体积来调——这是设计如此,不是 bug。
- 加性场景加载下 APV 不自动注册:社区常见问题,运行时需要手动
ProbeReferenceVolume.instance.SetActiveBakingSet(...)并指定lightingScenario。 - 流送没生效:先确认 URP Asset 里开了 Disk Streaming(见 2.1);GPU Streaming 依赖 Disk Streaming,别只开后者。
- 间距权衡:Min Probe Spacing 太小 → 内存爆;Max 太大 → 光照变化快的地方(阴影边界、门窗附近)糊。先小范围烘焙看摆放再定。
- Light Probe Group 不能转 APV:老项目迁移只能重新放置。
5. 与 UE4 Volumetric Lightmap 的区别
UE 的 Volumetric Lightmap(VLM)是 UE4 时代取代 Indirect Lighting Cache 的方案,给动态物体和体积雾补间接光。它和 APV 是同一个思路(烘焙体积探针 + 间接索引 + 3D 纹理存储),但工程取舍不同:
| 维度 | Unity APV | UE4/5 Volumetric Lightmap |
|---|---|---|
| 定位 | Unity 6 烘焙 GI 终点,统一 lightmap + Light Probes | 补动态物体的间接光,静态仍走 lightmap |
| 密度结构 | 多级 subdivision 层级,按几何距离自动选(默认 1/3/9/27m) | 粗格 + detail cell:靠近静态几何的 brick 内细分更密的 cell |
| 存储 | 3D 纹理图集(多张 array),chunk 级流送 | 单个体积纹理 + 间接索引,数据全量驻留 |
| 烘焙 | GPU 光追(DXR/Vulkan),五段管线,支持迭代 / scenario | Lightmass(CPU/分布式),Importance Volume 控制范围 |
| 探针数据 | SH L2(9 系数)+ Validity + Sky Occlusion | SH(9 系数)+ SkyBentNormal + DirectionalLightShadowing |
| 镜面 | 走 URP / HDRP 的 Reflection Probe | 走 Reflection Capture |
| 流送 | 按 chunk 视锥加载,面向开放世界 | 流送弱:level streaming 要求所有 level 一起 build |
| 内存 | 按需流送,可控 | 官方例子:Paragon 从 5MB 涨到 30MB;空区域不可回收 |
几个值得注意的差异点:
- “自适应”的实现方式不同。APV 用真正的多级 subdivision(每个层级独立选择间距,1/3/9/27m 档位),VLM 是”粗格 + 几何附近细分 detail cell”,密度档位靠
Detail Cell Size和Maximum Brick Memory上限约束。VLM 的密度控制更粗。 - 存储与流送是最大分野。VLM 官方明确是”单个体积纹理”,空空间不能回收、多个重叠体积不能混合,内存压力大;APV 从设计上就是”图集 + chunk 流送”,是奔着开放世界去的。
- 镜面一致:两者探针里存的都是漫反射 SH,镜面都靠反射捕获/探针——这点上两个引擎没有分岔。
一句话总结:VLM 是”烘焙体积探针”这个想法的第一代工程化,APV 是同一想法的”流送化、层级化”终版。在各自引擎里,UE 的 VLM 生态位后来被 Lumen 抢走(静态场景仍可用),Unity 则没有引擎级实时 GI,APV 就是烘焙路线的收敛终点——这也正是 Base-渲染方案演进总览 里说的”两条引擎路线分歧”。
6. 总结
APV 的知识链可以压缩为:
1 | |
它解决的问题:不用手摆探针、逐像素采样过渡平滑、支持开放世界、能多套光照切换。代价:探针不能手调、镜面要另配反射探针、漏光需要 dilation/virtual offset 修补。理解了”brick 层级 + 图集 + 流送 + SH”这套结构,再回头看任何体积探针方案(包括 UE 的 VLM),都能立刻定位它属于哪条线。