Unity Shader-后处理:Bloom全屏泛光
一.簡介
今天來學習一下全屏Bloom效果,有時候也叫Glow效果,中文一般叫做“全屏泛光”,這是一種可以模擬出HDR的全屏后處理效果,但是實現原理與HDR相差很遠,效果比HDR差一些,但是比HDR的性能要節省很多。這篇文章里我們只是實現了一版基于全屏顏色遮罩的Bloom處理,具體針對某個對象進行Bloom的效果在以后的文章中會進行講解。
二.原理介紹
這里不得不提一下傳說中的HDR,從接觸引擎開始,就一群大牛們經常討論到這個詞,然而作為一個新手,一直對這個傳說中的技術抱著“敬畏”的態度,不過如今逐漸熟悉了一些渲染相關的東西,賤賤地,也沒有那么害怕這個技術了。今天就來學習一下HDR,不過HDR不是這篇文章的主角,所以只是簡要介紹。
1.HDR
HDR(High Dynamic Range),翻譯過來就是高動態范圍。所謂High,指的是亮度的范圍更高。我們知道,正常屏幕上一個像素是由RGB三原色組成的,每個通道用八位二進制表示,也就是0-255,轉化為16進制,就是白色為0xFFFFFF,黑色為0x000000。而真實世界的亮度的最大值遠遠比屏幕能夠顯示的亮度大,比如太陽的亮度會是我們屏幕亮度的幾萬倍,而我們的雖然不能識別這樣廣范圍的亮度,但是屏幕上的亮度范圍是遠遠不能表達真實世界的亮度分布的。假設我們真實世界的亮度是0-10000,那么這個就是我們所謂的High,比我們屏幕上的亮度范圍要高。如果不使用HDR,就會出現場景中大塊的亮或者暗,造成場景對比度不明顯,影響畫面效果。
要怎樣用有限的亮度分布模擬更高范圍的亮度分布,就是HDR在做的事情。實現這樣的功能的技術叫做ToneMapping,據不官方翻譯,叫做色調映射技術。這種技術會讓畫面對比度更加柔和,將高的亮度范圍更加平滑地縮放到0-255這一低光照范圍,主要運用的原理是局部適應性。我們人眼,在比較暗的地方,原來也能看清楚東西,但是如果突然到了一個比較亮的地方,我們會什么都看不清,需要矯正一會兒才能適應當前的亮度水平,之前玩過一小陣子CryEngine3,默認是開啟這種效果的,感覺還是屌屌的。關于ToneMapping技術的原理,可以參考這篇文章和這篇文章,還有這篇屌炸天的論文,這里就不過多介紹了。
2.Bloom
這篇文章的主題并不是HDR,而是Bloom。Bloom可以模擬出HDR的效果,但是原理上和HDR相差甚遠。HDR實際上是通過映射技術,來達到整體調整全局亮度屬性的,這種調整是顏色,強度等都可以進行調整,而Bloom僅僅是能夠將光照范圍調高達到過飽和,也就是讓亮的地方更亮。不過Bloom效果實現起來簡單,性能消耗也小,卻可以達到不錯的效果。關于HDR和Bloom之間的差別,可以參考這篇文章。
這里介紹一下Bloom的實現原理,其實比較簡單,首先我們需要設置一個我們要泛光的亮度閾值,第一遍處理時,我們需要對原場景圖進行篩選,所有小于這個閾值的像素都被篩掉,所有大于該值的像素留下來,這樣,我們就得到了一張只包含需要泛光部分的貼圖,其余部分是黑色的;泛光效果是由衍射效果產生的,我們現實世界中看到的泛光效果,最亮的地方實際上是會向暗的地方擴散的,也就是說在亮的地方,邊界是不明顯的,所以我們就需要對泛光是部分,也就是我們上一步操作的結果圖片進行模糊操作,達到光溢出的效果,最后,我們將處理過的圖像和原圖像進行疊加,就得到了最終的效果。老外有篇文章寫得比較好,這里我把這張圖借來用用:
在DX上直接實現Bloom的可以參照這篇文章
三.代碼實現
c#部分:
[csharp]view plaincopy
usingUnityEngine;
usingSystem.Collections;
[ExecuteInEditMode]
publicclassBloomEffect:PostEffectBase
{
//分辨率
publicintdownSample=1;
//采樣率
publicintsamplerScale=1;
//高亮部分提取閾值
publicColorcolorThreshold=Color.gray;
//Bloom泛光顏色
publicColorbloomColor=Color.white;
//Bloom權值
[Range(0.0f,1.0f)]
publicfloatbloomFactor=0.5f;
voidOnRenderImage(RenderTexturesource,RenderTexturedestination)
{
if(_Material)
{
//申請兩塊RT,并且分辨率按照downSameple降低
RenderTexturetemp1=RenderTexture.GetTemporary(source.width>>downSample,source.height>>downSample,0,source.format);
RenderTexturetemp2=RenderTexture.GetTemporary(source.width>>downSample,source.height>>downSample,0,source.format);
//直接將場景圖拷貝到低分辨率的RT上達到降分辨率的效果
Graphics.Blit(source,temp1);
//根據閾值提取高亮部分,使用pass0進行高亮提取
_Material.SetVector("_colorThreshold",colorThreshold);
Graphics.Blit(temp1,temp2,_Material,0);
//高斯模糊,兩次模糊,橫向縱向,使用pass1進行高斯模糊
_Material.SetVector("_offsets",newVector4(0,samplerScale,0,0));
Graphics.Blit(temp2,temp1,_Material,1);
_Material.SetVector("_offsets",newVector4(samplerScale,0,0,0));
Graphics.Blit(temp1,temp2,_Material,1);
//Bloom,將模糊后的圖作為Material的Blur圖參數
_Material.SetTexture("_BlurTex",temp2);
_Material.SetVector("_bloomColor",bloomColor);
_Material.SetFloat("_bloomFactor",bloomFactor);
//使用pass2進行景深效果計算,清晰場景圖直接從source輸入到shader的_MainTex中
Graphics.Blit(source,destination,_Material,2);
//釋放申請的RT
RenderTexture.ReleaseTemporary(temp1);
RenderTexture.ReleaseTemporary(temp2);
}
}
}
shader部分:
[csharp]view plaincopy
Shader"Custom/BloomEffect"{
Properties{
_MainTex("Base(RGB)",2D)="white"{}
_BlurTex("Blur",2D)="white"{}
}
CGINCLUDE
#include"UnityCG.cginc"
//用于閾值提取高亮部分
structv2f_threshold
{
float4pos:SV_POSITION;
float2uv:TEXCOORD0;
};
//用于blur
structv2f_blur
{
float4pos:SV_POSITION;
float2uv:TEXCOORD0;
float4uv01:TEXCOORD1;
float4uv23:TEXCOORD2;
float4uv45:TEXCOORD3;
};
//用于bloom
structv2f_bloom
{
float4pos:SV_POSITION;
float2uv:TEXCOORD0;
float2uv1:TEXCOORD1;
};
sampler2D_MainTex;
float4_MainTex_TexelSize;
sampler2D_BlurTex;
float4_BlurTex_TexelSize;
float4_offsets;
float4_colorThreshold;
float4_bloomColor;
float_bloomFactor;
//高亮部分提取shader
v2f_thresholdvert_threshold(appdata_imgv)
{
v2f_thresholdo;
o.pos=mul(UNITY_MATRIX_MVP,v.vertex);
o.uv=v.texcoord.xy;
//dx中紋理從左上角為初始坐標,需要反向
#ifUNITY_UV_STARTS_AT_TOP
if(_MainTex_TexelSize.y<0)
o.uv.y=1-o.uv.y;
#endif
returno;
}
fixed4frag_threshold(v2f_thresholdi):SV_Target
{
fixed4color=tex2D(_MainTex,i.uv);
//僅當color大于設置的閾值的時候才輸出
returnsaturate(color-_colorThreshold);
}
//高斯模糊vertshader(上一篇文章有詳細注釋)
v2f_blurvert_blur(appdata_imgv)
{
v2f_bluro;
_offsets*=_MainTex_TexelSize.xyxy;
o.pos=mul(UNITY_MATRIX_MVP,v.vertex);
o.uv=v.texcoord.xy;
o.uv01=v.texcoord.xyxy+_offsets.xyxy*float4(1,1,-1,-1);
o.uv23=v.texcoord.xyxy+_offsets.xyxy*float4(1,1,-1,-1)*2.0;
o.uv45=v.texcoord.xyxy+_offsets.xyxy*float4(1,1,-1,-1)*3.0;
returno;
}
//高斯模糊pixelshader(上一篇文章有詳細注釋)
fixed4frag_blur(v2f_bluri):SV_Target
{
fixed4color=fixed4(0,0,0,0);
color+=0.40*tex2D(_MainTex,i.uv);
color+=0.15*tex2D(_MainTex,i.uv01.xy);
color+=0.15*tex2D(_MainTex,i.uv01.zw);
color+=0.10*tex2D(_MainTex,i.uv23.xy);
color+=0.10*tex2D(_MainTex,i.uv23.zw);
color+=0.05*tex2D(_MainTex,i.uv45.xy);
color+=0.05*tex2D(_MainTex,i.uv45.zw);
returncolor;
}
//Bloom效果vertexshader
v2f_bloomvert_bloom(appdata_imgv)
{
v2f_bloomo;
//mvp矩陣變換
o.pos=mul(UNITY_MATRIX_MVP,v.vertex);
//uv坐標傳遞
o.uv.xy=v.texcoord.xy;
o.uv1.xy=o.uv.xy;
#ifUNITY_UV_STARTS_AT_TOP
if(_MainTex_TexelSize.y<0)
o.uv.y=1-o.uv.y;
#endif
returno;
}
fixed4frag_bloom(v2f_bloomi):SV_Target
{
//取原始清晰圖片進行uv采樣
fixed4ori=tex2D(_MainTex,i.uv1);
//取模糊普片進行uv采樣
fixed4blur=tex2D(_BlurTex,i.uv);
//輸出=原始圖像,疊加bloom權值*bloom顏色*泛光顏色
fixed4final=ori+_bloomFactor*blur*_bloomColor;
returnfinal;
}
ENDCG
SubShader
{
//pass0:提取高亮部分
Pass
{
ZTestOff
CullOff
ZWriteOff
Fog{ModeOff}
CGPROGRAM
#pragmavertexvert_threshold
#pragmafragmentfrag_threshold
ENDCG
}
//pass1:高斯模糊
Pass
{
ZTestOff
CullOff
ZWriteOff
Fog{ModeOff}
CGPROGRAM
#pragmavertexvert_blur
#pragmafragmentfrag_blur
ENDCG
}
//pass2:Bloom效果
Pass
{
ZTestOff
CullOff
ZWriteOff
Fog{ModeOff}
CGPROGRAM
#pragmavertexvert_bloom
#pragmafragmentfrag_bloom
ENDCG
}
}
}
注:PostEffectBase類是后處理類的基類,在前面的文章已有詳細介紹,這里不再貼出。
四.效果展示
原始圖片效果:
開啟Bloom效果,過濾閾值設為灰色(128,128,128),泛光顏色為白色(256,256,256),泛光權重為1:
開啟Bloom效果,過濾閾值設為灰色(0,0,0),泛光顏色為白色(256,256,256),泛光權重為1,一種過度曝光的效果:
開啟Bloom效果,過濾閾值設為灰色(0,0,0),泛光顏色為綠色(0,256,0),泛光權重為1,夜視儀效果:
本篇文章介紹的主要是全屏泛光的實現方式,也就是泛光的部分只是通過一個全屏的顏色閾值來進行設定,超過這一閾值的顏色就進行泛光操作,否則不會泛光。這樣做效率較高,但是沒辦法控制單獨的物體進行泛光。網上也有這種針對單獨對象的bloom操作,可以實現更加細致的Bloom效果,這篇文章可以進行參考。
總結
以上是生活随笔為你收集整理的Unity Shader-后处理:Bloom全屏泛光的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 算法入门篇四 桶排序
- 下一篇: Java调用百度OCR文字识别的接口