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 ------------------------------------------------------------------