UnityShader实例13:屏幕特效之均值模糊(Box Blur)
均值模糊(Box Blur)
概述
因为公司手游项目需求。须要一个适合手机平台的模糊效果,同一时候须要开放一个參数便于调节模糊值。我首先想到的就是ps里面的均值模糊。
查资料能够知道均值模糊是一种高速的图像模糊技术,相比与传统的卷积模糊(如高斯模糊),均值模糊能够更加有效率的完毕对图像模糊。在unity官方自带imageeffect包也有一个blur的屏幕特效,用的就是均值模糊算法,仅仅只是他仅仅採样了离原像素上下左右模糊半径(Blur
Spread)距离的四个像素进行平均处理。然后做迭代(Iterations)处理。即将模糊后的图像再次採样模糊,迭代次数越高越模糊。同一时候也会产生很多其它的drawcall,本文的效果就是从这个样例简化而来,使之适合手游项目,而且傻瓜式方便的调节參数。
原理
均值模糊的原理是通过图形滤波器来把一个像素和周围的像素一起求平均值。比方一个三阶的图像滤波器构造事实上就是一个3*3的数组(n阶数组中的元素成为滤波器的系数和滤波器的权重。n称为滤波器的阶)。相当于把一个像素和周围8个像素相加在一起再除以9求平均值,等于把一个像素和周围的像素搅拌在一起,自然就模糊了。均值滤波器与高斯模糊的滤波器不同的地方。就是採样的像素权重是相等的。因此效果相对而言要比高斯模糊差。但速度却要快一些。本例事实上仅仅採样了四个像素求和做平均。然后通过迭代多次採样。实现比較好的模糊效果。
以下是一个三阶的均值滤波器:
本例採用的滤波器:
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完整代码
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}
}
}
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,不做模糊处理。
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)的更多相关文章
- 图像处理------快速均值模糊(Box Blur)
图像模糊的本质, 从数字信号处理的角度看,图像模糊就要压制高频信号保留低频信号, 压制高频的信号的一个可选择的方法就是卷积滤波.选择一个低频滤波器,对图像上的 每个像素实现低频滤波,这样整体效果就是一 ...
- UnityShader实例15:屏幕特效之Bloom
http://blog.csdn.net/u011047171/article/details/48522073 Bloom特效 概述 Bloom,又称“全屏泛光”,是游戏中 ...
- UnityShader之屏幕特效基础
1.什么是屏幕特效 我们这里讲的屏幕特效技术,指的是在渲染完整个场景后得到的屏幕图象的基础上,再对这个屏幕图像做一系列处理,实现出屏幕特效,使用这种技术可以为屏幕画面增添各种风格的艺术效果,比如泛光. ...
- Unity shader学习之屏幕后期处理效果之均值模糊
均值模糊,也使用卷积来实现,之不过卷积中每个值均相等,且相加等于1. 代码如下, 子类: using UnityEngine; public class MeanBlurRenderer : Post ...
- Unity Shader-后处理:简单均值模糊
一.简介 今天来学习一下后处理中比较常用的一种效果,屏幕模糊效果.模糊效果,在图像处理中经常用到,Photoshop中也有类似的滤镜.我们在游戏中也会经常用到.因为屏幕模糊效果是一些高级后处理效果 ...
- Unity shader(CG) 写一个 散色、折射、反射、菲涅尔、gamma、简单后期屏幕特效
http://www.lai18.com/content/506918.html 1.自生要求是很重要的,当然不是什么强迫工作之类的,而是自己有限的能力上不断的扩展兴趣上的内容. 2.用生活的眼光去发 ...
- 在C#里实现各种窗口切换特效,多达13种特效
原文:http://www.cnblogs.com/clayui/archive/2011/06/28/2092126.html 预览: 下载 这次clayui给大家带来了比较实用的东西,因为时间 ...
- Android实例-获取屏幕的物理分辨率
相关资料: http://blog.qdac.cc/?p=1161 实例代码: unit Unit1; interface uses System.SysUtils, System.Types, Sy ...
- UnityShader实例09:Stencil Buffer&Stencil Test
http://blog.csdn.net/u011047171/article/details/46928463 Stencil Buffer&Stencil Test 在开始前先吐槽下uni ...
随机推荐
- Swift入门(四)——可选类型(Optionals)与断言(Assert)
可选类型是什么? 首先看一个问题,Swift中String类型的变量有一个叫做toInt的方法,能够把String类型变量转换为Int类型变量. var stringValue = "5&q ...
- [Tailwind] Control What Variations are Generated for Each Utility Class Module in Tailwind
In this lesson, we learn how to control what utility classes are generated for each utility class mo ...
- Java-MyBatis:MyBatis 3 | SQL 语句构建器类
ylbtech-Java-MyBatis:MyBatis 3 | SQL 语句构建器类 1.返回顶部 1. SQL语句构建器类 问题 Java程序员面对的最痛苦的事情之一就是在Java代码中嵌入SQL ...
- DB-MySQL:MySQL 教程
ylbtech-DB-MySQL:MySQL 教程 1.返回顶部 1. MySQL 教程 MySQL 是最流行的关系型数据库管理系统,在WEB应用方面 MySQL 是最好的RDBMS(Relation ...
- block的一些注意事项
1,定义block时是可以同时进行赋值的 2,block中是代码块,就是里面写的是语句,需要加分号 3,在block中,允许有多条语句 4,在带有参数的block中,声明部分参数名可以省略,但是建议写
- Selenium启动不同浏览器
1.启动Chrome "webdriver.chrome.driver" System.setProperty("webdriver.chrome.driver" ...
- Memcache使用场景
session //php文件中 ini_set("session.save_handler", "memcache"); ini_set("sess ...
- React实现单例组件
问题背景 在工作中遇到了这样一个场景,写了个通用的弹窗组件,却在同一个页面中多次使用了该组件.当点击打开弹窗时,可想而知,一次性打开了多个弹窗,而业务需求只需要打开一个. 我个人在解决问题过程中的一些 ...
- 什么是CNN--Convolutional Neural Networks
是近些年在机器视觉领域很火的模型,最先由 Yan Lecun 提出. 如果想学细节可以看 Andrej Karpathy 的 cs231n . How does it work? 给一张图片,每个圆负 ...
- Android和IOS等效MD5加密
最近在Android和IOS上都需要对用户的某些输入进行简单的加密,于是采用MD5加密方式. 首先将目的字符串加密一次,获得32位字符串 然后将32位字符串拆为2段,分别加密1次 最后将加密后的2段拼 ...