是什么?
顧名思義就是在得到屏幕渲染圖后我們在對這個圖做一定的處理,這個處理就差不多我們平時p圖的哪些操作,改亮度,對比度,模糊啥的。
怎么做?
這里unity給我們一個接口-OnRenderImage
//src為我們得到屏幕渲染圖 dest為我們處理后的一定結果
MonoBehaviour.OnRenderImage (RenderTexture src,RenderTexture dest)
在這個方法中我們一般使用Graphics.Blit來做融合處理。
public static void Blit(Texture source, RenderTexture dest, Material mat, [Internal.DefaultValue("-1")] int pass);
public static void Blit(Texture source, RenderTexture dest, Material mat);
public static void Blit(Texture source, Material mat);
簡單的原理就是建一個材質把這個材質的效果疊加到source圖上去,所以我們的所寫的Shader中必須有一個叫_MainTex的屬性。
都說是后處理了,最后渲染得到的圖是通過攝像機得到的,所以我們需要把這些處理的腳本放在跟攝像機一個GameObject上。首先我們需要判斷當前平臺是否支持,然后需要創建一個材質。這里我們有一個基類PostEffectBase。如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[ExecuteInEditMode]
[RequireComponent(typeof(Camera))]
public class PostEffectBase : MonoBehaviour
{
// Use this for initialization
void Start()
{
CheckResoures();
}
//在最開始調用
protected void CheckResoures()
{
var isSupported = CheckSupport();
if (!isSupported)
{
NoSupport();
}
}
//檢查是否支持
protected bool CheckSupport()
{
if (SystemInfo.supportsImageEffects == false || SystemInfo.supportsRenderTextures == false)
{
Debug.LogWarning("此平臺不支持圖片效果后處理");
return false;
}
return true;
}
//取消該腳本效果
protected void NoSupport()
{
enabled = false;
}
//檢查shader是否存在并創建該shader的材質
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;
}
}
}
}
調整屏幕亮度、飽和度、對比度
要做到這個三個效果我們只需要在片元方法中,調節其中顏色值即可。首先獲得屏幕:
fixed4 renderTex = tex2D(_MainTex, i.uv);
首先我們來看如何調整亮度:
fixed3 finalCol = renderTex.rgb * _Brightness;
在調整飽和度:
//這個應該是一個經驗公式
fixed luminance = 0.2125 * renderTex.r + 0.7254 * renderTex.g + 0.0721 * renderTex.b;
fixed3 luminaceColor = fixed3(luminance, luminance, luminance);
finalCol == lerp(luminaceColor, finalCol, _Saturation);
最后設置對比度即可:
fixed3 avgColor = fixed3(0.5, 0.5, 0.5);
finalCol = lerp(avgColor,finalCol,_Contrast);
return fixed4(finalCol,renderTex.a);
我們在OnRenderImage中設置這三個參數即可:
void OnRenderImage(RenderTexture src, RenderTexture dest)
{
if (material!=null)
{
material.SetFloat("_Brightness",brightness);
material.SetFloat("_Saturation",saturation);
material.SetFloat("_Contrast",contrast);
Graphics.Blit(src,dest,material);
}else
{
Graphics.Blit(src,dest);
}
}
亮度的效果還是可以用來做開場動畫和結束動畫,挺好看的。
邊緣檢測
卷積: 是指使用卷積核對一張圖像中的每個像素進行一系列操作。卷積核一般是一個正方形網格結構(3x3或者是2x2),每正方形網格中的格子都會有一個數值,稱為權重值。既然是積就需要相乘,比如我們需要計算某一個像素的卷積,就把一個卷核的中心格子放在哪個像素上,然后依次計算核中每個元素和其覆蓋的圖像像素值得乘積并求和,得到該像素的卷積。
這個我看了幾次,才看明白哦。這個就很像乘以矩陣啥的,我們可以把卷積核理解成一個常量。對,就是相當于掃雷那種感覺我們在排一個點是否是雷就看周圍的數字嘛,這個周圍的就可以理解為卷積核。我們這個時候已知周圍的數字,然后把周圍的字與那個格子的顏色相乘然后,最后把所有相乘的值相加就得到了卷積。
那我們現在獲得了卷積值,卷積值越大就越可能是邊緣點。我們知道邊緣為什么是邊緣?就是因為他與其他地方的顏色差別很。當然這個卷積核肯定是特定的,這樣就可以檢測出來顏色大小。
我這里沒有實現這個功能,說的是我的shader不支持平臺。。。。
高斯模糊
使用的原理與求邊緣的原理差不多,也是使用一個卷積核,但是這里叫做高斯核,這里有一個求高斯核的權重的公式,
然后我們可以跟求邊緣一樣求得卷積。這里我們做了一個優化,把二維的正方形高斯核轉化成兩個一維的高斯核,就是轉化中間的橫豎那一列和行。
Bloom效果
這個效果是做到一種高光部分過爆的效果,帶有朦朧效果。
原理就是先生成高亮部分的圖,然后對這個圖做高斯模糊最終獲得一張圖(模擬光線圖),然后把模糊圖與原圖做一個混合即可。
所以這用了4個pass塊,幾次來處理。
運動模糊
做運動模糊一般有很多種方式:
1.累計緩存:把每一幀的圖像記錄下來,然后把記錄下來的圖片連續混合起來顯示,這樣就可以做出動態模糊同時也可以做出虛影的效果。這個消耗很大。
2.速度緩存:緩存中存儲了各個像素當前的運動速度,然后利用該值來決定模糊的方向大小。
這里作者使用得的第一個方式的優化來實現,在得到之前渲染結果,不斷把當前的渲染圖像疊加到之前的渲染圖像中,從而產生一種運動軌跡的視覺效果。
在OnRenderImage中,我們就把當前渲染得到得的圖不斷存入acumulationTexture中。
void OnRenderImage(RenderTexture src, RenderTexture dest)
{
if (material != null)
{
if (accumulationTexture==null||accumulationTexture.width!=src.width||
accumulationTexture.height!=src.height)
{
DestroyImmediate(accumulationTexture);
accumulationTexture=new RenderTexture(src.width,src.height,0);
accumulationTexture.hideFlags=HideFlags.HideAndDontSave;
Graphics.Blit(src,accumulationTexture);
}
//恢復操作 發生在渲染到紋理而該紋理又沒有被提前清空或銷毀的情況
accumulationTexture.MarkRestoreExpected();
material.SetFloat("_BlurAmount",1.0f-blurAmount);
Graphics.Blit(src,accumulationTexture,material);
Graphics.Blit(accumulationTexture,dest);
}
else
{
Graphics.Blit(src, dest);
}
}
我們在shader中需要分別處理透明通道和顏色通道在兩個pass塊里,因為我們在獲得模糊虛影時,需要設置虛影的長度,這個長度就是我們的設置的模糊參數。
看起來還是很酷炫得的。