多层纹理
有时我们会需要给同一表面施加多层不同的纹理,这一情况在地形中常常出现,例如一个地形上同时会有泥土,岩石,草,花等不同纹理,此时,针对每一层纹理,我们都需要mask来表示该层纹理在地形上的分布。
如果给每一层纹理都附加一张mask图片,即使我们每张mask使用的都是灰度图,多张图片对系统依然是一个负担。在此,我们可以把每张灰度图作为一个通道,由此一张有4个通道(R,G,B,A)的图片就可以存储4张不同的mask。
每通道存储一个mask原理图解
下图为我们预期使用的4张不同的纹理
下图为我们的mask纹理,图示为各个通道的内容
我们需要先在属性段里添加5个2D贴图,分别为4张纹理以及1张mask纹理
Properties
{
_RTexture ("Red Channel Texture", 2D) = ""{}
_GTexture ("Green Channel Texture", 2D) = ""{}
_BTexture ("Blue Channel Texture", 2D) = ""{}
_ATexture ("Alpha Channel Texture", 2D) = ""{}
_MaskTexture ("Mask Texture", 2D) = ""{}
}
在SubShader块内的 CGPROGRAM 内再次声明这5个变量,
在 #pragma surface surf Lambert 后添加
sampler2D _RTexture;
sampler2D _GTexture;
sampler2D _BTexture;
sampler2D _ATexture;
sampler2D _MaskTexture;
再修改 surface 函数,在其中分别使用mask纹理的 R,G,B,A 通道来控制4张贴图。
将 surface 函数修改为
void surf (Input IN, inout SurfaceOutput o) {
float4 rTexData = tex2D(_RTexture, IN.uv_MaskTexture);
float4 gTexData = tex2D(_GTexture, IN.uv_MaskTexture);
float4 bTexData = tex2D(_BTexture, IN.uv_MaskTexture);
float4 aTexData = tex2D(_ATexture, IN.uv_MaskTexture);
float4 maskData = tex2D(_MaskTexture, IN.uv_MaskTexture);
float4 finalColor;
finalColor = lerp(float4(1,1,1,1), rTexData, maskData.r);
finalColor = lerp(finalColor, gTexData, maskData.g);
finalColor = lerp(finalColor, bTexData, maskData.b);
finalColor = lerp(finalColor, aTexData, maskData.a);
finalColor.a = 1.0;
finalColor = saturate(finalColor);
o.Albedo = finalColor.rgb;
o.Alpha = finalColor.a;
}
最终实现的效果为
最终的多层纹理 Shader 为
Shader "HineNotes/CookbookCh_02/rgbaMask" {
Properties {
_RTexture ("Red Channel Texture", 2D) = ""{}
_GTexture ("Green Channel Texture", 2D) = ""{}
_BTexture ("Blue Channel Texture", 2D) = ""{}
_ATexture ("Alpha Channel Texture", 2D) = ""{}
_MaskTexture ("Mask Texture", 2D) = ""{}
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
#pragma surface surf Lambert
sampler2D _RTexture;
sampler2D _GTexture;
sampler2D _BTexture;
sampler2D _ATexture;
sampler2D _MaskTexture;
struct Input {
float2 uv_MaskTexture;
};
void surf (Input IN, inout SurfaceOutput o) {
float4 rTexData = tex2D(_RTexture, IN.uv_MaskTexture);
float4 gTexData = tex2D(_GTexture, IN.uv_MaskTexture);
float4 bTexData = tex2D(_BTexture, IN.uv_MaskTexture);
float4 aTexData = tex2D(_ATexture, IN.uv_MaskTexture);
float4 maskData = tex2D(_MaskTexture, IN.uv_MaskTexture);
float4 finalColor;
finalColor = lerp(float4(1,1,1,1), rTexData, maskData.r);
finalColor = lerp(finalColor, gTexData, maskData.g);
finalColor = lerp(finalColor, bTexData, maskData.b);
finalColor = lerp(finalColor, aTexData, maskData.a);
finalColor.a = 1.0;
finalColor = saturate(finalColor);
o.Albedo = finalColor.rgb;
o.Alpha = finalColor.a;
}
ENDCG
}
FallBack "Diffuse"
}
在上面的 surf 函数中,我们用到了CGFX 标准库 中的内建函数 lerp()
函数 | 描述 |
---|---|
lerp(a,b,f) | 线性插值计算: ( 1 - f ) * a + b * f |
其中,a和b为相同类型的向量,或标量。f可以为标量,或与a、b类型匹配的向量 |
lerp函数的作用,即是在 a 与 b 之间利用 f 进行线性插值。举例来说,当 f = 0 时,插值结果为 a, f = 1 时, 插值结果为 b。
下图为 lerp() 函数的作用图解
在 shader 代码当中,我们首先将 R 贴图与白色利用 R mask 线性插值,再将其结果利用 B mask 与 B 贴图线性插值,如此反复对每个贴图与原先的结果进行线性插值,最终就得到了混合后的结果。