通过相机Render出一张RT,再对这张RT做高斯模糊,有2个好处:
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Serialization;
using UnityEngine.UI;
public class GaussianBlurUI : MonoBehaviour
{
public Camera[] blurTargetCameras;
private RenderTexture grabRenderTexture;
private Material material;
private Vector4[] offsetAndWeights = new Vector4[29];
private RenderTextureFormat rtFormat;
private static List<float> weights = new List<float>();
[Range(1,4)]
[Header("RT的缩放因子,值越大尺寸越小")]
public int scaler = 2;
[Range(1,4)]
[Header("模糊程度,值越高模糊越厉害")]
public int blurLevel = 3;
public RawImage outputRawImage;
//key:pass, value:samples
private Dictionary<int, int> blurLevelDict = new Dictionary<int, int>()
{
{0, 7},{1,11},{2,19},{3,29}
};
public void Awake()
{
rtFormat = SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.ARGBHalf)
? RenderTextureFormat.ARGBHalf
: RenderTextureFormat.Default;
Shader shader = Shader.Find("Custom/GaussianBlur");
material = new Material(shader);
material.renderQueue = 3000;
grabRenderTexture = new RenderTexture(Screen.width/scaler, Screen.height/scaler,0, rtFormat, RenderTextureReadWrite.Linear);
grabRenderTexture.autoGenerateMips = false;
}
private int GetPass()
{
Debug.Assert(blurLevel>=1&&blurLevel<=4, "Invalid blur level!");
return blurLevel - 1;
}
public void SetBlurTargetCamera(params Camera[] cams)
{
blurTargetCameras = cams;
}
public bool CaptureWithCamera()
{
if (outputRawImage == null)
{
Debug.LogError("must set outputRawImage!");
return false;
}
if (blurTargetCameras == null || blurTargetCameras.Length == 0)
{
Debug.LogError("blur error, need one camera at least!");
return false;
}
var pass = GetPass();
var rt = RenderTexture.GetTemporary(grabRenderTexture.width, grabRenderTexture.height, 24, rtFormat);
for (int i = 0; i < blurTargetCameras.Length; i++)
{
Camera cam = blurTargetCameras[i];
if (cam == null || cam.enabled == false || cam.gameObject.activeInHierarchy == false)
{
continue;
}
RenderTexture temp = cam.targetTexture;
cam.targetTexture = rt;
cam.Render();
cam.targetTexture = temp;
}
grabRenderTexture.DiscardContents();
Graphics.Blit(rt, grabRenderTexture);
RenderTexture.ReleaseTemporary(rt);
ProcessRenderTexture(pass);
outputRawImage.texture = grabRenderTexture;
return true;
}
private void ProcessRenderTexture(int pass, RenderTexture rt = null)
{
int nsamples2 = blurLevelDict[pass]; //samples in shader
int nsamples = nsamples2 * 2 - 1;
int width = (nsamples - 1) / 2;
if (rt == null)
{
rt = grabRenderTexture;
}
GenerateGaussianWeights(width, ref weights);
var temp = RenderTexture.GetTemporary(rt.width, rt.height, 0, rtFormat, RenderTextureReadWrite.Linear);
//v
GenerateConvolutionFilter(ref weights, width, true, rt.width, rt.height, ref offsetAndWeights);
material.SetVectorArray("_OffsetAndWeights", offsetAndWeights);
Graphics.Blit(rt, temp, material, pass);
//h
GenerateConvolutionFilter(ref weights, width, false, rt.width, rt.height, ref offsetAndWeights);
material.SetVectorArray("_OffsetAndWeights", offsetAndWeights);
temp.filterMode = FilterMode.Bilinear;
grabRenderTexture.DiscardContents();
Graphics.Blit(temp, rt, material, pass);
RenderTexture.ReleaseTemporary(temp);
}
static float Gaussian(float x, float s)
{
return Mathf.Exp(-(s * x) * (s * x));
}
static void GenerateGaussianWeights(int width, ref List<float> weights)
{
weights.Clear();
float s = 3.0F / width;
int size = width * 2 + 1;
float sum = 0f;
for (int x = 0; x < size; x++)
{
weights.Add(Gaussian(x - width, s));
sum += weights[x];
}
for (int x = 0; x < size; x++)
{
weights[x] /= sum;
}
}
void GenerateConvolutionFilter(ref List<float> weights, int width, bool vertical, int img_width, int img_height, ref Vector4[] output)
{
int nsamples = 2 * width + 1;
int nsamples2 = (int) Mathf.Ceil(nsamples / 2f);
for (int i = 0; i < nsamples2; i++)
{
float a = weights[i * 2];
float b;
if (i*2+1>nsamples - 1)
{
b = 0;
}
else
{
b = weights[i * 2 + 1];
}
float weight = a + b;
float offset = b*(a + b);
float x_offset = 0, y_offset = 0;
if (vertical)
{
y_offset = i * 2 - width + offset;
}
else
{
x_offset = i * 2 - width + offset;
}
x_offset = x_offset / img_width;
y_offset = y_offset / img_height;
output[i] = new Vector4(x_offset, y_offset, weight, 0);
}
}
}
half4 fragBlur7(v2f_img i): SV_Target{
half3 color = 0.0;
for(int s = 0; s < 7; s++){
half3 offsetAndWeight = _OffsetAndWeights[s].xyz;
half3 sample = tex2D(_MainTex, i.uv + offsetAndWeight.xy).xyz;
color += sample * offsetAndWeight.z;
}
return half4(color, 1.0);
}
half4 fragBlur11(v2f_img i): SV_Target{
half3 color = 0.0;
for(int s = 0; s < 11; s++){
half3 offsetAndWeight = _OffsetAndWeights[s].xyz;
half3 sample = tex2D(_MainTex, i.uv + offsetAndWeight.xy).xyz;
color += sample * offsetAndWeight.z;
}
return half4(color, 1.0);
}
half4 fragBlur19(v2f_img i): SV_Target{
half3 color = 0.0;
for(int s = 0; s < 19; s++){
half3 offsetAndWeight = _OffsetAndWeights[s].xyz;
half3 sample = tex2D(_MainTex, i.uv + offsetAndWeight.xy).xyz;
color += sample * offsetAndWeight.z;
}
return half4(color, 1.0);
}
half4 fragBlur29(v2f_img i): SV_Target{
half3 color = 0.0;
for(int s = 0; s < 29; s++){
half3 offsetAndWeight = _OffsetAndWeights[s].xyz;
half3 sample = tex2D(_MainTex, i.uv + offsetAndWeight.xy).xyz;
color += sample * offsetAndWeight.z;
}
return half4(color, 1.0);
}
Shader "Custom/GaussianImageEffect"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
}
SubShader
{
Cull Off ZWrite Off ZTest Always
pass{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
v2f vert(appdata v){
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
sampler2D _MainTex;
fixed4 frag(v2f i) : SV_TARGET{
fixed4 col = tex2D(_MainTex, i.uv);
col.rgb = pow(col.rgb, 1.25);
return col;
}
ENDCG
}
}
FallBack "Diffuse"
}