Unity屏幕效果-高斯模糊.md

mac2024-01-30  34

效果

通过相机Render出一张RT,再对这张RT做高斯模糊,有2个好处:

避免事实运算,优化性能(实时高斯模糊只有15-20帧左右,静态几乎没性能影响)不适用Screen.Capture截屏的原因是,截屏有可能会截取到未渲染完成的画面,或者画面里有不希望出现的元素等。

C#层

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); } } }

shader部分

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); }

RT上材质

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" }
最新回复(0)