上周我們公司原畫師畫了一張很好看的鐳射衣服原畫,這個材質號稱原畫師一畫就廢的材質。BulingBuling的。我們原畫師非常強,畫的真好。看到後很有實現的欲望。於是我要挑戰一下設計界甲方的最高難度,五彩斑斕黑的哥哥,五彩斑斕白。說了這麼多,其實非常非常容易,核心代碼就幾句話的事情。我是在拾人牙慧。並且實現的方式也不止一種。
先上最後效果圖
1 分析
首先我們先了解一下,這種效果會在哪裡用到,比如:車漆的反射,泡泡,這種比較洋氣的鐳射衣服也會用到。但是鐳射衣服一般外面還會有一層類似透明雨披的東西或者就是透明的。我這裡沒有現成的模型就沒有去製作,要是想做的話把模型丟進MAX擠出一層也可以。
這裡我找了一些五彩斑斕白的參考:
這個圖讓我想起來小時候外婆家豬圈棚就是這個紙
話說我也挺想做這種的,奈何沒有找到類似的模型,自己又懶得做(其實是做的醜),只能找個裙子湊合湊合(所以說啊,美術大佬就非常重要)
這種的把豬圈棚紙當雨披穿的,顏色相對上面的比較飽和,類似UE4的HueShift節點
但是本文不是基於這個UE節點的算法製作的,這裡就不去贅述。
2 思考
看完上面的圖,那麼問題來了,我們常見的泡泡是不是也是這種?沒錯,泡泡也可以認為是具有偏振彩色反射的。但是泡泡有一個更加洋氣的名字,叫薄膜幹涉。
感興趣的同學可以去這個github庫看看。
去年春節的時候我也實現過一個類似的,跟我有一年塑料朋友圈友情的朋友應該見到過。
仔細觀察這種材質,大概就是彩虹色漸變+反光+透明(如果有的話),光的原理涉及到光的幹涉。題外話,大家知道一根筷子進水和空氣會發生一次折射,那麼泡泡是一個閉合的球體,會發生兩次折射。當然我們這裡實現的是不透明的衣服,折射不在我們的考慮範圍內,我們考慮反射就好。
首先我們想一下,這個不靈不靈的效果,和什麼有關呢?燈光方向?視口方向?攝像機的位置?法線方向?
3 實現
對,其實都有關係 ,首先我們來講解一下燈光方向,即世界空間的燈光方向,一個float3的向量,但是我們這裡假設燈光是不變的就只有一盞光,光照模型中我們也假設是一個標準的PBS光照模型(如果講清PBS真的要講幾天幾夜,這裡我就直接用的Unity標準的表面著色器,對標ASE的stanrad,畢竟本文重點是偏振幅彩色反射),向量具有方向性。視口方向同理,即顯示生活中我們人眼的觀察方向(需要歸一化)。
這裡的代碼實現原理是用攝像機位置減去空間物體的位置得來的結果。
// 視覺方向v。即相機到物體的向量歸一化。// 等價UnityWorldSpaceViewDir(mul(unity_ObjectToWorld,v.vertex))fixed viewDir = normalize(_WorldSpaceCameraPos.xyz - mul(unity_ObjectToWorld,v.vertex).xyz);
Unity為我們定義好了攝像機位置的參數,在UnityShaderVariables.cginc這個文件中我們可以查到
#define _WorldSpaceCameraPos unity_StereoWorldSpaceCameraPos[unity_StereoEyeIndex]
直接可以通過_WorldSpaceCameraPos 這個方法拿到對應的向量數據
節點版本:我們這裡使用了攝像機位置和一個float值相乘,為什麼?下面第二個Debug給大家看。再和世界空間法線點乘獲得值
Debug(1):
我們發現當我旋轉視口的時候(即當前的攝像機位置產生了變化),顏色變化了。
然後我們將點乘的值給Cos函數後,再給一個藍色。
Debug :可以看到,當我移動攝像機位置和遠近的時候,顏色會發生不同程度的偏移,當我縮放上面的Tile值時,藍色部分重疊的也就更加頻繁。
我想這裡大家應該是可以理解的,我再解釋一下:
攝像機位置距離越遠,點乘值就會改變,而點乘是可以與float值相乘且滿足交換律的。當我的值發生了變化,Cos也就發生了變化,cos是一個波浪弧線,當裡面的自變量越大,弧線也就越密集。就會得到上面的效果。
那麼我們對應的表面著色器代碼應該怎麼寫呢?
(這裡照顧一下新人能夠實現效果,說了一下創建)
首先我們先創建一個表面著色器
創建後發現裡面有代碼,沒錯,裡面已經預製了一個完整的PBS,我們只需要填充就好(當然,這裡得解釋一下,對於沒有了解過UnityShader
機制的朋友來說,表面著色器完全是一個黑合。如果沒有研究過PBR的實現方式,並不知道Unity到底幫我們做了什麼。很方便,但是Pass和變體也很多)不過對練習和初學來說卻非常好入門。
以下為全部代碼:
Shader"Custom/CustomFilm"{Properties{_Metallic("Metallic",Range( 0 , 1)) = 1_ContrastMask1("ContrastMask1",Color) = (0,0,1,1)_Smoothness("Smoothness",Range( 0 , 1)) = 1_Tile("Tile",Float) = 1_MainTex("MainTex",2D) = "white" {}_MainColor("MainColor",Color) = (1,1,1,0)_MaskRgb("MaskRgb",Color) = (1,1,1,0)}SubShader{Tags{ "RenderType"="Opaque" }CGPROGRAM #pragma surface surf Standard fullforwardshadows #pragma target 3.0sampler2D_MainTex;structInput{float2uv_MainTex;float3worldNormal;};half_Smoothness;half_Metallic;half_Tile;half4_MainColor;half4_MaskRgb;half4_ContrastMask1;voidsurf (Input IN, inout SurfaceOutputStandard o){half3WorldNormal = normalize (IN.worldNormal);half3CameraPos = _WorldSpaceCameraPos * _Tile;half3DotPostion = dot (CameraPos,WorldNormal);half3Mask01 = saturate ( cos (DotPostion) * _ContrastMask1.rgb);half3CrossColor = cross (_ContrastMask1.rgb , float4(1,1,1,1));half3Mask02 = saturate ( sin (DotPostion) * CrossColor);half3AddMask = Mask01 + Mask02;halfRGBToGray = saturate ( 0.2989 * AddMask.r + 0.587 * AddMask.g + 0.114 * AddMask.b);half4c = tex2D (_MainTex, IN.uv_MainTex) * _MaskRgb;half3FinalColor = lerp (c , _MainColor , RGBToGray);o.Albedo = FinalColor.rgb;o.Metallic = _Metallic;o.Smoothness = _Smoothness;o.Alpha = 1;}ENDCG}FallBack"Diffuse"}
這個還是很容易的,我們首先去定義Properties內我們用到的所有東西
同時定義好聲明類型
OK,這個時候,我們往輸入結構體內獲得世界法線。
相對於Unlit來講,表面Shader封裝的非常完美,不需要我們再進行矩陣轉換獲得世界空間的法線
在輸出結構體內歸一化後進行下面的計算,計算步驟和上面節點過程是一樣的。
好,回歸節點:我們下面要計算sin
這裡和上面一樣,不重複廢話了。
然後我們將藍色(0,0,1)和白色(1,1,1)叉乘,結果就是垂直於這兩個三維向量的向量所代表的顏色(這裡是叉乘的性質,不了解的需要學一下數學基礎)注意哦,叉乘是不滿足交換律的。我沒有用normalize是想增強顏色對比。
最後我們與sin相乘著色與之前的藍色相加,紅綠藍三個通道就出來了。
對應的代碼
下面,我要這個相加後的三維向量,給我變成一個灰度圖。因為我是用來做線性插值(Lerp)的變量的。
這裡就涉及到一個RGB轉灰度的公式了,有三種常用的轉法,ASE自帶轉灰度的節點,Unity好像也是帶的,但是我忘了是哪個函數,知道的朋友可以下面告訴我。
最常用也最類似的的轉灰度公式即是這個(一個經驗數據):RGB 按照 0.2989 R,0.5870 G 和 0.1140 B 的比例構成像素灰度值我們這裡相加即可,因為並不重複,也可以使用三個通道的值相加後除3,求一個平均值。
我們拿到了一個float值後傳給lerp
既然要做五彩斑斕的白,我們lerp的AB值肯定要有一個是白的,這張花花綠綠的圖是什麼?這張圖不得不提一句,是經過圖形界大量的工作科研者測量出來的一個偏振彩色圖(當然你要使用PS自己拉一個漸變也可以的,達到效果即可,同理UE4的一個HueShift節點。圖形第一奧義:看起來是對的就是對的。)
對應的代碼:
這裡扯一下Lerp這個函數,當我用自研引擎寫glsl時,我才知道Unity封裝的是有多好,GLSL內對應的Lerp函數叫mix,也就是說我每一種情況都要考慮到,不能和Unity內一樣(相對來說Unity的寫法比GLSL隨意多了)。
最後,如果想加上一點亮晶晶的顆粒感,可以用視口方向點乘世界空間法線(給一張法線貼圖,UV平鋪給高)這裡我就直接用圖表表示了,以下是圖表和效果
本文到這裡就結束啦,感謝各位看官,如需要工程文件,可以加群:867472754裡面找 ,一起討論問題。歡迎各路神仙!
如果遇到上文問題或者錯誤可以聯繫我,敬請斧正!