均值模糊(Box Blur)



概述


因为公司手游项目需求。须要一个适合手机平台的模糊效果,同一时候须要开放一个參数便于调节模糊值。我首先想到的就是ps里面的均值模糊。

查资料能够知道均值模糊是一种高速的图像模糊技术,相比与传统的卷积模糊(如高斯模糊),均值模糊能够更加有效率的完毕对图像模糊。在unity官方自带imageeffect包也有一个blur的屏幕特效,用的就是均值模糊算法,仅仅只是他仅仅採样了离原像素上下左右模糊半径(Blur
Spread)距离的四个像素进行平均处理。然后做迭代(Iterations)处理。即将模糊后的图像再次採样模糊,迭代次数越高越模糊。同一时候也会产生很多其它的drawcall,本文的效果就是从这个样例简化而来,使之适合手游项目,而且傻瓜式方便的调节參数。


原理

均值模糊的原理是通过图形滤波器来把一个像素和周围的像素一起求平均值。比方一个三阶的图像滤波器构造事实上就是一个3*3的数组(n阶数组中的元素成为滤波器的系数和滤波器的权重。n称为滤波器的阶)。相当于把一个像素和周围8个像素相加在一起再除以9求平均值,等于把一个像素和周围的像素搅拌在一起,自然就模糊了。均值滤波器与高斯模糊的滤波器不同的地方。就是採样的像素权重是相等的。因此效果相对而言要比高斯模糊差。但速度却要快一些。本例事实上仅仅採样了四个像素求和做平均。然后通过迭代多次採样。实现比較好的模糊效果。

以下是一个三阶的均值滤波器:

      [1,1,1]
1/9 [1,1,1]
[1,1,1]

本例採用的滤波器:

         [0,1,0]
 1/4  [1,0,1]
[0,1,0]



Shader代码实现


shader代码部分相对照较简单,首先须要定义两个内部变量,_MainTex_TexelSize和_BlurOffsets。这两个变量都属于unity的黑魔法,官方文档并没有具体说明。_MainTex_TexelSize的解释能够參考这里,_BlurOffsets这个參数是用来接收从C#脚本里面穿过来的參数,即模糊半径。


		half4 _MainTex_TexelSize;
half4 _BlurOffsets;

在顶点输入结构体里面定义一个一维四阶数组用来存储uv坐标:


		struct v2f {
float4 vertex : SV_POSITION;
half2 texcoord : TEXCOORD0;
half2 taps[4] : TEXCOORD1;
};

在vert函数里面将uv坐标进行偏移获得原像素上下左右偏移_BlurOffsets像素,并存储在taps[]数组里面,传给frag函数



		v2f vert (appdata_t v)
{
v2f o;
o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
o.texcoord = v.texcoord - _BlurOffsets.xy * _MainTex_TexelSize.xy;//将C#脚本传过来的图像偏移回原位置
o.taps[0] = o.texcoord + _MainTex_TexelSize * _BlurOffsets.xy;
o.taps[1] = o.texcoord - _MainTex_TexelSize * _BlurOffsets.xy;
o.taps[2] = o.texcoord + _MainTex_TexelSize * _BlurOffsets.xy * half2(1,-1);
o.taps[3] = o.texcoord - _MainTex_TexelSize * _BlurOffsets.xy * half2(1,-1);
return o; }

在frag函数使用vert函数传过来的uv坐标数组採样图像然后进行叠加后平均化。以达到模糊效果:



		fixed4 frag (v2f i) : SV_Target
{
half4 color = tex2D(_MainTex, i.taps[0]);
color += tex2D(_MainTex, i.taps[1]);
color += tex2D(_MainTex, i.taps[2]);
color += tex2D(_MainTex, i.taps[3]);
return color * 0.25;
}

Shader完整代码

VF版本号代码01


Shader "PengLu/ImageEffect/Unlit/BlurBox" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
} SubShader {
Pass {
ZTest Always ZWrite Off CGPROGRAM
#pragma vertex vert
#pragma fragment frag #include "UnityCG.cginc" struct appdata_t {
float4 vertex : POSITION;
float2 texcoord : TEXCOORD0;
}; struct v2f {
float4 vertex : SV_POSITION;
half2 texcoord : TEXCOORD0;
half2 taps[4] : TEXCOORD1;
}; sampler2D _MainTex;
half4 _MainTex_TexelSize;
half4 _BlurOffsets; v2f vert (appdata_t v)
{
v2f o;
o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
o.texcoord = v.texcoord - _BlurOffsets.xy * _MainTex_TexelSize.xy;
o.taps[0] = o.texcoord + _MainTex_TexelSize * _BlurOffsets.xy;
o.taps[1] = o.texcoord - _MainTex_TexelSize * _BlurOffsets.xy;
o.taps[2] = o.texcoord + _MainTex_TexelSize * _BlurOffsets.xy * half2(1,-1);
o.taps[3] = o.texcoord - _MainTex_TexelSize * _BlurOffsets.xy * half2(1,-1);
return o; } fixed4 frag (v2f i) : SV_Target
{
half4 color = tex2D(_MainTex, i.taps[0]);
color += tex2D(_MainTex, i.taps[1]);
color += tex2D(_MainTex, i.taps[2]);
color += tex2D(_MainTex, i.taps[3]);
return color * 0.25;
}
ENDCG
}
}
Fallback off
}


C#脚本代码



C#脚本相对也比較简单,这个脚本主要负责把抓取屏幕并传递參数给shader进行模糊处理。这个脚本是从官方的简化而来,将迭代的次数固定为2,仅仅留下模糊半径一个參数调节。关键的两个函数须要注意下:


            Graphics.BlitMultiTap (source, dest, material,
new Vector2(-off, -off),
new Vector2(-off, off),
new Vector2( off, off),
new Vector2( off, -off)
);


RenderTexture buffer = RenderTexture.GetTemporary(rtW, rtH, 0);

Graphics.BlitMultiTap函数官方解释在这里 
,大概意思是传递多个位图块给shader进行处理,每一个位图块有各自的偏移(由vector[]数组决定)。

但实际上我測试发现,这个函数传递的位图块是配合固定管线编程使用的。在5.0下面的blur特效shader代码里有下面代码:上面那个函数生成的四个纹理块一次相应下面四个_MainTex,shader代码里面并没有偏移,都是从Graphics.BlitMultiTap传递过来。

SubShader {
Pass {
ZTest Always Cull Off ZWrite Off Fog { Mode Off }
SetTexture [_MainTex] {constantColor (0,0,0,0.25) combine texture * constant alpha}
SetTexture [_MainTex] {constantColor (0,0,0,0.25) combine texture * constant + previous}
SetTexture [_MainTex] {constantColor (0,0,0,0.25) combine texture * constant + previous}
SetTexture [_MainTex] {constantColor (0,0,0,0.25) combine texture * constant + previous}
}
}


而在unity5.0的官方blur shader里面已经将固定管线的相关代码删除。而对vert&frag shader编程,它实际上传递了一个uv坐标偏移值为(off,off)的位图块给shader。并将偏移值传给内置变量_BlurOffsets。我们在相关代码里要又一次做偏移。才会有偏移效果。因此Graphics.BlitMultiTap我们能够改成:




            Graphics.BlitMultiTap (source, dest, material,new Vector2(off, off) );


RenderTexture.GetTemporary这个函数在这里主要是用来又一次设定抓取的屏幕图像的长宽,在本例中我们将长宽设置成原来的8分之1后然后再传递给shader处理,能够是採样计算消耗减少到原来的64分之1,仅仅是因此多消耗一个drawcall。

因为还迭代了两次,最后这个屏幕特效须要消耗4个drawcall,当然因为做了个推断,当Blur
Size为0时,仅仅消耗1个drallcall,不做模糊处理。


完整C#脚本例如以下:

using UnityEngine;
using System.Collections;
using System; [ExecuteInEditMode]
[AddComponentMenu ("PengLu/ImageEffect/Blurbox")]
public class ImageEffect_BlurBox : MonoBehaviour {
#region Variables
public Shader BlurBoxShader = null;
private Material BlurBoxMaterial = null; [Range(0.0f, 1.0f)]
public float BlurSize = 0.5f; #endregion #region Properties Material material
{
get
{
if(BlurBoxMaterial == null)
{
BlurBoxMaterial = new Material(BlurBoxShader);
BlurBoxMaterial.hideFlags = HideFlags.HideAndDontSave;
}
return BlurBoxMaterial;
}
}
#endregion
// Use this for initialization
void Start () { BlurBoxShader = Shader.Find("PengLu/ImageEffect/Unlit/BlurBox"); // Disable if we don't support image effects
if (!SystemInfo.supportsImageEffects)
{
enabled = false;
return;
} // Disable the image effect if the shader can't
// run on the users graphics card
if (!BlurBoxShader || !BlurBoxShader.isSupported)
enabled = false;
return; } public void FourTapCone (RenderTexture source, RenderTexture dest,int iteration)
{
float off = BlurSize*iteration+0.5f;
Graphics.BlitMultiTap (source, dest, material,
new Vector2(-off, -off),
new Vector2(-off, off),
new Vector2( off, off),
new Vector2( off, -off)
);
} private void DownSample4x (RenderTexture source, RenderTexture dest)
{
float off = 1.0f;
// Graphics.Blit(source, dest, material);
Graphics.BlitMultiTap (source, dest, material,
new Vector2(off, off),
new Vector2(-off, off),
new Vector2( off, off),
new Vector2( off, -off)
);
} void OnRenderImage (RenderTexture sourceTexture, RenderTexture destTexture)
{
if(BlurSize != 0 && BlurBoxShader != null){ int rtW = sourceTexture.width/8;
int rtH = sourceTexture.height/8;
RenderTexture buffer = RenderTexture.GetTemporary(rtW, rtH, 0); DownSample4x (sourceTexture, buffer); for(int i = 0; i < 2; i++)
{
RenderTexture buffer2 = RenderTexture.GetTemporary(rtW, rtH, 0);
FourTapCone (buffer, buffer2,i);
RenderTexture.ReleaseTemporary(buffer);
buffer = buffer2;
}
Graphics.Blit(buffer, destTexture); RenderTexture.ReleaseTemporary(buffer);
} else{
Graphics.Blit(sourceTexture, destTexture); } } // Update is called once per frame
void Update () {
#if UNITY_EDITOR
if (Application.isPlaying!=true)
{
BlurBoxShader = Shader.Find("PengLu/ImageEffect/Unlit/BlurBox"); }
#endif } public void OnDisable () {
if (BlurBoxMaterial)
DestroyImmediate (BlurBoxMaterial);
}
}

终于模糊特效效果:


watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">


UnityShader实例13:屏幕特效之均值模糊(Box Blur)的更多相关文章

  1. 图像处理------快速均值模糊(Box Blur)

    图像模糊的本质, 从数字信号处理的角度看,图像模糊就要压制高频信号保留低频信号, 压制高频的信号的一个可选择的方法就是卷积滤波.选择一个低频滤波器,对图像上的 每个像素实现低频滤波,这样整体效果就是一 ...

  2. UnityShader实例15:屏幕特效之Bloom

    http://blog.csdn.net/u011047171/article/details/48522073 Bloom特效       概述        Bloom,又称“全屏泛光”,是游戏中 ...

  3. UnityShader之屏幕特效基础

    1.什么是屏幕特效 我们这里讲的屏幕特效技术,指的是在渲染完整个场景后得到的屏幕图象的基础上,再对这个屏幕图像做一系列处理,实现出屏幕特效,使用这种技术可以为屏幕画面增添各种风格的艺术效果,比如泛光. ...

  4. Unity shader学习之屏幕后期处理效果之均值模糊

    均值模糊,也使用卷积来实现,之不过卷积中每个值均相等,且相加等于1. 代码如下, 子类: using UnityEngine; public class MeanBlurRenderer : Post ...

  5. Unity Shader-后处理:简单均值模糊

    一.简介   今天来学习一下后处理中比较常用的一种效果,屏幕模糊效果.模糊效果,在图像处理中经常用到,Photoshop中也有类似的滤镜.我们在游戏中也会经常用到.因为屏幕模糊效果是一些高级后处理效果 ...

  6. Unity shader(CG) 写一个 散色、折射、反射、菲涅尔、gamma、简单后期屏幕特效

    http://www.lai18.com/content/506918.html 1.自生要求是很重要的,当然不是什么强迫工作之类的,而是自己有限的能力上不断的扩展兴趣上的内容. 2.用生活的眼光去发 ...

  7. 在C#里实现各种窗口切换特效,多达13种特效

    原文:http://www.cnblogs.com/clayui/archive/2011/06/28/2092126.html 预览:   下载 这次clayui给大家带来了比较实用的东西,因为时间 ...

  8. Android实例-获取屏幕的物理分辨率

    相关资料: http://blog.qdac.cc/?p=1161 实例代码: unit Unit1; interface uses System.SysUtils, System.Types, Sy ...

  9. UnityShader实例09:Stencil Buffer&Stencil Test

    http://blog.csdn.net/u011047171/article/details/46928463 Stencil Buffer&Stencil Test 在开始前先吐槽下uni ...

随机推荐

  1. thrift java示例

    thrift java示例 使用IntelliJ IDEA作为开发工具: 增加proto文件夹,里面写上sayHello.proto syntax = "proto3"; opti ...

  2. Ruby中使用patch HTTP方法

    Ruby中使用patch HTTP方法 如果使用patch,在后台可以看到只更新了改动的部分: Started PATCH "/ads/5/update" for ::1 at 2 ...

  3. zzulioj--1613--少活一年?(稍微有点坑,水!)

    1613: 少活一年? Time Limit: 1 Sec  Memory Limit: 128 MB Submit: 344  Solved: 70 SubmitStatusWeb Board De ...

  4. 【NOI 2002】 银河英雄传说

    [题目链接] https://www.luogu.org/problemnew/show/P1196 [算法] 并查集 [代码] #include<bits/stdc++.h> using ...

  5. Android 多个APK共享数据

    Android给每个APK进程分配一个单独的用户空间,其manifest中的userid就是对应一个Linux用户(Android 系统是基于Linux)的.所以不同APK(用户)间互相访问数据默认是 ...

  6. const,var,let 区别

    js中const,var,let区别 1.const定义的变量不可以修改,而且必须初始化. 声明的是常量 1 const b = 2;//正确 2 // const b;//错误,必须初始化 3 co ...

  7. WebApi笔记

    WebApi有一段时间没用了,这几天用webapi做了一个接口供第三方调用,又重新折腾了下,做个笔记记录下心得,防止遗忘. 1.webapi使用的RESTful风格的传参方式,其实就是充分利用HTTP ...

  8. MFC+OpenGL可编程管线

    [github链接] 网上的代码大都是固定管线渲染的,今天下午整理了下,把setPixelFormat.初始化glew.创建GL 4,2 context等操作封装到一个MFC类OpenGLWidget ...

  9. Photoshop把图片调成固定的像素。

    1.用PhotoShop打开需要修改的图片. 2.点击“窗口”菜单的“图层”子菜单,打开图层控制面板(快捷键F7).3.用鼠标左键双击“图层”面板的“背景”图层.在弹出窗口中点击“确定”按钮,解锁背景 ...

  10. Comparison of programming paradigms

    Main paradigm approaches[edit] The following are widely considered the main programming paradigms, a ...