RenderFeature基础


也可以看知乎 https://zhuanlan.zhihu.com/p/425214124

https://www.sardinefish.com/blog/458

URP Post Processing

https://mp.weixin.qq.com/s/JSsAWv3s-jBJHZb5JV5tyw

后处理相关学习 http://blog.coolcoding.cn/?p=3472

outline https://alexanderameye.github.io/outlineshader

https://samdriver.xyz/article/scriptable-render

官方后处理Demo https://github.com/Unity-Technologies/UniversalRenderingExamples

一堆 https://github.com/YuSiangLai/URPCustomPostProcessing

outline https://github.com/yahiaetman/URPCustomPostProcessingStack

godray https://www.raywenderlich.com/22027819-volumetric-light-scattering-as-a-custom-renderer-feature-in-urp

URP官方处理自遮挡显示的 https://docs.unity3d.com/Packages/com.unity.render-pipelines.universal@11.0/manual/renderer-features/how-to-custom-effect-render-objects.html

遮挡显示及描边效果 https://zhuanlan.zhihu.com/p/170241589

某大佬网站,当前页面是renderfeature(基础) https://samdriver.xyz/article/scriptable-render

深度描边 https://alexanderameye.github.io/notes/edge-detection-outlines/

黑白闪 https://www.bilibili.com/video/BV1Cr4y127Jz?spm_id_from=333.999.0.0

图片

commandbuffer.Drawxxxxx

图片

【百人斩】凶恶的真实(745806106)  20:51:51

如果是用了Scriptable render pass的话

【百人斩】凶恶的真实(745806106)  20:52:08

它里面有一个RenderTargetIdentifier[] m_ColorAttachments

ConfigureTarget用来暂存pass里用到的RenderTargetIdentifier

创建Render Feature

Create-Rendering-Universal Render Pipeline-Render Feature

模板

  • 学习用
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    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
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
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.50.50.5), tex.xyz, _contranst); //对比度
                return tex;
            }
            ENDHLSL
        }
    }
}
  • Kawase
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
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
        }
    }
}
  • 径向模糊
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
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模板
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
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.shader

HSV.shader

Kawase.shader

分析

图片

开头贴标签

第一部分

在继承 ScriptableRendererFeature 的类里面

设置renderer面板

第二部分

在继承 ScriptableRenderPass 的类里面

  1. 声明变量
  2. 从相机上copy图像
  3. #核心#重写 Execute 函数,来控制如何渲染(记得清理缓存)
  4. 给renderer面板传递参数
  5. 添加pass
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    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
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    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的起点是否为左上

图片

图片

没搞懂的

  • 官方自带的会算cameraMatrix
    官方的 RenderObjectsPass.cs 会在Execute部分算一遍相机的MVP矩阵,没明白为啥,感觉是应对相机偏移矩阵发生变化的情况

主要变化一个是 ProjectionMatrix 的 z 值也就是深度范围 [0,1] or [-1,1]

另一个就是 ViewMatrix 第四列的平移操作

  • enableRandomWrite
    没懂为啥需要随机写入

图片

官方后处理笔记

2019.4.30

RenderObjects.cs

RenderObjectsPass.cs

这个就是GUI,没细看

RenderObjectsPassFeatureEditor.cs


文章作者: Neilyodog
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Neilyodog !
评论
评论