一、引言
纹理最重要的目的就是控制模型的外观。贴图纹理改变模型每个像素的颜色、法线纹理通过改变物体法线实现模型表面凹凸效果、渐变纹理控制漫反射光照的结果实现自己想要的风格、遮罩纹理允许我们可以保护一些区域使他们免于修改。
在美术人员建模的时候,通常会在建模软件中利用纹理展开技术把纹理映射坐标存贮在每个顶点上,纹理映射坐标定义了该顶点在纹理中对应的2d 坐标。通常这些坐标用二维向量(u,v)来表示,其中u是横向坐标,v是纵向坐标。
二、贴图纹理
1.关键字与方法
_MainTex01("MainTex", 2D) = "white" {} //声明贴图属性,就可以在材质球上选择想要的贴图。
sampler2D _MainTex01; //常规声明贴图字段。
float4 _MainTex01_ST; //声明贴图的缩放和位移,_ST前面部分必须和贴图变量名一样
o.uv = TRANSFORM_TEX(v.texcoord,_MainTex01);//根据纹理缩放和偏移 计算UV
fixed3 albedo = tex2D(_MainTex01,i.uv).rgb; //使用uv进行纹理采样拿到贴图颜色。
2.示例代码
Shader "Unlit/BasicTexture"
{
Properties
{
//纹理贴图
_MainTex01("MainTex", 2D) = "white" {}
_Diffuse("Diffuse",Color) = (1,1,1,1)
_Specular("Specular",Color) = (1,1,1,1)
_Gloss("Gloss",Range(1,255)) = 3
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
//纹理贴图
sampler2D _MainTex01;
//材质球上对应的 纹理缩放和偏移
float4 _MainTex01_ST;
fixed4 _Diffuse;
fixed4 _Specular;
float _Gloss;
struct v2f
{
float3 worldNormal : TEXCOORD0;
float4 vertex : SV_POSITION;
float3 worldVertex : TEXCOORD1;
float2 uv : TEXCOORD2;
};
sampler2D _MainTex;
float4 _MainTex_ST;
v2f vert (appdata_base v)
{
v2f o;
//物体坐标转裁剪空间坐标
o.vertex = UnityObjectToClipPos(v.vertex);
//物体法线转世界坐标法线
o.worldNormal = UnityObjectToWorldNormal(v.normal);
//物体坐标转世界空间坐标
o.worldVertex = mul(unity_ObjectToWorld, v.vertex);
//根据纹理缩放和偏移 计算UV
o.uv = TRANSFORM_TEX(v.texcoord,_MainTex01);
return o;
}
fixed3 frag (v2f i) : SV_Target
{
//环境光
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
//纹理采样
fixed3 albedo = tex2D(_MainTex01,i.uv).rgb;
//世界坐标光源方向
fixed3 worldLight = UnityWorldSpaceLightDir(i.worldVertex);
//漫反射 点积为负为背光面
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * albedo * (dot(worldLight,i.worldNormal) * 0.5 + 0.5);
//反射光源方向
//fixed3 reflectDir = normalize(reflect(-worldLight,i.worldNormal));
//摄像机方向
fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldVertex));
//光源方向与摄像机方向的和
fixed3 halfDir = normalize(worldLight + viewDir);
//高光反射公式 BlinnPhong
fixed3 specular = _Specular.rgb * _LightColor0.rgb * pow(saturate(dot(i.worldNormal,halfDir)),_Gloss);
//最终颜色
fixed3 color = ambient + diffuse + specular;
return color;
}
ENDCG
}
}
}
三、法线纹理
1.关键字与方法
_BumpMap("Normal Map", 2D) = "bump" {} 声明法线贴图属性,就可以在材质球上选择想要的法线贴图。
_BumpScale("Bump Scale", float) = 1 //声明法线贴图的影响程度。
sampler2D _BumpMap;//常规声明法线贴图字段。
float4 _BumpMap_ST; //声明法线贴图的缩放和位移,_ST前面部分必须和法线贴图变量名一样
o.normalUV = TRANSFORM_TEX(v.texcoord, _BumpMap); //计算法线uv
fixed4 packedNormal = tex2D(_BumpMap,i.normalUV);//读取法线贴图
fixed3 tangentNormal = UnpackNormal(packedNormal); //将法线贴图设置成normal map 解压normal map
2.示例代码
Shader "Unlit/NormalMap01"
{
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;
float3 lightDir : TEXCOORD0;
float3 viewDir : TEXCOORD1;
float2 uv : TEXCOORD2;
float2 normalUV : TEXCOORD3;
};
v2f vert (appdata_tan v)
{
v2f o;
//模型坐标转裁剪坐标
o.vertex = UnityObjectToClipPos(v.vertex);
//获得uv坐标
o.uv = TRANSFORM_TEX(v.texcoord,_MainTex);
//读取法线贴图
o.normalUV = TRANSFORM_TEX(v.texcoord, _BumpMap);
//求副切向量
//float3 binormal = cross(v.normal,v.tangent.xyz) * v.tangent.w;
//求模型空间转切空间矩阵
//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);
//将法线贴图设置成normal map 解压normal map
fixed3 tangentNormal = UnpackNormal(packedNormal);
//设置法线强度
tangentNormal.xy *= _BumpScale;
//环境光
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
//贴图颜色
fixed3 albedo = tex2D(_MainTex,i.uv).rgb;
//漫反射
fixed3 diffuse = _LightColor0.rgb * albedo * _Diffuse.rgb * (dot(tangentNormal,tangentLightDir)*0.5 + 0.5);
//高光反射 半角向量
fixed3 halfDir = normalize(tangentLightDir + tangentviewDir);
//高光反射
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(tangentNormal,halfDir)),_Gloss);
fixed3 color = ambient + diffuse + specular;
return fixed4(color,1);
}
ENDCG
}
}
}
四、渐变纹理
、
1.原理
通过对法线方向和光照方向的点积做一次0.5倍的缩放和0.5大小的偏移来技术安半兰伯特部分的halfLambert。之后,我们使用halfLambert来构建一个纹理坐标,并用这个纹理坐标对渐变纹理进行采样。由于_RampTex实际就是一个一维纹理(它的纵坐标方向上颜色不变),因此纹理坐标u和v方向我们都使用halfLambert。然后把渐变纹理采样的到的颜色和材质颜色_Diffuse相乘,得到最终的漫反射颜色。
2.关键字与方法
_RampTex("MainTex", 2D) = "white" {}//声明渐变贴图属性,就可以在材质球上选择想要的渐变贴图。
sampler2D _RampTex; //声明渐变贴图的影响程度。
float4 _RampTex_ST; //声明渐变贴图的缩放和位移,_ST前面部分必须和渐变贴图变量名一样
o.uv = TRANSFORM_TEX(v.texcoord, _RampTex);//计算渐变uv
fixed halfLambert = dot(worldLightDir,i.worldNormal)*0.5+0.5;//计算半兰伯特值
fixed3 gradientCOlor = tex2D(_RampTex, fixed2(halfLambert,halfLambert)).rgb; //通过半兰伯特值获取渐变颜色
3.示例代码
Shader "Unlit/GradientTexture"
{
Properties
{
_RampTex("MainTex", 2D) = "white" {}
_Diffuse("Diffuse", Color) = (1,1,1,1)
_Specular("Specular", Color) = (1,1,1,1)
_Gloss("Gloss", Range(1,256)) = 5
}
SubShader
{
Tags {"LightMode"="ForwardBase" "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
sampler2D _RampTex;
float4 _RampTex_ST;
fixed4 _Diffuse;
fixed4 _Specular;
float _Gloss;
struct v2f
{
float4 vertex : SV_POSITION;
fixed3 worldNormal: TEXCOORD0;
float3 worldPos: TEXCOORD1;
float2 uv : TEXCOORD2;
};
v2f vert (appdata_base v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
fixed3 worldNormal = UnityObjectToWorldNormal( v.normal);
o.worldNormal = worldNormal;
o.worldPos = mul(unity_ObjectToWorld, v.vertex);
o.uv = TRANSFORM_TEX(v.texcoord, _RampTex);//v.texcoord.xy * _RampTex_ST.xy + _RampTex_ST.zw;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
//漫反射
fixed3 worldLightDir = UnityWorldSpaceLightDir(i.worldPos);
//计算半兰伯特值
fixed halfLambert = dot(worldLightDir,i.worldNormal)*0.5+0.5;
//渐变颜色
fixed3 gradientCOlor = tex2D(_RampTex, fixed2(halfLambert,halfLambert)).rgb;
//用半兰伯特值获取贴图是的颜色值
fixed3 diffuse = _LightColor0.rgb * gradientCOlor * _Diffuse.rgb;
//高光反射
//fixed3 reflectDir = normalize(reflect(-worldLightDir,i.worldNormal));
fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
//fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
fixed3 halfDir = normalize(worldLightDir + viewDir);
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(i.worldNormal,halfDir)),_Gloss);
fixed3 color = ambient + diffuse + specular;
return fixed4(color,1);
}
ENDCG
}
}
}
五、遮罩纹理
1.原理
在计算高光时,我们首先先对遮罩纹理_SpecularMask进行采样。选择遮罩纹理中像素的rgb分量中的一个,表明该点的高光放射强度,在这里我选择使用r分量计算掩码,然后我们用得到的掩码值和_SpecluarScale相乘,一起来控制高光反射的强度。
2.关键字和方法
_SpecularMask("Specular Mask",2D) = "white" {} //声明遮罩贴图属性,就可以在材质球上选择想要的遮罩贴图。
sampler2D _SpecularMask;//声明遮罩贴图的影响程度。
float4 _SpecularMask_ST;//声明遮罩贴图的缩放和位移,_ST前面部分必须和遮罩贴图变量名一样
fixed3 specularMask = tex2D(_SpecularMask,i.maskUV).r * _SpecularScale;//计算高光遮罩值
3.示例代码
Shader "Unlit/TextureMask"
{
Properties
{
_MainTex("MainTex", 2D) = "white" {}
_BumpMap("Normal Map", 2D) = "bump" {}
_BumpScale("Bump Scale", float) = 1
_SpecularMask("Specular Mask",2D) = "white" {}
_SpecularScale("Specular 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;
sampler2D _SpecularMask;
float4 _SpecularMask_ST;
float _SpecularScale;
float _BumpScale;
fixed4 _Diffuse;
fixed4 _Specular;
float _Gloss;
struct v2f
{
float4 vertex : SV_POSITION;
float4 uv : TEXCOORD0;
float4 TtiW0 : TEXCOORD1;
float4 TtiW1 : TEXCOORD2;
float4 TtiW2 : TEXCOORD3;
float2 maskUV : TEXCOORD4;
};
v2f vert (appdata_tan v)
{
v2f o;
//模型坐标转裁剪坐标
o.vertex = UnityObjectToClipPos(v.vertex);
//获得uv坐标
o.uv.xy = TRANSFORM_TEX(v.texcoord,_MainTex);
//获得法线贴图坐标
o.uv.zw = TRANSFORM_TEX(v.texcoord,_BumpMap);
//获取遮罩ui坐标
o.maskUV = TRANSFORM_TEX(v.texcoord,_SpecularMask);
//计算世界坐标下的顶点位置,法线,切线,副法线
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 * (dot(lightDir,worldNormal)*0.5+0.5);
//高光遮罩
fixed3 specularMask = tex2D(_SpecularMask,i.maskUV).r * _SpecularScale;
//高光反射
fixed3 halfDir = normalize(lightDir + viewDir);
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(worldNormal,halfDir)),_Gloss) * specularMask;
fixed3 color = ambient + diffuse + specular;
return fixed4(color,1);
}
ENDCG
}
}
}