是什么?
顧名思義就是在得到屏幕渲染圖后我們?cè)趯?duì)這個(gè)圖做一定的處理呀舔,這個(gè)處理就差不多我們平時(shí)p圖的哪些操作弥虐,改亮度,對(duì)比度媚赖,模糊啥的霜瘪。
怎么做?
這里unity給我們一個(gè)接口-OnRenderImage
//src為我們得到屏幕渲染圖 dest為我們處理后的一定結(jié)果
MonoBehaviour.OnRenderImage (RenderTexture src,RenderTexture dest)
在這個(gè)方法中我們一般使用Graphics.Blit來(lái)做融合處理惧磺。
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);
簡(jiǎn)單的原理就是建一個(gè)材質(zhì)把這個(gè)材質(zhì)的效果疊加到source圖上去颖对,所以我們的所寫(xiě)的Shader中必須有一個(gè)叫_MainTex的屬性。
都說(shuō)是后處理了豺妓,最后渲染得到的圖是通過(guò)攝像機(jī)得到的惜互,所以我們需要把這些處理的腳本放在跟攝像機(jī)一個(gè)GameObject上。首先我們需要判斷當(dāng)前平臺(tái)是否支持琳拭,然后需要?jiǎng)?chuàng)建一個(gè)材質(zhì)训堆。這里我們有一個(gè)基類(lèi)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();
}
//在最開(kāi)始調(diào)用
protected void CheckResoures()
{
var isSupported = CheckSupport();
if (!isSupported)
{
NoSupport();
}
}
//檢查是否支持
protected bool CheckSupport()
{
if (SystemInfo.supportsImageEffects == false || SystemInfo.supportsRenderTextures == false)
{
Debug.LogWarning("此平臺(tái)不支持圖片效果后處理");
return false;
}
return true;
}
//取消該腳本效果
protected void NoSupport()
{
enabled = false;
}
//檢查shader是否存在并創(chuàng)建該shader的材質(zhì)
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;
}
}
}
}
調(diào)整屏幕亮度白嘁、飽和度坑鱼、對(duì)比度
要做到這個(gè)三個(gè)效果我們只需要在片元方法中,調(diào)節(jié)其中顏色值即可絮缅。首先獲得屏幕:
fixed4 renderTex = tex2D(_MainTex, i.uv);
首先我們來(lái)看如何調(diào)整亮度:
fixed3 finalCol = renderTex.rgb * _Brightness;
在調(diào)整飽和度:
//這個(gè)應(yīng)該是一個(gè)經(jīng)驗(yàn)公式
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);
最后設(shè)置對(duì)比度即可:
fixed3 avgColor = fixed3(0.5, 0.5, 0.5);
finalCol = lerp(avgColor,finalCol,_Contrast);
return fixed4(finalCol,renderTex.a);
我們?cè)贠nRenderImage中設(shè)置這三個(gè)參數(shù)即可:
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);
}
}
亮度的效果還是可以用來(lái)做開(kāi)場(chǎng)動(dòng)畫(huà)和結(jié)束動(dòng)畫(huà)鲁沥,挺好看的。
邊緣檢測(cè)
卷積: 是指使用卷積核對(duì)一張圖像中的每個(gè)像素進(jìn)行一系列操作耕魄。卷積核一般是一個(gè)正方形網(wǎng)格結(jié)構(gòu)(3x3或者是2x2)画恰,每正方形網(wǎng)格中的格子都會(huì)有一個(gè)數(shù)值,稱(chēng)為權(quán)重值吸奴。既然是積就需要相乘允扇,比如我們需要計(jì)算某一個(gè)像素的卷積缠局,就把一個(gè)卷核的中心格子放在哪個(gè)像素上,然后依次計(jì)算核中每個(gè)元素和其覆蓋的圖像像素值得乘積并求和考润,得到該像素的卷積狭园。
這個(gè)我看了幾次,才看明白哦糊治。這個(gè)就很像乘以矩陣啥的唱矛,我們可以把卷積核理解成一個(gè)常量。對(duì)井辜,就是相當(dāng)于掃雷那種感覺(jué)我們?cè)谂乓粋€(gè)點(diǎn)是否是雷就看周?chē)臄?shù)字嘛绎谦,這個(gè)周?chē)木涂梢岳斫鉃榫矸e核。我們這個(gè)時(shí)候已知周?chē)臄?shù)字抑胎,然后把周?chē)淖峙c那個(gè)格子的顏色相乘然后燥滑,最后把所有相乘的值相加就得到了卷積渐北。
那我們現(xiàn)在獲得了卷積值阿逃,卷積值越大就越可能是邊緣點(diǎn)。我們知道邊緣為什么是邊緣赃蛛?就是因?yàn)樗c其他地方的顏色差別很恃锉。當(dāng)然這個(gè)卷積核肯定是特定的,這樣就可以檢測(cè)出來(lái)顏色大小呕臂。
我這里沒(méi)有實(shí)現(xiàn)這個(gè)功能破托,說(shuō)的是我的shader不支持平臺(tái)。歧蒋。土砂。。
高斯模糊
使用的原理與求邊緣的原理差不多谜洽,也是使用一個(gè)卷積核萝映,但是這里叫做高斯核,這里有一個(gè)求高斯核的權(quán)重的公式阐虚,
然后我們可以跟求邊緣一樣求得卷積序臂。這里我們做了一個(gè)優(yōu)化,把二維的正方形高斯核轉(zhuǎn)化成兩個(gè)一維的高斯核实束,就是轉(zhuǎn)化中間的橫豎那一列和行奥秆。
Bloom效果
這個(gè)效果是做到一種高光部分過(guò)爆的效果,帶有朦朧效果咸灿。
原理就是先生成高亮部分的圖构订,然后對(duì)這個(gè)圖做高斯模糊最終獲得一張圖(模擬光線圖),然后把模糊圖與原圖做一個(gè)混合即可避矢。
所以這用了4個(gè)pass塊悼瘾,幾次來(lái)處理签赃。
運(yùn)動(dòng)模糊
做運(yùn)動(dòng)模糊一般有很多種方式:
1.累計(jì)緩存:把每一幀的圖像記錄下來(lái),然后把記錄下來(lái)的圖片連續(xù)混合起來(lái)顯示分尸,這樣就可以做出動(dòng)態(tài)模糊同時(shí)也可以做出虛影的效果锦聊。這個(gè)消耗很大。
2.速度緩存:緩存中存儲(chǔ)了各個(gè)像素當(dāng)前的運(yùn)動(dòng)速度箩绍,然后利用該值來(lái)決定模糊的方向大小孔庭。
這里作者使用得的第一個(gè)方式的優(yōu)化來(lái)實(shí)現(xiàn),在得到之前渲染結(jié)果材蛛,不斷把當(dāng)前的渲染圖像疊加到之前的渲染圖像中圆到,從而產(chǎn)生一種運(yùn)動(dòng)軌跡的視覺(jué)效果。
在OnRenderImage中卑吭,我們就把當(dāng)前渲染得到得的圖不斷存入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);
}
//恢復(fù)操作 發(fā)生在渲染到紋理而該紋理又沒(méi)有被提前清空或銷(xiāo)毀的情況
accumulationTexture.MarkRestoreExpected();
material.SetFloat("_BlurAmount",1.0f-blurAmount);
Graphics.Blit(src,accumulationTexture,material);
Graphics.Blit(accumulationTexture,dest);
}
else
{
Graphics.Blit(src, dest);
}
}
我們?cè)趕hader中需要分別處理透明通道和顏色通道在兩個(gè)pass塊里,因?yàn)槲覀冊(cè)讷@得模糊虛影時(shí)豆赏,需要設(shè)置虛影的長(zhǎng)度挣菲,這個(gè)長(zhǎng)度就是我們的設(shè)置的模糊參數(shù)嘉蕾。
看起來(lái)還是很酷炫得的彻况。