在学习完上一节之后,已经了解了在Unity 中如何实现一个高光 Shader ,但是会有一个问题,就是效果看起来不切实际,如下面的问题
我用一张图片贴到了Cube上面,然后用了一个高光材质,得到了下图的效果。
其实这个效果还算可以,但是认真看就会发现,这个结果是不符合自然现象的。
这个箱子是木头的,然后有铁皮 作为封条。
首先不符合常理的是为什么这个木头箱子会反光!
可能木头箱子打蜡了,然后就反光,但是为什么打蜡的木头 和 铁皮 看起来是一样的,光滑度是一样的吗?
转自http://blog.csdn.net/huutu http://www.thisisgame.com.cn
这样一种效果是很难解释的通的。
那么如何模拟真实的情况,也就是该反光的地方才反光,不该反光的地方不反光?
回想一下高光的原理,高光是根据反射光与 视线的角度来求出高光的强度值的。对于上面的箱子,木材 和 铁片是在同一个平面上的,所以求出的高光强度值是相同的。
也就是说,按照上一节的做法是不能将 铁片 和 木材的高光强度值区分开来的。
那么我们要想一个办法。
首先想到的是,把铁片 和 木材分开来,木材作为单独的一张贴图,铁片作为另外一张贴图,里面是空白的。
灯光只作用于铁片这一张贴图。我们计算出来的 高光强度 Specular * 铁片贴图的RGB。因为铁片中间是黑色的,所以铁片的中间这一块的 RGB 都是 0 ,所以实际上只有外侧铁片的地方,才真正受到了光照的影响!然后再和 木材的贴图的颜色相加。
转自http://blog.csdn.net/huutu http://www.thisisgame.com.cn
由此引入这一节的知识 -- 高光贴图。
如上面所说,需要两个贴图,木材这一个贴图只接受漫反射光照,而铁片这一个高光贴图 接收高光。
在 Shader 中定义对应的变量
Properties { _MainTex ("Base (RGB)", 2D) = "white" {} _SpecularColor("Specular Color",Color)=(1,1,1,1) _SpecularTexture("Specular Texture",2D)="white" {} _SpecularPower("Specular Power",Range(0.1,100))=1 }
我们在 Suf 函数中,对两个纹理取样,然后存储到 SurfaceOutput 结构体中传入到 光照模型函数。
然后会遇到一个问题,SurfaceOutput 结构体,是Unity 定义的一个结构体,其定义存在与 Lighting.cginc 文件中
struct SurfaceOutput { fixed3 Albedo; fixed3 Normal; fixed3 Emission; half Specular; fixed Gloss; fixed Alpha; };
所以这次我们要自定义一个 SurfaceOutput 结构体,添加一个 SpecularColor 变量。
struct CustomSurfaceOutput { fixed3 Albedo; fixed3 Normal; fixed3 Emission; half Specular; fixed3 SpecularColor; fixed Gloss; fixed Alpha; };
void surf (Input IN, inout CustomSurfaceOutput o) { half4 c = tex2D (_MainTex, IN.uv_MainTex); o.Albedo = c.rgb; o.Alpha = c.a; } inline fixed4 LightingCustomPhong(CustomSurfaceOutput s,fixed3 lightDir,half3 viewDir,fixed atten) { fixed4 c; c.rgb=s.Albedo; c.a=s.Alpha; return c; }
转自http://blog.csdn.net/huutu http://www.thisisgame.com.cn
指定光照模型为 CustomPhong
CGPROGRAM #pragma surface surf CustomPhong
struct Input { float2 uv_MainTex; float2 uv_SpecularTexture; };
void surf (Input IN, inout CustomSurfaceOutput o) { //不接受高光的,漫反射贴图,例如木头 half4 c = tex2D (_MainTex, IN.uv_MainTex); o.Albedo = c.rgb; o.Alpha = c.a; //接收高光的,高光贴图,例如铁皮 half4 specularC=tex2D(_SpecularTexture,IN.uv_SpecularTexture); o.SpecularColor=specularC.rgb; //用r值作为系数,如果当前UV坐标是位于铁片里面黑色的那一块,那么rgb都是0,这样里面黑色的那一块其实是无效的。 o.Specular = specularC.r; }
修改光照函数
inline fixed4 LightingCustomPhong(CustomSurfaceOutput s,fixed3 lightDir,half3 viewDir,fixed atten) { //首先计算漫反射; float diffuse=max(0,dot(s.Normal,lightDir)); //计算漫反射颜色; float3 diffuseColor=_LightColor0*s.Albedo * diffuse; //计算反射光方向向量 float3 halfReflectVector=normalize(lightDir + viewDir); //计算反射光强度;如果当前位置是铁片黑色的那一块,那么Specular是0,这里就没有高光了。 float specular = pow( max(0,dot(s.Normal,halfReflectVector)) , _SpecularPower) * s.Specular; //计算高光颜色 高光贴图采样颜色 * 反射光强度 * 编辑器中指定的高光颜色 * 光照颜色; float3 specularColor =_LightColor0.rgb* s.SpecularColor * specular * _SpecularColor.rgb *(atten*5); fixed4 c; c.rgb=diffuseColor + specularColor; c.a=s.Alpha; return c; }
示例工程下载:
http://pan.baidu.com/s/1dFyiyDb