均值模糊(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. [Performance] Optimize Paint and Composite for the website

    "Paint" is one of the most preference killer, it can easily cost more than 60fps, and once ...

  2. HTTP Error 500.19

    HTTP Error 500.19 - Internal Server Error The requested page cannot be accessed because the related ...

  3. Jquery validform

    一.validform是什么?            validform是一款智能的表单验证js插件,它是基于jQuery库与css,我们只需要把表单对象放入,             就可以对整个表 ...

  4. Maven 学习笔记(三)

    有时我们在项目中可能需要打包一个可执行的 jar 包,我最近也遇见了,很傻很天真的用了如下配置: <packaging>jar</packaging> 效果一如既往的好,打包成 ...

  5. BZOJ 3230 后缀数组+ST

    思路: 首先我们已经会了后缀数组求本质不同的子串个数 这道题跟那个差不多 首先我们可以知道按字典序排好的每个后缀之前包含多少本质不同的字串 就是sigma(n-sa[i]+1-ht[i]+bi[i-1 ...

  6. 在Ubuntu Server下搭建LAMP环境

    1 LAMP的安装 LAMP通常是指Linux+Apache+MySQL+PHP组合形成的一套可以运行PHP程序的体系,并不是一个软件的名称.没有安装MySQL的服务器依然可以在其它条件完备的情况下运 ...

  7. Android设计模式——Builder(建造者)模式

    1.建造者模式是一步一步创建一个复杂对象的创建模式.该模式是为了将构建复杂对象的过程和他的部件解耦,使得构建过程和部件表示隔离开. 2.Bulider模式的定义是:将一个复杂对象的构建与它的表示分离, ...

  8. Python 之 PyCharm使用

    PyCharm  的官方网站地址是:https://www.jetbrains.com/pycharm/download/ 教育版:https://www.jetbrains.com/pycharm- ...

  9. Python 之 入门须知

    1.Python2.0不支持中文,3.0支持 2.版本问题

  10. Golden Gate 概述

    概述: 是什么?Oracle GoldenGate 提供异构环境间事务数据的实时.低影响的捕获.路由.转换和交付. 非侵入: 不建触发器,不建中间表,无需增量标记或时间戳字段 不在源表上进行数据查询 ...