調整螢幕的亮度,飽和度和對比度 【Unity Shader入門精要12.2】
阿新 • • 發佈:2018-12-15
//12.1 //一個用於檢查的基類 //檢查當前平臺是否支援渲染紋理和螢幕特效,是否支援Unity Shader using System.Collections.Generic; using UnityEngine; //在編輯模式執行 [ExecuteInEditMode] //需要繫結元件(型別(相機)) [RequireComponent(typeof(Camera))] public class PostEffectsBase : MonoBehaviour { //新建檢查資源函式 開始時呼叫 protected void CheckResources() { //新建布林型別 呼叫檢查支援函式 bool isSupported = CheckSupport(); //如果 檢查支援函式結果是假 if (isSupported == false) { //呼叫沒有支援函式 NotSupported(); } } //新建檢查支援函式 呼叫CheckResources檢查此平臺上的支援 protected bool CheckSupport() { //if (訪問系統和硬體資訊.支援影象效果 為假 或 訪問系統和硬體資訊.支援渲染紋理 為假) if (SystemInfo.supportsImageEffects == false || SystemInfo.supportsRenderTextures == false) { //輸出 balabala Debug.LogWarning("This Platform does not support image effects or render textures."); //返回 假 return false; } //返回真 return true; } //新建 沒有支援 函式,當平臺不支援此效果時呼叫 protected void NotSupported() { //enabled 啟用的行為被更新,禁用的行為沒有被更新 enabled = false; } //開始函式 protected void Start() { //呼叫 檢查資源函式 CheckResources(); } //當需要建立此效果所使用的材質時呼叫 // 材質 檢查著色器和建立材質 ,接受兩個引數(渲染器,材質) protected Material CheckShaderAndCreateMaterial(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; } } }
//12.2 //放在攝像機上,呼叫基類檢查是否可用, //有shader的介面,並對應調整亮度,飽和度,對比度的值,建立新的材質,顯示到螢幕上 using System.Collections; using System.Collections.Generic; using UnityEngine; //繼承12.1節中建立的基類PostEffectsBase public class BrightnessSaturationAndContrast : PostEffectsBase { //宣告該效果需要的shader,並據此建立相應的材質 public Shader briSatConShader; private Material briSatConMaterial; public Material material { get { //briSatConMaterial是指定的shader , CheckShaderAndCreateMaterial得到對應的材質 briSatConMaterial = CheckShaderAndCreateMaterial(briSatConShader, briSatConMaterial); return briSatConMaterial; } } //在指令碼中提供調節亮度,飽和度,對比度的引數 [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; //定義OnRenderImage函式來進行真正的特效處理 void OnRenderImage(RenderTexture src, RenderTexture dest) { //每當OnRenderImage函式被呼叫的時候都檢查材質是否可用 if (material !=null) { //如果可用就把引數傳遞 material.SetFloat("_Brightness", brightness); material.SetFloat("_Saturation", saturation ); material.SetFloat("_Contrast", contrast ); //再呼叫Graphics.Blit進行處理 Graphics.Blit(src, dest, material); } else { //否則直接把影象顯示到螢幕上,不做任何處理 Graphics.Blit(src, dest); } } }
//12.2 調整螢幕的亮度,飽和度和對比度 Shader "Unlit/Chapter12-BrightnessSaturationAndContrast" { Properties { //主紋理 _MainTex ("Texture", 2D) = "white" {} //亮度 _Brightness("Brightness",Float)=1 //飽和度 _Saturation("Saturation",Float)=1 //對比度 _Contrast("Contrat",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; float2 uv : TEXCOORD0; }; //頂點著色器 /*使用了Unity內建的appdata_img結構體作為頂點著色器的輸入 它只包含了影象處理時必須的頂點座標和紋理座標等變數*/ 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); //Apply brightness 調整亮度 //顏色值=源顏色*亮度係數 fixed3 finalColor = renderTex.rgb*_Brightness; //飽和度的計算 //計算對應的luminance(亮度值),通過對每一個顏色分量乘以一個特定的係數再相加得到的 fixed luminance = 0.2125*renderTex.r + 0.7154*renderTex.g + 0.0721*renderTex.b; //使用該亮度值建立了一個飽和度為0的顏色值。 fixed luminanceColor = fixed3(luminance, luminance, luminance); //使用_Saturation屬性在 飽和度為0的顏色 和上一步得到的顏色 之間進行插值 finalColor = lerp(luminanceColor, finalColor, _Saturation); //Apply contrast 對比度計算 //建立一個對比度為零的顏色值(各分量均為0.5) fixed3 avgColor = fixed3(0.5, 0.5, 0.5); //使用_Contrast與 對比度為0的顏色值, 和 上一步得到的顏色之間進行插值 finalColor = lerp(avgColor, finalColor, _Contrast); //返回最後的顏色值和a通道 return fixed4(finalColor, renderTex.a); } ENDCG } } }
一個基本的屏幕後處理指令碼的系統,調整螢幕亮度,對比,飽和度的
三部分,一個C#用於檢查,一個C#用於連線攝像頭和shader並傳遞引數和呼叫檢查的基類,一個Shader用於實現渲染
p245有關於OnRenderImage函式的相關資訊,還有 Graphics.Blit函式的使用
OnRenderImage:
- MonoBehavior.OnRenderImage(RenderTexture src ,RenderTexture dest);
- 當指令碼宣告此函式後,Unity會把當前的影象儲存在第一個引數對應的源渲染紋理中,通過函式一系列操作後再把目標渲染紋理,即第二個引數對應的渲染紋理顯示到螢幕上。
Graphics.Blit:
- Public Static void Blit(Texture scr,RenderTexture dest);
- Public Static void Blit(Texture scr,RenderTexture dest,Material mat,int pass=-1);
- Public Static void Blit(Texture scr,RenderTexture dest,int pass=-1);
- scr對應源紋理,scr紋理將會被傳遞給shader中命名為_MainTex的紋理屬性。dest 目標渲染紋理。mat 我們使用的材質。pass 預設為-1,表示將會依次呼叫Shader內的所有Pass
通常過程:首先在攝像機中新增一個用於屏幕後處理的指令碼,在這個指令碼中實現OnRenderImage函式來獲取當前螢幕的渲染紋理。然後呼叫Graphics.Blit函式使用特定的UnityShader來對當前的影象進行處理,並把返回的渲染紋理顯示到螢幕上。對於一些複雜的螢幕特效,可能需要多次呼叫Graphics.Blit函式來對上一步的輸出結果進行下一步處理