UnityShader之屏幕特效(一)

时间:2024-01-22 10:01:17

1、什么是屏幕特效

我们这里讲的屏幕特效技术,指的是在渲染完整个场景后得到的屏幕图象的基础上,再对这个屏幕图像做一系列处理,实现出屏幕特效,使用这种技术可以为屏幕画面增添各种风格的艺术效果,比如泛光、景深、模糊等等。
 

2、unity实现屏幕特效的原理

如上所述,要实现屏幕特效,首先要抓取渲染完整个场景得到的屏幕图像,在unity中,提供了OnRenderImage函数,方便我们进行这样的操作。它的声明如下:MonoBehaviour.OnRenderImage(RenderTexture src,RenderTexture dest);unity会把当前渲染得到的图像存储在第一个参数对应的渲染纹理中,通过自定义的一系列操作后,得到目标渲染纹理(第二个参数中的渲染纹理),目标渲染纹理会最终显示在屏幕上。在OnRenderImage函数中,使用Graphics.Blit函数来对渲染纹理进行处理。这里我们做一个可以改变画面亮度、饱和度、以及对比度的简单屏幕特效。
 

3、C#脚本实现

////////////////////////////////////////////////////////////////////
//                          _ooOoo_                               //
//                         o8888888o                              //
//                         88" . "88                              //
//                         (| ^_^ |)                              //
//                         O\  =  /O                              //
//                      ____/`---'\____                           //
//                    .'  \\|     |//  `.                         //
//                   /  \\|||  :  |||//  \                        //
//                  /  _||||| -:- |||||-  \                       //
//                  |   | \\\  -  /// |   |                       //
//                  | \_|  ''\---/''  |   |                       //
//                  \  .-\__  `-`  ___/-. /                       //
//                ___`. .'  /--.--\  `. . ___                     //
//              ."" '<  `.___\_<|>_/___.'  >'"".                  //
//            | | :  `- \`.;`\ _ /`;.`/ - ` : | |                 //
//            \  \ `-.   \_ __\ /__ _/   .-` /  /                 //
//      ========`-.____`-.___\_____/___.-`____.-'========         //
//                           `=---='                              //
//      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^        //
//                 佛祖保佑          永无BUG                      //
////////////////////////////////////////////////////////////////////


using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[ExecuteInEditMode]
[RequireComponent(typeof(Camera))]
public class ScreenEffect : MonoBehaviour {

    public Shader effectShader;  
    private Material effectMaterial;
    public Material EffectMaterial
    {
        get
        {
            effectMaterial = CreateMaterial(effectShader, effectMaterial);
            return effectMaterial;
        }
    }

    [Range(0.0f, 3.0f)]
    public float brightness = 1.0f;  //亮度

    [Range(0.0f, 3.0f)]
    public float saturation = 1.0f;  //饱和度

    [Range(0.0f, 3.0f)]
    public float contrast = 1.0f;    //对比度


    void OnRenderImage(RenderTexture src, RenderTexture dest)
    {
        if (EffectMaterial != null)
        {
            EffectMaterial.SetFloat("_Brightness", brightness);
            EffectMaterial.SetFloat("_Saturation", saturation);
            EffectMaterial.SetFloat("_Contrast", contrast);
            //对渲染纹理进行处理
            Graphics.Blit(src, dest, EffectMaterial);
        }
        else
        {
            Graphics.Blit(src, dest);
        }
    }

    private void Start()
    {
        bool isSupported = CheckSupport();

        if (isSupported == false)
        {
            this.enabled = false;
        }
    }

   
    // 检测当前平台是否支持屏幕特效
    private bool CheckSupport()
    {
        if (SystemInfo.supportsImageEffects == false || SystemInfo.supportsRenderTextures == false)
        {
            Debug.LogWarning("当前平台不支持!");
            return false;
        }
        return true;
    }

    
    // 创建材质
    private Material CreateMaterial(Shader shader, Material material)
    {
        if (shader == null)
        {
            return null;
        }

        if (shader.isSupported && material && material.shader == shader)
            return material;

        if (!shader.isSupported)
        {
            return null;
        }
        else
        {
            material = new Material(shader);
            material.hideFlags = HideFlags.DontSave;
            if (material)
                return material;
            else
                return null;
        }
    }
}

4、Shader实现

Shader "yzpShader/ScreenEffect" {
    Properties {
        _MainTex ("Base (RGB)", 2D) = "white" {}  //需要处理的渲染纹理
        _Brightness ("Brightness", Float) = 1     //亮度
        _Saturation("Saturation", Float) = 1      //饱和度
        _Contrast("Contrast", Float) = 1          //对比度
    }
    SubShader {
        Pass {  
            ZTest Always Cull Off ZWrite Off
            
            CGPROGRAM  
            #pragma vertex vert  
            #pragma fragment frag  
              
            #include "UnityCG.cginc"  
              
            sampler2D _MainTex;  
            half _Brightness;
            half _Saturation;
            half _Contrast;
              
            struct v2f {
                float4 pos : SV_POSITION;
                half2 uv: TEXCOORD0;
            };
              
            v2f vert(appdata_img v) {
                v2f o;
                
                o.pos = UnityObjectToClipPos(v.vertex);
                
                o.uv = v.texcoord;
                         
                return o;
            }
        
            fixed4 frag(v2f i) : SV_Target {
                fixed4 renderTex = tex2D(_MainTex, i.uv);  
                  
                // 计算亮度改变后的颜色
                fixed3 finalColor = renderTex.rgb * _Brightness;
                
                // 计算饱和度改变后的颜色
                fixed luminance = 0.2125 * renderTex.r + 0.7154 * renderTex.g + 0.0721 * renderTex.b;
                fixed3 luminanceColor = fixed3(luminance, luminance, luminance);
                finalColor = lerp(luminanceColor, finalColor, _Saturation);
                
                // 计算对比度改变后的颜色
                fixed3 avgColor = fixed3(0.5, 0.5, 0.5);
                finalColor = lerp(avgColor, finalColor, _Contrast);
                
                return fixed4(finalColor, renderTex.a);  
            }  
              
            ENDCG
        }  
    }
    
    Fallback Off
}

5、测试结果

首先将C#脚本挂在场景相机上,并将上面写好的shader拖拽到ScreenEffect脚本的effectShader上,修改该脚本的brightness、saturation、contrast值,可以得出下面这样的效果: