Unity3D-Shader-復古電影熒幕特效

【舊博客轉移 - 2015年12月6日 18:12】
 
今天用Shader作了一個復古熒幕效果,老電視機放映的感受,寫篇文章記錄一下
 

 

 
原始圖片:

 

 
沒錯,這就是電影《泰坦尼克號》的劇照。船撞到冰山上翻了,氣溫很是低,男主角傑克,找到了一塊木板,讓女主角睡在上面保住了性命,本身被凍成冰塊沉入海底,仍是蠻感人的。
 
實現原理就是用到幾張特效圖,加入一些抖動效果
 
1.暈影圖( Vignette effect)

 

 
2.屏幕劃痕 Scratches和 灰塵污點 Dust

 

 

 
 
 
Shader實現:
 half2 mainTexUV = half2(i.uv.x, i.uv.y+(_RandomValue*_SinTime.z * 0.005));
 fixed4 mainTex = tex2D(_MainTex, mainTexUV);

 

採樣主紋理,UV值y加上了一個隨機值,實現上下抖動的效果。app

_SinTime是Unity內置的一個變量,用來獲取一個-1到1範圍的sin函數值dom

 half2 scratchesUV = half2(i.uv.x + (_RandomValue * _SinTime.z * _ScratchesXSpeed),
 i.uv.y + (_RandomValue * _Time.x * _ScratchesYSpeed));
 fixed4 scratchesTex = tex2D(_ScratchesTex, scratchesUV);

 

採樣劃痕紋理,這裏的UV值,採用隨機數乘以X,Y軸分別的速度,來實現屏幕隨機位置的閃動效果,灰塵紋理也是同樣的處理函數

 //轉成YIQ色彩空間,取出亮度值
 fixed lum = dot(fixed3(0.299, 0.587, 0.114), mainTex.rgb);

 fixed4 finalColor = lum + lerp(_SepiaColor, _SepiaColor + fixed4(0.1f, 0.1f, 0.1f, 0.1f), _RandomValue);

 

這一步是把RGB顏色空間轉換成YIQ顏色空間,YIQ色彩空間一般被電視系統所採用,在YIQ系統中,Y份量表明圖像的亮度信息,I、Q兩個份量則攜帶顏色信息,I份量表明從橙色到青色的顏色變化,而Q份量則表明從紫色到黃綠色的顏色變化。將彩色圖像從RGB轉換到YIQ色彩空間,能夠把彩色圖像中的亮度信息與色度信息分開,分別獨立進行處理。spa

再加上一個棕褐色調_SepiaColor,這裏用lerp函數作一個線性插值,實現明暗之間的漸變.net

fixed3 constantWhite = fixed3(1, 1, 1);

finalColor = lerp(finalColor, finalColor * vignetteTex, _VignetteAmount);
finalColor.rgb *= lerp(scratchesTex, constantWhite, _RandomValue);
finalColor.rgb *= lerp(dustTex, constantWhite, (_RandomValue * _SinTime.z));
finalColor = lerp(mainTex, finalColor, _EffectAmount);

 

最後把顏色彙總,用一些線性插值實現漸變,而後把顏色值相乘獲得最後的結果。返回給fragment着色器輸出,就能夠獲得上面的效果。code

完整Shader:blog

Shader "lijia/OldEffect" {
 Properties {
 //原圖
 _MainTex("MainTex", 2D) = "white" {}
 //暈影圖
 _VignetteTex("VignetteTex", 2D) = "white" {}
 _VignetteAmount ("Vignette Opacity", Range(0, 1)) = 1
 //劃痕
 _ScratchesTex("ScratchesTex", 2D) = "white" {}
 _ScratchesXSpeed("ScratchesXSpeed", float) = 100
 _ScratchesYSpeed("ScratchesYSpeed", float) = 100
 //灰塵
 _DustTex("DustTex", 2D) = "white" {}
 _DustXSpeed("_DustXSpeed", float) = 100
 _DustYSpeed("_DustYSpeed", float) = 100
 //老舊的褐色調
 _SepiaColor("_SepiaColor", Color) = (1, 1, 1, 1)

 _RandomValue("RandomValue", float) = 1.0
 _EffectAmount ("Old Film Effect Amount", Range(0, 1)) = 1
 }

 SubShader {
 Tags{"RenderType" = "Opaque"}
 Pass {
 CGPROGRAM
 #pragma vertex vert
 #pragma fragment frag

 #include "UnityCG.cginc"

 sampler2D _MainTex;
 float4 _MainTex_ST;

 sampler2D _VignetteTex;
 sampler2D _ScratchesTex;
 sampler2D _DustTex;

 float _EffectAmount;
 float _RandomValue;
 float _VignetteAmount;

 float _ScratchesXSpeed;
 float _ScratchesYSpeed;
 float _DustXSpeed;
 float _DustYSpeed;

 fixed4 _SepiaColor;

 struct v2f {
 float4 pos : SV_POSITION;
 float2 uv : TEXCOORD0;
 };

 v2f vert(appdata_base v)
 {
 v2f o;
 o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
 o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
 return o;
 }

 fixed4 frag(v2f i): COLOR
 {
 //採樣主紋理 uv.y值加上一些隨機因素實現抖動的效果 _SinTime是Unity內置的變量 用來獲取一個-1到1的正弦值
 half2 mainTexUV = half2(i.uv.x, i.uv.y+(_RandomValue*_SinTime.z * 0.005));
 fixed4 mainTex = tex2D(_MainTex, mainTexUV);

 fixed4 vignetteTex = tex2D(_VignetteTex, i.uv);

 half2 scratchesUV = half2(i.uv.x + (_RandomValue * _SinTime.z * _ScratchesXSpeed),
 i.uv.y + (_RandomValue * _Time.x * _ScratchesYSpeed));
 fixed4 scratchesTex = tex2D(_ScratchesTex, scratchesUV); 

 half2 dustUV = half2(i.uv.x + (_RandomValue * _SinTime.z * _DustXSpeed),
 i.uv.y + (_Time.x * _DustYSpeed));
 fixed4 dustTex = tex2D(_DustTex, dustUV);

 //變成YIQ 值
 fixed lum = dot(fixed3(0.299, 0.587, 0.114), mainTex.rgb);

 fixed4 finalColor = lum + lerp(_SepiaColor, _SepiaColor + fixed4(0.1f, 0.1f, 0.1f, 0.1f), _RandomValue);

 fixed3 constantWhite = fixed3(1, 1, 1);

 finalColor = lerp(finalColor, finalColor * vignetteTex, _VignetteAmount);
 finalColor.rgb *= lerp(scratchesTex, constantWhite, _RandomValue);
 finalColor.rgb *= lerp(dustTex, constantWhite, (_RandomValue * _SinTime.z));
 finalColor = lerp(mainTex, finalColor, _EffectAmount);

 return finalColor;
 }

 ENDCG
 }
 }
 FallBack "Diffuse"
}

 

 

擴展:遊戲

上面講的是基於一張原圖作的處理,若是咱們想實如今動態內容上加上這種老電影效果呢?好比遊戲中播放劇情的時候,在屏幕上加上一個老電影特效,提升帶入感。圖片

這時咱們能夠用混合模式ci

 

官方文檔的解釋

Blend DstColor Zero是乘以Color緩衝區的顏色的,而這個特效恰好是用到乘法

分了兩個Pass來實現

 第一個Pass先處理色調
Pass {
Blend DstColor Zero
CGPROGRAM
#pragma vertex vert_img
#pragma fragment frag

#include "UnityCG.cginc"

fixed4 _SepiaColor;
float _RandomValue;

fixed4 frag(v2f_img i): COLOR
{
fixed4 color = fixed4(0.299, 0.587, 0.114, 1);
color = color + lerp(_SepiaColor, _SepiaColor + fixed4(0.1f, 0.1f, 0.1f, 0.1f), _RandomValue);
return color;
}
ENDCG
}

 

第二個Pass再把剩下的圖片疊加上去,跟以前同樣處理就好了。
fixed4 frag(v2f_img i): COLOR
{
fixed4 vignetteTex = tex2D(_VignetteTex, i.uv);

half2 scratchesUV = half2(i.uv.x + (_RandomValue * _SinTime.z * _ScratchesXSpeed),
i.uv.y + (_RandomValue * _Time.x * _ScratchesYSpeed));
fixed4 scratchesTex = tex2D(_ScratchesTex, scratchesUV);

half2 dustUV = half2(i.uv.x + (_RandomValue * _SinTime.z * _DustXSpeed),
i.uv.y + (_Time.x * _DustYSpeed));
fixed4 dustTex = tex2D(_DustTex, dustUV);

fixed3 constantWhite = fixed3(1, 1, 1);

fixed4 finalColor = fixed4(1, 1, 1, 1);//這裏使用1,由於混合模式會乘Color緩衝的顏色
finalColor = lerp(finalColor, finalColor*vignetteTex, _RandomValue);
finalColor.rgb *= lerp(scratchesTex, constantWhite, _RandomValue);
finalColor.rgb *= lerp(dustTex, constantWhite, _RandomValue*_SinTime.z);
return finalColor;
}

 

感謝http://blog.csdn.net/candycat1992,讀你的文章讓我學到了不少知識

相關文章
相關標籤/搜索