Blur 算法 (Unity 5.0 Shader)
一:简单 Blur 算法
一个像素的颜色值由其邻近的若干像素和自己的颜色值的平均值重新定义,以此达到模糊的效果。
如下图,红色的像素点的值将由它和它周围的24个像素的平均值重新定义。计算的范围一般由一个“半径”来决定,表示由该点为中心、“半径”为距离
辐射的范围,对于下图“半径”为2。
二:shader代码
1 Shader "Unlit/Blur" 2 { 3 Properties 4 { 5 _MainTex ("Texture", 2D) = "white" {} 6 _Width("Image XRes", Float) = 512 7 _Height("Image YRes", Float) = 512 8 _Radius("Radius", Range(0, 50)) = 4 9 } 10 11 SubShader 12 { 13 Tags { "RenderType"="Opaque" } 14 LOD 100 15 16 Pass 17 { 18 CGPROGRAM 19 #pragma vertex vert 20 #pragma fragment frag 21 22 #include "UnityCG.cginc" 23 24 struct appdata 25 { 26 float4 vertex : POSITION; 27 float2 uv : TEXCOORD0; 28 }; 29 30 struct v2f 31 { 32 float2 uv : TEXCOORD0; 33 float4 vertex : SV_POSITION; 34 }; 35 36 sampler2D _MainTex; 37 float4 _MainTex_ST; 38 float _Width; 39 float _Height; 40 float _Radius; 41 42 v2f vert (appdata v) 43 { 44 v2f o; 45 o.vertex = mul(UNITY_MATRIX_MVP, v.vertex); 46 o.uv = TRANSFORM_TEX(v.uv, _MainTex); 47 return o; 48 } 49 50 fixed4 frag (v2f i) : SV_Target 51 { 52 float horizontal = 1 / _Width; 53 float vertical = 1 / _Height; 54 55 int radius = _Radius; 56 int count = (2 * radius + 1) * (2 * radius + 1); 57 fixed x = i.uv.x; 58 fixed y = i.uv.y; 59 60 fixed4 col = fixed4(0,0,0,0); 61 62 for (int m = -radius; m <= radius; ++m) { 63 fixed u = clamp(x + m * horizontal, 0, 1); 64 for (int n = -radius; n <= radius; ++n) { 65 fixed v = clamp(y + n * vertical, 0, 1); 66 col += tex2D(_MainTex, fixed2(u, v)); 67 } 68 } 69 70 return col / count; 71 } 72 73 ENDCG 74 } 75 } 76 }
因为unity shader并没有像glsl 3.0的textureSize这种获取texture尺寸的类似接口,需要定义两个Property : _Width和_Height,用于根据实际使
用的图片传入图片的尺寸。
在shader中对于texture的像素点寻址是通过texture的uv坐标, 所以将计算移到UV坐标系进行。
效果:从左到右是 原图、半径为5、半径为15
三:分析
目前的设计存在的一个问题是当图片比较大的时候,运行效率会很低。相对比较耗时的操作为 第66行 代码
col += tex2D(_MainTex, fixed2(u, v)),假设图片分辨率为1280 * 1024、半径为50,对于每一个像素都会执行一次
frag函数, 这第66行调用的次数为3409182720,属于10亿数量级,对于 2K、4K的图片就更不得了。
对此,首先可以将半径的范围降低,加入为[0, 20]