Unity 6.3
0 онлайн 42 гостей 3 в системе
Вход

Реализовать HDR Выход совместимые пользовательские накладки в URP

На этой странице показано, как создать компонент скриптового рендерера, выполняющий следующие действия:

  • Композиция выхода одной камеры на другую камеру.
  • Применяет тоновое отображение, чтобы сделать настраиваемое наложение совместимым с эффектами выхода HDR.

Это включает в себя шейдер, который применяет тоновое отображение к накладке, в зависимости от порядка кадров.

См. Введение в функции скриптового рендеринга и Введение в скриптовые рендеринговые проходы для более подробной информации.

Этот пример разделен на следующие разделы:

Предварительные условия

В этом примере предполагается следующее:

  • Проект Unity использует URP в качестве активного конвейера отображения.
  • Проект настроен для рендеринга HDR со следующими настройками:
  • Активный ресурс URP имеет режим Режим сортировки установлен на High Dynamic Range.
  • У активного ассета URP включены HDR.
  • Установки Project включают HDR Вывод.

Настройка сцены

Для того, чтобы пример работал правильно, вы должны сначала настроить примерную сцену, как показано в следующих инструкциях.

  1. Создайте куб GameObject и установите его позицию в точке начала сцены (X: 0, Y: 0, Z: 0).

  2. Выравнить Main Camera так, чтобы куб был четко виден.

  3. Создать новую камеру и назвать ее Оверлей Camera.

  4. Расположите накладную камеру справа от Main Camera и выравнивайте ее так, чтобы куб был четко виден.

  5. Установите свойство накладной камеры Тип фона на Твердый цвет в окне Inspector.

  6. Установите цвет фона накладки камеры на чисто чёрный с RGBA значениями 0, 0, 0, 0.

  7. Создайте текстуру рендеринга и назовите ее OverlayRenderTexture. Чтобы создать текстуру рендеринга, перейдите в Ассеты > Создать > Рендеринг > Render Texture.

    Примечание: Для лучшей точности HDR используйте формат текстуры рендеринга с плавающим знаком. Для этого выберите текстуру рендеринга, а затем в окне Inspector измените Формат цвета на формат с суффиксом _SFLOAT.

  8. Назначьте текстуру рендеринга накладки свойству Output Texture накладной камеры. Для этого откройте Overlay Camera в Inspector и перейдите в Выпуск > Output Texture и выберите OverlayRenderTexture из списка ассетов.

  9. Создайте новый ассет Universal Renderer для накладной камеры и назовите его OverlayRenderer. Для этого перейдите в Ассеты > Создать > Рендеринг > URP Universal Renderer.

  10. Выберите активный ресурс URP, затем в окне Inspector перейдите в Рендирование > Список рендереров > +. Выберите OverlayRenderer. Это добавит рендерер накладки в список рендереров.

  11. Выберите накладную камеру, затем в окне Inspector перейдите в Рендеринг > Рендерер. Выберите OverlayRenderer. Это настроит накладную камеру на использование накладного рендера.

Сцена теперь готова для создания настраиваемого наложения с помощью Scriptable Renderer Features.

Создание настраиваемого наложения Render Pass

Чтобы создать настраиваемое наложение, совместимое с HDR Output, необходимо использовать Scriptable Render Pass для создания наложения. HDR Output применяет тоновое отображение к выводу главной камеры во время пост-обработки. В результате выход главной камеры и накладной камеры имеет разное тоновое отображение. Этот проход рендеринга затем происходит после пост-обработки для применения тонового отображения к выводу накладной камеры.

Чтобы создать проход отображения для данного примера, выполните следующие действия:

  1. Создайте скрипт C# и назовите его CustomOverlayRenderPass.

  2. В скрипте удалите код, который Unity вставил в CustomOverlayRenderPass class.

  3. Добавьте следующие директивы using.

    using UnityEngine;
    using UnityEngine.Rendering;
    using UnityEngine.Rendering.Universal;
    using UnityEngine.Rendering.RenderGraphModule;
    
  4. Создать новый CustomOverlayRenderPass класс, наследующий ScriptableRenderPass класс и имеет атрибут [SupportedOnRenderer(typeof(UniversalRendererData) -->.

    [SupportedOnRenderer(typeof(UniversalRendererData))] -->
    public class CustomOverlayRenderPass : ScriptableRenderPass
    {
      
    }
    
  5. Добавьте свойства Material passMaterial и RTHandle 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 взаимодействовать с текстурой рендеринга накладки.

    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 class.

  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. Настройте свойства, которые необходимы для того, чтобы проход рендеринга смог очистить текстуру накладки, как показано ниже.

    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, задайте внутреннюю текстуру цветовой LUT, которую HDR принимает на вход, и передайте её параметры в шейдер.

    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. Установите ключевое слово на шейдере, чтобы включить отображение тонов, и добавьте команду для переноса текстуры накладки на цветовую цель активной камеры.

    builder.SetRenderFunc(static (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, готовый для добавления в рендерер с помощью функции Scriptable Renderer Feature.

Полный код для этого раздела см. в Код пользовательского Render Pass для наложения.

Создание настраиваемого наложения Скриптовый отображатель

Чтобы добавить CustomOverlayRenderPass в рендерер, необходимо создать Scriptable Renderer Feature, выполнив следующие действия.

  1. Создайте скрипт C# и назовите его CustomOverlayRendererFeature.

  2. В скрипте удалите код, который Unity вставил в CustomOverlayRendererFeature class.

  3. Добавьте следующие директивы using.

    using UnityEngine;
    using UnityEngine.Rendering;
    using UnityEngine.Rendering.Universal;
    
  4. Настройка нового CustomOverlayRendererFeature класс, наследующий ScriptableRendererFeature class.

    public class CustomOverlayRendererFeature : ScriptableRendererFeature
    {
      
    }
    
  5. Добавьте следующие свойства, чтобы содержать ассеты и данные, необходимые для прохождения отображения.

    public class CustomOverlayRendererFeature : ScriptableRendererFeature
    {
        public Shader hdrShader;
        public RenderTexture passOverlayTexture;
    
        Material passMaterial;
    
        CustomOverlayRenderPass overlayRenderPass = null;
    }
    
  6. Создайте метод AddRenderPasses и используйте его для применения наложения только в представлении Game и к последней камере в стеке камер.

    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();
    }
    

Полный код для этого раздела см. в Настраиваемое наложение Скриптовый код функции рендеринга.

Создание настраиваемого накладного шейдера

Материал, созданный CustomOverlayRendererFeature, требует настраиваемого шейдера для обработки наложения и изменений выхода 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 на значение exposure, затем 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;
    }
    

Сейдер теперь завершен и готов для использования в скриптах CustomOverlayRenderPass и CustomOverlayRendererFeature.

Чтобы увидеть полный код для этого раздела, см. Настраиваемый код шейдера наложения.

Завершение настраиваемого наложения

Чтобы завершить создание настраиваемого наложения, вы должны настроить созданные вами скрипты, чтобы применить их эффекты к рендерам в сцене. Следующие шаги демонстрируют, как это сделать.

  1. Найдите и выберите основной рендерер, используемый активным ассетом URP.
  2. В окне Inspector выберите Добавить функцию рендерера > Настраиваемая функция рендерера накладок, чтобы добавить скрипт CustomOverlayRendererFeature.
  3. Назначьте шейдер CustomOverlayBlit свойству Шейдер настраиваемого наложения Scriptable Renderer Feature.
  4. Назначьте OverlayRenderTexture свойству Overlay Texture настраиваемого элемента Scriptable Renderer Feature.

Настраиваемое наложение теперь завершено и должно появиться над основным выходом камеры в режиме воспроизведения. Наложение должно быть тонировано так же, как и основной выход камеры, без видимых различий. Это должно быть похоже на скриншот ниже.

Куб в середине изображения Game с кубом под другим углом в качестве наложения, тональное отображение для соответствия HDR Выход
Куб в середине Game с кубом под другим углом в качестве наложения, тональное отображение для соответствия HDR Выход.

Примечание: Конечный результат может отличаться в зависимости от расположения накладной камеры.

Полные образцы кода

Настраиваемый пароль отображения накладок

Ниже приведен полный образец кода для Scriptable Render Pass из примера.

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(static (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);
            });
        }
    }
}

Настраиваемое наложение Скриптовый рендер Код функции

Ниже приведен полный образец кода для функции Scriptable Renderer Feature из примера.

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
        }
    }
}