本系列主要参考《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;
3 .然后,我们需要确定我们已经使用合适的变量对 Input 结构体进行了更新,这样我们就可以将模型的 UV 应用到法线贴图上了。
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;
}
下图为使用我们提供的标量值来修改法线贴图的结果图: