zt3ff3n 发表于 2022-7-4 18:42

unity urp 14 render feature实现简单后处理系统

一、思路

在unity urp中,unity官方不像在build in管线种一样提供了后处理的接口,所以需要借助render feature来实现后处理效果。
总的思路就是,render feature可以获取到某一时刻绘制出来的图像(比如全部不透明物体,或者全部物体绘制后),之后获取到这个图像进行处理,再将处理的结果放回到渲染管线中。
1.1流程

首先要建立一个类继承ScriptableRendererFeature
public class BrightnessPosetProcessing : ScriptableRendererFeature
{
   //...
}然后创建一个自己的pass类
public class BrightnessPosetProcessing : ScriptableRendererFeature
{
    //*************************************************
    //**********      自定义Pass类   **************
    //**************************************************
    class CustomRenderPass : ScriptableRenderPass
    {
       //....
    }
}之后在Create接口中,对自己的Pass进行初始化(这里也是最开始运行的地方),注意pass的声明要写在函数外
    private CustomRenderPass pass;
    //*************************************************
    //**********          初始化      **************
    //**************************************************
    public override void Create()
    {
      pass = new CustomRenderPass(setting.passEvent, setting.mat, name);
    }最后,获取相机输出的图像,使用该pass处理,然后将这个pass添加进渲染管线中
    private CustomRenderPass pass;
    //*************************************************
    //**********          初始化      **************
    //**************************************************
    public override void Create()
    {
      pass = new CustomRenderPass(setting.passEvent, setting.mat, name);
    }
    //*************************************************
    //**********      将pass添加到渲染管线   **************
    //**************************************************
    public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
    {
      //获取当前相机中的图像
      var src = renderer.cameraColorTarget;
      pass.SetUp(src);
      //将该pass添加到渲染管线中
      renderer.EnqueuePass(pass);
    }1.2核心实现

以上流程中的核心是自定义pass的实现,而自定义pass中的核心是Execute函数的实现,这个函数会在每一帧绘制的时候被调用。
      public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
      {
            //从命令缓冲区池获取一个带标签的命令缓冲区,该标签名在帧调试器中可以见到
            CommandBuffer cmd = CommandBufferPool.Get(passTag);
            //获取目标相机的描述信息,创建一个结构体,里面有render texture各中信息,比如尺寸,深度图精度等等
            RenderTextureDescriptor opaqueDesc = renderingData.cameraData.cameraTargetDescriptor;
            //设置深度缓冲区,0表示不需要深度缓冲区
            opaqueDesc.depthBufferBits = 0;
            //申请临时图像
            cmd.GetTemporaryRT(passTempleColorTex.id, opaqueDesc);
            //将源图像放入材质中计算,然后存储到临时缓冲区中
            cmd.Blit(passSource, passTempleColorTex.Identifier(), passMat);
            //将临时缓冲区的结果存回源图像中
            cmd.Blit(passTempleColorTex.Identifier(), passSource);
            //执行命令缓冲区
            context.ExecuteCommandBuffer(cmd);
            //释放命令缓存
            CommandBufferPool.Release(cmd);
            //释放临时render texture
            cmd.ReleaseTemporaryRT(passTempleColorTex.id);
      }二、代码

SimplePostProcessing.shader
Shader "Unlit/16_SimplePostProcessing"
{
    Properties
    {
      _MainTex ("Texture", 2D) = "white" {}
      _Brightness("Brightness", Range(0,1)) = 1
      _Saturate("Saturate", Range(0,1)) = 1
      _Contrast("Constrast", Range(-1,2)) = 1
    }
    SubShader
    {
      Tags
      {
            "RenderPipeline"="UniversalRenderPipeline"
      }
      Cull Off
      ZWrite Off
      ZTest Always
      
      HLSLINCLUDE
      #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
      CBUFFER_START(UnityPerMaterial)
      float _Brightness;
      float _Saturate;
      float _Contrast;      
      CBUFFER_END
      TEXTURE2D(_MainTex);
      SAMPLER(sampler_MainTex);
      struct Attributes
      {
            float4 positionOS: POSITION;
            float2 texcoord: TEXCOORD0;
      };
      struct Varyings
      {
            float4 positionCS:SV_POSITION;
            float2 uv:TEXCOORD0;
      };
      ENDHLSL

      Pass
      {
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            Varyings vert(Attributes input)
            {
                Varyings output;
                output.positionCS = TransformObjectToHClip(input.positionOS);
                output.uv = input.texcoord;
                return output;
            }

            half4 frag (Varyings input) : SV_Target
            {
                half4 tex = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, input.uv);
                float grey = 0.21*tex.x+0.72*tex.y+0.072*tex.z;
                tex.xyz *= _Brightness;
                tex.xyz = lerp(float3(grey,grey,grey), tex.xyz, _Saturate);
                tex.xyz = lerp(float3(0.5,0.5,0.5),tex.xyz, _Contrast);
                return tex;
            }
            ENDHLSL
      }
    }
}
BrightnessPostProcessing.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;

public class BrightnessPosetProcessing : ScriptableRendererFeature
{
    //定义配置类
   
    public class Setting
    {
      //默认在透明物体绘制完成后进行处理
      public RenderPassEvent passEvent = RenderPassEvent.AfterRenderingTransparents;
      //后续需要用到的材质
      public Material mat;
    }
   
    public Setting setting = new Setting();
   
    //*************************************************
    //**********      自定义Pass类   **************
    //**************************************************
    class CustomRenderPass : ScriptableRenderPass
    {
      //后处理用到的材质
      public Material passMat = null;
      private RenderTargetIdentifier passSource { get; set; }//源图像,目标图像
      private RenderTargetHandle passTempleColorTex;//临时计算的图像
      //标签名,用于在帧调试器中显示缓冲区的名称
      private string passTag;
      
      //构造函数
      public CustomRenderPass(RenderPassEvent passEvent, Material material, string tag)
      {
            this.renderPassEvent = passEvent;
            this.passMat = material;
            passTag = tag;
      }

      //设置Pass
      public void SetUp(RenderTargetIdentifier source)
      {
            this.passSource = source;
      }

      public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
      {
            //从命令缓冲区池获取一个带标签的命令缓冲区,该标签名在帧调试器中可以见到
            CommandBuffer cmd = CommandBufferPool.Get(passTag);
            //获取目标相机的描述信息,创建一个结构体,里面有render texture各中信息,比如尺寸,深度图精度等等
            RenderTextureDescriptor opaqueDesc = renderingData.cameraData.cameraTargetDescriptor;
            //设置深度缓冲区,0表示不需要深度缓冲区
            opaqueDesc.depthBufferBits = 0;
            //申请临时图像
            cmd.GetTemporaryRT(passTempleColorTex.id, opaqueDesc);
            //将源图像放入材质中计算,然后存储到临时缓冲区中
            cmd.Blit(passSource, passTempleColorTex.Identifier(), passMat);
            //将临时缓冲区的结果存回源图像中
            cmd.Blit(passTempleColorTex.Identifier(), passSource);
            //执行命令缓冲区
            context.ExecuteCommandBuffer(cmd);
            //释放命令缓存
            CommandBufferPool.Release(cmd);
            //释放临时render texture
            cmd.ReleaseTemporaryRT(passTempleColorTex.id);
      }
    }

    private CustomRenderPass pass;
    //*************************************************
    //**********          初始化      **************
    //**************************************************
    public override void Create()
    {
      pass = new CustomRenderPass(setting.passEvent, setting.mat, name);
    }

    //*************************************************
    //**********      将pass添加到渲染管线   **************
    //**************************************************
    public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
    {
      //获取当前相机中的图像
      var src = renderer.cameraColorTarget;
      pass.SetUp(src);
      //将该pass添加到渲染管线中
      renderer.EnqueuePass(pass);
    }
}
三、结果





四、参考

[笔记]URP-屏幕后处理
为了更好用的后处理——扩展URP后处理踩坑记录
urp管线的自学hlsl之路 第十七篇 建立自己的后处理系统并调整屏幕的亮度饱和度对比度
页: [1]
查看完整版本: unity urp 14 render feature实现简单后处理系统