MIT 许可证 NuGet

首页 / 功能 / 全局光照

全局光照与环境光遮蔽

两种尺度下的两项技术。DDGI 把一次反弹的间接光带到整个房间; GTAO 则加深辐照度探针因粒度太粗而无法分辨的褶皱。两者都完全 动态,也都是无需改动场景代码就能关闭的 compute 特效。

DDGI 速览

基于场景有符号距离场体素化的动态漫反射全局光照。探针对着 SDF 追踪光线,把辐射度累积进辐照度图集,并用切比雪夫遮蔽—— 每个探针命中距离的均值与方差——来拒绝穿透墙壁漏光。 它完全动态:移动一面墙,反弹光就会跟随。

地面实况环境光遮蔽在 AfterScene compute 阶段运行,且只应用于 间接漫反射,因此它与 glTF 遮蔽贴图是叠加关系而不是互相对抗。

// In the app constructor, before
// graphics initialization:
RenderQuality.DefaultGlobalIllumination
    = GiMode.Ddgi;

// At runtime, through the live tier:
RenderQuality.Current.GiIntensity = 0.6f;
RenderQuality.Current.AoIntensity = 0.8f;

GlobalIllumination 是初始化时读取的模式—— 它烘焙着色器变体并分配探针链——所以之后切换它改变的是强度, 而不是成本。引擎会明确记录哪些旋钮是实时的、哪些是锁定的, 而不是隐藏这种差异。

由 DDGI 照明的室内 DDGI · GTAO · TAA
没有烘焙光照贴图,没有手工放置的光照探针。

探针体积是如何搭起来的

默认值描述了一个覆盖 32 单位立方体的 16×8×16 网格—— 2048 个探针——每个探针投射 128 条光线,每帧刷新一半网格。 选择这些数字是为了让 DDGI 在笔记本 GPU 上负担得起, 而不是让它达到最好的效果。

设置默认值含义
GiProbeGridX/Y/Z16 / 8 / 16每个轴的探针数量。Y 刻意更浅——房间的宽度大于高度。
GiVolumeSize32体积的世界单位边长。探针间距等于它除以网格数。
GiSdfResolution64探针所追踪的 SDF 的体素分辨率。
GiRaysPerProbe128每次探针更新投射的光线数。
GiProbeUpdateDivisor2每帧刷新的探针比例;2 表示一半。
GiTraceMaxSteps64每条光线的球体追踪步数预算。
GiShadowSteps24朝太阳方向的阴影光线的步数预算。

体积跟随相机,因此 32 单位的立方体是间接光上的移动窗口, 而不是场景大小的界限。大型开放场景仍然会失去远处的反弹光—— 单级联探针就是这里的诚实上限。

漏光控制与时间行为

可用 DDGI 与不可用 DDGI 之间的差别,大部分在于漏光拒绝和 探针遗忘的速度。下面是这两方面的旋钮。

设置默认值含义
GiChebyshevOcclusionon基于方差的可见性测试。关掉它是理解它为何存在的最快方式。
GiProbeValidityon把几何内部的探针标记为无效,使其停止贡献。
GiNormalBias0.25沿法线把采样点推离表面,是自漏光的常规疗法。
GiBackfaceThreshold0.5背面命中比例超过该值即视为探针被封闭。
GiHysteresis0.97探针辐照度的时间混合。越低反应越快、闪烁越多。
GiBackfaceHysteresis0.99对主要看到背面的探针使用更慢的混合。
GiIntensity0.4缩放间接光贡献。实时。
GiBounceGain1.0探针间反弹的反馈增益。
GiPunctualShadowoff探针光线是否除太阳外也受点光源遮挡。
滞后是一个取舍,不是质量设置

在 0.97 时,一个探针大约需要三十帧才能收敛到新的光照条件。 打开一盏灯,反弹光要半秒多才淡入。降低该值它会更快到达, 但过程中会沸腾。没有能两者兼得的设置。

SDF 由什么构建:64 个代理

探针不追踪你的三角形。它们追踪一个有符号距离场,该场由控件在帧期间 贡献的代理体积构建——而 GiProxies.MaxProxies 是 64。

真正会咬人的限制

六十四个代理,放在一个 4 KB 缓冲区里。超过之后, GiProxies.Overflow 变为 true,多余的体积被丢弃—— 这些几何体仍然被绘制、仍然被直接光照亮,但对间接光不可见。 如果反弹光穿透了某个实体,请检查 Count 和 Overflow。

控件通过重写 CollectGiProxy 并调用 TryAdd 或 TryAddSphere 来加入该场;两者在预算耗尽时返回 false 而不是抛异常。Control 上两个关键表面属性是:

属性含义
GiAlbedo代理反射的颜色。红墙正是通过它给房间染上红色,而不是通过其纹理。
GiEmissive代理发射的颜色,发光物体正是这样在不作为光源的情况下贡献反弹光。

实际结论:给墙壁、地板和大型遮挡物做代理,把杂物排除在外。 六十四个描述房间的粗略体积,胜过六十四个描述家具的体积。

GTAO

地面实况环境光遮蔽,在 AfterScene 阶段由场景深度和法线计算,并以 compute://gtao/ao 发布。它处理 DDGI 无法处理的 尺度:椅腿与地板相接处的暗化完全位于单个探针单元之内。

设置默认值含义
AmbientOcclusionGtao模式选择器;初始化时读取。
AoRadius0.5世界空间采样半径,以单位计。实时。
AoIntensity1.0遮蔽项的强度。实时。

AO 只应用于间接漫反射。把它应用于直接光是常见的捷径, 会在受光表面上产生暗边,而这里并非如此——这也意味着在 没有环境光或 GI 可遮蔽的场景里,AO 看起来会更弱。

查看 GI 正在做什么

正是为此存在两个调试特效。 Sdf3DViewEffect 渲染一个与探针所追踪类型相同的 距离场切片,DdgiEffect 则在 compute://ddgi/sdfslice 下发布自己的切片, 与辐照度和深度图集并列。两者都是可寻址纹理,因此把它们放上屏幕 只需一个 Sprite2D。如果 GI 看起来不对,答案通常在 SDF 里:没有代理的物体根本不在场里。

后处理与 compute 特效 介绍了这些调试视图 是如何注册的,Apps/Engine 展示了它们接到 屏幕开关上的样子。