ShaderLab 法线贴图(凹凸材质)
http://www.manew.com/thread-97665-1-1.html
内容说明:
1. 法线贴图的原理
2. 法线贴图的unity实现
1. 法线贴图的原理
2. 法线贴图的unity实现
1. 法线贴图的原理
1.1 什么是法线贴图?
每个顶点都有一条法线,三角形内部法线由插值计算得出,颜色则是直接从纹理取数据。法线贴图的基本思想就是像纹理采样一样为法线取值。
1.2法线纹理
1.1 什么是法线贴图?
每个顶点都有一条法线,三角形内部法线由插值计算得出,颜色则是直接从纹理取数据。法线贴图的基本思想就是像纹理采样一样为法线取值。
1.2法线纹理
下图是一张法线的纹理图:
每个纹素的RGB值实际上表示的是XYZ向量:颜色的分量取值范围为0到1,而向量的分量取值范围是-1到1;可以建立从纹素到法线的简单映射:
法线纹理的映射方式和漫反射纹理相似。麻烦之处在于如何将法线从各三角形局部空间(切线空间tangent space,亦称图像空间image space)变换到模型空间(着色计算所采用的空间)。
1.3切线空间
大家对矩阵已经十分熟悉了,应该知道定义一个空间需要三个向量。现在Up向量已经有了,即法线:
大家对矩阵已经十分熟悉了,应该知道定义一个空间需要三个向量。现在Up向量已经有了,即法线:
然后是切线T:垂直于法线的向量。但这样的切线有很多个:
这么多切线中该选哪个呢?理论上哪一个都行。但我们必须保持连续一致性,以免衔接处出现瑕疵。Unity已经给我提供好了Tangent。
定义一组基需要三个向量,因此我们还得计算副切线B。可以使用法线和切线叉乘计算得到。那么切空间就可以表示成如下图这样了。
定义一组基需要三个向量,因此我们还得计算副切线B。可以使用法线和切线叉乘计算得到。那么切空间就可以表示成如下图这样了。
已知T、B、N向量之后,即可得下面这个漂亮的矩阵,完成从切线空间到模型空间的变换:
只需对上述矩阵求逆即可得逆变换。这个矩阵(正交阵,即各向量相互正交的矩阵,参见下文“延伸阅读”小节)的逆矩阵恰好也就是其转置矩阵,计算十分简单:
2. unity中的实现
我们看下unity实现的代码:
我们看下unity实现的代码:
[C#] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
Shader
"Custom/NewSurfaceShader"
{
Properties
{
_MainText( "MainText" ,2D)= "white" {}
_BumpMap( "BumpMap" ,2D)= "white" {}
}
SubShader
{
pass{
Tags{ "LightMode" = "ForwardBase" }
CGPROGRAM
#pragma
vertex vert
#pragma
fragment frag
#include
"UnityCG.cginc"
float4
_LightColor0;
sampler2D
_BumpMap;
sampler2D
_MainText;
struct
v2f {
float4
pos:SV_POSITION;
float2
uv:TEXCOORD0;
float3
lightDir:TEXCOORD1;
};
v2f
vert (appdata_full v) {
v2f
o;
o.pos=mul(UNITY_MATRIX_MVP,v.vertex);
o.uv=v.texcoord.xy;
TANGENT_SPACE_ROTATION;
o.lightDir=
mul(_World2Object, _WorldSpaceLightPos0).xyz; //Direction
Light
o.lightDir=mul(rotation,o.lightDir);
return
o;
}
float4
frag(v2f i):COLOR
{
float4
c=1;
float3
N=UnpackNormal(tex2D(_BumpMap,i.uv));
float
diff=max(0,dot(N,i.lightDir));
c=_LightColor0*diff;
c*=tex2D(_MainText,i.uv);
return
c;
}
ENDCG
}
}
}
|
我们把光照贴图给附上,材质分别给一个平面和一个球看下效果:
2.3 UnpackNormal函数说明
[C#] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
inline
fixed3 UnpackNormalDXT5nm (fixed4 packednormal)
{
fixed3
normal;
normal.xy
= packednormal.wy * 2 - 1;
normal.z
= sqrt(1 - saturate(dot(normal.xy, normal.xy)));
return
normal;
}
inline
fixed3 UnpackNormal(fixed4 packednormal)
{
#if
defined(UNITY_NO_DXT5nm)
return
packednormal.xyz * 2 - 1;
#else
return
UnpackNormalDXT5nm(packednormal);
#endif
}
|
可以看见不同的压缩格式解析有一定的区别,上面讲解的不是很全面,想了解更多可以自行百度下。
2.4 用Surface Shader来实现下
[C#] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
Shader
"Custom/SurfaceShader"
{
Properties
{
_MainTex
( "Base
(RGB)" ,
2D) = "white"
{}
_BumpMap
( "Normalmap" ,
2D) = "bump"
{}
}
SubShader
{
Tags
{ "RenderType" = "Opaque"
}
LOD
250
CGPROGRAM
#pragma
surface surf Lambert noforwardadd
sampler2D
_MainTex;
sampler2D
_BumpMap;
struct
Input {
float2
uv_MainTex;
};
void
surf (Input IN, inout SurfaceOutput o) {
fixed4
c = tex2D(_MainTex, IN.uv_MainTex);
o.Albedo
= c.rgb;
o.Alpha
= c.a;
o.Normal
= UnpackNormal(tex2D(_BumpMap, IN.uv_MainTex));
}
ENDCG
}
FallBack
Off
}
|
代码下载地址http://pan.baidu.com/s/1boCvoIz