Unity Shader:Blur
花了一晚上的时间终于看懂Image Effect中的Blur,其实很简单,就是一下子没有理解到。
原理:使用两个一维[1*7]的高斯滤波模板,一个用在x方向,另一个用在y方向。高斯滤波有模糊的效果。
js脚本参数:
Down Sample:OnRenderImage中获取的图像进行降采样,其实就是把要处理的纹理变小。有利于加快shader运行速度。
Blur Size:在使用高斯模板时,相邻像素点的间隔。越大间隔越远,图像越模糊。但过大的值会导致失真。
Blur Iterations:迭代次数,越大模糊效果越好,但消耗越大。
Blur Type:两个不同的shader,后一个是前一个的优化版本,但差别不大。
具体代码分析:
function OnRenderImage (source : RenderTexture, destination : RenderTexture) {
if(CheckResources() == false) {
Graphics.Blit (source, destination);
return;
}
var widthMod : float = 1.0f / (1.0f * (1<<downsample)); // 降采样系数的倒数,用于调整降采样后,相邻像素的间隔
// blurMaterial.SetVector ("_Parameter", Vector4 (blurSize * widthMod, -blurSize * widthMod, 0.0f, 0.0f));
source.filterMode = FilterMode.Bilinear;
var rtW : int = source.width >> downsample; // >> 是除法的优化
var rtH : int = source.height >> downsample;
// downsample
var rt : RenderTexture = RenderTexture.GetTemporary (rtW, rtH, 0, source.format);
rt.filterMode = FilterMode.Bilinear;
// 对应的shader的Pass 0
Graphics.Blit (source, rt, blurMaterial, 0); //首先对图像进行降采样,同时进行简单的模糊
var passOffs = blurType == BlurType.StandardGauss ? 0 : 2; // 选择不同的blurtype,就调用不同的shader pass
for(var i : int = 0; i < blurIterations; i++) {
var iterationOffs : float = (i*1.0f);
// _Parameter.x 记录的是 相邻像素的间隔,随着迭代次数增大
blurMaterial.SetVector ("_Parameter",
Vector4 (blurSize * widthMod + iterationOffs, -blurSize * widthMod - iterationOffs, 0.0f, 0.0f));
// vertical blur 垂直滤波
var rt2 : RenderTexture = RenderTexture.GetTemporary (rtW, rtH, 0, source.format);
rt2.filterMode = FilterMode.Bilinear;
Graphics.Blit (rt, rt2, blurMaterial, 1 + passOffs); // 对应着shader的pass 1,2
RenderTexture.ReleaseTemporary (rt);
rt = rt2;
// horizontal blur 水平滤波
rt2 = RenderTexture.GetTemporary (rtW, rtH, 0, source.format);
rt2.filterMode = FilterMode.Bilinear;
Graphics.Blit (rt, rt2, blurMaterial, 2 + passOffs); // 对应着shader的pass 3,4
RenderTexture.ReleaseTemporary (rt);
rt = rt2;
}
Graphics.Blit (rt, destination);
RenderTexture.ReleaseTemporary (rt);
}
接着分析shader文件:
先看5个pass,分别是用在上文cs脚本中的Bilt函数中。
SubShader {
ZTest Off Cull Off ZWrite Off Blend Off
Fog { Mode off }
// 0
Pass {
CGPROGRAM
#pragma vertex vert4Tap
#pragma fragment fragDownsample
#pragma fragmentoption ARB_precision_hint_fastest
ENDCG
}
//
Pass {
ZTest Always
Cull Off
CGPROGRAM
#pragma vertex vertBlurVertical
#pragma fragment fragBlur8
#pragma fragmentoption ARB_precision_hint_fastest
ENDCG
}
//
Pass {
ZTest Always
Cull Off
CGPROGRAM
#pragma vertex vertBlurHorizontal
#pragma fragment fragBlur8
#pragma fragmentoption ARB_precision_hint_fastest
ENDCG
}
// alternate blur
//
Pass {
ZTest Always
Cull Off
CGPROGRAM
#pragma vertex vertBlurVerticalSGX
#pragma fragment fragBlurSGX
#pragma fragmentoption ARB_precision_hint_fastest
ENDCG
}
//
Pass {
ZTest Always
Cull Off
CGPROGRAM
#pragma vertex vertBlurHorizontalSGX
#pragma fragment fragBlurSGX
#pragma fragmentoption ARB_precision_hint_fastest
ENDCG
}
}
pass 0:在降采样的同时,进行简单地模糊处理。
v2f_tap vert4Tap ( appdata_img v )
{
v2f_tap o; o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
// 取像素周围的点
o.uv20 = v.texcoord + _MainTex_TexelSize.xy;
o.uv21 = v.texcoord + _MainTex_TexelSize.xy * half2(-.5h,-.5h);
o.uv22 = v.texcoord + _MainTex_TexelSize.xy * half2(.5h,-.5h);
o.uv23 = v.texcoord + _MainTex_TexelSize.xy * half2(-.5h,.5h); return o;
} fixed4 fragDownsample ( v2f_tap i ) : SV_Target
{
fixed4 color = tex2D (_MainTex, i.uv20);
color += tex2D (_MainTex, i.uv21);
color += tex2D (_MainTex, i.uv22);
color += tex2D (_MainTex, i.uv23);
return color / ;
}
接下来的pass 1,2 和pass 3, 4,都是分别在x y两个方向进行高斯滤波。
先看看高斯滤波模板:
static const half4 curve4[] = { half4(0.0205,0.0205,0.0205,), half4(0.0855,0.0855,0.0855,), half4(0.232,0.232,0.232,),
half4(0.324,0.324,0.324,), half4(0.232,0.232,0.232,), half4(0.0855,0.0855,0.0855,), half4(0.0205,0.0205,0.0205,) };
这是 [1*7]的模板,对中间点像素的左右两边各3个像素,总共7个像素进行加权求和,得到新的像素值。
pass 1,2的只有vert函数不一样,分别是取水平和垂直方向的偏差值。
v2f_withBlurCoords8 vertBlurHorizontal (appdata_img v)
{
v2f_withBlurCoords8 o;
o.pos = mul (UNITY_MATRIX_MVP, v.vertex); o.uv = half4(v.texcoord.xy,,);
o.offs = _MainTex_TexelSize.xy * half2(1.0, 0.0) * _Parameter.x; // 水平方向的偏差值 return o;
} v2f_withBlurCoords8 vertBlurVertical (appdata_img v)
{
v2f_withBlurCoords8 o;
o.pos = mul (UNITY_MATRIX_MVP, v.vertex); o.uv = half4(v.texcoord.xy,,);
o.offs = _MainTex_TexelSize.xy * half2(0.0, 1.0) * _Parameter.x; // 垂直方向的偏差值 return o;
} half4 fragBlur8 ( v2f_withBlurCoords8 i ) : SV_Target
{
half2 uv = i.uv.xy;
half2 netFilterWidth = i.offs;
half2 coords = uv - netFilterWidth * 3.0; // 这里从中心点偏移3个间隔,从最左边或者是最上边开始进行加权累加 half4 color = ;
for( int l = ; l < ; l++ )
{
half4 tap = tex2D(_MainTex, coords);
color += tap * curve4[l]; // 像素值乘上对应的权值
coords += netFilterWidth; // 移到下一个像素
}
return color;
}
在pass 1,2中的uv值都是float2向量,然而寄存器可以一次性储存float4,即可以一个float4值存储两个uv值。并且像素着色器函数中,计算相邻像素的步骤,可以放在顶点着色器中。于是就有下面这个版本:
v2f_withBlurCoordsSGX vertBlurHorizontalSGX (appdata_img v)
{
v2f_withBlurCoordsSGX o;
o.pos = mul (UNITY_MATRIX_MVP, v.vertex); o.uv = v.texcoord.xy;
half2 netFilterWidth = _MainTex_TexelSize.xy * half2(1.0, 0.0) * _Parameter.x;
half4 coords = -netFilterWidth.xyxy * 3.0;
// 计算左右相邻各3个像素的坐标
o.offs[] = v.texcoord.xyxy + coords * half4(.0h,.0h,-.0h,-.0h);
coords += netFilterWidth.xyxy;
o.offs[] = v.texcoord.xyxy + coords * half4(.0h,.0h,-.0h,-.0h);
coords += netFilterWidth.xyxy;
o.offs[] = v.texcoord.xyxy + coords * half4(.0h,.0h,-.0h,-.0h); return o;
} v2f_withBlurCoordsSGX vertBlurVerticalSGX (appdata_img v)
{
v2f_withBlurCoordsSGX o;
o.pos = mul (UNITY_MATRIX_MVP, v.vertex); o.uv = half4(v.texcoord.xy,,);
half2 netFilterWidth = _MainTex_TexelSize.xy * half2(0.0, 1.0) * _Parameter.x;
half4 coords = -netFilterWidth.xyxy * 3.0;
// 计算上下相邻各3个像素的坐标
o.offs[] = v.texcoord.xyxy + coords * half4(.0h,.0h,-.0h,-.0h);
coords += netFilterWidth.xyxy;
o.offs[] = v.texcoord.xyxy + coords * half4(.0h,.0h,-.0h,-.0h);
coords += netFilterWidth.xyxy;
o.offs[] = v.texcoord.xyxy + coords * half4(.0h,.0h,-.0h,-.0h); return o;
} half4 fragBlurSGX ( v2f_withBlurCoordsSGX i ) : SV_Target
{
half2 uv = i.uv.xy; half4 color = tex2D(_MainTex, i.uv) * curve4[]; // 中间像素,乘上对应的权值 for( int l = ; l < ; l++ )
{
half4 tapA = tex2D(_MainTex, i.offs[l].xy);
half4 tapB = tex2D(_MainTex, i.offs[l].zw);
color += (tapA + tapB) * curve4[l]; // 由于模板是对称的,可以使用相同的权值
} return color; }
结论:
通过调试,发现使用downsampler为1,iteration为2时,调整blursize可以得到较好的效果,并且性能较好。但blursize为0时,还是模糊图像,想做成那种从清晰到模糊的动画,估计还要调整一下代码。
Unity Shader:Blur的更多相关文章
- 【Unity Shader】Shader基础
目录 Chapter3 Unity Shader 基础 Chapter3 Unity Shader 基础 概述 在Unity需要材质(Material)与Unity Shader配合使用来达到满意的效 ...
- Unity Shader入门精要学习笔记 - 第3章 Unity Shader 基础
来源作者:candycat http://blog.csdn.net/candycat1992/article/ 概述 总体来说,在Unity中我们需要配合使用材质和Unity Shader才能达 ...
- Unity3D学习(八):《Unity Shader入门精要》——透明效果
前言 在实时渲染中要实现透明效果,通常会在渲染模型时控制它的透明通道. Unity中通常使用两种方法来实现透明 :(1)透明度测试(AlphaTest)(2)透明度混合(AlphaBlend).前者往 ...
- Unity3D学习(六):《Unity Shader入门精要》——Unity的基础光照
前言 光学中,我们是用辐射度来量化光. 光照按照不同的散射方向分为:漫反射(diffuse)和高光反射(specular).高光反射描述物体是如何反射光线的,漫反射则表示有多少光线会被折射.吸收和散射 ...
- Unity Shader 基础(1): RenderType & ReplacementShader
很多Shader中都会定义RenderType这个类型,但是一直搞不明白到底是干嘛的,官方文档是这样结解释的:Rendering with Replaced Shaders Rendering wit ...
- unity shader入门(一):基本结构话痨版
unity shader 有三种形式:表面着色器(Surface Shader),顶点/片元着色器(Vertex/Fragment Shader),固定函数着色器(Fixed Function Sha ...
- Unity Shader 基础(4) 由深度纹理重建坐标
在PostImage中经常会用到物体本身的位置信息,但是Image Effect自身是不包含这些信息的,因为屏幕后处其实是使用特定的材质渲染一个刚好填满屏幕的四边形面片(四个角对应近剪裁面的四个角). ...
- Unity Shader 基础(3) 获取深度纹理
Unity提供了很多Image Effect效果,包含Global Fog.DOF.Boom.Blur.Edge Detection等等,这些效果里面都会使用到摄像机深度或者根据深度还原世界坐标实现各 ...
- Unity shader学习之屏幕后期处理效果之高斯模糊
高斯模糊,见 百度百科. 也使用卷积来实现,每个卷积元素的公式为: 其中б是标准方差,一般取值为1. x和y分别对应当前位置到卷积中心的整数距离. 由于需要对高斯核中的权重进行归一化,即使所有权重相加 ...
随机推荐
- vs 添加坚竖虚线(垂直虚线、肾虚线 by 我的Y韬)
Indent Guides https://visualstudiogallery.msdn.microsoft.com/e792686d-542b-474a-8c55-630980e72c30 vs ...
- Env:autojump安装使用
注:这里只介绍我使用的方式,当然不是唯一方式 作用:autojump可以快速进行路径导航,具备记忆历史路径:不仅仅是可以进入当前路径下的某个路径,也可以是其他历史路径 1. 下载 首先,$ git c ...
- 使用NSOperation使用,创建线程中传递多个参数
参考:http://blog.csdn.net/dqjyong/article/details/7677557 参考:http://stackoverflow.com/questions/232761 ...
- (WPF) MVVM: ComboBox Binding, XML 序列化
基本思路还是在View的Xmal里面绑定ViewModel的属性,虽然在View的后台代码中也可以实现binding,但是还是在Xmal里面相对的代码量要少一些. 此例子要实现的效果就是将一个List ...
- HDU 2176 取(m堆)石子游戏(Nim)
取(m堆)石子游戏 题意: Problem Description m堆石子,两人轮流取.只能在1堆中取.取完者胜.先取者负输出No.先取者胜输出Yes,然后输出怎样取子.例如5堆 5,7,8,9,1 ...
- opencv,关于物体检测
关于物体检测 环境:opencv 2.4.11+vs2013 参考: http://www.cnblogs.com/tornadomeet/archive/2012/06/02/2531705.htm ...
- dede的幻灯片除了首页其他页面也显示的方法,
<script language='javascript'> linkarr = new Array(); picarr = new Array(); textarr = new Arra ...
- linux命令(12)uniq去重
转载地址:http://blog.51yip.com/shell/1022.html 实例详细说明linux下去除重复行命令uniq 一,uniq干什么用的 文本中的重复行,基本上不是我们所要的,所以 ...
- 为FaceBook审核提交模拟器包及自己验证模拟器包
为FaceBook审核提交模拟器包及自己验证模拟器包折腾了一番,因为我的项目是用cocoapods管理的,所以跟普通直接运行name.xcodeproj项目有所不同. 切入正题 1.先设置 relea ...
- 拥抱高效、拥抱 Bugtags 之来自用户的声音(四)
小编按:这是一篇 Bugtags 用户来稿,经过一段时间的密集使用,他已然觉得 Bugtags 是 App 开发者的好帮手,感谢解铃 App - 楚琪同学对 Bugtags 的信赖和支持.小编在这里诚 ...