【舊博客轉移 - 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加上了一個隨機值,實現上下抖動的效果。
_SinTime是Unity內置的一個變量,用來獲取一個-1到1范圍的sin函數值
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色彩空間,可以把彩色圖像中的亮度信息與色度信息分開,分別獨立進行處理。
再加上一個棕褐色調_SepiaColor,這里用lerp函數做一個線性插值,實現明暗之間的漸變
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着色器輸出,就可以得到上面的效果。
完整Shader:
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" }
擴展:
上面講的是基於一張原圖做的處理,如果我們想實現在動態內容上加上這種老電影效果呢?比如游戲中播放劇情的時候,在屏幕上加上一個老電影特效,提高帶入感。
這時我們可以用混合模式
官方文檔的解釋
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,讀你的文章讓我學到了很多知識