Unity shader学习之屏幕后期处理效果之高斯模糊

高斯模糊,见 百度百科

也使用卷积来实现,每个卷积元素的公式为:

其中б是标准方差,一般取值为1。

x和y分别对应当前位置到卷积中心的整数距离。

由于需要对高斯核中的权重进行归一化,即使所有权重相加为1,因此e前面的系数实际不会对结果产生任何影响。

转载请注明出处:http://www.cnblogs.com/jietian331/p/7238032.html

综上,公式简化为:

G(x,y) = e-(x*x+y*y)/2

 

因此,高斯核计算代码如下:

  1 using System;
  2 
  3 namespace TestShell
  4 {
  5     class Program
  6     {
  7         static void Main(string[] args)
  8         {
  9             Console.WriteLine("输入需要得到的高斯卷积核的维数(如3,5,7...):");
 10 
 11             string input = Console.ReadLine();
 12             int size;
 13 
 14             if (!int.TryParse(input, out size))
 15             {
 16                 Console.WriteLine("不是数字...");
 17                 return;
 18             }
 19 
 20             // 计算
 21             double[] r2 = null;
 22             double[,] r = null;
 23             try
 24             {
 25                 r = CalcGaussianBlur(size, out r2);
 26             }
 27             catch (Exception ex)
 28             {
 29                 Console.WriteLine("错误: " + ex.Message);
 30             }
 31 
 32             if (r != null && r2 != null)
 33             {
 34                 // 卷积如下:
 35                 Console.WriteLine();
 36                 Console.WriteLine("{0}x{0}的高斯卷积核如下:", size);
 37                 for (int i = 0; i < r.GetLongLength(0); i++)
 38                 {
 39                     for (int j = 0; j < r.GetLongLength(1); j++)
 40                     {
 41                         Console.Write("{0:f4}\t", r[i, j]);
 42                     }
 43                     Console.WriteLine();
 44                 }
 45                 Console.WriteLine();
 46 
 47                 Console.WriteLine("可拆成2个一维的数组:");
 48                 for (int i = 0; i < r2.Length; i++)
 49                 {
 50                     Console.Write("{0:f4}\t", r2[i]);
 51                 }
 52                 Console.WriteLine();
 53                 Console.WriteLine();
 54 
 55                 Console.WriteLine("验证,使用这2个一维的数组也可以得到同样的结果:");
 56                 for (int i = 0; i < size; i++)
 57                 {
 58                     for (int j = 0; j < size; j++)
 59                     {
 60                         Console.Write("{0:f4}\t", r2[i] * r2[j]);
 61 
 62                     }
 63                     Console.WriteLine();
 64                 }
 65             }
 66 
 67             Console.WriteLine();
 68             Console.WriteLine("按任意键结束...");
 69             Console.ReadKey();
 70         }
 71 
 72         static double[,] CalcGaussianBlur(int size, out double[] r2)
 73         {
 74             if (size < 3)
 75                 throw new ArgumentException("size < 3");
 76             if (size % 2 != 1)
 77                 throw new ArgumentException("size % 2 != 1");
 78 
 79             double[,] r = new double[size, size];
 80             r2 = new double[size];
 81             int center = (int)Math.Floor(size / 2f);
 82             double sum = 0;
 83 
 84             for (int i = 0; i < size; i++)
 85             {
 86                 for (int j = 0; j < size; j++)
 87                 {
 88                     int x = Math.Abs(i - center);
 89                     int y = Math.Abs(j - center);
 90                     double d = CalcItem(x, y);
 91                     r[i, j] = d;
 92                     sum += d;
 93                 }
 94             }
 95 
 96             for (int i = 0; i < size; i++)
 97             {
 98                 for (int j = 0; j < size; j++)
 99                 {
100                     r[i, j] /= sum;
101                     if (i == j)
102                         r2[i] = Math.Sqrt(r[i, i]);
103                 }
104             }
105 
106             return r;
107         }
108 
109         static double CalcItem(int x, int y)
110         {
111             return Math.Pow(Math.E, -(x * x + y * y) / 2d);
112         }
113     }
114 }
View Code

 

工具在: https://files.cnblogs.com/files/jietian331/CalcGaussianBlur.zip

 

一个5 x 5的高斯核如下:

 

使用2个一维数组可简化计算量,提高性能,通过观察可知,只需要计3个数:

 

使用unity shader屏幕后期处理来实现高斯模糊,代码如下。

子类:

 1 using UnityEngine;
 2 
 3 public class GaussianBlurRenderer : PostEffectRenderer
 4 {
 5     [Range(1, 8)]
 6     [SerializeField]
 7     public int m_downSample = 2;      // 降采样率
 8     [Range(0, 4)]
 9     [SerializeField]
10     public int m_iterations = 3;        // 迭代次数
11     [Range(0.2f, 3f)]
12     [SerializeField]
13     public float m_blurSpread = 0.6f;        // 模糊扩散量
14 
15     protected override void OnRenderImage(RenderTexture src, RenderTexture dest)
16     {
17         int w = (int)(src.width / m_downSample);
18         int h = (int)(src.height / m_downSample);
19         RenderTexture buffer0 = RenderTexture.GetTemporary(w, h);
20         RenderTexture buffer1 = RenderTexture.GetTemporary(w, h);
21         buffer0.filterMode = FilterMode.Bilinear;
22         buffer1.filterMode = FilterMode.Bilinear;
23         Graphics.Blit(src, buffer0);
24 
25         for (int i = 0; i < m_iterations; i++)
26         {
27             Mat.SetFloat("_BlurSpread", 1 + i * m_blurSpread);
28 
29             Graphics.Blit(buffer0, buffer1, Mat, 0);
30             Graphics.Blit(buffer1, buffer0, Mat, 1);
31         }
32 
33         Graphics.Blit(buffer0, dest);
34         RenderTexture.ReleaseTemporary(buffer0);
35         RenderTexture.ReleaseTemporary(buffer1);
36     }
37 
38     protected override string ShaderName
39     {
40         get { return "Custom/Gaussian Blur"; }
41     }
42 }
GaussianBlurRenderer

 

shader:

 1 // Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
 2 
 3 Shader "Custom/Gaussian Blur"
 4 {
 5     Properties
 6     {
 7         _MainTex("Main Texture", 2D) = "white" {}
 8         _BlurSpread("Blur Spread", float) = 1
 9     }
10 
11     SubShader
12     {
13         CGINCLUDE
14         
15         sampler2D _MainTex;
16         float4 _MainTex_TexelSize;
17         uniform float _BlurSpread;
18 
19         struct appdata
20         {
21             float4 vertex : POSITION;
22             float2 uv : TEXCOORD0;
23         };
24 
25         struct v2f
26         {
27             float4 pos : SV_POSITION;
28             float2 uv[5] : TEXCOORD0;
29         };
30 
31         v2f vertHorizontal(appdata v)
32         {
33             v2f o;
34             o.pos = UnityObjectToClipPos(v.vertex);
35             float tsx = _MainTex_TexelSize.x * _BlurSpread;
36             o.uv[0] = v.uv + float2(tsx * -2, 0);
37             o.uv[1] = v.uv + float2(tsx * -1, 0);
38             o.uv[2] = v.uv;
39             o.uv[3] = v.uv + float2(tsx * 1, 0);
40             o.uv[4] = v.uv + float2(tsx * 2, 0);
41             return o;
42         }
43 
44         v2f vertVertical(appdata v)
45         {
46             v2f o;
47             o.pos = UnityObjectToClipPos(v.vertex);
48             float tsy = _MainTex_TexelSize.y * _BlurSpread;
49             o.uv[0] = v.uv + float2(0, tsy * -2);
50             o.uv[1] = v.uv + float2(0, tsy * -1);
51             o.uv[2] = v.uv;
52             o.uv[3] = v.uv + float2(0, tsy * 1);
53             o.uv[4] = v.uv + float2(0, tsy * 2);
54             return o;
55         }
56 
57         fixed4 frag(v2f i) : SV_TARGET
58         {
59             float g[3] = {0.0545, 0.2442, 0.4026};
60             fixed4 col = tex2D(_MainTex, i.uv[2]) * g[2];
61             for(int k = 0; k < 2; k++)
62             {
63                 col += tex2D(_MainTex, i.uv[k]) * g[k];
64                 col += tex2D(_MainTex, i.uv[4 - k]) * g[k];
65             }
66             return col;
67         }
68 
69         ENDCG
70 
71         Pass
72         {
73             Name "HORIZONTAL"
74             ZTest Always
75             ZWrite Off
76             Cull Off
77 
78             CGPROGRAM
79             #pragma vertex vertHorizontal
80             #pragma fragment frag
81             ENDCG
82         }
83 
84         Pass
85         {
86             Name "VERTICAL"
87             ZTest Always
88             ZWrite Off
89             Cull Off
90 
91             CGPROGRAM
92             #pragma vertex vertVertical
93             #pragma fragment frag
94             ENDCG
95         }
96     }
97 
98     Fallback Off
99 }
Custom/Gaussian Blur

 

调整参数:

DownSample,即降采样率,越大性能越好,图像越模糊,但过大可能会使图像像素化。

Iteraitions, 即迭代次数,越大图像模糊效果越好,但性能也会下降。

BlurSpread,即模糊扩散量,越大图像越模糊,但过大会造成虚影。

 

 

效果如下:

 

 

URP管线下如何实现高斯模糊,shader代码如下:

  1 Shader "TA/Unlit/Feature/Post Process Blur"
  2 {
  3     Properties
  4     {
  5         _MainTex("Main Texture", 2D) = "white" {}
  6         _BlurSpread("Blur Spread", float) = 1
  7     }
  8 
  9     SubShader
 10     {
 11         Tags
 12         {
 13             "RenderType" = "Opaque"
 14             "RenderPipeline" = "UniversalPipeline"
 15         }
 16 
 17         HLSLINCLUDE
 18         #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
 19 
 20         TEXTURE2D(_MainTex); SAMPLER(sampler_MainTex);
 21         float4 _MainTex_TexelSize;
 22         float _BlurSpread;
 23  
 24         struct appdata
 25         {
 26             float4 vertex : POSITION;
 27             float2 uv : TEXCOORD0;
 28         };
 29  
 30         struct v2f
 31         {
 32             float4 pos : SV_POSITION;
 33             float2 uv[5] : TEXCOORD0;
 34         };
 35  
 36         v2f vertHorizontal(appdata v)
 37         {
 38             v2f o;
 39             o.pos = TransformObjectToHClip(v.vertex.xyz);
 40             float tsx = _MainTex_TexelSize.x * _BlurSpread;
 41             o.uv[0] = v.uv + float2(tsx * -2, 0);
 42             o.uv[1] = v.uv + float2(tsx * -1, 0);
 43             o.uv[2] = v.uv;
 44             o.uv[3] = v.uv + float2(tsx * 1, 0);
 45             o.uv[4] = v.uv + float2(tsx * 2, 0);
 46             return o;
 47         }
 48  
 49         v2f vertVertical(appdata v)
 50         {
 51             v2f o;
 52             o.pos = TransformObjectToHClip(v.vertex.xyz);
 53             float tsy = _MainTex_TexelSize.y * _BlurSpread;
 54             o.uv[0] = v.uv + float2(0, tsy * -2);
 55             o.uv[1] = v.uv + float2(0, tsy * -1);
 56             o.uv[2] = v.uv;
 57             o.uv[3] = v.uv + float2(0, tsy * 1);
 58             o.uv[4] = v.uv + float2(0, tsy * 2);
 59             return o;
 60         }
 61  
 62         half4 frag(v2f i) : SV_TARGET
 63         {
 64             float g[3] = {0.0545, 0.2442, 0.4026};
 65             half4 col = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.uv[2]) * g[2];
 66             for(int k = 0; k < 2; k++)
 67             {
 68                 col += SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.uv[k]) * g[k];
 69                 col += SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.uv[4 - k]) * g[k];
 70             }
 71             return col;
 72         }
 73         ENDHLSL
 74 
 75         Pass
 76         {
 77             Name "HORIZONTAL"
 78             ZTest Always
 79             ZWrite Off
 80             Cull Off
 81  
 82             HLSLPROGRAM
 83             #pragma vertex vertHorizontal
 84             #pragma fragment frag
 85             ENDHLSL
 86         }
 87  
 88         Pass
 89         {
 90             Name "VERTICAL"
 91             ZTest Always
 92             ZWrite Off
 93             Cull Off
 94  
 95             HLSLPROGRAM
 96             #pragma vertex vertVertical
 97             #pragma fragment frag
 98             ENDHLSL
 99         }
100     }
101 
102     FallBack "Hidden/Universal Render Pipeline/FallbackError"
103 }

Feature代码如下:

  1 using Frame;
  2 using System;
  3 using UnityEngine;
  4 using UnityEngine.Rendering;
  5 using UnityEngine.Rendering.Universal;
  6 
  7 namespace TA
  8 {
  9     public class URPFeature_Blur : ScriptableRendererFeature
 10     {
 11         #region Pass & Setting
 12 
 13         class Settings
 14         {
 15             public int m_DownSample = 2; //降采样率
 16             public int m_Iterations = 3; //迭代次数
 17             public float m_BlurSpread = 0.6f; //模糊扩散量
 18 
 19             NormalAssetItem<Material> m_Asset;
 20 
 21             public Material Mat
 22             {
 23                 get
 24                 {
 25                     if (m_Asset == null && YooAssetPatchMgr.Singleton.IsPatchDone)
 26                     {
 27                         m_Asset = new NormalAssetItem<Material>("feature@featureblur", YooAssetPackages.PackageArtCloth);
 28                         m_Asset.Load();
 29                     }
 30 
 31                     return m_Asset.IsLoadComplete ? m_Asset.AssetObj : null;
 32                 }
 33             }
 34 
 35             public void Release()
 36             {
 37                 if (m_Asset != null)
 38                 {
 39                     m_Asset.Destroy();
 40                     m_Asset = null;
 41                 }
 42             }
 43         }
 44 
 45         class URPPass_Blur : ScriptableRenderPass
 46         {
 47             Settings m_Settings;
 48             RenderTargetIdentifier m_Source;
 49             int m_TempRTID0 = Shader.PropertyToID($"{nameof(URPFeature_Blur)}_TempRT0");
 50             int m_TempRTID1 = Shader.PropertyToID($"{nameof(URPFeature_Blur)}_TempRT1");
 51 
 52             public URPPass_Blur(Settings settings)
 53             {
 54                 m_Settings = settings;
 55             }
 56 
 57             public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData)
 58             {
 59                 m_Source = renderingData.cameraData.renderer.cameraColorTarget;
 60             }
 61 
 62             public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
 63             {
 64                 CommandBuffer cmd = CommandBufferPool.Get($"{nameof(URPFeature_Blur)}_cmd");
 65 
 66                 var data = renderingData.cameraData.cameraTargetDescriptor;
 67                 int width = data.width / m_Settings.m_DownSample;
 68                 int height = data.height / m_Settings.m_DownSample;
 69 
 70                 cmd.GetTemporaryRT(m_TempRTID0, width, height, 0, FilterMode.Trilinear, RenderTextureFormat.ARGB32);
 71                 cmd.Blit(m_Source, m_TempRTID0);
 72 
 73                 for (int i = 0; i < m_Settings.m_Iterations; ++i)
 74                 {
 75                     m_Settings.Mat.SetFloat("_BlurSpread", 1 + i * m_Settings.m_BlurSpread);
 76 
 77                     // 第一轮
 78                     cmd.GetTemporaryRT(m_TempRTID1, width, height, 0, FilterMode.Bilinear, RenderTextureFormat.ARGB32);
 79                     cmd.Blit(m_TempRTID0, m_TempRTID1, m_Settings.Mat, 0);
 80                     cmd.ReleaseTemporaryRT(m_TempRTID0);
 81 
 82                     // 第二轮
 83                     cmd.GetTemporaryRT(m_TempRTID0, width, height, 0, FilterMode.Bilinear, RenderTextureFormat.ARGB32);
 84                     cmd.Blit(m_TempRTID1, m_TempRTID0, m_Settings.Mat, 1);
 85                     cmd.ReleaseTemporaryRT(m_TempRTID1);
 86                 }
 87 
 88                 cmd.Blit(m_TempRTID0, m_Source);
 89                 cmd.ReleaseTemporaryRT(m_TempRTID0);
 90 
 91                 context.ExecuteCommandBuffer(cmd);
 92                 CommandBufferPool.Release(cmd);
 93             }
 94         }
 95 
 96         #endregion
 97 
 98 
 99         Settings m_Settings;
100         URPPass_Blur m_Pass;
101 
102 
103         public override void Create()
104         {
105             m_Settings = new();
106             m_Pass = new URPPass_Blur(m_Settings);
107             m_Pass.renderPassEvent = RenderPassEvent.AfterRenderingPostProcessing;
108         }
109 
110         public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
111         {
112             if (m_Settings.Mat)
113             {
114                 renderer.EnqueuePass(m_Pass);
115             }
116         }
117 
118         protected override void Dispose(bool disposing)
119         {
120             base.Dispose(disposing);
121             if (m_Settings != null)
122             {
123                 m_Settings.Release();
124                 m_Settings = null;
125             }
126         }
127 
128         /// <summary>
129         /// 设置参数
130         /// </summary>
131         /// <param name="downSample">降采样率</param>
132         /// <param name="iterations">迭代次数</param>
133         /// <param name="blurSpread">模糊扩散量</param>
134         public void SetParam(int downSample, int iterations, float blurSpread)
135         {
136             m_Settings.m_DownSample = downSample;
137             m_Settings.m_Iterations = iterations;
138             m_Settings.m_BlurSpread = blurSpread;
139         }
140 
141 
142 
143     }
144 }

 

posted @ 2017-07-26 10:01  孤独の巡礼  阅读(4858)  评论(0编辑  收藏  举报