Neilyodog Blog

renderFeature 基础

小学三年级都会写的renderFeature

创建Render Feature

Create-Rendering-Universal Render Pipeline-Render Feature

模板

  • 学习用
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;


// 首先由两个类组成
// 继承自 ScriptableRendererFeature 的 CustomRenderPassFeature
// 继承自 ScriptableRenderPass 的 CustomRenderPass


// CustomRenderPassFeature 负责把 Render Pass添加到 Renderer里面。
// Render Feature 在 renderpipline的任意阶段
public class CustomRenderPassFeature : ScriptableRendererFeature
{
    // 室实际的渲染工作
    // 在 ScriptableRenderPass 里,可以在狗仔甘薯里面去指定了 Event 在什么时间点执行(默认是不透明之后画)
    class CustomRenderPass : ScriptableRenderPass
    {
        // Configure() 在执行渲染过程之前,Renderer 将调用次方法。如果需要配置渲染目标及其清除状态,并创建临时渲染目标纹理,那就要重写这个方法。
        // 如果渲染过程未重写这个方法,则该渲染过程将渲染到激活状态下 Camera 的渲染目标。
        // 在每次 Execute 调用的前一帧调用,用来设置传递需要的东西
        public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor)
        {
        }
        // https://docs.unity3d.com/ScriptReference/Rendering.ScriptableRenderContext.html
        // Execute() 是这个类的核心方法,定义我们的执行规则;包含渲染逻辑,设置渲染状态,绘制渲染器或绘制程序网络,调度计算等。
        public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
        {
        }


        // 可用于释放通过此过程创建的分配资源。完成渲染相机后调用。就可以使用此回调释放此渲染过程创建的所有资源。
        public override void FrameCleanup(CommandBuffer cmd)
        {
        }
    }


    CustomRenderPass m_ScriptablePass;  // 实例化
    // Create() 用来初始化这个 Feature 的资源。这里初始化完了之后要给 AddRenderPasses 函数来在渲染中插入改pass
    public override void Create()
    {
        m_ScriptablePass = new CustomRenderPass();
        // 通过 Event 控制在render pipline的何时插入 Pass 以及顺序
        m_ScriptablePass.renderPassEvent = RenderPassEvent.AfterRenderingOpaques;
    }




    // AddRenderPasses() 在 Renderer 中插入一个或多个 ScriptableRenderPass,对这个Renderer 每个摄像机都设置一次
    // 每个相机,没帧调用一次
    public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
    {
        renderer.EnqueuePass(m_ScriptablePass);
    }
}
  • 屏幕HSV
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;


[ExecuteInEditMode] // 让编辑器在不运行状态下运行
public class mybiltxf : ScriptableRendererFeature
{
    [System.Serializable]   // renderfeature 的面板
    public class mysetting//定义一个设置的类
    {
        public string PassName = "调色";
        public Material mymat;
        [Range(0, 1)] public float Brightness = 1;
        [Range(0, 1)] public float Saturate = 1;
        [Range(-1, 2)] public float Constrast = 1;
        public RenderPassEvent passEvent = RenderPassEvent.AfterRenderingTransparents; // 增加一个可控渲染事件来决定渲染位置
    }
    public mysetting setting = new mysetting(); // 实例化
    class CustomRenderPass : ScriptableRenderPass//自定义pass
    {
        public Material passMat = null;
        public float Brightness;
        public float Saturate;
        public float Constrast;
        public string name;
        public FilterMode passfiltermode { get; set; } //图像的模式,这里设置方便整体调整,这里相当于拿相机rt的设置
        private RenderTargetIdentifier passSource { get; set; } //源图像,目标图像
        public RenderTargetIdentifier Temp1;
        string passTag; // 给cmd对象池用来调用的


        public void setup(RenderTargetIdentifier sour) // 把相机的图像copy过来
        {
            this.passSource = sour;
        }
        //类似OnRenderimagePass,图像处理部分
        public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
        {
            int TempID1 = Shader.PropertyToID("Temp1");
            int brightness = Shader.PropertyToID("_brightness");
            int saturate = Shader.PropertyToID("_saturate");
            int contranst = Shader.PropertyToID("_contranst");


            CommandBuffer cmd = CommandBufferPool.Get(passTag); // 类似于cbuffer,把整个渲染命令圈起来
            RenderTextureDescriptor opaquedesc = renderingData.cameraData.cameraTargetDescriptor;   // 拿到相机数据
            cmd.GetTemporaryRT(TempID1, opaquedesc); //申请一个临时图像
            Temp1 = new RenderTargetIdentifier(TempID1);


            cmd.SetGlobalFloat(brightness, Brightness);  // 往shader里传面板上的值
            cmd.SetGlobalFloat(saturate, Saturate);
            cmd.SetGlobalFloat(contranst, Constrast);


            cmd.Blit(passSource, Temp1, passMat,0);  //把源贴图输入到材质对应的pass里处理,并把处理结果的图像存储到临时图像;
            cmd.Blit(Temp1, passSource);             //然后把临时图像又存到源图像里


            context.ExecuteCommandBuffer(cmd); //执行命令缓冲区的该命令
            CommandBufferPool.Release(cmd); //释放该命令
        }
    }
    CustomRenderPass mypass;


    public override void Create()//进行初始化,这里最先开始
    {
        mypass = new CustomRenderPass();
        mypass.renderPassEvent = setting.passEvent; // 渲染位置
        mypass.Brightness = setting.Brightness; // 参数
        mypass.Saturate = setting.Saturate;
        mypass.Constrast = setting.Constrast;
        mypass.passMat = setting.mymat; // 读取材质球
        mypass.name = setting.PassName; // frameDebug 里的渲染名字
    }
    //传值到pass里
    public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
    {
        mypass.setup(renderer.cameraColorTarget);
        renderer.EnqueuePass(mypass);   // 设置到渲染流程中
    }
}

shader

Shader "WX/URP/Post/post"
{


    Properties
    {
        [HideInInspector]_MainTex ("MainTex", 2D) = "white" {}
        // _brightness ("Brightness", Range(0, 1)) = 1
        // _saturate ("Saturate", Range(0, 1)) = 1
        // _contranst ("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 _contranst;
        CBUFFER_END

        TEXTURE2D(_MainTex);
        SAMPLER(sampler_MainTex);


        struct a2v
        {
            float4 positionOS: POSITION;
            float2 texcoord: TEXCOORD;
        };


        struct v2f
        {
            float4 positionCS: SV_POSITION;
            float2 texcoord: TEXCOORD;
        };


        ENDHLSL


        pass
        {
            HLSLPROGRAM
            #pragma vertex VERT
            #pragma fragment FRAG


            v2f VERT(a2v i)
            {
                v2f o;
                o.positionCS = TransformObjectToHClip(i.positionOS.xyz);
                o.texcoord = i.texcoord;
                return o;
            }


            half4 FRAG(v2f i): SV_TARGET
            {
                half4 tex = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.texcoord);
                //灰度图,即计算明度。用的是Luminance公式,在sRGB空间下
                float gray = 0.21 * tex.x + 0.72 * tex.y + 0.072 * tex.z;
                tex.xyz *= _brightness;//计算亮度
                tex.xyz = lerp(float3(gray, gray, gray), tex.xyz, _saturate); //饱和度。这里相当于在去色后和当前的图中lerp了
                tex.xyz = lerp(float3(0.5, 0.5, 0.5), tex.xyz, _contranst); //对比度
                return tex;
            }
            ENDHLSL
        }
    }
}
  • Kawase
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;


[ExecuteInEditMode] // 让编辑器在不运行状态下运行
public class Kawase : ScriptableRendererFeature
{
    [System.Serializable]   // renderfeature 的面板
    public class featureSetting
    {
        public string passRenderName = "KawaseBlur";
        public Material passMat;
        [Range(1, 10)] public int downsample = 1;
        [Range(1, 10)] public int loop = 2;
        [Range(0.5f, 5)] public float blur = 0.5f;
        [Range(0, 1)] public float lerp = 1;    // 对比process前后区别
        public RenderPassEvent passEvent = RenderPassEvent.AfterRenderingTransparents;  // copy具体哪个阶段的图像
    }
    public featureSetting setting = new featureSetting();  // 实例化
    class CustomRenderPass : ScriptableRenderPass
    {
        public Material passMat = null;
        public string passName;
        public int passdownsample = 1;
        public int passloop = 2;
        public float passblur = 4;
        public float lerp = 1;
        string passTag; // 名字,方便从frameDebug里看到
        // public RenderTargetIdentifier Scoure;
        private RenderTargetIdentifier passSource { get; set; } // 源图像


        public void setup(RenderTargetIdentifier sour) // 把相机的图像copy过来,具体哪张取决于renderEvent
        {
            this.passSource = sour;
        }
        public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
        {
            // 类似shaderGUI里的传递,这里就不是setfloat了,就是拿shaderProperty的ID了,因为下面要用id创建临时图像
            int TempID1 = Shader.PropertyToID("Temp1");         // 临时图像,也可以用 Handle,ID不乱就可以
            int TempID2 = Shader.PropertyToID("Temp2");
            int Lerp = Shader.PropertyToID("_Lerp");
            int ScoureID = Shader.PropertyToID("_SourceTex");   // 为了方便对比prosses前后的区别


            CommandBuffer cmd = CommandBufferPool.Get(passTag); // 类似于cbuffer,把整个渲染命令圈起来
            RenderTextureDescriptor getCameraData = renderingData.cameraData.cameraTargetDescriptor;   // 拿到相机数据,方便创建共享屬性的rt
            int width = getCameraData.width / passdownsample;   // downSample
            int height = getCameraData.height / passdownsample;


            // 获取临时渲染纹理 注意hdr的支持
            cmd.GetTemporaryRT(TempID1, width, height, 0, FilterMode.Bilinear, RenderTextureFormat.DefaultHDR); // 申请一个临时图像,并设置相机rt的参数进去
            cmd.GetTemporaryRT(TempID2, width, height, 0, FilterMode.Bilinear, RenderTextureFormat.DefaultHDR);
            cmd.GetTemporaryRT(ScoureID, getCameraData);


            RenderTargetIdentifier Temp1 = TempID1;
            RenderTargetIdentifier Temp2 = TempID2;
            RenderTargetIdentifier Scoure = ScoureID;
            // Scoure = new RenderTargetIdentifier(ScoureID);   // 感觉上下两种效果上区别不大


            // 设置参数
            cmd.SetGlobalFloat("_Blur", 1f);
            cmd.SetGlobalFloat(Lerp, lerp);
            // 拷贝
            cmd.Blit(passSource, Scoure);           // 把原始图像存起来做lerp操作,对比前后变化
            cmd.ReleaseTemporaryRT(ScoureID);       // 释放 RT
            cmd.Blit(passSource, Temp1, passMat);   //把源贴图输入到材质对应的pass里处理,并把处理结果的图像存储到临时图像;
            for (int t = 1; t < passloop; t++)      // 每次循环相当于把已经模糊的图片放进来进行模糊运算
            {
                cmd.SetGlobalFloat("_Blur", t * passblur); // 1.5
                cmd.Blit(Temp1, Temp2, passMat);
                var temRT = Temp1;
                Temp1 = Temp2;
                Temp2 = temRT;
            }
            // cmd.SetGlobalFloat("_Blur", passloop * passblur); // 这里相当于在loop外又模糊一次,还不如直接调 passloop 的参数
            // cmd.Blit(Temp1, passSource, passMat);
            cmd.Blit(Temp1, passSource);


            // 释放
            cmd.ReleaseTemporaryRT(TempID1);    // 看起来好像如果不释放的话就会一直运行,会影响 passSource 的 Tex 图像
            cmd.ReleaseTemporaryRT(TempID2);
            context.ExecuteCommandBuffer(cmd);  //执行命令缓冲区的该命令
            CommandBufferPool.Release(cmd);     //释放该命令
        }
    }
    CustomRenderPass m_ScriptablePass;
    public override void Create()
    {
        m_ScriptablePass = new CustomRenderPass();
        m_ScriptablePass.renderPassEvent = setting.passEvent;   // 渲染位置
        m_ScriptablePass.passMat = setting.passMat;             // 材质
        m_ScriptablePass.passName = setting.passRenderName;     // 渲染名称
        m_ScriptablePass.passblur = setting.blur;               // 变量
        m_ScriptablePass.passloop = setting.loop;
        m_ScriptablePass.passdownsample = setting.downsample;
        m_ScriptablePass.lerp = setting.lerp;
    }


    public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
    {
        m_ScriptablePass.setup(renderer.cameraColorTarget);     // 通过setup函数设置不同的渲染阶段的渲染结果进 passSource 里面
        renderer.EnqueuePass(m_ScriptablePass);                 // 执行
    }
}

shader

Shader "WX/URP/Post/Kawaseblur"
{
    Properties
    {
      _MainTex("MainTex",2D) = "white"{}
    //_Blur("Blur",float)=2
      // _Lerp("对比前后区别",range(0,1)) = 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 _Blur,_Lerp;
        float4 _MainTex_TexelSize;
        CBUFFER_END


        TEXTURE2D(_MainTex);
        SAMPLER(sampler_MainTex);
        TEXTURE2D(_SourceTex); // 原图
        SAMPLER(sampler_SourceTex);


         struct a2v
         {
             float4 positionOS:POSITION;
             float2 texcoord:TEXCOORD;
         };


         struct v2f
         {
             float4 positionCS:SV_POSITION;
             float2 texcoord:TEXCOORD;
         };
         v2f VERT(a2v i)//水平方向的采样
            {
                v2f o;
                o.positionCS = TransformObjectToHClip(i.positionOS.xyz);
                o.texcoord = i.texcoord;
                return o;
            }
         half4 FRAG(v2f i) :SV_TARGET
            {
                float4 col;
                half4 tex = SAMPLE_TEXTURE2D(_MainTex,sampler_MainTex,i.texcoord);
                // 其实感觉这里的意思是 UV + 方向 * 贴图大小的像素距离 * 偏移量
                tex += SAMPLE_TEXTURE2D(_MainTex,sampler_MainTex,i.texcoord + float2(-1,-1) * _MainTex_TexelSize.xy * _Blur);
                tex += SAMPLE_TEXTURE2D(_MainTex,sampler_MainTex,i.texcoord + float2(1,-1) * _MainTex_TexelSize.xy * _Blur);
                tex += SAMPLE_TEXTURE2D(_MainTex,sampler_MainTex,i.texcoord + float2(-1,1) * _MainTex_TexelSize.xy * _Blur);
                tex += SAMPLE_TEXTURE2D(_MainTex,sampler_MainTex,i.texcoord + float2(1,1) * _MainTex_TexelSize.xy * _Blur);
                tex /= 5;
                float3 Source = SAMPLE_TEXTURE2D(_SourceTex,sampler_SourceTex,i.texcoord).rgb;
                tex.rgb = lerp(Source,tex.rgb,_Lerp);
                // return half4(Source,1);  // debug
                return tex;
            }
        ENDHLSL


        pass
        {
            HLSLPROGRAM
            #pragma vertex VERT
            #pragma fragment FRAG
            ENDHLSL
        }
    }
}
  • 径向模糊
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
// 让编辑器在不运行状态下运行
[ExecuteInEditMode]
public class radialBlur : ScriptableRendererFeature
{
    // 让这些设置可以显示在面板上
    [System.Serializable]
    public class setting
    {
        public string PassName = "径向模糊";
        public Material RadialBlurMat = null;
        [Range(0, 1)] public float x = 0.5f;
        [Range(0, 1)] public float y = 0.5f;
        [Range(1, 24)] public int loop = 5;
        [Range(1, 16)] public float blur = 3;
        [Range(1, 4)] public int downsample = 2;
        [Range(0, 1)] public float instensity = 1;
        public RenderPassEvent passEvent = RenderPassEvent.AfterRenderingTransparents;
    }
    public setting mysetitng = new setting(); // 实例化
    class CustomRenderPass : ScriptableRenderPass
    {
        public Material mymat;
        public string name;
        public float x;
        public float y;
        public int loop;
        public float instensity;
        public float blur;
        public int downsample;
        public RenderTargetIdentifier Source { get; set; }
        //public RenderTargetIdentifier BlurTex;
        public RenderTargetIdentifier Temp1;
        public RenderTargetIdentifier Temp2;
        int ssW;
        int ssH;
        public void setupmypass(RenderTargetIdentifier source)
        {
            this.Source = source;
        }
        // 执行
        public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
        {
            // int BlurTexID = Shader.PropertyToID("_BlurTex"); // 模糊前的,shader 里没有这张图
            int TempID1 = Shader.PropertyToID("Temp1"); // 空的临时图像
            int TempID2 = Shader.PropertyToID("_SourceTex"); //
            int loopID = Shader.PropertyToID("_Loop");
            int Xid = Shader.PropertyToID("_X");
            int Yid = Shader.PropertyToID("_Y");
            int BlurID = Shader.PropertyToID("_Blur");
            int instenID = Shader.PropertyToID("_Instensity");  // 其实就是pass2 lerp用的


            RenderTextureDescriptor SSdesc = renderingData.cameraData.cameraTargetDescriptor;   // 拿到相机数据
            ssH = SSdesc.height / downsample;
            ssW = SSdesc.width / downsample;
            CommandBuffer cmd = CommandBufferPool.Get(name); // 通过对象池来调用,大量数据处理的时候特别有用
            cmd.GetTemporaryRT(TempID1, ssW, ssH, 0, FilterMode.Bilinear, RenderTextureFormat.RGB111110Float);//用来存降采样的
            //cmd.GetTemporaryRT(BlurTexID, SSdesc);  // 模糊图前的 ,pass2 要用
            cmd.GetTemporaryRT(TempID2, SSdesc);    // 这边不降采样的图就直接拿相机的渲染设置了


            // 实例化
            Temp1 = new RenderTargetIdentifier(TempID1);
            Temp2 = new RenderTargetIdentifier(TempID2);


            cmd.SetGlobalFloat(loopID, loop);
            cmd.SetGlobalFloat(Xid, x);
            cmd.SetGlobalFloat(Yid, y);
            cmd.SetGlobalFloat(BlurID, blur);
            cmd.SetGlobalFloat(instenID, instensity);


            // 一个Blit就是FrameDebug里一次结果
            cmd.Blit(Source, Temp1);    // 存储降采样的源图,用于pass0计算模糊图
            cmd.Blit(Source, Temp2);    // 存储源图,用来lerp前后效果的(正常不应该这么用,在shader的for循环里做手脚)
            cmd.Blit(Temp1, Source, mymat, 0);//pass0的模糊计算
            // 清理
            //cmd.ReleaseTemporaryRT(BlurTexID);
            cmd.ReleaseTemporaryRT(TempID1);
            cmd.ReleaseTemporaryRT(TempID2);
            context.ExecuteCommandBuffer(cmd);  // 调用
            CommandBufferPool.Release(cmd); // 清理
        }
    }
    CustomRenderPass m_ScriptablePass;
    public override void Create()
    {
        m_ScriptablePass = new CustomRenderPass();
        m_ScriptablePass.renderPassEvent = mysetitng.passEvent;
        m_ScriptablePass.blur = mysetitng.blur;
        m_ScriptablePass.x = mysetitng.x;
        m_ScriptablePass.y = mysetitng.y;
        m_ScriptablePass.instensity = mysetitng.instensity;
        m_ScriptablePass.loop = mysetitng.loop;
        m_ScriptablePass.mymat = mysetitng.RadialBlurMat;
        m_ScriptablePass.name = mysetitng.PassName;
        m_ScriptablePass.downsample = mysetitng.downsample;
    }
    public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
    {
        if (mysetitng.RadialBlurMat != null)
        {
            m_ScriptablePass.setupmypass(renderer.cameraColorTarget);
            // 把括号里的pass根据渲染设置(是有Create设置的)丢到渲染流程中
            renderer.EnqueuePass(m_ScriptablePass);
        }
        else
        {
            Debug.LogError("径向模糊材质球丢失!");
        }
    }
}

shader

Shader"WX/URP/Post/radialBrul"
{
    Properties
    {
        _MainTex("tex",2D) = "white"{}
    }
        SubShader
    {
        Tags{"RenderPipeline" = "UniversalRenderPipeline"}
        Cull Off ZWrite Off ZTest Always


        HLSLINCLUDE
        #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
        float4 _MainTex_ST;
        float _Loop;
        float _Blur;
        float _Y;
        float _X;
        float _Instensity;
        TEXTURE2D(_MainTex);
        SAMPLER(sampler_MainTex);
        TEXTURE2D(_SourceTex); // 原图
        SAMPLER(sampler_SourceTex);


         struct a2v
         {
             float4 positionOS:POSITION;
             float2 texcoord:TEXCOORD;
         };


         struct v2f
         {
             float4 positionCS:SV_POSITION;
             float2 texcoord:TEXCOORD;
         };
        ENDHLSL


        pass
        {


            HLSLPROGRAM
            #pragma vertex VERT
            #pragma fragment FRAG


            v2f VERT(a2v i)
            {
                v2f o;
                o.positionCS = TransformObjectToHClip(i.positionOS.xyz);
                o.texcoord = i.texcoord;
                return o;
            }


            half4 FRAG(v2f i) :SV_TARGET
            {
                float4 col = 0;
                float2 dir = (i.texcoord - float2(_X,_Y)) * _Blur * 0.01;
                for (int t = 0; t < _Loop; t++)
                {
                    col += SAMPLE_TEXTURE2D(_MainTex,sampler_MainTex,i.texcoord + dir * t) / _Loop;
                }
                float4 Source = SAMPLE_TEXTURE2D(_SourceTex,sampler_SourceTex,i.texcoord);//得到屏幕原始图
                return lerp(Source,col,_Instensity);
            }
            ENDHLSL
        }
    }
}
  • real模板
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;


[ExecuteInEditMode] // 让编辑器在不运行状态下运行
public class FullScreen : ScriptableRendererFeature
{
    [System.Serializable]   // renderfeature 的面板
    public class featureSetting
    {
        public string passRenderName = "渲染pass的名字";
        public Material passMat;
        public Color MainColor;
        public RenderPassEvent passEvent = RenderPassEvent.AfterRenderingTransparents;
    }
    public featureSetting setting = new featureSetting();  // 实例化
    class CustomRenderPass : ScriptableRenderPass
    {
        public Material passMat = null;
        public string passName;
        public Color MainColor;
        string passTag; // 给cmd对象池用来调用的
        private RenderTargetIdentifier passSource { get; set; } //源图像,这里一般是cameraColorTex


        public void setup(RenderTargetIdentifier sour) // 把相机的图像copy过来
        {
            this.passSource = sour;
        }
        public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
        {
            int TempID1 = Shader.PropertyToID("_MaskTex");
            int MColor = Shader.PropertyToID("_MainColor");


            CommandBuffer cmd = CommandBufferPool.Get(passTag); // 类似于cbuffer,把整个渲染命令圈起来
            RenderTextureDescriptor getCameraData = renderingData.cameraData.cameraTargetDescriptor;   // 拿到相机数据,方便创建共享屬性的rt
            cmd.GetTemporaryRT(TempID1, getCameraData); //申请一个临时图像,并设置相机rt的参数进去
            RenderTargetIdentifier Temp1 = TempID1;
            // 设置参数
            cmd.SetGlobalColor(MColor, MainColor);
            // 拷贝
            cmd.Blit(passSource, Temp1, passMat, -1);  //把源贴图输入到材质对应的pass里处理,并把处理结果的图像存储到临时图像;
            cmd.Blit(Temp1, passSource);             //然后把临时图像又存到源图像里


            cmd.ReleaseTemporaryRT(TempID1);
            context.ExecuteCommandBuffer(cmd); //执行命令缓冲区的该命令
            CommandBufferPool.Release(cmd); //释放该命令
        }
    }
    CustomRenderPass m_ScriptablePass;
    public override void Create()
    {
        m_ScriptablePass = new CustomRenderPass();
        m_ScriptablePass.renderPassEvent = setting.passEvent;   // 渲染位置
        m_ScriptablePass.passMat = setting.passMat;             // 材质
        m_ScriptablePass.passName = setting.passRenderName;     // 渲染名称


        m_ScriptablePass.MainColor = setting.MainColor;         // 变量
    }


    public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
    {
        m_ScriptablePass.setup(renderer.cameraColorTarget);     // 通过setup函数设置不同的渲染阶段的渲染结果进 passSource 里面
        renderer.EnqueuePass(m_ScriptablePass);                 // 执行
    }
}

shader

Shader "Neilyodog/URPUnlitShaderBasic"
{
    Properties
    {
        _MainTex("主贴图",2D) = "white"{}
        // _MainColor("主颜色",color) = (1,1,1,1)
        _MaskTex("mask",2D) = "white"{}
    }
    SubShader
    {
        Tags {"Queue"="Geometry" "RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline" }
        Pass
        {
            HLSLPROGRAM


            #pragma prefer_hlslcc gles
            #pragma exclude_renderers d3d11_9x


            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_fog


            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"


            struct Attributes
            {
                float4 positionOS   : POSITION;
                float2 uv : TEXCOORD0;
            };
            struct Varyings
            {
                float4 positionHCS  : SV_POSITION;
                float2 uv : TEXCOORD0;
                float fogCoord : TEXCOORD1;
            };

            CBUFFER_START(UnityPerMaterial)
            float4 _MainTex_ST;
            float4 _MainColor;
            CBUFFER_END


            TEXTURE2D(_MainTex);
            SAMPLER(sampler_MainTex);
            TEXTURE2D(_MaskTex);
            SAMPLER(sampler_MaskTex);


            Varyings vert(Attributes v)
            {
                Varyings o;
                o.positionHCS = TransformObjectToHClip(v.positionOS.xyz);
                o.uv = TRANSFORM_TEX(v.uv,_MainTex);
                o.fogCoord = ComputeFogFactor(o.positionHCS.z);
                return o;
            }
            half4 frag(Varyings i) : SV_Target
            {
                float2 screenUV = i.positionHCS.xy / _ScreenParams.xy;
                half4 c = SAMPLE_TEXTURE2D(_MainTex,sampler_MainTex,i.uv);
                half mask = SAMPLE_TEXTURE2D(_MaskTex,sampler_MaskTex,screenUV).r;
                //return half4(mask.xxx,1);
                c *= _MainColor * mask;
                c.rgb = MixFog(c.rgb,i.fogCoord);
                return c;
            }
            ENDHLSL
        }
    }
}

源文件

  • 径向模糊

radialBlur.cs

radialBlur.shader

  • HSV

HSV.cs

HSV.shader

  • Kawase

Kawase.cs

Kawase.shader

分析

文章配图

开头贴标签

第一部分

在继承 ScriptableRendererFeature 的类里面

设置renderer面板

第二部分

在继承 ScriptableRenderPass 的类里面

  1. 声明变量

  2. 从相机上copy图像

  3. #核心#重写 Execute 函数,来控制如何渲染(记得清理缓存)

  4. 给renderer面板传递参数

  5. 添加pass

using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
// 让编辑器在不运行状态下运行
[ExecuteInEditMode]
public class radialBlur : ScriptableRendererFeature
{
    // 让这些设置可以显示在面板上
    [System.Serializable]
    public class setting
    {
        public string PassName = "径向模糊";
        public Material RadialBlurMat = null;
        [Range(0, 1)] public float x = 0.5f;
        [Range(0, 1)] public float y = 0.5f;
        [Range(1, 24)] public int loop = 5;
        [Range(1, 16)] public float blur = 3;
        [Range(1, 4)] public int downsample = 2;
        [Range(0, 1)] public float instensity = 0.5f;
        public RenderPassEvent passEvent = RenderPassEvent.AfterRenderingTransparents;
    }
    public setting mysetitng = new setting(); // 实例化
    class CustomRenderPass : ScriptableRenderPass
    {
        public Material mymat;
        public string name;
        public float x;
        public float y;
        public int loop;
        public float instensity;
        public float blur;
        public int downsample;
        public RenderTargetIdentifier Source { get; set; }
        //public RenderTargetIdentifier BlurTex;
        public RenderTargetIdentifier Temp1;
        public RenderTargetIdentifier Temp2;
        int ssW;
        int ssH;
        public void setupmypass(RenderTargetIdentifier source)
        {
            this.Source = source;
        }
        // 执行
        public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
        {
            // int BlurTexID = Shader.PropertyToID("_BlurTex"); // 模糊前的,shader 里没有这张图
            int TempID1 = Shader.PropertyToID("Temp1");
            int TempID2 = Shader.PropertyToID("_SourceTex"); //
            int loopID = Shader.PropertyToID("_Loop");
            int Xid = Shader.PropertyToID("_X");
            int Yid = Shader.PropertyToID("_Y");
            int BlurID = Shader.PropertyToID("_Blur");
            int instenID = Shader.PropertyToID("_Instensity");  // 其实就是pass2 lerp用的

            RenderTextureDescriptor SSdesc = renderingData.cameraData.cameraTargetDescriptor;   // 拿到相机数据
            ssH = SSdesc.height / downsample;
            ssW = SSdesc.width / downsample;
            CommandBuffer cmd = CommandBufferPool.Get(name); // 通过对象池来调用,大量数据处理的时候特别有用
            cmd.GetTemporaryRT(TempID1, ssW, ssH, 0, FilterMode.Bilinear, RenderTextureFormat.RGB111110Float);//用来存降采样的
            //cmd.GetTemporaryRT(BlurTexID, SSdesc);  // 模糊图前的 ,pass2 要用
            cmd.GetTemporaryRT(TempID2, SSdesc);    // 这边不降采样的图就直接拿相机的渲染设置了

            // 实例化
            Temp1 = new RenderTargetIdentifier(TempID1);
            Temp2 = new RenderTargetIdentifier(TempID2);

            cmd.SetGlobalFloat(loopID, loop);
            cmd.SetGlobalFloat(Xid, x);
            cmd.SetGlobalFloat(Yid, y);
            cmd.SetGlobalFloat(BlurID, blur);
            cmd.SetGlobalFloat(instenID, instensity);

            // 一个Blit就是FrameDebug里一次结果
            cmd.Blit(Source, Temp1);    // 存储降采样的源图,用于pass0计算模糊图
            cmd.Blit(Source, Temp2);    // 存储源图,用来lerp前后效果的(正常不应该这么用,在shader的for循环里做手脚)
            cmd.Blit(Temp1, Source, mymat, 0);//pass0的模糊计算
            // 清理
            //cmd.ReleaseTemporaryRT(BlurTexID);
            cmd.ReleaseTemporaryRT(TempID1);
            cmd.ReleaseTemporaryRT(TempID2);
            context.ExecuteCommandBuffer(cmd);  // 调用
            CommandBufferPool.Release(cmd); // 清理
        }
    }
    CustomRenderPass m_ScriptablePass;
    public override void Create()
    {
        m_ScriptablePass = new CustomRenderPass();
        m_ScriptablePass.renderPassEvent = mysetitng.passEvent;
        m_ScriptablePass.blur = mysetitng.blur;
        m_ScriptablePass.x = mysetitng.x;
        m_ScriptablePass.y = mysetitng.y;
        m_ScriptablePass.instensity = mysetitng.instensity;
        m_ScriptablePass.loop = mysetitng.loop;
        m_ScriptablePass.mymat = mysetitng.RadialBlurMat;
        m_ScriptablePass.name = mysetitng.PassName;
        m_ScriptablePass.downsample = mysetitng.downsample;
    }
    public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
    {
        if (mysetitng.RadialBlurMat != null)
        {
            m_ScriptablePass.setupmypass(renderer.cameraColorTarget);
            // 把括号里的pass根据渲染设置(是有Create设置的)丢到渲染流程中
            renderer.EnqueuePass(m_ScriptablePass);
        }
        else
        {
            Debug.LogError("径向模糊材质球丢失!");
        }
    }
}

shader

Shader"WX/URP/Post/radialBrul"
{
    Properties
    {
        _MainTex("tex",2D) = "Wwhite"{}
    }
        SubShader
    {
        Tags{"RenderPipeline" = "UniversalRenderPipeline"}
        Cull Off ZWrite Off ZTest Always


        HLSLINCLUDE
        #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
        float4 _MainTex_ST;
        float _Loop;
        float _Blur;
        float _Y;
        float _X;
        float _Instensity;
        TEXTURE2D(_MainTex);
        SAMPLER(sampler_MainTex);
        TEXTURE2D(_SourceTex); // 原图
        SAMPLER(sampler_SourceTex);


         struct a2v
         {
             float4 positionOS:POSITION;
             float2 texcoord:TEXCOORD;
         };


         struct v2f
         {
             float4 positionCS:SV_POSITION;
             float2 texcoord:TEXCOORD;
         };
        ENDHLSL


        pass
        {


            HLSLPROGRAM
            #pragma vertex VERT
            #pragma fragment FRAG


            v2f VERT(a2v i)
            {
                v2f o;
                o.positionCS = TransformObjectToHClip(i.positionOS.xyz);
                o.texcoord = i.texcoord;
                return o;
            }


            half4 FRAG(v2f i) :SV_TARGET
            {
                float4 col = 0;
                float2 dir = (i.texcoord - float2(_X,_Y)) * _Blur * 0.01;
                for (int t = 0; t < _Loop; t++)
                {
                    col += SAMPLE_TEXTURE2D(_MainTex,sampler_MainTex,i.texcoord + dir * t) / _Loop;
                }
                float4 Source = SAMPLE_TEXTURE2D(_SourceTex,sampler_SourceTex,i.texcoord);//得到屏幕原始图
                return lerp(Source,col,_Instensity);
            }
            ENDHLSL
        }
    }
}

一些坑

  • public面板

如果采用新建一个 setting 类的方式去创建面板的话,大概流程是:

  1. class setting

  2. public setting

  3. ScriptableRenderPass public 一样的 setting

  4. Execute 中 SetGlobalFloat

  5. Create 中把外部面板中的数据拿到 ScriptableRenderPass 中去

同时,shader中注意 Properties 中不用新建数据

  • 对于Blit 函数

cmd.Blit(a,b,mat,-1); 等同于

mat.SetTexture("_a",a);

cmd.Blit(null,b,mat,-1);

cmd.Blit和Blit等效果

文章配图

  • cameraColorTarget用 var格式

(当时还不懂var是啥意思)

文章配图

  • CommandBufferPool.Release(cmd) & cmd.clear()

CommandBufferPool.Release(cmd) : 把资源释放掉了,cmd就没了

cmd.clear() : 只是把这个buffer里面的命令清空

  • 原神fullScreen三角形

文章配图

  • 对于 GetTemporaryRT 函数

相当于new RT,然后给RenderTargetIdentifier

添加“获取临时渲染纹理”命令。(一般是获取相机的RT数据)

开启HDR后需要把 Format 改成 DefaultHDR

跟着相机走的话可以这样写

文章配图

这样写跟一个一个数据去getCameraData的最终呈现效果不一样

    • 另外感觉一些数值,例如depth buffer如果不需要的话应该可以存更低的精度
  • 对于 ReleaseTemporaryRT 函数

必须释放,不然会内存泄漏,只是释放的时机不同

旭辉老师说是如果cmd.GetTemporaryRT出来的话,释放cmd这个应该就会释放,但遇到过如果不释放就黑屏的问题

  • CommandBufferPool.Get("name")

相当于给这个cmd起名,方便后面查找

  • RenderTargetIdentifier & RenderTargetHandle 的区别

Handle是自己定义的

文章配图

  • 所有涉及到bilt计算的RT都需要给一个临时rt

因为最终material面板上这个tex的位置应该是一张rt

  • 如果该pass没有贴图,blit会默认给MainTex

blit(RTID,cameraSource) 会自动把RTID给MainTex

  • 给贴图赋值

只有两种Set可以,bilt并不行

  • Filter
  • cmd.GetTemporal & RenderTexture.GetTemporal

陈参说cmd只有一帧,返回的是handle(类似指针);而renderTexture是持续的。

  • 对于 context.ExecuteCommandBuffer(cmd)

看官方自带的 RenderObjectsPass.cs 中,执行cmd的方法执行了两次

这是因为官方怕之前有其他的cmd啥的,先执行一次,实际并不影响

  • Configure 和 Execute

Configure 是在 Execute 调用的前一帧调用

其实这俩一样,就是在Configure里官方会给你优化,两边用的api不太一样

  • 对于Depth在后处理后,game和scene深度差距

copyDepth.hlsl 里有详细说明,对于GL之外的平台需要反转投影矩阵的y

CopyDepthPass.cs 里面是这样做的 IsCameraProjectionMatrixFlipped 函数用于判断uv的起点是否为左上

文章配图

文章配图

官方后处理笔记

2019.4.30

RenderObjects.cs

RenderObjectsPass.cs

这个就是GUI,没细看

RenderObjectsPassFeatureEditor.cs

— END —