您的位置:首页 > 移动开发 > Unity3D

【Unity Shaders】法线贴图

2016-04-13 17:47 246 查看
本系列主要参考《Unity
Shaders and Effects Cookbook》一书(感谢原书作者),同时一位写过这个书籍理解博客的博主妈妈说女孩子要自立自强

她的博客帮我更好的理解shader这本书籍。也因此希望可以延续这份分享。

本书所有的插图:https://www.packtpub.com/sites/default/files/downloads/5084OT_Images.pdf

本书所需的代码和资源:http://download.csdn.net/detail/candycat1992/6798871

========================================== 分割线 ==========================================

写在前面

由于刚刚开始学习shader,所以理解还是很片面。但是我也希望可以努力学习,争取未来有更深入的理解。而且这是第一次写博客,还不知道如何写出好看的格式,我会慢慢改进的。

准备工作

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


下图为使用我们提供的标量值来修改法线贴图的结果图:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: