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

效果

通過相機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"
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章