Unity URP + Compute Shader 路径追踪器实战:从可用到可优化

这篇文章总结我在一个 Unity 客户端项目里实现并迭代路径追踪器的完整过程:

基于 URP ScriptableRenderPass + HLSL Compute Shader + SDF 场景表示,从"能跑"走到"可持续优化"。


一、项目目标

我想在 Unity 里做一个可控、可扩展的离线路径追踪风格渲染流程,目标是:

  • 支持基础材质:漫反射 / 金属 / 电介质(玻璃)
  • 支持发光体直接采样与多次反弹
  • 支持时间累积降噪
  • 在工程上可调参、可回退、可分阶段优化

二、整体架构(CPU + GPU)

1) CPU 侧(C# / URP Pass)

核心职责:

  • 扫描场景对象(Collider + MeshFilter)并构建 SdfShape 列表
  • 把几何和材质参数上传到 GPU(ComputeBuffer
  • 管理历史纹理、分辨率缩放、相机变化重置
  • 调度 Compute Shader 内核(单核 or 分阶段)

2) GPU 侧(Compute Shader)

核心职责:

  • 射线生成(像素抖动多样本)
  • SDF Ray March 求交 + 法线估计
  • BSDF 采样与路径积分(含俄罗斯轮盘赌)
  • 发光体直接采样 + MIS 权重
  • 当前帧结果与历史样本融合

三、渲染主流程(按帧)

下面是精简后的伪代码:

pseudo 复制代码
function ExecuteFrame():
    if needRebuildSdfBuffer():
        shapes = collectSceneShapes()
        upload(SDFShapes, shapes)

        emissiveIndices = []
        for i in range(shapes.count):
            if shapes[i].isQuadLike and shapes[i].emission > eps:
                emissiveIndices.add(i)
        upload(EmissiveIndices, emissiveIndices)

    setupRenderTargets(traceResolutionScale)
    if cameraMoved:
        resetHistorySamples()

    dispatch(CSPathTraceStage)   // 只做本帧采样
    dispatch(CSComposeStage)     // 历史融合并输出

    copyCurrentToHistory()
    blitToCameraTarget()

四、路径追踪核心(GPU)

路径积分伪代码:

pseudo 复制代码
function TracePath(ray):
    radiance = 0
    throughput = 1

    for bounce in [0..maxBounces):
        hit = rayMarchSDF(ray)
        if !hit:
            break

        mat = fetchMaterial(hit.shape)

        if mat.emissive:
            radiance += throughput * mat.emissionColor
            break

        if mat.isMetal:
            wi = reflect(ray.dir, hit.normal)
            throughput *= fresnelSchlick(...)
        else if mat.isDielectric:
            wi = reflect_or_refract_by_fresnel(...)
        else:
            radiance += sampleDirectLightMIS(hit, throughput)
            wi = cosineHemisphereSample(hit.normal)
            throughput *= mat.albedo

        if bounce >= rrStart:
            if random() > survivalProb(throughput):
                break
            throughput /= survivalProb

        ray = offsetRay(hit.pos, wi, normalBias)

    return clampHuePreserve(radiance)

五、关键技术点

  • SDF 几何表示:sphere / box / quad 统一距离场,便于在 Compute 中做统一求交
  • Ray Marching:按距离步进,命中阈值 + 偏移避免自相交
  • 材质模型
    • 漫反射:余弦半球采样
    • 金属:反射 + Schlick Fresnel
    • 电介质:折射/反射概率 + 全反射判断
  • 直接光采样:对发光面片采样点,计算面积 PDF
  • MIS(Power Heuristic):平衡"光源采样"和"BSDF采样"贡献
  • 时间累积color + sampleCount 方式做稳定融合
  • 颜色稳定性:用"亮度保持色相"的 clamp,避免 RGB 白化/偏色

六、这次优化里最有效的一步

发光索引缓存(CPU 预构建 + GPU 直接索引)

之前直接光采样每次都要在 shader 里遍历 _SDFCount 做发光体筛选,热点非常明显。

优化后流程变成:

  • CPU 构建 SDF 时顺便筛发光体索引
  • 上传 EmissiveIndices + _EmissiveCount
  • GPU 直接 chosen = EmissiveIndices[pick] 取样本对象

伪代码:

pseudo 复制代码
// CPU
emissiveIndices = filter(shapes, shape => shape.type is emissiveQuad && shape.emission > eps)
upload(EmissiveIndices, emissiveIndices)

// GPU
if _EmissiveCount == 0: return 0
pick = randomInt(0, _EmissiveCount-1)
lightIndex = EmissiveIndices[pick]
sample(lightIndex)

优点:

  • 降低 shader 中重复遍历
  • 提高直接光阶段吞吐
  • 结构清晰,后续可扩展到多类光源索引

七、技术栈清单

  • 引擎与渲染管线:Unity + URP
  • CPU 侧:C#、ScriptableRendererFeature、ScriptableRenderPass、CommandBuffer
  • GPU 侧:HLSL Compute Shader(多 kernel 分阶段)
  • 数据结构:ComputeBuffer(结构化缓冲)、RenderTexture(ARGBFloat)
  • 算法:SDF Ray Marching、Monte Carlo Path Tracing、MIS、Russian Roulette、Temporal Accumulation
  • 工程策略:相机变化检测、静态/动态 SDF 重建策略、分辨率缩放、更新帧间隔控制

八、后续优化路线

  • Step 1(推荐下一步):自适应采样(方差驱动,低噪像素降采样)
  • Step 2(大头):SDF 空间加速结构(网格分桶/BVH)降低全量遍历复杂度
  • Step 3(画质稳定):重投影历史融合(motion vector + neighborhood clamp)
相关推荐
weixin_423995002 小时前
unity 物体转向鼠标点击方向2d和3d
unity·计算机外设·游戏引擎
mxwin2 小时前
Unity URP 下 Shader 变体 (Variants):multi_compile 与 shader_feature的关键字管理及变体爆炸防控策略
unity·游戏引擎
RReality4 小时前
【Unity Shader URP】全息扫描线(Hologram Scanline)源码+脚本控制
ui·unity·游戏引擎·图形渲染
渔民小镇5 小时前
一次编写到处对接 —— 为 Godot/Unity/React 生成统一交互接口
java·分布式·游戏·unity·godot
RReality18 小时前
【Unity Shader URP】序列帧动画(Sprite Sheet)实战教程
unity·游戏引擎
mxwin18 小时前
Unity URP 多线程渲染:理解 Shader 变体对加载时间的影响
unity·游戏引擎·shader
呆呆敲代码的小Y20 小时前
【Unity工具篇】| 游戏完整资源热更新流程,YooAsset官方示例项目
人工智能·游戏·unity·游戏引擎·热更新·yooasset·免费游戏
nainaire20 小时前
自学虚幻引擎记录1
游戏引擎·虚幻
想你依然心痛1 天前
HarmonyOS 5.0游戏开发实战:构建高性能2D休闲游戏引擎与 monetization 系统
华为·游戏引擎·harmonyos