using UnityEngine;
using UnityEngine.UI;
public class UIBgBlur : MonoBehaviour
{
// 高斯模糊迭代次数
[Range(0, 6)]
public int Iterations = 5;
// 每次迭代纹理坐标偏移的速度
[Range(0.2f, 4.0f)]
public float BlurSpread = 3f;
// 降采样比率
[Range(1, 8)]
public int DownSample = 4;
private RawImage _blurImage;
private RenderTexture _srcTexture, _destTexture;
private Texture2D _texture2D;
private Material _material;
private void Awake()
{
_blurImage = transform.GetComponent<RawImage>();
_material = _blurImage.material;
_blurImage.material = null;
_srcTexture = new RenderTexture(Screen.width, Screen.height, 24, RenderTextureFormat.ARGB32);
_destTexture = new RenderTexture(Screen.width, Screen.height, 24, RenderTextureFormat.ARGB32);
SetEnanble(true);
}
public void SetSortingOrder(int order)
{
Canvas blurCanvas = GetComponent<Canvas>();
blurCanvas.overrideSorting = true;
blurCanvas.sortingOrder = order;
}
public void SetEnanble(bool isOK)
{
Camera camera = Camera.main;
_blurImage.gameObject.SetActive(isOK && camera != null);
if (isOK)
DrawCameraBlur(camera);
}
/// <summary>
/// 绘制相机的图片
/// </summary>
private void DrawCameraBlur(Camera camera)
{
if (camera == null)
return;
//保存相机渲染用于后面恢复
var cameraPrevT = camera.targetTexture;
camera.targetTexture = _srcTexture;
camera.Render();
// 还原相机渲染目标
camera.targetTexture = cameraPrevT;
OnRender(_srcTexture, _destTexture);
// 设置当前活动的渲染纹理
RenderTexture.active = _destTexture;
//创建一个Texture2D
if (_texture2D == null)
_texture2D = new Texture2D(_destTexture.width, _destTexture.height, TextureFormat.RGB24, false);
var texRect = new Rect(0, 0, _destTexture.width, _destTexture.height);
_texture2D.ReadPixels(texRect, 0, 0);
_texture2D.Apply();
// 清空
RenderTexture.active = null;
// 赋值给RawImage
_blurImage.texture = _texture2D;
}
/// <summary>
/// 用Shader渲染高斯模糊
/// </summary>
private void OnRender(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++)
{
// 设置模糊尺寸(纹理坐标的偏移量)
_material.SetFloat("_BlurSize", 1.0f + i * BlurSpread);
RenderTexture buffer1 = RenderTexture.GetTemporary(rtW, rtH, 0);
// 渲染垂直的Pass
Graphics.Blit(buffer0, buffer1, _material, 0);
RenderTexture.ReleaseTemporary(buffer0);
buffer0 = buffer1;
buffer1 = RenderTexture.GetTemporary(rtW, rtH, 0);
// 渲染水平的Pass
Graphics.Blit(buffer0, buffer1, _material, 1);
RenderTexture.ReleaseTemporary(buffer0);
buffer0 = buffer1;
}
Graphics.Blit(buffer0, dest);
RenderTexture.ReleaseTemporary(buffer0);
}
else
{
Graphics.Blit(src, dest);
}
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Shader "MyShader/GaussianBlur" { // 高斯模糊
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {} // 主纹理
_BlurSize ("Blur Size", Float) = 1.0 // 模糊尺寸(纹理坐标的偏移量)
}
SubShader {
CGINCLUDE
#include "UnityCG.cginc"
sampler2D _MainTex; // 主纹理
half4 _MainTex_TexelSize; // _MainTex的像素尺寸大小, float4(1/width, 1/height, width, height)
float _BlurSize; // 模糊尺寸(纹理坐标的偏移量)
struct v2f {
float4 pos : SV_POSITION; // 模型空间顶点坐标
half2 uv[5]: TEXCOORD0; // 5个邻域的纹理坐标
};
v2f vertBlurVertical(appdata_img v) { // 垂直模糊顶点着色器
v2f o;
o.pos = UnityObjectToClipPos(v.vertex); // 模型空间顶点坐标变换到裁剪空间, 等价于: mul(UNITY_MATRIX_MVP, 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); // 模型空间顶点坐标变换到裁剪空间, 等价于: mul(UNITY_MATRIX_MVP, 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;
}
fixed4 fragBlur(v2f i) : SV_Target {
float weight[3] = {0.4026, 0.2442, 0.0545}; // 大小为5的一维高斯核,实际只需记录3个权值
fixed3 sum = tex2D(_MainTex, i.uv[0]).rgb * weight[0];
for (int j = 1; j < 3; j++) {
sum += tex2D(_MainTex, i.uv[j * 2 - 1]).rgb * weight[j]; // 中心右侧或下侧的纹理*权值
sum += tex2D(_MainTex, i.uv[j * 2]).rgb * weight[j]; // 中心左侧或上侧的纹理*权值
}
return fixed4(sum, 1.0);
}
ENDCG
ZTest Always Cull Off ZWrite Off
Pass {
NAME "GAUSSIAN_BLUR_VERTICAL"
CGPROGRAM
#pragma vertex vertBlurVertical
#pragma fragment fragBlur
ENDCG
}
Pass {
NAME "GAUSSIAN_BLUR_HORIZONTAL"
CGPROGRAM
#pragma vertex vertBlurHorizontal
#pragma fragment fragBlur
ENDCG
}
}
FallBack "Diffuse"
}