回顾我们之前写过的基础纹理贴图,我们设置了一个固定的“BaseColor”来控制材质球的基底颜色。那有没有一种可能,让我们的材质球,根据不同阴影部分或者顶点信息,来改变我们的基地颜色呢?——答案是渐变纹理
。
渐变纹理最早出现在Gooch等人在1998年发表的一片著名的论文《A Non-Photorealistic Lighting Model For Automatic Technical illustration》提出,作者提出了一种给予冷到暖色调(Cool-To-Warm Tones)的着色技术,来营造一种插画风格的渲染效果。
可以看出,上面三图是分别对于各自的渐变纹理进行的效果展示。最右侧的便是卡通风格的渐变纹理——该渐变纹理中的色点是突变的,且没有平滑的颜色过渡,以此来模拟卡通中的阴影色块。
对于纹理的选择,我们这里并不是单纯的使用三个颜色来直接更改我们的颜色过渡。而是使用纹理的形式(将在后面使用颜色调整来实现)。而我们也知道,图片纹理的类型Wrap Type可以分为如下几种:
对于我们的渐变纹理,这里建议使用Clamp,而不是Repeat。
因为在接下来的代码,我们会使用fixed2(halfLambert,halfLambert)
来对渐变纹理进行采样,但是使用Repeat将会超出[0,1]的范围,比如采样取值为1.0001后,经过[0,1]截取,就会变成0.0001,但是使用0.0001的值,将会得到纹理最左侧的颜色。而使用Clamp却不会发生这种事情。
Properties{
_Color("Base Color",Color) = (1,1,1,1)
_RampTex("Ramp Tex",2D) = "white"{ }
_Specular("Specular",Color) = (1,1,1,1)
_Gloss("Gloss",Range(8.0,256)) = 20
}
其中RampTex就是用来存放我们的渐变纹理的,原理与基础纹理是类似的。
SubShader{
Pass{
Tags{"LightMode"="ForwardBase"}
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Color;
sampler2D _RampTex;
float4 _RampTex_ST;
fixed4 _Specular;
float _Gloss;
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;
};
v2f vert(a2v v) {
v2f o;
//将形参的v得到的POSITION顶点信息,传入到v2f类型的o之中的pos作为片元位置信息
o.pos = UnityObjectToClipPos(v.vertex);
//将v的法线信息从物体坐标转换成世界左边传入到o的
o.worldNormal = UnityObjectToWorldNormal(v.normal);
//使用mul矩阵运算,将v的顶点信息,转换为世界坐标,然后将每个顶点的xyz信息存储到o的worldPos(TEXCOORD类型)之中
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
//使用Unity内置的TRANSFORM_TEX宏来计算经过平铺和偏移后的纹理坐标
o.uv = TRANSFORM_TEX(v.texcoord, _RampTex);
return o;
//处理完毕,将处理完的信息传入流水线下一流程——也就是传入frag之中v2f 类型的形参 i
}
fixed4 frag(v2f i) : SV_Target{
//用worldNormal存放世界坐标下的顶点方向的信息
fixed3 worldNormal = normalize(i.worldNormal);
//用worldLightDir存放世界坐标下的灯光方向的信息
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
//获得周围的光源信息的xyz位置坐标。这里是非常理想的环境下,因为我们只有一个平行光且没有Skybox的光,
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
//使用半兰伯特法,计算光照信息—— 0.5×( n · I ) + 0.5
fixed halfLambert = 0.5 * dot(worldNormal, worldLightDir) + 0.5;
fixed3 diffuseColor = tex2D(_RampTex, fixed2(halfLambert, halfLambert)).rgb * _Color.rgb;
fixed3 diffuse = _LightColor0.rgb * diffuseColor;
//视线 v
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);
}
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
Shader"LeonShader/Shader_7_3_RampTexture"{
Properties{
_Color("Base Color",Color) = (1,1,1,1)
_RampTex("Ramp Tex",2D) = "white"{ }
_Specular("Specular",Color) = (1,1,1,1)
_Gloss("Gloss",Range(8.0,256)) = 20
}
SubShader{
Pass{
Tags{"LightMode"="ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Color;
sampler2D _RampTex;
float4 _RampTex_ST;
fixed4 _Specular;
float _Gloss;
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;
};
v2f vert(a2v v) {
v2f o;
//将形参的v得到的POSITION顶点信息,传入到v2f类型的o之中的pos作为片元位置信息
o.pos = UnityObjectToClipPos(v.vertex);
//将v的法线信息从物体坐标转换成世界左边传入到o的
o.worldNormal = UnityObjectToWorldNormal(v.normal);
//使用mul矩阵运算,将v的顶点信息,转换为世界坐标,然后将每个顶点的xyz信息存储到o的worldPos(TEXCOORD类型)之中
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
//使用Unity内置的TRANSFORM_TEX宏来计算经过平铺和偏移后的纹理坐标
o.uv = TRANSFORM_TEX(v.texcoord, _RampTex);
return o;
//处理完毕,将处理完的信息传入流水线下一流程——也就是传入frag之中v2f 类型的形参 i
}
fixed4 frag(v2f i) : SV_Target{
//用worldNormal存放世界坐标下的顶点方向的信息
fixed3 worldNormal = normalize(i.worldNormal);
//用worldLightDir存放世界坐标下的灯光方向的信息
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
//获得周围的光源信息的xyz位置坐标。这里是非常理想的环境下,因为我们只有一个平行光且没有Skybox的光,
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
//使用半兰伯特法,计算光照信息—— 0.5×( n · I ) + 0.5
fixed halfLambert = 0.5 * dot(worldNormal, worldLightDir) + 0.5;
fixed3 diffuseColor = tex2D(_RampTex, fixed2(halfLambert, halfLambert)).rgb * _Color.rgb;
fixed3 diffuse = _LightColor0.rgb * diffuseColor;
//视线 v
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"
}