概念
1.通过改变法线的方向,间接的影响光照
2.法线贴图是将法线的方向转换成color储存在贴图的每个像素里
3.法线的方向每个分量的值是-1-1,贴图的color值是0-1向量换算成color每个分量 (normal+1)/2,color换算成法线就 (pixel*2-1)
法线贴图储存法线的空间
既然法线贴图像素储存的是法线,那处于那个坐标空间呢
贴图里储存那个空间的法线都是可以的,为什么都是储存的是切线空间下的坐标呢
如果储存模型空间,发生了改变,那么法线信息就会错误,切线空间下就不同
储存在世界和模型空间的耦合性
想象一下如果贴图里存储的是世界空间下的法线,模型发生旋转,顶点的位置发生改变,顶点和贴图中的n位置绑定,n中储存的法线法向不变,就会出现错误,模型空间同理
代码示例
Shader "Unlit/012"
{
Properties
{
_MainTex("MainTex", 2D) = "white" {}
_BumpMap("Normal Map", 2D) = "bump" {}
_BumpScale("Bump Scale", float) = 1
_Diffuse("Diffuse", Color) = (1,1,1,1)
_Specular("Specular", Color) = (1,1,1,1)
_Gloss("Gloss", Range(1,256)) = 5
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _BumpMap;
float4 _BumpMap_ST;
float _BumpScale;
fixed4 _Diffuse;
fixed4 _Specular;
float _Gloss;
struct v2f
{
float4 vertex : SV_POSITION;
fixed3 lightDir: TEXCOORD0;
float3 viewDir: TEXCOORD1;
float2 uv : TEXCOORD2;
float2 normalUv : TEXCOORD3;
};
v2f vert (appdata_tan v)
{
v2f o;
//将顶点转到世界空间
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
o.normalUv = TRANSFORM_TEX(v.texcoord,_BumpMap);
//float3 binormal = cross(v.normal,v.tangent.xyz);
//构建模型空间到切线空间矩阵
//float3x3 rotation = float3x3(v.tangent.xyz, binormal,v.normal);
TANGENT_SPACE_ROTATION;
//求切线空间光源方向及视角方向
o.lightDir = mul(rotation, ObjSpaceLightDir(v.vertex)).xyz;
o.viewDir = mul(rotation, ObjSpaceViewDir(v.vertex)).xyz;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed3 tangentLightDir = normalize(i.lightDir);
fixed3 tangentviewDir = normalize(i.viewDir);
fixed4 packedNormal = tex2D(_BumpMap,i.normalUv);
//tangentNormal.xy = (packedNormal.xy * 2 - 1) * _BumpScale;
fixed3 tangentNormal = UnpackNormal(packedNormal);
tangentNormal.xy *= _BumpScale;
tangentNormal.z = sqrt(1 - saturate(dot(tangentNormal.xy,tangentNormal.xy)));
//环境光
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
fixed3 albedo = tex2D(_MainTex,i.uv).rgb;
//漫反射
fixed3 diffuse = _LightColor0.rgb * albedo * _Diffuse.rgb * (dot(tangentLightDir,tangentNormal)*0.5+0.5);
//高光反射
fixed3 halfDir = normalize(tangentLightDir + tangentviewDir);
fixed3 color = ambient + diffuse ;
return fixed4(color,1);
}
ENDCG
}
}
}
代码思路
漫反射的强度是根据光线方向,和法线夹角的cos计算的
从贴图解析法线方向,将从unityshder获取模型空间光线方向,将其转到切线空间。
通过获取的切线和光线方向计算每个片元color的强度
函数介绍
TANGENT_SPACE_ROTATION;
这个指令会构建一个从模型空间到切线空间的旋转矩阵,矩阵的name为rotation,和
float3 binormal = cross(v.normal,v.tangent.xyz);
float3x3 rotation = float3x3(v.tangent.xyz, binormal,v.normal);
是相同的 。
数学概念
sqrt(1 - dot(tangentNormal.xy,tangentNormal.xy));
解析的法线一定是一个单位向量,任意一个直角三角形的a方+b方=c方
c表示向量的模,1-a方=b方 b方的sqrt就=b的length
旋转矩阵
Ap=换算后的切线向量
Oc=模型空间到切线空间的偏移值
矩阵里的Xc,Yc,Zc表示模型空间X轴Y轴Z轴的单位向量
我这里讲的要是不太清除,可以去看看过渡矩阵和空间变换
UnpackNormal(packedNormal);
表示的是将像素的颜色值转成法线的方向