1.此代码放到Camera上
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GaussianBlur : PostEffectsBase
{
public Shader GaussianBlurShader;
private Material _gaussianBlurMaterial = null;
public Material Material
{
get
{
_gaussianBlurMaterial = CheckShaderAndCreateMaterial(GaussianBlurShader, _gaussianBlurMaterial);
return _gaussianBlurMaterial;
}
}
//模糊迭代次数,数值越大越模糊
[Range(0, 4)] public int Iterations = 3;
//每次迭代的模糊范围,数值越大越模糊,但过大会造成虚影
[Range(0.2f, 3.0f)] public float BlurSpread = 0.6f;
//缩放系数,数值越大越模糊,需要处理的像素越少,但过大会使图像像素化
[Range(1, 8)] public int DownSample = 2;
/* //版本一,只应用模糊
void OnRenderImage(RenderTexture src, RenderTexture dest)
{
if (Material != null)
{
int rtW = src.width;
int rtH = src.height;
//使用RenderTexture.GetTemporary函数分配了一块与屏幕图像大小相同的缓冲区
//高斯模糊需要调用两个Pass,需要使用缓冲区来存储第一个Pass执行完毕后的模糊结果
RenderTexture buffer = RenderTexture.GetTemporary(rtW, rtH, 0);
//使用第一个Pass(竖直方向的一维高斯核)处理src,并存储再buffer中,最后一个参数是Pass的索引
Graphics.Blit(src, buffer, Material, 0);
//使用第二个Pass(水平方向的一维高斯核)处理buffer,并输出最终结果,最后一个参数是Pass的索引
Graphics.Blit(buffer, dest, Material, 1);
//释放之前分配的缓存
RenderTexture.ReleaseTemporary(buffer);
}
else
{
Graphics.Blit(src, dest);
}
}
*/
/* //版本二,利用缩放对图像进行降采样,减少需要处理的像素个数,提高性能
//适当降采样可以得到更好的模糊效果,但过大的缩放系数可能会造成图像像素化
void OnRenderImage(RenderTexture src, RenderTexture dest)
{
if (Material != null)
{
//利用缩放系数,使用小于屏幕分辨率的尺寸
int rtW = src.width / DownSample;
int rtH = src.height / DownSample;
RenderTexture buffer = RenderTexture.GetTemporary(rtW, rtH, 0);
//设置临时渲染纹理的滤波模式为双线性
//这样在调用第一个Pass时,我们需要处理的像素个数就是原来的几分之一
buffer.filterMode = FilterMode.Bilinear;
//调用第一个Pass(竖直方向)处理src,并存储在缓冲区
Graphics.Blit(src, buffer, Material, 0);
//调用第二个Pass(水平方向)处理buffer,并输出最终效果
Graphics.Blit(buffer, dest, Material, 1);
//释放缓冲区
RenderTexture.ReleaseTemporary(buffer);
}
else
{
Graphics.Blit(src, dest);
}
}
*/
//版本三,考虑高斯模糊的迭代次数,使用两个缓存,循环调用Pass
void OnRenderImage(RenderTexture src, RenderTexture dest)
{
if (Material != null)
{
int rtW = src.width / DownSample;
int rtH = src.height / DownSample;
//定义第一个缓存
RenderTexture buffer0 = RenderTexture.GetTemporary(rtW, rtH, 0);
buffer0.filterMode = FilterMode.Bilinear;
//图像缩放后存储入缓存中
Graphics.Blit(src, buffer0);
//循环迭代
for (int i = 0; i < Iterations; i++)
{
//将迭代的模糊范围穿给shader
Material.SetFloat("_BlurSize", 1.0f + i * BlurSpread);
//定义第二个缓存
RenderTexture buffer1 = RenderTexture.GetTemporary(rtW, rtH, 0);
//执行第一个Pass, 输入buffer0, 输出buffer1
Graphics.Blit(buffer0, buffer1, Material, 0);
//释放buffer0
RenderTexture.ReleaseTemporary(buffer0);
//将buffer1存储到buffer0
buffer0 = buffer1;
//重新分配buffer1
buffer1 = RenderTexture.GetTemporary(rtW, rtH, 0);
//执行第二个pass,输入buffer0,输出buffer1
Graphics.Blit(buffer0, buffer1, Material, 1);
//释放buffer0
RenderTexture.ReleaseTemporary(buffer0);
//将buffer1存储入buffer0
buffer0 = buffer1;
}
//迭代完成后,最终值存储在buffer0中,输出最终值
Graphics.Blit(buffer0, dest);
//释放buffer0
RenderTexture.ReleaseTemporary(buffer0);
}
else
{
Graphics.Blit(src, dest);
}
}
}
2.此Shader赋值给上面的代码
Shader "UnityShaderBook/Chapter12/GaussianBlur"
{
Properties
{
_MainTex ("Base(RGB)", 2D) = "white" {}
_BlurSize("Blur Size", Float) = 1.0
}
SubShader
{
//使用CGINCLUDE & ENDCG组织代码,这些代码不需要包含在任何Pass中
//使用时,只需要在Pass中直接制定需要使用的顶点着色器和片元着色器函数名即可
//由于高斯模糊需要定义两个Pass,但他们的片元着色器代码时相同的,使用CGINDCLUDE可以避免编写两个完全一样的frag函数
CGINCLUDE
#include "UnityCG.cginc"
//定义与属性对应的变量
sampler2D _MainTex;
//计算相邻像素的纹理坐标偏移量,为得到相邻像素的纹理坐标
half4 _MainTex_TexelSize;
float _BlurSize;
//分别定义两个Pass使用的顶点着色器
struct v2f {
float4 pos : SV_POSITION;
//使用5*5的高斯核,二维高斯核会被拆分成两个大小为5的一维高斯核,所以只需要计算5个纹理坐标
half2 uv[5] : TEXCOORD0;
};
//竖直方向的顶点着色器代码
v2f vertBlurVertical(appdata_img v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
half2 uv = v.texcoord;
//当前采样纹理
o.uv[0] = uv;
//邻域采样纹理
o.uv[1] = uv + float2(0.0, _MainTex_TexelSize.y * 1.0) * _BlurSize;
o.uv[2] = uv - float2(0.0, _MainTex_TexelSize.y * 1.0) * _BlurSize;
o.uv[3] = uv + float2(0.0, _MainTex_TexelSize.y * 2.0) * _BlurSize;
o.uv[4] = uv - float2(0.0, _MainTex_TexelSize.y * 2.0) * _BlurSize;
return o;
}
//水平方向的顶点着色器代码
v2f vertBlurHorizontal(appdata_img v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
half2 uv = v.texcoord;
//当前采样纹理
o.uv[0] = uv;
//邻域采样纹理
o.uv[1] = uv + float2(_MainTex_TexelSize.x * 1.0, 0.0) * _BlurSize;
o.uv[2] = uv - float2(_MainTex_TexelSize.x * 1.0, 0.0) * _BlurSize;
o.uv[3] = uv + float2(_MainTex_TexelSize.x * 2.0, 0.0) * _BlurSize;
o.uv[4] = uv - float2(_MainTex_TexelSize.x * 2.0, 0.0) * _BlurSize;
return o;
}
//定义两个Pass共用的片元着色器
fixed4 fragBlur(v2f i) : SV_Target{
//由于5*5的高斯核可以拆分成两个大小为5的一维高斯核,并且由于对称性,所以只需要记录3个高斯权重
float weight[3] = {0.4026,0.2442,0.0545};
//初始化滤波值,当前像素值乘以它的权重值
fixed3 sum = tex2D(_MainTex, i.uv[0]).rgb * weight[0];
//根据对称性,进行了两次迭代,每次迭代包含了两次纹理采样,并把像素值和权重相乘后的结果叠加到滤波值中
for (int it = 1; it < 3; it++) {
sum += tex2D(_MainTex, i.uv[it * 2 - 1]).rgb * weight[it];
sum += tex2D(_MainTex, i.uv[it * 2]).rgb * weight[it];
}
return fixed4(sum, 1.0);
}
ENDCG
//定义高斯模糊使用的两个Pass
//设置渲染状态
ZTest Always Cull Off ZWrite Off
Pass
{
//定义了名字,在其他Shader中可以直接通过名字来使用该Pass,不需要再重复写代码
NAME"GAUSSIAN_BLUR_VERTICAL"
CGPROGRAM
#pragma vertex vertBlurVertical
#pragma fragment fragBlur
ENDCG
}
Pass
{
NAME"GAUSSIAN_BLUR_HORIZONTAL"
CGPROGRAM
#pragma vertex vertBlurVertical
#pragma fragment fragBlur
ENDCG
}
}
Fallback "Diffuse"
}