高斯模糊是图像模糊处理中非常经典和常见的一种算法,也是Bloom屏幕效果的基础。

实现高斯模糊同样用到了卷积的概念,关于卷积的概念和原理详见我的另一篇博客:

https://www.cnblogs.com/koshio0219/p/11137155.html

通过高斯方程计算出的卷积核称为高斯核,一个5*5的高斯核对它进行权重归一化如下:

0.0030 0.0133 0.0219 0.0133 0.0030
0.0133 0.0596 0.0983 0.0596 0.0133
0.0219 0.0983 0.1621 0.0983 0.0219
0.0133 0.0596 0.0983 0.0596 0.0133
0.0030 0.0133 0.0219 0.0133 0.0030

通过表也可以很清楚的看到,离原点越近的点模糊程度影响越大,反之越小。

为了优化计算,可以将这个5*5矩阵简化为两个矩阵分别计算,得到的效果是相同的。

它们分别是一个1*5的横向矩阵和一个5*1的纵向矩阵,这样我们只需要对横纵向矩阵分别进行一次采样既可,这样可以很大程度的减少计算量。

拆分之后结果如下:

我们发现,最终的计算只需要记录3个权重值既可,它们是weight[3]={0.4026,0.2442,0.0545};

具体实现:

1.实现调整高斯模糊参数的脚本。

为了进一步优化计算,这里加入了降采样系数,模糊范围缩放;为此,需要在外部增加模糊采样的迭代次数,具体如下:

 using UnityEngine;

 public class GaussianBlurCtrl : ScreenEffectBase
{
private const string _BlurSize = "_BlurSize";//只有模糊范围需要在GPU中计算 [Range(, )]
public int iterations = ;//迭代次数
[Range(0.2f, )]
public float blurSize = 0.6f;//模糊范围
[Range(, )]
public int downSample = ;//降采样系数 private void OnRenderImage(RenderTexture source, RenderTexture destination)
{
if (Material != null)
{
//得到屏幕的渲染纹理后直接除以降采样系数以成倍减少计算量,但过大时模糊效果不佳
int rtw = source.width/downSample;
int rth = source.height/downSample; RenderTexture buffer0 = RenderTexture.GetTemporary(rtw, rth, );
buffer0.filterMode = FilterMode.Bilinear; Graphics.Blit(source, buffer0); //利用迭代次数对模糊范围加以控制,用到了类似于双缓冲的方式对纹理进行处理
for (int i = ; i < iterations; i++)
{
//设置采样范围,根据迭代次数范围增加,之后会与纹理坐标进行乘积操作,固基础值为1
Material.SetFloat(_BlurSize, blurSize*i+); RenderTexture buffer1 = RenderTexture.GetTemporary(rtw, rth, );
Graphics.Blit(buffer0, buffer1, Material, );
//每次处理完立即释放相应缓存,因为Unity内部已经对此做了相应的优化
RenderTexture.ReleaseTemporary(buffer0);
buffer0 = RenderTexture.GetTemporary(rtw, rth, );
Graphics.Blit(buffer1, buffer0,Material, );
RenderTexture.ReleaseTemporary(buffer1);
}
Graphics.Blit(buffer0, destination);
RenderTexture.ReleaseTemporary(buffer0);
}
else
Graphics.Blit(source, destination);
}
}

基类脚本见:

https://www.cnblogs.com/koshio0219/p/11131619.html

2.在Shader中分别进行横向和纵向的模糊计算,分为两个Pass进行,具体如下:

 Shader "MyUnlit/GaussianBlur"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
}
SubShader
{
Tags { "RenderType"="Opaque" } //CGINCLUDE中的代码可被其他Pass重复调用,用于简化不必要的重复代码
CGINCLUDE #pragma multi_compile_fog
#include "UnityCG.cginc" struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
}; struct v2f
{
half2 uv[] : TEXCOORD0;
UNITY_FOG_COORDS()
float4 pos : SV_POSITION;
}; sampler2D _MainTex;
float4 _MainTex_TexelSize;
float _BlurSize; //用于计算纵向模糊的纹理坐标元素
v2f vert_v(appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
half2 uv = v.uv; //以扩散的方式对数组进行排序,只偏移y轴,其中1和2,3和4分别位于原始点0的上下,且距离1个单位和2个像素单位
//得到的最终偏移与模糊范围的控制参数进行乘积
o.uv[] = uv;
o.uv[] = uv + float2(0.0, _MainTex_TexelSize.y*1.0)*_BlurSize;
o.uv[] = uv - float2(0.0, _MainTex_TexelSize.y*1.0)*_BlurSize;
o.uv[] = uv + float2(0.0, _MainTex_TexelSize.y*2.0)*_BlurSize;
o.uv[] = uv - float2(0.0, _MainTex_TexelSize.y*2.0)*_BlurSize; UNITY_TRANSFER_FOG(o, o.vertex);
return o;
} //用于计算横向模糊的纹理坐标元素
v2f vert_h(appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
half2 uv = v.uv; //与上面同理,只不过是x轴向的模糊偏移
o.uv[] = uv;
o.uv[] = uv + float2( _MainTex_TexelSize.x*1.0,0.0)*_BlurSize;
o.uv[] = uv - float2( _MainTex_TexelSize.x*1.0,0.0)*_BlurSize;
o.uv[] = uv + float2( _MainTex_TexelSize.x*2.0,0.0)*_BlurSize;
o.uv[] = uv - float2( _MainTex_TexelSize.x*2.0,0.0)*_BlurSize; UNITY_TRANSFER_FOG(o, o.vertex);
return o;
} //在片元着色器中进行最终的模糊计算,此过程在每个Pass中都会进行一次计算,但计算方式是统一的
fixed4 frag(v2f i) : SV_Target
{
float weights[] = {0.4026,0.2442,0.0545}; fixed4 col = tex2D(_MainTex, i.uv[]); fixed3 sum = col.rgb*weights[]; //对采样结果进行对应纹理偏移坐标的权重计算,以得到模糊的效果
for (int it = ; it < ; it++)
{
sum += tex2D(_MainTex, i.uv[ * it - ]).rgb*weights[it];//对应1和3,也就是原始像素的上方两像素
sum += tex2D(_MainTex, i.uv[ * it]).rgb*weights[it];//对应2和4,下方两像素
}
fixed4 color = fixed4(sum, 1.0);
UNITY_APPLY_FOG(i.fogCoord, color);
return color;
} ENDCG ZTest Always
Cull Off
ZWrite Off //纵向模糊Pass,直接用指令调用上面的函数
Pass
{
NAME "GAUSSIANBLUR_V"
CGPROGRAM
#pragma vertex vert_v
#pragma fragment frag ENDCG
} //横向模糊Pass
Pass
{
NAME "GAUSSIANBLUR_H"
CGPROGRAM
#pragma vertex vert_h
#pragma fragment frag ENDCG
}
}
Fallback Off
}

效果如下:

Unity Shader 屏幕后效果——高斯模糊的更多相关文章

  1. Unity Shader 屏幕后效果——颜色校正

    屏幕后效果指的是,当前整个场景图已经渲染完成输出到屏幕后,再对输出的屏幕图像进行的操作. 在Unity中,一般过程通常是: 1.建立用于处理效果的shader和临时材质,给shader脚本传递需要控制 ...

  2. Unity Shader 屏幕后效果——边缘检测

    关于屏幕后效果的控制类详细见之前写的另一篇博客: https://www.cnblogs.com/koshio0219/p/11131619.html 这篇主要是基于之前的控制类,实现另一种常见的屏幕 ...

  3. Unity Shader 屏幕后效果——全局雾

    Unity内置的雾效需要在每个shader中分别编写,造成了极大的不便.这里利用屏幕后处理产生可单独控制且自由度更高的雾效. 屏幕后雾效的本质在于,通过深度纹理重构出每个像素在世界空间中的位置,根据得 ...

  4. Unity Shader 屏幕后效果——景深

    景深效果的原理是,在摄像机的近裁剪平面和远裁剪平面之间可以设置一个焦距,在这个距离所在的平面上的物体最为清晰,而这个距离之前或之后的物体成像是一种模糊状态(根据距离逐渐模糊,最终达到最为模糊的状态). ...

  5. Unity Shader 屏幕后效果——Bloom外发光

    Bloom的原理很简单,主要是提取渲染图像中的亮部区域,并对亮部区域进行模糊处理,再与原始图像混合而成. 一般对亮部进行模糊处理的部分采用高斯模糊,关于高斯模糊,详见之前的另一篇博客: https:/ ...

  6. Unity Shader 屏幕后效果——摄像机运动模糊(速度映射图实现)

    速度映射图主要是为了得到每个像素相对于前一帧的运动矢量,其中一种方法是使用摄像机的深度纹理来推导. 推导过程如下: 先由深度纹理逆推出NDC(归一化的设备坐标)下的顶点坐标,利用VP矩阵(视角*投影矩 ...

  7. Unity Shader实现描边效果

    http://gad.qq.com/article/detail/28346 描边效果是游戏里面非常常用的一种效果,一般是为了凸显游戏中的某个对象,会给对象增加一个描边效果.本篇文章和大家介绍下利用S ...

  8. Unity Shader 之 透明效果

    透明效果 透明效果一般有两种实现方法: 第一种,使用透明度测试(Alpha Test) 第二种,使用透明度混合(Alpha Blending) 透明度测试和透明度混合机制: 透明度测试(Alpha T ...

  9. Unity实现屏幕抖动效果(通过Camera Viewpoint实现)

    由于游戏死亡时一般都需要屏幕抖一下下. 所以百度了下相关写法,发现方法很多~~~ 找来找去,找到个简单粗暴地,啥都不需要,一个脚本拖动到Camera上就可以了 略微修改了一点点,share一下 usi ...

随机推荐

  1. PERFORM参数传递

    参数传递:将主程序变量传递给子例程形式参数传递类型值传:子例程中参数变量的值的改变,不影响外部程序实际变量的值. , B , C TYPE I. WRITE:'A=',A,'B=',B,'C=',C. ...

  2. 高性能TcpServer(C#) - 3.命令通道(处理:掉包,粘包,垃圾包)

    高性能TcpServer(C#) - 1.网络通信协议 高性能TcpServer(C#) - 2.创建高性能Socket服务器SocketAsyncEventArgs的实现(IOCP) 高性能TcpS ...

  3. C# 人民币大写金额转换

    /// <summary>       /// 转换人民币大小金额       /// </summary>       /// <param name="nu ...

  4. golang中,slice的几个易混淆点

    slice在golang中是最常用的类型,一般可以把它作为数组使用,但是比数组要高效呀.不过,我感觉这个东西用的不好坑太多了.还是需要了解下他底层的实现 slice的结构定义 type slice s ...

  5. [Tomcat源码分析] Eclipse中搭建Apache Tomcat源码调试环境

    网上很多文章都推荐使用Ant下载编译,但本地实践中屡屡失败,无法下载. 后来参考 https://blog.csdn.net/xiongyouqiang/article/details/7894107 ...

  6. reduce求和真方便

    1.reduce的用法. reduce是JavaScript中的一个方法,常用于数组求和,接收两个参数,第一个参数为累加函数,第二个参数为初始值,这个初始值是前面那个累加函数的参数.如果不指定初始值, ...

  7. Lumen5.7快速实现Captcha图片验证码功能

    公司发送短信注册的接口需要防刷,需要一个图形验证码,不考虑收费产品. Lumen5.7+nginx+mysql 使用了这个作者的扩展包,只讲实现.https://github.com/Youngyez ...

  8. ZKEACMS 无法运行问题汇总

    前言 如果你还不知道ZKEACMS,不妨先了解一下. ASP.NET MVC 开源建站系统 ZKEACMS 推荐,从此网站“拼”起来 官方地址:http://www.zkea.net/zkeacms ...

  9. spring-cloud-kubernetes服务发现之在k8s环境下开发spring cloud应用

    通常情况下,我们的线上的服务在迁移到k8s环境下的时候,都是采用平滑迁移的方案.服务治理与注册中心等都是采用原先的组件.比如spring cloud应用,在k8s环境下还是用原来的一套注册中心(如eu ...

  10. 使用Nginx+Openresty实现WAF功能

    什么是WAF Web应用防护系统(也称为:网站应用级入侵防御系统.英文:Web Application Firewall,简称: WAF).利用国际上公认的一种说法:Web应用防火墙是通过执行一系列针 ...