纹理最初的目的就是使用一张图片来控制模型的外观。使用纹理映射技术,我们可以把一张图“黏”在模型表面,逐纹素的控制模型的颜色。
在Shader中,我们还需要为纹理类型的属性声明一个float4类型的变量_MainTex_ST。其中,_MainTex_ST的名字不是任意起的。在Unity中,我们需要使用纹理名_ST的方式来声明某个纹理的属性。其中,ST是缩放(scale)和平移(translation)的缩写。_MainTex_ST可以让我们得到该纹理的缩放和平移的值。_MainTex_ST.xy存储的是缩放值,_MainTex_ST.zw存储的是偏移值。
shader "custom/single Texture"{
//1.为了使用纹理,我们需要在Properties语义块中添加纹理属性。
Properties{
_Color("Color Tint",Color) = (1,1,1,1)
_MainTex("Main Tex",2D) = "white" {}
_Specular("Specular",Color) = (1,1,1,1)
_Gloss("Gloss",Range(8.0,256)) = 20
}
//2.在subshader语义块中定义了一个Pass语义块。这是因为顶点/片元着色器的代码需要写在Pass语义块,而非SubShader语义块中。而且,我们在Pass的第一行指明了该Pass的光照模式:
SubShader{
Pass{
Tags{ "LightMode" = "ForwardBase" }
//3.然后,使用CGPROGRAM和ENDCG来包围CG代码片,以定义最重要的顶点着色器和片元着色器代码。首先,我们使用#pragma指令来告诉Unity我们的顶点着色器和片元着色器叫什么。
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
//4.为了使用Unity内置的一些变量,还要包含Unity的内置文件Lighting.cginc
#include "Lighting.cginc"
//5.定义和Properties声明的属性类型相匹配的变量。
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _Specular;
float _Gloss;
//6.定义顶点着色器的输入和输出结构体(输出结构体同时也是片元着色器的输入结构体)
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
};
//此处与逐顶点光照的结构体不同
struct v2f {
float4 pos : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
float2 uv : TEXCOORD2;
};
//顶点着色器只需要计算世界空间下的法线方向和顶点坐标,并把他们传递给片元着色器。在顶点着色器中,我们使用纹理的属性值_MainTex_ST来对顶点纹理坐标进行变换,得到最终的纹理坐标。计算过程是,首先使用缩放属性_MainTex_ST.xy对顶点纹理坐标进行缩放,然后再使用偏移属性_MainTex_ST.zw对结果进行偏移,Unity提供了一个内置宏TRANSFORM_TEX来帮我们计算上述过程。TRANSFORM_TEX是在UnityCG.cginc定义的:
//define TRANSFORM_TEX(tex,name) (tex.xy * name##_ST.xy = name##_ST.zw) 它接受两个参数,第一个参数是顶点纹理坐标,第二个参数是纹理名,在它的实现中,将利用纹理名_ST的方式来计算变换后的纹理坐标。
v2f vert(a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
o.uv = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
return o;
}
//我们还需要实现片元着色器,并在计算漫反射是使用纹理中的纹素值:我们首先计算世界空间下的法线方向和光照方向。然后,使用CG的tex2D函数对纹理进行采样。它的第一个参数是需要被采样的纹理,第二个参数是一个float2类型的纹理坐标,它将返回计算得到的纹素值。我们使用采样结果和颜色属性_Color的乘积来作为材质的反射率albedo,并把它和环境光照想成得到环境光部分。随后,我们使用albedo来计算漫反射光照的结果,
fixed4 frag(v2f i) : SV_Target{
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
fixed3 albedo = tex2D(_MainTex, i.uv).rgb * _Color.rgb;
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));
fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
fixed3 halfDir = normalize(worldLightDir + viewDir);
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)), _Gloss);
return fixed4(ambient + diffuse + specular, 1.0);
}
ENDCG
}
}
Fallback "Specular"
}