pixel = (normal + 1) / 2
normal = pixel * 2 -1
法线贴图存储rgba值 分别的取值范围是0~1
而法线的取值范围是-1 ~ 1
所以要经过上面的公式相互转换
ps:法线贴图上的坐标对应的是切线空间下的法线方向 而不是模型空间下的。
因为只是是模型的原点变了不同的模型中没个点的切线是唯一不变的。
shader完整代码:
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
Shader "Custom/NormalMapShader-Frag"
{
Properties
{
_Color("Color",Color) = (1,1,1,1)
_MainTex("Main Tex",2D) = "white"{}
//_Gloss("Gloss",Range(8,200)) = 10
_Specular("Specular",Color) = (1,1,1,1)
_NormalMap("NormalMap",2D) = "bump"{} //bump 代表使用自身的法线
_BumpScale("BumpScale",Float) = 1 //法线高度力度
}
SubShader
{
pass
{
Tags { "LightMode"="ForwardBase" }
CGPROGRAM
#include "Lighting.cginc"
#pragma vertex vert
#pragma fragment frag
fixed4 _Color;
sampler2D _MainTex;
sampler2D _NormalMap;
half _Gloss;
fixed3 _Specular;
float4 _MainTex_ST;
float4 _NormalMap_ST;
float _BumpScale;
struct a2v
{
float4 vertex:POSITION;
//切线空间的确定是通过法线和切线来确定的
float3 normal:NORMAL;
float4 tangent:TANGENT;//tanget.w是用来确定切线空间坐标轴的方向的
float2 texcoord:TEXCOORD;
};
struct v2f
{
float4 pos:SV_POSITION;
fixed3 worldNormalDir:color;
float4 worldVert:TEXCOORD1;
float4 uv:TEXCOORD2; //xy 用来存储MainTex的纹理坐标 zw用来存储NormalMap的纹理坐标
float3 tangentSpaceLightDir:TEXCOORD0; //切线空间下光的方向
};
v2f vert(a2v v) //这里参数的名字必须是v 因为在这个宏TANGENT_SPACE_ROTATION里会调用v里的法线和切线
{
v2f f;
f.pos = UnityObjectToClipPos(v.vertex);//UnityObjectToClipPos(i.vertex);
//o.worldNormalDir = mul(i.normal,(float3x3)unity_WorldToObject);
//o.worldNormalDir = UnityObjectToWorldNormal(i.normal);
f.worldVert = mul(unity_ObjectToWorld, v.vertex);
f.uv.xy = v.texcoord * _MainTex_ST.xy + _MainTex_ST.zw;
f.uv.zw = v.texcoord * _NormalMap_ST.xy + _NormalMap_ST.zw;
TANGENT_SPACE_ROTATION;//调用这个宏后会得到一个矩阵rotation 用来把模型空间下的方向转成切线空间下
f.tangentSpaceLightDir = mul(rotation, ObjSpaceLightDir(v.vertex));
return f;
}
//把所有跟法线方向有关的运算全都放在切线空间下
float4 frag(v2f f):SV_TARGET
{
//fixed3 worldNormalDir = mul(i.normal,(float3x3)unity_WorldToObject);
//使用法线贴图后不需要直接取模型的了
//fixed3 normalDir = normalize(i.worldNormalDir); //这个矩阵把一个方向从世界空间转换到模型空间 这样放在后面就是模型到世界了
fixed4 normalColor = tex2D(_NormalMap,f.uv.zw);
//(贴图类型被unity识别成NormalType后会被unity进行处理使用自己的方法会读取的不正确使用unity提供的UnpackNormal)
fixed3 tangentSpaceNormalDir = UnpackNormal(normalColor);
tangentSpaceNormalDir.xy = tangentSpaceNormalDir.xy * _BumpScale; //模型原法线和法线贴图的轴应该是一样的不需要改变
tangentSpaceNormalDir = normalize(tangentSpaceNormalDir);//normalize(normalColor.xyz * 2 - 1);//切线空间下的法线的方向
//fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz);//对于每个顶点来说光的位置就是光的方向,因为光是平行的。
fixed3 lightDir = normalize(f.tangentSpaceLightDir);
fixed3 halfLambert = dot(tangentSpaceNormalDir,lightDir) * 0.5 + 0.5; //半兰伯特光照公式 max(dot(normalDir,lightDir),0);
fixed3 texColor = tex2D(_MainTex,f.uv) * _Color;
fixed3 diffuse = _LightColor0.rgb * halfLambert * texColor;//取得漫反射的颜色
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb * texColor;//获取环境光
//fixed3 reflectDir = normalize(reflect(-lightDir,normalDir));//反射光方向
//fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.pos.xyz);//视野方向
fixed3 viewDir = normalize(UnityWorldSpaceViewDir(f.worldVert.xyz));
fixed3 halfDir = normalize(viewDir + lightDir);
//fixed3 specular = _LightColor0.rgb * _Specular * pow(max(dot(tangentSpaceNormalDir, halfDir) , 0) , _Gloss);
fixed3 tempColor = diffuse + ambient;
return fixed4(tempColor,1);
}
ENDCG
}
}
//FallBack "Diffuse"
}
效果: