来源:《UNITY SHADER入门精要》
**遮罩纹理(mask texture)**可以允许我们保护某些区域,使得它们免于某些修改。比如我们有一些模型的表面我们不希望有 镜面反射 或者漫反射,有些地方希望光照影响强一点,有些地方希望弱一点。这就需要用到遮罩纹理。
使用遮罩纹理的流程一般是:通过采样得到遮罩纹理的纹素值,然后使用其中某个(或某几个)通道的值(例如texl.r)来与某种表面属性进行相乘,这样,当该通道的值为0时,可以保护表面不受该属性的影响。总而言之,使用遮罩纹理可以让美术人员更加精准(像素级别)地控制模型表面的各种性质。
我们的例子的遮罩纹理中的每个RBG是一样的,因为实际上我们只需要一个通道的信息就足够了。这也说明,我们有一部分的空间被浪费了。实际的游戏制作中,我们往往会充分利用遮罩纹理中的每一个颜色通道来存储不同的表面属性。比如:高光反射的强度可以存储在 R 通道中,高光反射的指数存储在 B 通道中,边缘光照的强度存储在 G 通道中,自发光强度存储在 A 通道中。
Shader "Unity Shaders Book/Chapter 7/Mask Texture" {
Properties {
_Color ("Color Tint", Color) = (1, 1, 1, 1)
_MainTex ("Main Tex", 2D) = "white" {}
_BumpMap ("Normal Map", 2D) = "bump" {}
_BumpScale("Bump Scale", Float) = 1.0
_SpecularMask ("Specular Mask", 2D) = "white" {}
_SpecularScale ("Specular Scale", Float) = 1.0
_Specular ("Specular", Color) = (1, 1, 1, 1)
_Gloss ("Gloss", Range(8.0, 256)) = 20
}
属性中的 SpecularMask
即是我们需要使用的高光反射遮罩纹理, _SpecularScale
则是用于控制遮罩影响度的系数。
SubShader {
Pass {
Tags { "LightMode"="ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _BumpMap;
float _BumpScale;
sampler2D _SpecularMask;
float _SpecularScale;
fixed4 _Specular;
float _Gloss;
定义和对应我们需要的变量,我们需要 _MianTex
主纹理、_BumpMap
法线纹理、SpecularMask
遮罩纹理。他们的偏移属性共同都使用的 _MainTex_ST
。
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 tangent : TANGENT;
float4 texcoord : TEXCOORD0;
};
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float3 lightDir: TEXCOORD1;
float3 viewDir : TEXCOORD2;
};
因为有用到法线纹理的缘故,肯定是需要增加法线、切线这些变量的。
v2f vert(a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv.xy = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
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 tangentLightDir = normalize(i.lightDir);
fixed3 tangentViewDir = normalize(i.viewDir);
fixed3 tangentNormal = UnpackNormal(tex2D(_BumpMap, i.uv));
tangentNormal.xy *= _BumpScale;
tangentNormal.z = sqrt(1.0 - 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, tangentLightDir));
fixed3 halfDir = normalize(tangentLightDir + tangentViewDir);
// Get the mask value
fixed specularMask = tex2D(_SpecularMask, i.uv).r * _SpecularScale;
// Compute specular term with the specular mask
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(tangentNormal, halfDir)), _Gloss) * specularMask;
return fixed4(ambient + diffuse + specular, 1.0);
}
片元着色器中,我们会用到遮罩纹理,用以控制模型表面的高光反射强度。
因为我们这里的遮罩纹理只打算影响高光反射,所以,只有从第 16 行开始,才有变化。在 _SpecularMask
中,用 uv 采样得到它的 RGB 值,但是我们只取用它的 r 值,因为这里例子中的遮罩纹理中的rgb分量都是一样的。得到的值与 _SpacularScale
相乘。一起控制高光反射的强度。
ENDCG
}
}
FallBack "Specular"
}