上文咱们学了surface shader。这玩意在开始的时候啊,在定义哪个函数处理surface时用一定要指定Lighting model(即光照模型)的。自带的是Lambert和BlinnPhong.本文首先对这两个进行说明,后面讲解如何自定义光照模型及对官方实例的解析。http://docs.unity3d.com/Documentation/Components/SL-SurfaceShaderLighting.html
一、简单光反射模型
1.反射,其实就是光照到物体上,然后再由物体反射出去。这种过程就叫反射。
2.漫反射,物体表面可能是不光滑的,因此自然法线就不是均匀的,那么这种情况下,当几条平行光照向物体时,反射回来的光线就不再是平行的了,这种情况叫漫反射。
3.镜面反射:与漫反射对应,如果物体表面光滑,即法线均匀(不一定都平行,会有曲面的情况),则反射回来的也是均匀的,这种情况叫镜面反射。
4.环境光:光线照到指定物体的周围物体,这时指定物体上有光线反射到,这种光可称为环境光或泛光。
5.光照 = 反射光 + 镜面光 + 环境光 + 自发光。
对于简单光反射模型: 入射光 = 反射光 + 吸引光。而实际上来说还需要加上投射光与散射光。
二、Lambert和BlinnPhong
1.冯式反射模型:Phong reflection 是一种将漫反射与镜面反射进行关联在一些的反射方法。
2.Lambert:一种主要应用于漫反射的光照模型。
3.BlinnPhong: 一种主要应用于镜面反射的光照模型。
三、自定义光照模型
1.规则:定义一个函数,必须以 Lighting开头,可以写在shader文件里的任意位置或其他include的文件。
=>half4 LightingName (SurfaceOutput s, half3 lightDir, half atten); 这种是无viewDir,在上文中有说到viewDir就是返回给视角的方向,不用依赖此值,就类似于漫反射的效果。因为漫反射中出来的光线是由不规则的表面来决定。
=>half4 LightingName (SurfaceOutput s, half3 lightDir, half3 viewDir, half atten); 这个方法是相对于上面的,需要依赖于viewDir。
=>half4 LightingName_PrePass (SurfaceOutput s, half4 light); 这个方法是可选的,如果需要光照延时(deferred),就加上此方法,不写则全部采用快速光照(forward)。
2.自定义解码光照
注意,如果使用了LightingName_PrePass,则光照解码的方法在此方法之前,因此是有了光照延时。具体会针对与SingleLightMapping、DualLightMapping和StandardLightMapping三种的方法,与LightMapping的定义函数类似,具体的这里不介绍,可参考官方文档:http://docs.unity3d.com/Documentation/Components/SL-SurfaceShaderLighting.html
三、实例学习
1.自定义一个简单的漫反射光照模型:
#pragma surface surf SimpleLambert //1 half4 LightingSimpleLambert (SurfaceOutput s, half3 lightDir, half atten) { //2 half NdotL = dot (s.Normal, lightDir); //3 half4 c; //4 c.rgb = s.Albedo * _LightColor0.rgb * (NdotL * atten * 2); //5 c.a = s.Alpha; //6 return c; //7 }
//2: 光照模型的定义。这里说说atten,根据atten的意思(衰减器),可以理解到,这个参数就是光照的衰减值。
//3:算一点积,光照方向与法线的点积,可理解为光照与法线的夹角。
//4:定义输出的颜色四元组。
//5:光照的颜色 = 物体反射颜色 * 原光的颜色* 经过折射的衰减颜色。_LightColor0是在Lighting.cginc 里定义的,我理解成反射光的颜色。另外还有一个_SpecColor,对应是镜面反射光的颜色。此处由于资料稀缺,全部个人的学习所得,如有错误,欢迎指出!
//6:设置光的透明与反射透明一致。
//7:返回。
2.Diffuse wrap
#pragma surface surf WrapLambert half4 LightingWrapLambert (SurfaceOutput s, half3 lightDir, half atten) { half NdotL = dot (s.Normal, lightDir); half diff = NdotL * 0.5 + 0.5; //! half4 c; c.rgb = s.Albedo * _LightColor0.rgb * (diff * atten * 2); c.a = s.Alpha; return c; }
3.Toon Ramp
#pragma surface surf Ramp sampler2D _Ramp; half4 LightingRamp (SurfaceOutput s, half3 lightDir, half atten) { half NdotL = dot (s.Normal, lightDir); half diff = NdotL * 0.5 + 0.5; half3 ramp = tex2D (_Ramp, float2(diff)).rgb; //1 half4 c; c.rgb = s.Albedo * _LightColor0.rgb * ramp * (atten * 2); c.a = s.Alpha; return c; }
//1中,直接将diff强转成float2的坐标系,向_Ramp的材质中查找出相应的rgb。最终也以此rgb值及其他光照值得出光照的rgb值。这种效果可以考虑来做光晕。这里我用一张具体的图,以float2(diff)为uv找的效果会有点奇怪。不过现象可以反映出来。
4.Simple Specular
#pragma surface surf SimpleSpecular half4 LightingSimpleSpecular (SurfaceOutput s, half3 lightDir, half3 viewDir, half atten) { half3 h = normalize (lightDir + viewDir); half diff = max (0, dot (s.Normal, lightDir)); float nh = max (0, dot (s.Normal, h)); float spec = pow (nh, 48.0); half4 c; c.rgb = (s.Albedo * _LightColor0.rgb * diff + _LightColor0.rgb * spec) * (atten * 2); c.a = s.Alpha; return c; }
c.rgb = (s.Albedo * _LightColor0.rgb * diff * (atten * 2)) + (_LightColor0.rgb * spec * (atten * 2));
float nh = max (0, dot (s.Normal, normalize (lightDir + viewDir)));
后面取pow是为了放小nh的值,其值越小,则镜面反射就越小。