法线纹理
法线纹理中存储的就是表面的法线方向。由于法线方向的分量范围在[-1, 1],而像素的分量范围为[0, 1],因此我们需要做一个映射, 通常使用的映射就是:
这就要求,我们在Shader中对法线纹理进行纹理采样后,还需要对结果进行一次反映射的过程,以得到原先的法线方向。反映射的过程实际就是使用上面映射函数的逆函数:
在切线空间下进行光照计算
//纹理贴图
_MainTex("MainTex",2D)="white"{}
//法线贴图
_BumpMap("Normal Map",2D) = "bump"{}
//定义
sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _BumpMap;
float4 _BumpMap_ST;
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(normalize(v.normal),normalize(v.tangent.xyz)) * v.tangent.w;
//float3x3 rotation = float3x3(v.tangent.xyz,binormal,v.normal);
TANGENT_SPACE_ROTATION;
//求切线空间光源方向及视角方向
//我们使用Unity的内置函数ObjSpaceLightDir和ObjSpaceViewDir来得到模型空间下的光照和视角方向
//再利用变换矩阵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);
//UnpackNormal函数得到法线方向
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 * (max(0,dot(tangentLightDir,tangentNormal))*0.5 + 0.5);
//高光反射
fixed3 halfDir = normalize(tangentLightDir + tangentviewDir);
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0,dot(tangentNormal,halfDir)),_Gloss);
fixed3 color = ambient + diffuse + specular;
return fixed4(color,1);
}
在世界空间下进行光照计算
struct v2f
{
float4 vertex:SV_POSITION;
float4 uv:TEXCOORD0;
float4 TtiW0 : TEXCOORD1;
float4 TtiW1 : TEXCOORD2;
float4 TtiW2 : TEXCOORD3;
};
v2f vert (appdata_tan v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
//TRANSFORM_TEX将模型顶点的uv和Tiling、Offset两个变量进行运算,计算出实际显示用的定点uv。
o.uv.xy = TRANSFORM_TEX(v.texcoord,_MainTex);
o.uv.zw = TRANSFORM_TEX(v.texcoord,_BumpMap);
//计算世界坐标下的顶点位置,法线,切线,副法线
float3 worldPos = mul(unity_ObjectToWorld,v.vertex).xyz;
fixed3 worldNormal = UnityObjectToWorldNormal(v.normal);
fixed3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz);
fixed3 worldBinormal = cross(worldNormal,worldTangent) * v.tangent.w;
//按列摆放得到从切线空间到世界空间的变化矩阵
o.TtiW0 = float4(worldTangent.x,worldBinormal.x,worldNormal.x,worldPos.x);
o.TtiW1 = float4(worldTangent.y,worldBinormal.y,worldNormal.y,worldPos.y);
o.TtiW2 = float4(worldTangent.z,worldBinormal.z,worldNormal.z,worldPos.z);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
//求世界坐标
float3 worldPos =float3(i.TtiW0.w,i.TtiW1.w,i.TtiW2.w);
//计算世界空间下的光照和视角
fixed3 lightDir = normalize(UnityWorldSpaceLightDir(worldPos));
fixed3 viewDir = normalize(UnityWorldSpaceViewDir(worldPos));
//获得法线纹理
fixed4 packedNormal = tex2D(_BumpMap,i.uv.zw);
fixed3 tangentNormal = UnpackNormal(packedNormal);
tangentNormal.xy *= _BumpScale;
//切线空间法线转换到世界坐标
fixed3 worldNormal = normalize(float3(dot(i.TtiW0.xyz,tangentNormal),dot(i.TtiW1.xyz,tangentNormal),dot(i.TtiW2.xyz,tangentNormal)));
//环境光
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
fixed3 albedo = tex2D(_MainTex,i.uv.xy).rgb;
//漫反射
fixed3 diffuse = _LightColor0.rgb * albedo * _Diffuse.rgb * (max(0,dot(lightDir,tangentNormal))*0.5 + 0.5);
//高光反射
fixed3 halfDir = normalize(lightDir + viewDir);
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0,dot(tangentNormal,halfDir)),_Gloss);
fixed3 color = ambient + diffuse + specular;
return fixed4(color,1);
}
从视觉表现上, 在切线空间下和在世界空间下计算光照几乎没有任何差别。
渐变纹理
//漫反射
fixed3 worldLightDir = UnityWorldSpaceLightDir(i.worldPos);
fixed halfLambert = max(0,dot(worldLightDir,i.worldNormal))*0.5 + 0.5;
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * tex2D(_RampTex,fixed2(halfLambert,halfLambert));
使用的纹理依次是:
遮罩纹理(mask texture)
它非常有用,在很多商业游戏中都可以见到它的身影。那么什么是遮罩呢?简单来讲,遮罩允许我们可以保护某些区域,使它们免于某些修改。
例如,在之前的实现中,我们都是把高光反射应用到模型表面的所有地方,即所有的像素都使用同样大小的高光强度和高光指数。但有时,我们希望模型表面某些区域的反光强烈一些,而某些区域弱一些。为了得到更加细腻的效果,我们就可以使用一张遮 罩纹理来控制光照。另一种常见的应用是在制作地形材质时需要混合多张图片,例如表现草地的纹理、表现石子的纹理、表现裸露土地的纹理等,使用遮罩纹理可以控制如何混合这些纹理。