Unity3D ShaderLab 各向異性高光
各向異性時一種模擬物體表面溝槽方向性的高光反射類型,它會修改或延伸垂直方向上的高光。當我們想模擬金屬拉絲高光的時候,它非常適合。下面就一步一步實現。
首先創建Shader,再創建材質球。然后雙擊Shader 打開編輯器。
1:修改Properties
Properties { //添加屬性; _MainTint("Diffuse Tint",Color)=(1,1,1,1) _MainTex ("Base (RGB)", 2D) = "white" {} _SpecularColor("Specular Color",Color)=(1,1,1,1) _SpecPower("Specular Power", Range(0.1,1))=0.5 _Specular("Specular Amount",Range(0.1,1))=0.5 _AnisoDir("Aniso Image",2D)=""{} _AnisoOffset("Aniso Offset",Range(-1,1))=0.5 }
2:SubShader添加變量
CGPROGRAM #pragma surface surf AnisoPhong #pragma target 3.0
//設置變量;
sampler2D _MainTex; sampler2D _AnisoDir; float4 _MainTint; float4 _SpecularColor; float _AnisoOffset; float _SpecPower; float _Specular; 3:加入SurfaceAnisoOutput輸出結構體,修改輸入結構體 struct Input { float2 uv_MainTex; float2 uv_AnisoDir; };
//自定義輸出結構體;
struct SurfaceAnisoOutput { fixed3 Albedo; fixed3 Normal; fixed3 Emission; fixed3 AnisoDirection; half Specular; fixed Gloss; fixed Alpha; };
4:實現光照模型函數LightingAnisoPhong
//自定義光照模型;
inline fixed4 LightingAnisoPhong(SurfaceAnisoOutput s,fixed3 lightDir,half3 viewDir, fixed atten){ fixed3 halfVector = normalize(normalize(lightDir)+normalize(viewDir)); float NdotL = saturate( dot(s.Normal,lightDir) ); fixed HdotA = dot(normalize(s.Normal+s.AnisoDirection),halfVector); float aniso = max(0,sin(radians(HdotA+_AnisoOffset)*180)); float spec = saturate(pow(aniso,s.Gloss*128)*s.Specular); fixed4 c; c.rgb = (s.Albedo * _LightColor0.rgb*NdotL)+(_LightColor0.rgb * _SpecularColor.rgb*spec)*(atten*2); c.a=1; return c; }
5:修改surf函數獲取法線
void surf (Input IN, inout SurfaceAnisoOutput o) { half4 c = tex2D (_MainTex, IN.uv_MainTex)*_MainTint; float3 anisoTex = UnpackNormal(tex2D(_AnisoDir,IN.uv_AnisoDir)); o.AnisoDirection = anisoTex; o.Specular = _Specular; o.Gloss = _SpecPower; o.Albedo = c.rgb; o.Alpha = c.a; }
返回unity。我們設置一下參數
最終效果如下:
在上面的編寫過程中,我們主要關注我們自定義的光照函數。
首先,我們聲明了自定義的輸出結構體SurfaceAnisoOutput,這樣做的目的是因為我們需要從各向異性法線貼圖中得到每個像素信息,
而且在一個表面着色器中獲得像素信息的唯一方法就是在surf函數中使用tex2D方法獲取。通過我們自定義的SurfaceAnisoOutput輸出結構體,
在光照函數和surf函數之間建立了數據傳遞方式。我們在surf函數中使用anisoTex 屬性來存儲每個像素的rgb訊息,然后通過輸出結構體的AnisoDirection 屬性傳遞給光照函數。
這樣,我們就可以開始進行光照運算,采用半角矢量的方法,我們避免了全反射和散射的計算,也就只需要計算頂點發現和光線向量兩者的點乘積。
fixed3 halfVector = normalize(normalize(lightDir)+normalize(viewDir));
float NdotL = saturate( dot(s.Normal,lightDir) );
然后,我們對鏡面高光進行修改計算,將頂點法線和各向異性法線貼圖上每個像素進行求和,再與halfVector進行點乘運算,
最后我們得到一個float值。該值為1,表面物體表面法線和halfVector平行,該值為0,他們是垂直的。
最終的sin函數計算后,我們就得到了中間有亮點且基於halfVector的環形效果。
fixed HdotA = dot(normalize(s.Normal+s.AnisoDirection),halfVector);
float aniso = max(0,sin(radians(HdotA+_AnisoOffset)*180));
最后在對aniso的值進行放大,得出s.Gloss求冪,通過s.Specular降低它的強度。
這樣,我們就得到了上圖的效果。整個Shader核心光照函數也就介紹完畢。
code start -----------------------------------------------------------------
Shader "91YGame/AnisotropicSpecular" { Properties { //添加屬性; _MainTint("Diffuse Tint",Color)=(1,1,1,1) _MainTex ("Base (RGB)", 2D) = "white" {} _SpecularColor("Specular Color",Color)=(1,1,1,1) _SpecPower("Specular Power", Range(0.1,1))=0.5 _Specular("Specular Amount",Range(0.1,1))=0.5 _AnisoDir("Aniso Image",2D)=""{} _AnisoOffset("Aniso Offset",Range(-1,1))=0.5 } SubShader { Tags { "RenderType"="Opaque" } LOD 200 CGPROGRAM #pragma surface surf AnisoPhong #pragma target 3.0 //設置變量; sampler2D _MainTex; sampler2D _AnisoDir; float4 _MainTint; float4 _SpecularColor; float _AnisoOffset; float _SpecPower; float _Specular; struct Input { float2 uv_MainTex; float2 uv_AnisoDir; }; //自定義輸出結構體; struct SurfaceAnisoOutput { fixed3 Albedo; fixed3 Normal; fixed3 Emission; fixed3 AnisoDirection; half Specular; fixed Gloss; fixed Alpha; }; //自定義光照模型; inline fixed4 LightingAnisoPhong(SurfaceAnisoOutput s,fixed3 lightDir,half3 viewDir, fixed atten){ fixed3 halfVector = normalize(normalize(lightDir)+normalize(viewDir)); float NdotL = saturate( dot(s.Normal,lightDir) ); fixed HdotA = dot(normalize(s.Normal+s.AnisoDirection),halfVector); float aniso = max(0,sin(radians(HdotA+_AnisoOffset)*180)); float spec = saturate(pow(aniso,s.Gloss*128)*s.Specular); fixed4 c; c.rgb = (s.Albedo * _LightColor0.rgb*NdotL)+(_LightColor0.rgb * _SpecularColor.rgb*spec)*(atten*2); c.a=1; return c; } void surf (Input IN, inout SurfaceAnisoOutput o) { half4 c = tex2D (_MainTex, IN.uv_MainTex)*_MainTint; float3 anisoTex = UnpackNormal(tex2D(_AnisoDir,IN.uv_AnisoDir)); o.AnisoDirection = anisoTex; o.Specular = _Specular; o.Gloss = _SpecPower; o.Albedo = c.rgb; o.Alpha = c.a; } ENDCG } FallBack "Diffuse" }
code end ------------------------------------------------------------------