本系列文章由Aimar_Johnny编写,欢迎转载,转载请标明出处,谢谢。
http://blog.youkuaiyun.com/lzhq1982/article/details/78588206
1、遮罩纹理在游戏里应用很广,前几日我们的美术还让我写了个遮罩纹理的shader,用遮罩纹理的rgb通道分别显示草,石和土地的叠加效果。何为遮罩纹理呢,还拿我刚说的那个举例,为了实现土地上有石头和草的效果,如果简单处理,那就是美术自己画,麻烦不说,而且没办法灵活控制,所以遮罩纹理就大显神威了,草,石头和土地是三张普通纹理,如果我把土地放最下面,上面叠加上草的纹理,草的纹理上扣几个洞,这样下面的土就漏出来了,草上面再叠加上石头纹理,同样石头纹理再扣几个洞,那下面的草和土地也漏出来一部分,这样三者就能呈现出混合的效果了,那个这个扣洞的效果,我们就可以交给一个特殊的纹理来做,比如我拿一张纹理,分别用rgb通道来显示草,石头和土地,那这rgb就是三个洞,这个纹理就是遮罩纹理。
2、书上介绍使用遮罩纹理的流程说的非常好:通过采样得到遮罩纹理的纹素值,然后使用其中某个(或某几个)通道的值(rgb)来与某种表面属性进行相乘,这样,当该通道的值为0时,可以保护表面不受该属性的影响。可以让美术人员更加精准的控制模型表面的各种性质。
3、书上的例子是使用一张高光遮罩纹理,逐像素的控制模型表面的高光反射强度。先上图:
可以看出,遮罩纹理可以更加精细的控制光照细节。
4、直接上代码:
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
Shader "CustomShader/Texture/MaskShader"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_BumpTex ("BumpTex", 2D) = "white" {}
_MaskTex ("MaskTex", 2D) = "white" {}
_BumpScale ("BumpScale", Float) = 1.0
_MaskScale ("MaskScale", Float) = 1.0
_Color ("Color Tint", Color) = (1, 1, 1, 1)
_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"
sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _BumpTex;
float4 _BumpTex_ST;
sampler2D _MaskTex;
float4 _MaskTex_ST;
float _BumpScale;
float _MaskScale;
fixed4 _Color;
fixed4 _Specular;
float _Gloss;
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal : NORMAL;
float4 tangent : TANGENT;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
float3 lightDir : TEXCOORD1;
float3 viewDir : TEXCOORD2;
};
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
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 lightDir = normalize(i.lightDir);
fixed3 viewDir = normalize(i.viewDir);
fixed4 bumpColor = tex2D(_BumpTex, i.uv);
fixed3 tangentNormal = UnpackNormal(bumpColor);
tangentNormal.xy *= _BumpScale;
tangentNormal.z = sqrt(1 - saturate(dot(tangentNormal.xy, tangentNormal.xy)));
fixed3 albedo = tex2D(_MainTex, i.uv).rgb * _Color.rgb;
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(tangentNormal, lightDir));
fixed mask = tex2D(_MaskTex, i.uv).r * _MaskScale;
fixed3 halfDir = normalize(viewDir + lightDir);
fixed3 specular = _LightColor0.rgb * _Specular * pow(max(0, dot(tangentNormal, halfDir)), _Gloss) * mask;
return fixed4(ambient + diffuse + specular, 1.0);
}
ENDCG
}
}
FallBack "Specular"
}
5、我们看看上面的代码,其实去掉光照部分,剩下的代码很少。
首先代码的大部分是用法线纹理实现凹凸效果的部分,这部分不了解的可以看前面有关法线纹理的文章。这里用了在切线空间计算的方法,实则不可取,应该用世界空间计算的方法,但这里不是重点,我们剥离出法线纹理和光照传递计算的部分,重点看遮罩纹理的部分:
fixed mask = tex2D(_MaskTex, i.uv).r * _MaskScale;
遮罩纹理的变量是_MaskTex,上面代码就是从遮罩纹理中采样,并只利用了r通道,然后用_MaskScale对该值进行缩放。我们再看用到mask的地方:
fixed3 specular = _LightColor0.rgb * _Specular * pow(max(0, dot(tangentNormal, halfDir)), _Gloss) * mask;
这是处理高光的代码,与以往高光处理的唯一区别是后面乘了这个mask,你可以想想,如果mask是0,则相乘结果是0,也就是无高光,mask越大,高光越强,而这个mask是由遮罩纹理的r决定的,美术人员只需要控制这个遮罩纹理的r通道,就可以控制该物体各个部分的高光效果了,当然_MaskScale也可以间接影响。
5、遮罩纹理虽然叫遮罩,但其实是用纹理的rgba通道间接影响其他纹理的显示效果的一种手段,这点很重要。只要你想得到,遮罩纹理将会发生很大的作用。