Version: 6000.3
语言: 中文
在URP中启用HDR输出
在URP中使用HDR输出的可脚本渲染通道问题排查

在 URP 中实现与 HDR 输出兼容的自定义叠加层

本页演示如何创建执行以下作的可脚本渲染器功能:

  • 将一个的输出合成相机在场景中创建特定视点图像的组件。输出要么绘制到屏幕上,要么作为纹理捕获。更多信息
    请参阅术语表
    到另一台摄像机上。
  • 适用色调映射将图像的 HDR 值重新映射到适合在屏幕上显示的范围的过程。更多信息
    请参阅术语表
    使自定义叠加层与HDR高动态范围
    请参阅术语表
    输出。

这包括着色器在 GPU 上运行的程序。更多信息
请参阅术语表
根据帧顺序将色调映射应用于叠加层。

有关更多信息,请参阅可编写脚本渲染器功能简介 和 可编写脚本渲染通道简介

此示例分为以下部分:

先决条件

此示例假设以下情况:

  • Unity 项目使用 URP 作为活动渲染管线 获取场景内容并将其显示在屏幕上的一系列作。Unity 允许您从预构建的渲染管道中进行选择,或编写自己的渲染管道。更多信息
    请参阅术语表
    .
  • 项目设置为HDR渲染,并使用以下设置:
  • 活动URP资产的 调色模式(Grading Mode) 设置为 高动态范围(High Dynamic Range)。
  • 活动URP资产启用了HDR
  • 项目设置(Project Settings) 广泛的设置集合,允许您配置物理、音频、网络、图形、输入和项目的许多其他区域的行为方式。更多信息
    请参阅术语表
    启用 HDR 输出。

设置场景

要使示例正常工作,您必须首先设置一个示例场景场景包含游戏的环境和菜单。将每个唯一的场景文件视为一个独特的关卡。在每个场景中,你放置你的环境、障碍物和装饰品,基本上是将你的游戏设计和构建成碎片。更多信息
请参阅术语表
如以下说明所示。

  1. 创建多维数据集游戏对象Unity 场景中的基本对象,可以表示角色、道具、风景、相机、航路点等。游戏对象的功能由附加到它的组件定义。更多信息
    请参阅术语表
    并将其位置设置为场景中的原点(X:0、Y:0、Z:0)。

  2. 对齐Main Camera使立方体清晰可见。

  3. 创建一个新摄像机,并将其命名为 Overlay Camera

  4. 将叠加相机放置在Main Camera并对齐它,使立方体清晰可见。

  5. 检查器一个 Unity 窗口,显示有关当前选定游戏对象、资产或项目设置的信息,允许您检查和编辑值。更多信息
    请参阅术语表
    窗。

  6. 将叠加摄像机背景的颜色设置为透明黑色,RGBA 值为0, 0, 0, 0.

  7. 创建渲染纹理并将其命名为 OverlayRenderTexture。要创建渲染纹理,请转到 资产(Assets) > 创建(Create) > 渲染(Create Rendering) > 渲染纹理(Render Texture)。

    注意:为了获得更好的HDR精度,请对渲染纹理格式使用带符号浮点格式。为此,请选择渲染纹理,然后在 Inspector 窗口中将 Color Format 更改为具有_SFLOAT后缀。

  8. 将叠加渲染纹理分配给叠加摄像机的 输出纹理(Output Texture) 属性。为此,请打开Overlay Camera,然后转到 输出(Output) > 输出纹理(Output Texture),然后从资产列表中选择 OverlayRenderTexture

  9. 为叠加摄像机创建新的通用渲染器资源,并将其命名为 OverlayRenderer。为此,请转到 资产(Assets) > 创建>渲染(Create Rendering > URP 通用渲染器)。

  10. 选择活动的URP资源,然后在 检查器(Inspector) 窗口中转到 渲染(Rendering) > 渲染器列表(Renderer List) > +。选择 OverlayRenderer。这会将覆盖渲染器添加到渲染器列表中。

  11. 选择叠加摄像机,然后在“检查器”窗口中转到“渲染”>“渲染器”。选择 OverlayRenderer。这将覆盖摄像机设置为使用覆盖渲染器。

场景现在已准备好,您可以使用可编写脚本渲染器功能创建自定义叠加层。

创建自定义叠加渲染通道

要创建与HDR输出兼容的自定义叠加层,你必须使用可编写脚本的渲染通道来创建叠加层。HDR 输出在后处理在图像出现在屏幕上之前通过应用滤镜和效果来改善产品视觉效果的过程。你可以使用后期处理效果来模拟物理摄像机和胶片属性,例如泛光和景深。更多信息 后处理, 后处理, 后处理
术语表中查看
.因此,主摄像头和叠加摄像头的输出具有不同的色调映射。然后,此渲染通道在后期处理后发生,以将色调映射应用于叠加摄像机的输出。

若要为此示例创建渲染通道,请使用以下步骤:

  1. 创建 C# 脚本并将其调用CustomOverlayRenderPass.

  2. 在脚本中,删除 Unity 在CustomOverlayRenderPass类。

  3. 添加以下内容using指令。

    using UnityEngine;
    using UnityEngine.Rendering;
    using UnityEngine.Rendering.Universal;
    using UnityEngine.Rendering.RenderGraphModule;
    
  4. 创建一个新的CustomOverlayRenderPass继承自ScriptableRenderPassclass 并具有属性[SupportedOnRenderer(typeof(UniversalRendererData) -->.

    [SupportedOnRenderer(typeof(UniversalRendererData))] -->
    public class CustomOverlayRenderPass : ScriptableRenderPass
    {
      
    }
    
  5. 添加属性Material passMaterialRTHandle passOverlayTexture到渲染通道,如下所示。

    [SupportedOnRenderer(typeof(UniversalRendererData))] -->
    public class CustomOverlayRenderPass : ScriptableRenderPass
    {
        Material passMaterial;
        RTHandle overlayTextureHandle;
    }
    
  6. 创建一个构造函数方法,该方法将材质作为参数并将其分配给passMaterial.此方法还为渲染通道创建分析采样器,并将其设置为在AfterRenderingPostProcessing事件。

    public CustomOverlayRenderPass(Material material)
    {
        passMaterial = material;
        profilingSampler = new ProfilingSampler(nameof(CustomOverlayRenderPass));
    
        renderPassEvent = RenderPassEvent.AfterRenderingPostProcessing;
    }
    
  7. 添加一个Setup方法。使用此方法和参数创建RTHandle从叠加纹理中,如下所示。使用RTHandle允许RenderPass用于与叠加层交互的 API渲染纹理(render texture一种特殊类型的纹理,在运行时创建和更新。要使用它们,请先创建一个新的渲染纹理,并指定要渲染到其中的摄像机之一。然后,你可以在材质中使用渲染纹理,就像使用常规纹理一样。更多信息
    请参阅术语表
    .

    public void Setup(Texture overlayTex)
    {
        if (overlayTextureHandle != overlayTex)
        {
            overlayTextureHandle?.Release();
            overlayTextureHandle = RTHandles.Alloc(overlayTex);
        }
    }
    
  8. 实现Dispose方法,以便在渲染通道被销毁时释放叠加纹理。

    public void Dispose()
    {
        overlayTextureHandle?.Release();
    }
    
  9. 创建两个结构体,一个名为CopyData,另一个名为PassData,其中包含如下所示的属性。这些结构体包含 URP 实现渲染通道所需的关键属性。

    struct CopyData
    {
        public TextureHandle source;
    }
    
    struct PassData
    {
        public TextureHandle source;
        public TextureHandle overlayTexture;
        public TextureHandle internalLut;
        public Vector4 lutParams;
        public Material material;
    }
    
  10. 添加RecordRenderGraph方法如下图所示。

    public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
    {
    
    }
    

实现 RecordRenderGraph 方法

RecordRenderGraph方法CustomOverlayRenderPass类。

  1. 从帧数据中获取后期处理、资源和相机数据。

    UniversalPostProcessingData postProcessingData = frameData.Get<UniversalPostProcessingData>();
    UniversalResourceData resourceData = frameData.Get<UniversalResourceData>();
    UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
    
  2. 从资源数据中获取活动颜色纹理。

    TextureHandle activeCameraColor = resourceData.activeColorTexture;
    
  3. 创建纹理以存储活动摄像机颜色目标。

    RenderTextureDescriptor colorCopyDescriptor = cameraData.cameraTargetDescriptor;
    colorCopyDescriptor.depthBufferBits = (int) DepthBits.None;
    TextureHandle copiedColor = UniversalRenderer.CreateRenderGraphTexture(renderGraph, colorCopyDescriptor, "_CustomCameraColorCopy", false);
    
  4. 创建一个RasterRenderPass将活动摄像机颜色目标复制到纹理中。副本将用于处理混合。

    using (var builder = renderGraph.AddRasterRenderPass<CopyData>("Custom Overlay Render Pass - Copy Camera", out var passData))
    {
        passData.source = activeCameraColor;
        builder.UseTexture(passData.source, AccessFlags.Read);
        builder.SetRenderAttachment(copiedColor, 0, AccessFlags.WriteAll);
    
        builder.SetRenderFunc((CopyData data, RasterGraphContext context) =>
        {
            Blitter.BlitTexture(context.cmd, data.source, new Vector4(1, 1, 0, 0), 0.0f, false);
        });
    }
    
  5. 创建另一个RasterRenderPass将叠加纹理复制到具有自定义材质的活动摄像机颜色目标。这是您在本指南的这一部分中添加的其余代码的容器。

    using (var builder = renderGraph.AddRasterRenderPass<PassData>("Custom Overlay Render Pass - Blit Overlay", out var passData))
    {
        
    }
    
  6. 设置渲染通道所需的属性blit“位块传输”的简写术语。blit作是将数据块从内存中的一个位置传输到另一个位置的过程。
    请参阅术语表
    叠加纹理,如下所示。

    using (var builder = renderGraph.AddRasterRenderPass<PassData>("Custom Overlay Render Pass - Blit Overlay", out var passData))
    {
        passData.material = passMaterial;
    
        builder.SetRenderAttachment(activeCameraColor, 0, AccessFlags.Write);
    
        passData.source = copiedColor;
        builder.UseTexture(passData.source, AccessFlags.Read);
    }
    
  7. 将纹理导入渲染图系统,然后将纹理设置为输入。

    passData.overlayTexture = renderGraph.ImportTexture(passOverlayTexture);
    builder.UseTexture(passData.overlayTexture, AccessFlags.Read);
    
  8. 检查后期处理和 HDR 颜色分级。如果HDR输出的配置正确,请将HDR用作输入的内部颜色LUT纹理设置,并将其参数传递给着色器。

    if (postProcessingData.gradingMode == ColorGradingMode.HighDynamicRange && cameraData.postProcessEnabled)
    {
        passData.internalLut = resourceData.internalColorLut;
        builder.UseTexture(passData.internalLut, AccessFlags.Read);
    
        int lutHeight = postProcessingData.lutSize;
        int lutWidth = lutHeight * lutHeight;
    
        float postExposure = 1.0f;
        ColorAdjustments colorAdjustments = VolumeManager.instance.stack.GetComponent<ColorAdjustments>();
        if (colorAdjustments != null)
        {
            postExposure = Mathf.Pow(2.0f, colorAdjustments.postExposure.value);
        }
    
        passData.lutParams = new Vector4(1f / lutWidth, 1f / lutHeight, lutHeight - 1f, postExposure);
    }
    

    注意:如果禁用后期处理,则在此渲染过程之后将应用HDR颜色转换,并且摄像机输出的预期色彩空间为默认的Rec709。此示例中的代码使用if语句,以防止此渲染通道在应用HDR之前更改叠加摄像机的输出。

  9. 在着色器上设置关键字以启用色调映射,并添加命令以将叠加纹理blit到活动摄像机颜色目标。

    builder.SetRenderFunc((PassData data, RasterGraphContext context) =>
    {
        data.material.SetTexture("_OverlayTexture", data.overlayTexture);
    
        bool tonemappingActive = data.internalLut.IsValid();
        CoreUtils.SetKeyword(data.material, "TONEMAPPING", tonemappingActive);
        if (tonemappingActive)
        {
            data.material.SetTexture("_InternalLut", data.internalLut);
            data.material.SetVector("_InternalLut_Params", data.lutParams);
        }
            
        Blitter.BlitTexture(context.cmd, data.source, new Vector4(1, 1, 0, 0), data.material, 0);
    });
    

这样就完成了CustomOverlayRenderPass脚本,准备让可编写脚本渲染器功能将其添加到渲染器中。

有关本部分的完整代码,请参阅自定义叠加渲染密码

创建自定义叠加可编写脚本渲染器功能

要添加CustomOverlayRenderPass渲染器,你必须使用以下步骤创建可编写脚本渲染器功能。

  1. 创建 C# 脚本并将其调用CustomOverlayRendererFeature.

  2. 在脚本中,删除 Unity 在CustomOverlayRendererFeature类。

  3. 添加以下内容using指令。

    using UnityEngine;
    using UnityEngine.Rendering;
    using UnityEngine.Rendering.Universal;
    
  4. 设置新的CustomOverlayRendererFeature继承自ScriptableRendererFeature类。

    public class CustomOverlayRendererFeature : ScriptableRendererFeature
    {
      
    }
    
  5. 添加以下属性以包含渲染通道所需的资产和数据。

    public class CustomOverlayRendererFeature : ScriptableRendererFeature
    {
        public Shader hdrShader;
        public RenderTexture passOverlayTexture;
    
        Material passMaterial;
    
        CustomOverlayRenderPass overlayRenderPass = null;
    }
    
  6. 创建AddRenderPasses方法,并使用它仅在游戏视图中应用叠加层,并应用于摄像机堆栈中的最后一个摄像机。

    public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
    {
        if (renderingData.cameraData.cameraType != CameraType.Game || !renderingData.cameraData.resolveFinalTarget)
            return;
    }
    
  7. 之后if语句,将覆盖纹理传递给覆盖渲染通道,并将渲染通道排队。

    public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
    {
        if (renderingData.cameraData.cameraType != CameraType.Game || !renderingData.cameraData.resolveFinalTarget)
            return;
    
        overlayRenderPass.Setup(passOverlayTexture);
    
        renderer.EnqueuePass(overlayRenderPass);
    }
    
  8. 添加Create方法并创建CustomOverlayRenderPass使用新材料hdrShader.

    public override void Create()
    {
        passMaterial = CoreUtils.CreateEngineMaterial(hdrShader);
    
        overlayRenderPass = new CustomOverlayRenderPass(passMaterial);
    }
    
  9. 实现Dispose方法,以释放渲染器功能在应用渲染通道后创建的资源。

    protected override void Dispose(bool disposing)
    {
        CoreUtils.Destroy(passMaterial);
        overlayRenderPass.Dispose();
    }
    

有关本节的完整代码,请参阅自定义叠加可编写脚本渲染器功能代码

创建自定义叠加着色器

材料CustomOverlayRendererFeaturecreates 需要自定义着色器来处理叠加和 HDR 输出更改。以下步骤演示了如何创建能够执行此作的着色器。

  1. 创建一个新着色器并将其命名CustomOverlayBlit.

  2. 删除 Unity 自动生成的着色器代码,并设置着色器的轮廓,如下所示。

    Shader "Custom/CustomOverlayBlit"
    {
        SubShader
        {
            Tags{ "RenderPipeline" = "UniversalPipeline" }
    
            Pass
            {
                ZWrite Off ZTest Always Blend Off Cull Off
    
                HLSLPROGRAM
                    #pragma target 2.0
                    #pragma editor_sync_compilation
                    #pragma vertex Vert
                    #pragma fragment Frag
                    #pragma multi_compile_local_fragment _ TONEMAPPING
    
                    #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
                    #include "Packages/com.unity.render-pipelines.core/Runtime/Utilities/Blit.hlsl"
                    #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl"
                    
                    TEXTURE2D(_InternalLut);
                    TEXTURE2D_X(_OverlayTexture);
    
                    float4 _InternalLut_Params;
    
                    #define LutParams _InternalLut_Params.xyz
                    #define PostExposure _InternalLut_Params.w
    
                ENDHLSL
            }
        }
    }
    
  3. 创建一个名称为ApplyTonemapping和返回类型half3.此方法应具有以下参数:half3 input,TEXTURE2D_PARAM(lutTex, lutSampler),float3 lutParams,float exposure.

  4. ApplyTonemapping方法, 乘法input通过exposurevalue,则saturate修改后的input.

    half3 ApplyTonemapping(half3 input, TEXTURE2D_PARAM(lutTex, lutSampler), float3 lutParams, float exposure)
    {
        input *= exposure;
        float3 inputLutSpace = saturate(LinearToLogC(input));
    }
    
  5. 应用色调映射更改ApplyLut2D并返回结果。

    half3 ApplyTonemapping(half3 input, TEXTURE2D_PARAM(lutTex, lutSampler), float3 lutParams, float exposure)
    {
        input *= exposure;
        float3 inputLutSpace = saturate(LinearToLogC(input));
        return ApplyLut2D(TEXTURE2D_ARGS(lutTex, lutSampler), inputLutSpace, lutParams);
    }
    
  6. 创建标准Frag方法如下图所示。将此方法放在HLSLPROGRAM但在ApplyTonemapping方法。

    half4 Frag(Varyings input) : SV_Target
    {
    
    }
    
  7. Frag方法中,检索原始相机颜色和叠加颜色。

    half4 Frag(Varyings input) : SV_Target
    {
        half4 color = FragBlit(input, sampler_LinearClamp);
    
        half4 overlay = SAMPLE_TEXTURE2D_X(_OverlayTexture, sampler_LinearClamp, input.texcoord);
    }
    
  8. 创建一个if语句来检查着色器是否应应用色调映射。如果着色器应应用色调映射。使用ApplyTonemapping将其应用于叠加层的方法。

    half4 Frag(Varyings input) : SV_Target
    {
        half4 color = FragBlit(input, sampler_LinearClamp);
    
        half4 overlay = SAMPLE_TEXTURE2D_X(_OverlayTexture, sampler_LinearClamp, input.texcoord);
    
        #if TONEMAPPING
        overlay.rgb = ApplyTonemapping(overlay.rgb, TEXTURE2D_ARGS(_InternalLut, sampler_LinearClamp), LutParams, PostExposure);
        #endif
    }
    
  9. 将叠加层与原始相机颜色混合并返回结果。

    half4 Frag(Varyings input) : SV_Target
    {
        half4 color = FragBlit(input, sampler_LinearClamp);
    
        half4 overlay = SAMPLE_TEXTURE2D_X(_OverlayTexture, sampler_LinearClamp, input.texcoord);
    
        #if TONEMAPPING
        overlay.rgb = ApplyTonemapping(overlay.rgb, TEXTURE2D_ARGS(_InternalLut, sampler_LinearClamp), LutParams, PostExposure);
        #endif
    
        color.rgb = color.rgb * (1.0 - overlay.a) + overlay.rgb * overlay.a;
        return color;
    }
    

着色器现已完成,可在CustomOverlayRenderPassCustomOverlayRendererFeature 脚本一段代码,允许您创建自己的组件、触发游戏事件、随时间修改组件属性以及以您喜欢的任何方式响应用户输入。更多信息
请参阅术语表
.

若要查看本部分的完整代码,请参阅自定义覆盖着色器代码

完成自定义叠加

要完成自定义叠加,您必须设置您创建的脚本,以将其效果应用于场景中的渲染器。以下步骤演示了如何执行此作。

  1. 查找并选择活动URP资源使用的主渲染器。
  2. 在“检查器”窗口中,选择“添加渲染器功能”>“自定义叠加渲染器功能”以添加CustomOverlayRendererFeature脚本。
  3. 分配CustomOverlayBlitshader 添加到自定义叠加可脚本渲染器功能的 Shader 属性。
  4. 分配OverlayRenderTexture到自定义叠加可脚本渲染器功能的叠加纹理属性。

自定义叠加现已完成,应显示在播放模式下主摄像机输出的顶部。叠加层的色调映射应与主摄像头输出相同,没有明显的差异。这应该类似于下面的屏幕截图。

立方体位于游戏视图中间,从另一个角度将立方体作为叠加层,色调映射以匹配HDR输出
立方体位于游戏视图的中间,从另一个角度将立方体作为叠加层,色调映射以匹配HDR输出。

注意:最终结果可能会因叠加摄像机的位置而异。

完整的代码示例

自定义叠加渲染密码

以下是示例中可编写脚本渲染通道的完整代码示例。

using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
using UnityEngine.Rendering.RenderGraphModule;

[SupportedOnRenderer(typeof(UniversalRendererData))] -->
public class CustomOverlayRenderPass : ScriptableRenderPass
{
    Material passMaterial;
    RTHandle overlayTextureHandle;

    public CustomOverlayRenderPass(Material material)
    {
        passMaterial = material;
        profilingSampler = new ProfilingSampler(nameof(CustomOverlayRenderPass));

        // The render pass is executed after post processing, so the main camera target has been tonemapped but not the overlay texture
        renderPassEvent = RenderPassEvent.AfterRenderingPostProcessing;
    }

    public void Setup(Texture overlayTex)
    {
        //Create an RTHandle from the overlay texture, to import it into the render graph system
        if (overlayTextureHandle != overlayTex)
        {
            overlayTextureHandle?.Release();
            overlayTextureHandle = RTHandles.Alloc(overlayTex);
        }
    }

    public void Dispose()
    {
        overlayTextureHandle?.Release();
    }

    class CopyData
    {
        public TextureHandle source;
    }

    class PassData
    {
        public TextureHandle source;
        public TextureHandle overlayTexture;
        public TextureHandle internalLut;
        public Vector4 lutParams;
        public Material material;
    }
    
    public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
    {
        UniversalPostProcessingData postProcessingData = frameData.Get<UniversalPostProcessingData>();
        UniversalResourceData resourceData = frameData.Get<UniversalResourceData>();
        UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();

        TextureHandle activeCameraColor = resourceData.activeColorTexture;

        // Create a texture to copy the active camera color target into
        RenderTextureDescriptor colorCopyDescriptor = cameraData.cameraTargetDescriptor;
        colorCopyDescriptor.depthBufferBits = (int) DepthBits.None;
        TextureHandle copiedColor = UniversalRenderer.CreateRenderGraphTexture(renderGraph, colorCopyDescriptor, "_CustomCameraColorCopy", false);

        // Copy the active camera color target into the texture
        using (var builder = renderGraph.AddRasterRenderPass<CopyData>("Custom Overlay Render Pass - Copy Camera", out var passData))
        {
            passData.source = activeCameraColor;
            builder.UseTexture(passData.source, AccessFlags.Read);
            builder.SetRenderAttachment(copiedColor, 0, AccessFlags.WriteAll);

            builder.SetRenderFunc((CopyData data, RasterGraphContext context) =>
            {
                Blitter.BlitTexture(context.cmd, data.source, new Vector4(1, 1, 0, 0), 0.0f, false);
            });
        }

        using (var builder = renderGraph.AddRasterRenderPass<PassData>("Custom Overlay Render Pass - Blit Overlay", out var passData))
        {
            passData.material = passMaterial;

            builder.SetRenderAttachment(activeCameraColor, 0, AccessFlags.Write);

            passData.source = copiedColor;
            builder.UseTexture(passData.source, AccessFlags.Read);

            // Import the overlay texture that will be copied onto the camera color, and set it as an input
            passData.overlayTexture = renderGraph.ImportTexture(overlayTextureHandle);
            builder.UseTexture(passData.overlayTexture, AccessFlags.Read);

            // If post-processing is enabled on the main camera, apply the tonemapping to the overlay texture as well
            // If post processing is disabled, the HDR color conversion will be applied after this render pass and the expected colorspace for the cameras output is the default Rec709
            if (postProcessingData.gradingMode == ColorGradingMode.HighDynamicRange && cameraData.postProcessEnabled)
            {
                // Import the internal color LUT texture used for HDR color grading and tonemapping
                // This includes any HDR color conversion URP needs for the display, so the output of the camera is in the display's color gamut
                passData.internalLut = resourceData.internalColorLut;
                builder.UseTexture(passData.internalLut, AccessFlags.Read);

                // Pass LUT parameters to the shader
                int lutHeight = postProcessingData.lutSize;
                int lutWidth = lutHeight * lutHeight;

                float postExposure = 1.0f;
                ColorAdjustments colorAdjustments = VolumeManager.instance.stack.GetComponent<ColorAdjustments>();
                if (colorAdjustments != null)
                {
                    postExposure = Mathf.Pow(2.0f, colorAdjustments.postExposure.value);
                }

                passData.lutParams = new Vector4(1f / lutWidth, 1f / lutHeight, lutHeight - 1f, postExposure);
            }

            builder.SetRenderFunc((PassData data, RasterGraphContext context) =>
            {
                // Pass parameters to the shader
                data.material.SetTexture("_OverlayTexture", data.overlayTexture);

                // Set a keyword on the shader to enable tonemapping
                bool tonemappingActive = data.internalLut.IsValid();
                CoreUtils.SetKeyword(data.material, "TONEMAPPING", tonemappingActive);
                if (tonemappingActive)
                {
                    data.material.SetTexture("_InternalLut", data.internalLut);
                    data.material.SetVector("_InternalLut_Params", data.lutParams);
                }
                
                // Blit the overlay texture onto the camera color
                Blitter.BlitTexture(context.cmd, data.source, new Vector4(1, 1, 0, 0), data.material, 0);
            });
        }
    }
}

自定义叠加可编写脚本渲染器功能代码

以下是示例中可编写脚本渲染器功能的完整代码示例。

using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;

public class CustomOverlayRendererFeature : ScriptableRendererFeature
{
    public Shader hdrShader;
    public RenderTexture passOverlayTexture;

    Material passMaterial;

    CustomOverlayRenderPass overlayRenderPass = null;

    public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
    {
        // Render the overlay onto the main camera during Game view rendering only, for the last camera in the camera stack
        if (renderingData.cameraData.cameraType != CameraType.Game || !renderingData.cameraData.resolveFinalTarget)
            return;

        // Pass the overlay texture at runtime in case it changes
        overlayRenderPass.Setup(passOverlayTexture);

        // Enqueue the render pass to be executed
        renderer.EnqueuePass(overlayRenderPass);
    }

    public override void Create()
    {
        // Create a blit material from the given shader
        passMaterial = CoreUtils.CreateEngineMaterial(hdrShader);

        // Create the render pass
        overlayRenderPass = new CustomOverlayRenderPass(passMaterial);
    }

    protected override void Dispose(bool disposing)
    {
        // Destroy the render pass resources
        CoreUtils.Destroy(passMaterial);
        overlayRenderPass.Dispose();
    }
}

自定义覆盖着色器代码

下面是示例中着色器的完整代码示例。

Shader "Custom/CustomOverlayBlit"
{
    SubShader
    {
        Tags{ "RenderPipeline" = "UniversalPipeline" }

        Pass
        {
            ZWrite Off ZTest Always Blend Off Cull Off

            HLSLPROGRAM
                #pragma target 2.0
                #pragma editor_sync_compilation
                #pragma vertex Vert
                #pragma fragment Frag
                #pragma multi_compile_local_fragment _ TONEMAPPING

                #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
                #include "Packages/com.unity.render-pipelines.core/Runtime/Utilities/Blit.hlsl"
                #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl"
                
                TEXTURE2D(_InternalLut);
                TEXTURE2D_X(_OverlayTexture);

                float4 _InternalLut_Params;

                #define LutParams _InternalLut_Params.xyz
                #define PostExposure _InternalLut_Params.w

                half3 ApplyTonemapping(half3 input, TEXTURE2D_PARAM(lutTex, lutSampler), float3 lutParams, float exposure)
                {
                    input *= exposure;
                    float3 inputLutSpace = saturate(LinearToLogC(input)); // LUT space is in LogC
                    return ApplyLut2D(TEXTURE2D_ARGS(lutTex, lutSampler), inputLutSpace, lutParams);
                }

                half4 Frag(Varyings input) : SV_Target
                {
                    // Get the original camera color
                    half4 color = FragBlit(input, sampler_LinearClamp);

                    // Get the overlay color
                    half4 overlay = SAMPLE_TEXTURE2D_X(_OverlayTexture, sampler_LinearClamp, input.texcoord);

                    // Tonemap the overlay
                    #if TONEMAPPING
                    overlay.rgb = ApplyTonemapping(overlay.rgb, TEXTURE2D_ARGS(_InternalLut, sampler_LinearClamp), LutParams, PostExposure);
                    #endif

                    // Blend overlay and color
                    color.rgb = color.rgb * (1.0 - overlay.a) + overlay.rgb * overlay.a;
                    return color;
                }

            ENDHLSL
        }
    }
}
在URP中启用HDR输出
在URP中使用HDR输出的可脚本渲染通道问题排查