本系列主要参考《Unity Shaders and Effects Cookbook》一书(感谢原书作者),同时一位写过这个书籍理解博客的博主妈妈说女孩子要自立自强。
她的博客帮我更好的理解shader这本书籍。也因此希望可以延续这份分享。
本书所有的插图:https://www.packtpub.com/sites/default/files/downloads/5084OT_Images.pdf。
本书所需的代码和资源:http://download.csdn.net/detail/candycat1992/6798871
========================================== 分割线 ==========================================
1.创建一个新的材质和着色器,命名为NormalMap。
2.在场景视图将它们设置到一个新物体上。
3.并且准备一张纹理贴图。
4.把导入的法线贴图的Texture Type调成normal map
1.在Properties块中添加一个颜色和一个纹理属性:
Properties { _MainTint("Diffuse Tint",Color) = (1,1,1,1) _NormalTex("Normal Map",2D) = "bump"{} }
2.在CGPROGRAM描述语句下的subshader内声明相应的属性,以便我们在CGT程序片段中能够访问上述的两个属性:
sampler2D _NormalTex; float4 _MainTint;
struct Input { float2 uv_NormalTex; };
4.最后,我们使用Unity内置的UnpackNormal()函数从法线贴图当中提取法线信息。然后你只需将这些新的发现信息应用到Surface Shader 的输出即可:
void surf (Input IN, inout SurfaceOutput o) { float3 normalMap = UnpackNormal(tex2D(_NormalTex, IN.uv_NormalTex)); o.Normal = normalMap.rgb; o.Albedo = _MainTint.rgb; o.Alpha = _MainTint.a; }最终代码如下:
Shader "Custom/NormalMap" { Properties { //_MainTex ("Base (RGB)", 2D) = "white" {} _MainTint("Diffuse Tint",Color) = (1,1,1,1) _NormalTex("Normal Map",2D) = "bump"{} } SubShader{ Tags { "RenderType" = "Opaque" } LOD 200 CGPROGRAM #pragma surface surf Lambert //sampler2D _MainTex; sampler2D _NormalTex; float4 _MainTint; struct Input { float2 uv_NormalTex; }; void surf (Input IN, inout SurfaceOutput o) { float3 normalMap = UnpackNormal(tex2D(_NormalTex, IN.uv_NormalTex)); o.Normal = normalMap.rgb; o.Albedo = _MainTint.rgb; o.Alpha = _MainTint.a; } ENDCG } FallBack "Diffuse" }
下图展示的是应用法线贴图渲染之后的结果:
表现发现贴图效果的数学原理其实超出了本章的学习范围,但Unity已经在很多方面帮我们做好了这一切。它是为我们提供了相应的函数,这样我们不需要重复地编写代码。这也解释了为什么表面着色器是一种非常高效的着色器编写方式。
如果你查看Unity自带的UnityCG.cginc文件,会找到UnpackNormal()函数的定义。当你在着色器内声明该函数时,Unity将对你提供的发现贴图进行处理,并将运算后的正确数据直接返回给你,这样你就可以逐像素将法线信息应用到光照函数内了。
当使用UnpackNormal()函数对发现贴图进行处理后,会将处理后的值返回到SurfaceOutput结构体内,这样它就可以在光照函数中进行使用了。
这一步工作是由代码“o.Normal=normalMap.rgb;”来完成的。
可以在发现贴图着色器内添加一些控件,以便让用户可以自行调整发现贴图的强度。我们可以很容易地通过修改法线贴图变量的x和y坐标来完成。然后将修改后的值返回到计算中。
1.在Properties块中另外又添加一个属性,将其命名为_NormalMapIntensity,如下代码所示:
Properties { _MainTint("Diffuse Tint", Color) = (1,1,1,1) _NormalTex("Normal Map", 2D) = "bump" {} _NormalIntensity("Normal Map Intensity", Range(0,2)) = 1 }
2.确保你在SubShader函数中也声明了该属性:
//Link the property to the CG program sampler2D _NormalTex; float4 _MainTint; float _NormalIntensity;
3.将经过解压后的法线贴图变量值的x和y坐标值均乘上_NormalMapIntensity,将计算后的值作为法线贴图的变量值。现在就可以让用户在材质Inspector面板上调整发现贴图的强度了。
void surf(Input IN, inout SurfaceOutput o) { //Get teh normal Data out of the normal map textures //using the UnpackNormal() function. float3 normalMap = UnpackNormal(tex2D(_NormalTex, IN.uv_NormalTex)); normalMap = float3(normalMap.x * _NormalIntensity, normalMap.y * _NormalIntensity, normalMap.z); //Apply the new normals to the lighting model o.Normal = normalMap.rgb; o.Albedo = _MainTint.rgb; o.Alpha = _MainTint.a; }
下图为使用我们提供的标量值来修改法线贴图的结果图: