1. 原理和過程
屏幕后處理是綁定攝像機的,通過抓取當前攝像機渲染的圖像作為 SrcTextrue,然后按需依次調用處理接口,對 SrcTexture 進行處理,最后將處理完成的 DstTexture 顯示到屏幕上,整個過程的調度通過 C# 腳本完成。
抓取攝像機當前渲染圖像使用的接口如下:
OnRenderImage(RenderTexture _src, RenderTexture _dst)
其中:
- _src為抓取到的當前綁定攝像機的渲染圖像
- _dst為處理結束時的目標紋理
調用的處理接口如下:
Graphics.Blit(Texture _src, RenderTexture _dst)
Graphics.Blit(Texture _src, RenderTexture _dst, Material _mat)
Graphics.Blit(Texture _src, Material _mat, int _passIndex = -1)
其中:
- _src 為要處理的原始圖像
- _dst 為處理結束后存儲到的目標紋理
- _mat 為本次處理指定的材質,其主要作用是為本次處理提供使用的Shader,需要注意的是,_src會被賦值給該_mat所攜帶Shader的_MainTex變量,因此在實現Shader時,也需要聲明對應名字的變量用于接收原始圖像紋理
- _passIndex 為本次處理指定使用的Pass索引,默認為-1,表示按照順序依次執行Pass,否則為使用指定索引的Pass
2. 后處理腳本父類
我們可以建立一個后處理的父類,提供所有后處理腳本的基礎功能,如檢查效果可用性、初始化后處理所用材質等等。
腳本如下:
using UnityEngine;[ExecuteInEditMode]
[RequireComponent(typeof(Camera))]
public class PostEffectBase : MonoBehaviour
{void Start(){CheckResource();}void CheckResource(){bool _supported = CheckDefaultSupported() && CheckSpecificSupported();if (!_supported) OnNotSurported();}/// <summary>/// 用于后處理模塊通用設置/// 可被子類重寫,因為可能存在某個子類不需要指定條件的情況/// </summary>/// <returns></returns>protected virtual bool CheckDefaultSupported(){return true;}/// <summary>/// 不同后處理子類可以分別實現各自特殊的檢查/// </summary>/// <returns></returns>protected virtual bool CheckSpecificSupported(){return true;}/// <summary>/// 不滿足后處理啟用條件時禁用腳本/// </summary>void OnNotSurported(){enabled = false;}/// <summary>/// 初始化后處理使用的材質和Shader/// </summary>/// <param name="_shader"></param>/// <param name="_material"></param>/// <returns></returns>protected Material CheckShaderAndMaterial(Shader _shader, Material _material){if (null == _shader || !_shader.isSupported) return null;if (null == _material || _material.shader != _shader){_material = new Material(_shader);_material.hideFlags = HideFlags.DontSave;}return _material;}
}
3. 調整亮度、飽和度、對比度
下面的例子中,我們實現一個用于調整屏幕亮度、飽和度以及對比度的后處理效果。
首先,從Shader出發,考慮如何實現對于上述三項的調整:
- 對紋理(抓取的屏幕圖像)進行采樣,得到原始顏色
- 調整亮度:用原始顏色乘以亮度系數 _Brightness,即可得到調整后的處理顏色①
- 調整飽和度:通過 0.2125 * R + 0.7154 * G + 0.0721 * B 公式對原始顏色進行處理,得到紋理灰度值,然后按照飽和度系數 _Saturation 從灰度值到處理顏色①進行插值,即可得到增加了飽和度變化的處理顏色②
- 調整對比度:構建一個 (0.5, 0.5, 0.5) 的對比度為 0 的顏色,按照對比度系數 _Contrast 向處理顏色②進行插值,得到最終的顏色
然后,創建調用后處理的腳本,繼承自上文后處理父類:
- 為腳本指定所需Shader
- 實現OnRenderImage方法,為Shader所需的 _Brightness、 _Saturation、_Contrast 賦值,并調用 Blit 方法執行后處理
測試腳本:
using UnityEngine;public class PostEffect_BrightnessSaturationContrast : PostEffectBase
{public Shader BscShader;public Material BscMat;[Range(0.0f, 3.0f)]public float Brightness = 1;[Range(0.0f, 3.0f)]public float Saturation = 0.5f;[Range(0.0f, 3.0f)]public float Contrast = 0.5f;private void OnRenderImage(RenderTexture src, RenderTexture dest){Material _mat = CheckShaderAndMaterial(BscShader, BscMat);if (null == _mat) Graphics.Blit(src, dest);else{_mat.SetFloat("_Brightness", Brightness);_mat.SetFloat("_Saturation", Saturation);_mat.SetFloat("_Contrast", Contrast);Graphics.Blit(src, dest, _mat);}}
}
測試Shader:
Shader "MyShader/Chapter_12/Chapter_12_BSC_Shader"
{Properties{_MainTex("MainTex", 2D) = "white"{}}SubShader{Pass{Tags{"LightMode" = "ForwardBase"}ZTest AlwaysZWrite OffCull OffCGPROGRAM#pragma vertex vert#pragma fragment frag#pragma multi_compile_fwdbase#include "UnityCG.cginc"struct v2f{float4 pos : SV_POSITION;float2 uv : TEXCOORD0;};sampler2D _MainTex;float4 _MainTex_ST;fixed _Brightness;fixed _Saturation;fixed _Contrast;v2f vert(appdata_img v){v2f o;o.pos = UnityObjectToClipPos(v.vertex);o.uv = v.texcoord;return o;}fixed4 frag(v2f i) : SV_Target{fixed3 _samplerColor = tex2D(_MainTex, i.uv).rgb;fixed3 _finalColor = _samplerColor * _Brightness;_samplerColor *= _Brightness;//0.2125 * R + 0.7154 * G + 0.0721 * Bfixed _luminance = 0.2125 * _samplerColor.r + 0.7154 * _samplerColor.g + 0.0721 * _samplerColor.b;_finalColor = lerp(float3(_luminance, _luminance, _luminance), _finalColor, _Saturation);_finalColor = lerp(float3(0.5, 0.5, 0.5), _finalColor, _Contrast);return fixed4(_finalColor, 1);}ENDCG}}
}
測試效果:
亮度:
飽和度:
對比度: