Unity Shader 光线追踪原理与实战案例

从硬件加速到屏幕空间追踪,理解 URP 17+ 中光线追踪的核心机制与 5 个生产级应用

一、什么是 URP 光线追踪

传统光栅化渲染(rasterization)只计算 从光源到表面 的直接光照,无法自然处理反射、折射、间接光等复杂现象。 光线追踪(Ray Tracing) 则反转了光线方向:从相机发射"虚拟光线"进入场景,与几何体求交,从而天然支持这些视觉效果。

Unity 6 / URP 17 提供了完整的光线追踪集成,底层使用 DXR(DirectX Raytracing)或 Vulkan RT 后端。 关键的是 URP 提供了 多种渲染模式,可以根据项目硬件预算灵活选择:

渲染模式 光追类型 硬件要求 适用场景
Raster(光栅化) 无 任意 移动端、低端 PC
Raster + Screen Space RT SSR / SSGI / SSAO 任意 中端 PC 主机
Ray Traced 硬件 DXR / Vulkan RT RTX / RDNA2+ 高端 PC、PS5、Xbox
Path Traced 硬件 RT + 多次弹射 RTX 30+ 建议 电影级、CG 短片

二、URP 光线追踪的整体架构

URP 的光线追踪并不是取代整个前向/延迟渲染,而是 嵌入 现有管线的特定阶段, 仅对需要的视觉效果启用追踪计算。整体流程如下:

核心数据结构

URP 的光线追踪依赖三个核心数据对象:

  • RayTracingAccelerationStructure(加速结构) :BVH(Bounding Volume Hierarchy)形式的场景几何体组织,BLAS 描述单个网格,TLAS 描述整个场景实例层级。
  • RayTracingShader(追踪 shader) :包含 __raygen、__miss、__closesthit、__anyhit 等可调函数。
  • RayTracingPass :通过 CommandBuffer.DispatchRays 调度一次追踪计算。

三、追踪算法的核心数学

追踪的本质是 光线与几何体求交。在硬件 RT 出现之前,CPU 上有经典算法如 Möller-Trumbore 三角面求交; GPU 的硬件 RT 把这一计算交给 BVH 加速结构和专用电路(如 NVIDIA 的 RT Core)处理。

光线定义

R(t) = O + t · D, t ∈ tmin, tmax

其中 O 是起点,D 是方向,t 是沿光线的距离参数。

追踪递归方程(渲染方程)

Lo(p, ωo) = Le + ∫Ω f(p, ωi, ωo) · Li(p, ωi) · (n · ωi) dωi

渲染方程描述了从表面点 p 朝方向 ωo 出射的辐射亮度。蒙特卡洛路径追踪通过发射随机光线采样这个积分。

BVH 加速结构的工作方式

性能关键: BVH 重建(rebuild)是追踪性能的最大瓶颈之一。 静态场景只需构建一次,动态场景可以使用 RayTracingAccelerationStructure.Update 增量更新。 URP 内部会自动管理这个生命周期,开发者只需在 UniversalRenderPipelineAsset 中开启对应开关。

四、案例一:光线追踪环境光遮蔽(RTAO)

环境光遮蔽(AO)模拟的是 几何体之间的相互遮挡。传统 SSAO 用屏幕空间深度近似,效果受屏幕分辨率限制; RTAO 在半球空间内发射真实光线,能捕捉到更精确的远距离遮蔽关系。

算法原理

  1. 对每个表面点 p,沿法线半球方向采样 N 条光线
  2. 追踪每条光线,统计未命中(被遮挡)比例
  3. 将未命中率作为 AO 系数

AO(p) = 1 − (1/N) · Σ visibility(p, ωi)

URP 配置步骤

  1. 打开 UniversalRenderPipelineAsset,将 Rendering Path 改为 Deferred。
  2. 在 UniversalRendererData 上点击 Add Renderer Feature → Screen Space Ambient Occlusion。
  3. 将 Ambient Occlusion 的 Source 设为 Ray Traced(需 GPU 支持 DXR/Vulkan RT)。
  4. 调节 Max Ray Length、Sample Count、Denoiser 三个核心参数。

推荐参数: Sample Count 在 PC 设为 4--8,主机 设为 2--4(依赖时序降噪)。 Max Ray Length 决定 AO 半径------室内 2--5m,室外 5--20m。Max Ray Length 越大,性能开销越高。

五、案例二:光线追踪反射(RTR)

反射是 RT 最直观的应用。SSR(屏幕空间反射)只能反射屏幕上可见的物体,遇到屏幕外或被遮挡的几何体时只能 fallback 到 cubemap; RTR 不受屏幕限制,能反射场景中的任何物体。

URP 中的反射模式对比

反射模式 支持屏幕外几何体 性能开销 最佳场景
Reflection Probe(烘焙) 否(仅静态) 极低 室内静态场景
SSR 否 中 光滑地面、金属
RTR 是 中高 镜面、抛光金属、水面
Hybrid(SSR + RTR 切换) 是 可调 复杂动态场景
复制代码
// URP 中启用 RTR(伪代码基于 URP 17 Renderer Feature) class RayTracedReflectionsFeature : ScriptableRendererFeature { public enum Mode { Off, SSR, RTR, Hybrid } public Mode mode = Mode.Hybrid; public int sampleCount = 4; public float maxRayLength = 50f; public float smoothnessCutoff = 0.6f; public override void AddRenderPasses(ScriptableRenderer renderer, ...) { if (mode == Mode.Hybrid) { // 平滑度高 → 用 SSR(便宜) // 平滑度低 + 屏幕外 → 用 RTR } } }

六、案例三:光线追踪全局光照(RTGI)

全局光照(GI)模拟光能在场景中 多次弹射 的效果。传统 lightmap 是预计算的,无法处理动态物体; 屏幕空间 GI(SSGI)受屏幕限制。RTGI 通过追踪二次光线,能实时计算单次或多次弹射的间接光。

单次弹射 RTGI 算法

  1. 从当前着色点 p 沿法线半球采样 N 条光线
  2. 追踪每条光线,命中点 q 的位置和法线从 G-Buffer 读取
  3. 在 q 处直接查询光照(如 SH 环境光或主光)
  4. 累加作为 p 的间接光

LGI(p) ≈ (1/N) · Σ Ldirect(q) · visibility(p, q) · cos(θ)

**性能注意:**单次弹射 RTGI 在 1080p 下每像素采样 4 条光线、追踪到第一次命中时 通常已接近实时上限(高端 PC 约 4-8ms)。如果需要多次弹射,建议改为 Path Tracing 并启用 NRD 去噪。

七、案例四:光线追踪阴影(RTS)

光栅化阴影(shadow map)本质是 从光源视角渲染深度 ,必然存在分辨率、走样、级联开销等问题。 追踪阴影则从 接收表面向光源 发射一条光线,命中即被遮挡------非常直观。

算法对比

特性 Shadow Map Ray Traced Shadow
分辨率 受 shadow map 分辨率限制 无(按需追踪)
锯齿 有,需 PCF 软化 天然无锯齿
面积光源 困难(多张 cascade) 天然支持
性能 较低 高(与像素数相关)
动态光源 需要重建 无需重建

面积光的"软阴影"特性

追踪阴影天然支持 面积光源软阴影。对光源表面采样 N 个点,从接收表面向每个点追踪,求遮挡比例:

Visibility = (1/N) · Σ O_hit(p, light_i) == false

其中 N 越大阴影越柔和,但 N=4 配合去噪通常就够用。

八、案例五:路径追踪(Path Tracing)

路径追踪是 蒙特卡洛方法 直接求解渲染方程的极限形式:每条光线在表面多次弹射(反射、折射), 每次根据 BRDF 随机采样方向,最终结果是无偏的(unbiased)。

伪代码

复制代码
// 路径追踪 Ray Generation Shader 伪代码 [shader("raygeneration")] void RayGen() { float3 rayDir = GenerateCameraRay(DispatchRaysIndex().xy); float3 throughput = 1.0; float3 radiance = 0.0; for (int bounce = 0; bounce < MAX_BOUNCES; bounce++) { TraceRay(payload, origin, rayDir, ...); if (payload.hitDistance < 0) { radiance += throughput * SampleSky(rayDir); break; } // 命中表面,采样 BRDF float3 brdf = SampleBRDF(payload.normal, rayDir, payload.albedo); float3 newDir = CosineWeightedSample(payload.normal); radiance += throughput * payload.emission; throughput *= brdf * dot(payload.normal, newDir); rayDir = newDir; } _Output[DispatchRaysIndex().xy] = float4(radiance, 1); }

关于噪声: 路径追踪的输出在低采样数下充满噪点。生产实践中通常用 累积帧(progressive accumulation) 或 NRD 去噪器 提升质量。 URP 17 内置了对 NVIDIA NRD 库的封装,可直接调用。

九、性能优化:半分辨率追踪 + 时序去噪

追踪的瓶颈在 每像素光线数 × 总像素数。生产管线几乎都采用两个核心优化:

关键配置

复制代码
// URP 17 半分辨率追踪配置 var settings = new RayTracingSettings { resolutionScale = 0.5, // 半分辨率 sampleCount = 1, // 每像素光线数 maxRayLength = 30, // 最大追踪距离 denoiser = DenoiserType.NRD, // NRD 去噪 temporalAccumulation = true, // 启用时序累积 motionVectorRejection = 0.1f, // 动体区域检测阈值 };

实战数据: 4K 屏幕下,0.5 + NRD 的 RTR 大约 1.2ms,

而全分辨率 + 1spp 无去噪的 RTR 接近 4ms 且充满噪点。半分辨率方案性价比高约 5 倍。

十、完整配置流程

前置条件

  1. Unity 6(或 Unity 2023.3+ LTS)+ URP 17+。
  2. GPU 支持硬件 RT:NVIDIA RTX 系列、AMD RDNA2+、Intel Arc 显卡;或 Vulkan 1.2 + RT 扩展。
  3. Player Settings → Other Settings → 勾选 Ray Tracing。
  4. URP Asset 中 Rendering 改为 Deferred(光追仅在 Deferred 下可用)。

添加追踪 Renderer Feature

  1. 选中 UniversalRendererData,在 Inspector 中点击 Add Renderer Feature。
  2. 选择需要的效果:Screen Space Ambient Occlusion、Screen Space Reflections、Screen Space Global Illumination。
  3. 对每个 feature,将 Mode 改为 Ray Traced。
  4. 配置 Sample Count、Max Ray Length、Denoiser。
  5. 在场景中加入测试物体(带反射地板、面积光源)验证效果。

性能 vs 质量预设

预设 分辨率缩放 每像素光线 弹射数 去噪 4K 目标帧时
Ultra 1.0 8 4 (Path) NRD + 时序 ~8ms (RTX 4080)
High 0.5 4 2 NRD + 时序 ~3ms
Medium 0.5 1 1 NRD + 时序 ~1.5ms
Low 0.25 1 1 简单双边滤波 ~0.8ms

十一、常见坑与最佳实践

1. 透明物体追踪

默认追踪在 alpha test 物体(带 cutout 的植被、栅栏)上会产生错误结果。需要:

cs 复制代码
// 透明材质追踪时关闭 alpha test [shader("closesthit")] void ClosestHit(inout Payload payload) { if (alphaTestedMaterial) { // 调用 anyhit shader 检查 alpha IgnoreHit(); // alpha 不通过则忽略 } payload.hitDistance = RayTCurrent(); }

2. 动体更新

URP 默认每帧重建 BLAS 对所有 Skinned Mesh 来说开销巨大。 对骨骼动画物体推荐使用 RayTracingAccelerationStructure.Update 增量更新。

3. 不开去噪不要出图

追踪结果在低采样下噪点非常明显,不接 NRD/降噪器直接展示是业余做法。 始终在追踪 Pass 后跟一个去噪 Pass,否则用户能直接看到噪点。

4. 远距离光线"飞过"全空

Max Ray Length 过大会让大部分光线"飞过整个场景未命中",浪费算力。推荐值:

  • 室内:5 - 20m
  • 室外:50 - 200m
  • 超远景观:500m+,配合天空盒 fallback

5. 与 forward 模式不兼容

URP 的 Forward/Forward+ 模式 不支持 光线追踪 Renderer Feature。 启用 RT 之前必须确认项目使用 Deferred 模式,否则相关 feature 会被自动禁用。

十二、总结

光线追踪让 URP 的渲染能力跃升一个量级。理解下面的对应关系能帮助你快速决策:

视觉效果 追踪方案 核心收益 建议最低配置
精确 AO RTAO 远距离、复杂几何的遮蔽 RTX 2060 / RX 6600
完美反射 RTR + SSR 混合 屏幕外物体反射 RTX 2070 / RX 6700
动态间接光 RTGI 动态物体 GI、彩色反弹 RTX 3060 / RX 6800
面积光软阴影 RTS 无锯齿、面积光源 RTX 3050 / RX 6600
电影级真实感 Path Tracing 无偏全局解 RTX 3080 / RX 6900

一句话总结: URP 的追踪不是替代光栅化,而是光栅化之上的视觉补强。 用"半分辨率 + NRD 去噪"作为默认方案,把有限的 RT 算力花在刀刃上。

相关推荐
地狱为王9 小时前
Unity使用DPDFNet实现实时语音增强(降噪)
unity·语音增强·降噪·dpdfnet
平行云PVT10 小时前
国产信创实时云渲染解决方案:如何实现数字孪生与三维应用云化部署?
unity·webrtc·unreal engine·实时云渲染·国产信创·摩尔线程·砺算科技
夏炎黄11 小时前
Terrain地形剪裁
unity
小贺儿开发13 小时前
DeepSeek + Codex + MCP:Unity 游戏开发实测
科技·游戏·unity·ai·程序员·编程·deepseek
新的瑞拉公主13 小时前
Cocos plist 粒子一键导入 Unity:Editor 工具实现与参数映射
unity·游戏开发·cocos2d-x·粒子系统·editor工具
淡海水14 小时前
03-02-架构篇-Boehm GC架构解析
unity·架构·c#·gc·boehm
xcLeigh1 天前
Unity基础:Instantiate实例化——在运行时动态创建游戏物体
游戏·unity·游戏引擎·教程·instantiate
流浪打工人2 天前
Unity3d TimeLine中audio track的使用
unity
淡海水2 天前
02-07-原理篇-GC暂停时间的本质
开发语言·unity·c#·gc·暂停时间