Unity ScriptableRenderFeature与注入点完全指南
2026/4/24 11:06:14 网站建设 项目流程

深入理解 URP 渲染管线的可编程注入点,避免采样黑屏与时序错误,掌握自定义 Pass 的正确姿势

什么是 ScriptableRenderFeature?

ScriptableRenderFeature 是 Unity URP(Universal Render Pipeline)提供的核心扩展机制。它允许开发者在渲染管线的任意阶段插入自定义的渲染逻辑,实现诸如后处理效果、深度/法线抓取、自定义光照等高级功能。

核心概念

ScriptableRenderFeature =功能定义(何时执行?)+ ScriptableRenderPass =执行逻辑(做什么?)。二者配合使用,构成了 URP 渲染管线的插件系统。

URP 渲染管线阶段一览

注入点资源可用性对照表

不同的注入点对渲染资源的可用性各不相同。以下表格详细列出了各注入点可以安全访问的纹理资源:

注入点 (RenderPassEvent)深度图 _CameraDepthTexture颜色图(Opaque) _CameraOpaqueTexture颜色图(Transparent)
BeforeRenderingOpaques✓ 已完成✓ 已完成✗ 未写入
AfterRenderingOpaques✓ 已完成✓ 已完成✗ 未写入
BeforeRenderingTransparents✓ 已完成✓ 已完成△ 部分可用
AfterRenderingTransparents✓ 已完成✓ 已完成✓ 已完成
BeforeRenderingPostProcessing✓ 已完成✓ 已完成✓ 已完成
AfterRenderingPostProcessing✓ 已完成✓ 已完成✓ 已完成

关键提醒

深度图和颜色图的抓取需要在 UniversalRenderer Data 资产中启用相应选项。默认情况下,这些纹理可能不会被生成。

完整代码示例:创建自定义 Render Feature

第一步:定义 ScriptableRenderPass

using UnityEngine.Rendering.Universal; using UnityEngine.Rendering; public class CustomRenderPass : ScriptableRenderPass { // Pass 运行时数据 private RenderTargetIdentifier tempRT; private Material customMaterial; // 设置材质和其他资源 public void Setup(Material mat) { customMaterial = mat; } // 核心执行逻辑 public override void Execute( ScriptableRenderContext context, ref RenderingData<Rendering cameraTargetSettings> renderingData) { if (customMaterial == null) return; var cmd = new CommandBuffer(); cmd.name = "Custom Effect Pass"; // 在此处添加渲染逻辑 // 例如:Blit、绘制全屏Quad等 context.ExecuteCommandBuffer(cmd); cmd.Release(); } }

第二步:创建 ScriptableRenderFeature

using UnityEngine; using UnityEngine.Rendering.Universal; public class CustomRenderFeature : ScriptableRendererFeature { // ★ 核心:选择正确的注入点 // 可选:BeforeRenderingOpaques / AfterRenderingOpaques / // BeforeRenderingTransparents / AfterRenderingTransparents / // BeforeRenderingPostProcessing / AfterRenderingPostProcessing public RenderPassEvent passEvent = RenderPassEvent.AfterRenderingOpaques; private CustomRenderPass customPass; public Material customMaterial; // 初始化 Pass 实例 public override void Create() { customPass = new CustomRenderPass(); // 设置 Pass 的注入点和渲染优先级 customPass.renderPassEvent = passEvent; } // 将 Pass 添加到渲染管线 public override void AddRenderPasses( ScriptableRenderer renderer, ref RenderingData renderingData) { // 配置 Pass 并入队 customPass.Setup(customMaterial); renderer.EnqueuePass(customPass); } }

第三步:在 Pass 中正确采样纹理

public override void Execute( ScriptableRenderContext context, ref RenderingData renderingData) { var cameraData = renderingData.cameraData; var cmd = new CommandBuffer(); // ===================================== // ★ 核心:安全采样渲染资源 // ===================================== // 1. 采样深度图(如果需要) // 在 AfterRenderingOpaques 时深度图已可用 if (cameraData.camera.depthTextureMode.HasFlag(DepthTextureMode.Depth)) { var depthTex = Shader.GetGlobalTexture("_CameraDepthTexture"); cmd.SetGlobalTexture("_MyDepthTex", depthTex); } // 2. 采样不透明颜色图(如果需要) // 使用 _CameraOpaqueTexture 采样不透明物体渲染结果 var opaqueTex = Shader.GetGlobalTexture("_CameraOpaqueTexture"); cmd.SetGlobalTexture("_MyOpaqueTex", opaqueTex); // 3. 应用自定义效果(使用 Blit) var tempRT = RenderTexture.GetTemporary( cameraData.camera.pixelWidth, cameraData.camera.pixelHeight, 0, RenderTextureFormat.Default ); cmd.Blit(RenderTargetIdentifier.None, tempRT, customMaterial); cmd.Blit(tempRT, cameraData.targetTexture); RenderTexture.ReleaseTemporary(tempRT); context.ExecuteCommandBuffer(cmd); cmd.Release(); }

常见错误与解决方案

问题一:在透明物体 Pass 中采样透明颜色图得到黑屏

错误代码位置:BeforeRenderingTransparents或更早的位置尝试采样透明物体的渲染结果。

原因:透明物体的颜色缓冲在BeforeRenderingTransparents时还未被写入,此时读取只能得到黑屏或旧数据。

解决方案

将采样逻辑移到AfterRenderingTransparents注入点:

class TransparentGrabPass : ScriptableRenderPass { public override void Execute(ScriptableRenderContext context, Ref<RenderingData> renderingData) { // ✅ 在 AfterRenderingTransparents 时可以安全采样 var cmd = new CommandBuffer(); var transparentTex = RenderingUtils.GetTransparentTexture(renderingData); // 现在 transparentTex 包含正确的透明物体渲染结果 } }
问题二:在 Opaque 渲染前采样颜色图获得的是上一帧数据

错误理解:以为BeforeRenderingOpaques时颜色图已经是当前帧的内容。

实际原因:颜色图是在上一帧的AfterRenderingOpaques阶段写入的,并非当前帧。

解决方案

如果需要当前帧的不透明物体结果,在AfterRenderingOpaques之后使用:

  • AfterRenderingOpaques- 采样当前帧不透明结果(推荐)
  • BeforeRenderingTransparents- 同样可用
  • AfterRenderingTransparents- 全部渲染完成后
问题三:深度图在某些设备上不可用

表现:在 Editor 中正常,但在某些 Android/iOS 设备上出现黑屏或深度效果错误。

原因:未在 Renderer Data 中正确配置深度纹理,或设备不支持所需格式。

解决方案

在 URP Asset 中启用 Depth Texture 选项:

// 在 Pass 中检查深度图可用性 public override void Execute(ScriptableRenderContext context, Ref<RenderingData> renderingData) { var cameraData = renderingData.cameraData; if (!cameraData.camera.depthTextureMode..HasFlag(DepthTextureMode.Depth)) { Debug.LogWarning("Depth texture not available! Enable it in Renderer Data."); return; } // 安全使用深度图 var depthTex = Shader.GetGlobalTexture("_CameraDepthTexture"); }

最佳实践总结

Opaque 物体效果

深度图可用

颜色图(Opaque)可用

颜色图(Transparent)不可用

使用注入点:AfterRenderingOpaques

透明物体效果

深度图可用

颜色图(Opaque)可用

颜色图(Transparent)可用

使用注入点:AfterRenderingTransparents

全场景后处理

深度图可用

颜色图(Opaque)可用

颜色图(Transparent)可用

使用注入点:Before/AfterRenderingPostProcessing

延迟写入

所有纹理可用

最终合成的完整画面

无法再修改透明物体

使用注入点:AfterRenderingPostProcessing

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询