1. 渐变纹理
另外一种对于纹理的使用方式是通过渐变纹理为物体提供漫反射光照效果。
顾名思义,渐变纹理本身就是一张颜色渐变(可以是连续渐变,也可能是突变)的图片,这个渐变的过程模拟的就是光源从不同的角度照射物体的过程,因此对渐变纹理的采样通关不关心目标像素的位置信息,只关心光照角度。
通常来讲,渐变纹理都是左右不可以互相拼接的(像上图这样)。假设我们期望的采样点在纹理的最右侧(白色),但考虑到采样坐标的精度问题,实际的采样坐标可能为1.001,如果渐变纹理的 Wrap Mode 选择的是 Repeat,就会按照 0.001 进行采样,于是就会从渐变纹理的最左边进行采样(深红),从而导致一片白色中突然出现一点深红。因此,为了避免这种问题,渐变纹理的 Wrap Mode 需要选择为 Clamp。
如上图所示,我们在Shader中为物体设置了一张 MainTex 用于提供物体的基本颜色,然后设置了一张RampTex,用于根据光照角度提供漫反射颜色。
Shader如下:
Shader "MyShader/Chapter_7/Chapter_7_RampTexture"
{
Properties
{
_MainTex("MainTex", 2D) = "white"{}
_RampTex("RampTex", 2D) = ""{}
_Specular("Specular", Color) = (1,1,1,1)
_Gloss("Gloss", Range(1.0, 256.0)) = 16
}
SubShader
{
Pass
{
Tags {"LightMode" = "ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
struct a2v
{
float4 vertex : POSITION;
float3 normal : NORMAL;
float2 uv : TEXCOORD0;
};
struct v2f
{
float4 vertex : SV_POSITION;
float2 uv : TEXCOORD0;
float3 worldNormal : TEXCOORD1;
float3 worldPos : TEXCOORD2;
};
sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _RampTex;
fixed4 _Specular;
half _Gloss;
v2f vert(a2v v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
o.worldNormal = mul(v.normal, unity_WorldToObject);
o.worldPos = mul(unity_ObjectToWorld, v.vertex);
return o;
}
fixed4 frag(v2f i) : SV_Target
{
fixed3 _ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;
float3 _worldNormal = normalize(i.worldNormal);
float3 _worldLight = normalize(_WorldSpaceLightPos0.xyz);
fixed4 _albode = tex2D(_MainTex, i.uv);
float _halfLambert = 0.5 * dot(_worldNormal, _worldLight) + 0.5;
fixed4 _ramp = tex2D(_RampTex, _halfLambert.rr);
fixed3 _diffuse = _LightColor0.rgb * _albode.xyz * _ramp.rgb;
float3 _worldView = normalize(_WorldSpaceCameraPos.xyz - i.worldPos);
float3 _h = normalize(_worldView + _worldLight);
fixed3 _specular = _LightColor0.rgb * _Specular.xyz * pow(saturate(dot(_worldNormal, _h)) , _Gloss);
return fixed4(_ambient + _diffuse + _specular, 1);
}
ENDCG
}
}
}
效果如下:
2.遮罩纹理
所谓遮罩纹理,其实就是向Shader提供额外参数控制的一种手段。由于通过纹理采样可以覆盖到物体表面的每一个像素,因此我们可以通过将参数存储到纹理中为物体表面每一个像素提供更精细的控制。
比如在进行高光反射计算时,如果我们希望物体表面某些地方的高光强度更高,某些地方的高光强度更低,就可以将这个调整高光强度的系数存到一张纹理当中,在计算高光反射的最后乘以从纹理中采样得到的控制系数。
再比如我们在Shader 的 Properties 中设置了某些属性可以供使用者调整,但这个属性的值是对整个Shader生效的,或者说是对物体表面每一个像素生效的,就像之前Shader中常用的 _Gloss 属性,用于控制高光亮斑的大小,如果我们希望物体不同部分在计算高光反射时,分别按照不同的 _Gloss 值进行计算,就可以将需要的 _Gloss 值存到纹理中,在着色器中采样获得。
这个例子中,通过一张遮罩纹理的R通道为漫反射提供了一个额外的强度系数。
Shader如下:
Shader "MyShader/Chapter_7/Chapter_7_MaskTexture"
{
Properties
{
_MainTex("MainTex", 2D) = "white"{}
_BumpTex("BumpTex", 2D) = "bump"{}
_MaskTex("MaskTex", 2D) = ""{}
_MaskScale("MaskScale", float) = 5
_Specular("Specular", Color) = (1,1,1,1)
_Gloss("Gloss", Range(1.0, 256.0)) = 16
}
SubShader
{
Pass
{
Tags {"LightMode" = "ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
struct a2v
{
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 tangent : TANGENT;
float2 uv : TEXCOORD0;
};
struct v2f
{
float4 vertex : SV_POSITION;
float2 uv : TEXCOORD0;
float3 tangentView : TEXCOORD1;
float3 tangentLight : TEXCOORD2;
};
sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _BumpTex;
sampler2D _MaskTex;
half _MaskScale;
fixed4 _Specular;
half _Gloss;
v2f vert(a2v v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
TANGENT_SPACE_ROTATION;
o.tangentView = mul(rotation, ObjSpaceViewDir(v.vertex));
o.tangentLight = mul(rotation, ObjSpaceLightDir(v.vertex));
return o;
}
fixed4 frag(v2f i) : SV_Target
{
fixed3 _ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;
float3 _tangentLight = normalize(i.tangentLight);
fixed4 _packedNormal = tex2D(_BumpTex, i.uv);
float3 _tangentNormal = UnpackNormal(_packedNormal) ;
_tangentNormal.z = sqrt(1.0 - dot(_tangentNormal.xy, _tangentNormal.xy));
fixed4 _albode = tex2D(_MainTex, i.uv);
float _diffuseFix = tex2D(_MaskTex, i.uv).r * _MaskScale;
fixed3 _diffuse = _LightColor0.rgb * _albode.xyz * saturate(dot(_tangentNormal, _tangentLight)) * _diffuseFix;
float3 _tangentView = normalize(i.tangentView);
float3 _h = normalize(_tangentView + _tangentLight);
fixed3 _specular = _LightColor0.rgb * _Specular.xyz * pow(saturate(dot(_tangentNormal, _h)) , _Gloss);
return fixed4(_ambient + _diffuse + _specular, 1);
}
ENDCG
}
}
}
效果如下: