UnityShader13:渐变与遮罩

前置:UnityShader9:光照基础实例

一、渐变纹理

渐变纹理目的非常简单:改变漫反射颜色

一般来讲渐变纹理是一维的,可以理解为一条带有颜色的线,线上每一点的颜色代表者对应光照强度下物体应该显示的颜色或色调:

如果使用半兰伯特光照,其光照强度范围正好被映射到了 [0, 1],而纹理的uv坐标范围也是[0, 1],这样采样方式正是光照强度 = uv坐标

代码很简单,和前面实现镜面光照的代码几乎一模一样,就是在计算漫反射光照时多了一个纹理采样:

Shader "Jaihk662/RampShader1"
{
    Properties
    {
        _DiffuseColor ("DiffuseColor", Color) = (1.0, 1.0, 1.0, 1.0)
        _SpecularColor ("SpecularColor", Color) = (1.0, 1.0, 1.0, 1.0)
        _RampTex ("RampTex", 2D) = "white" {}
        _Gloss ("Gloss", Range(8.0, 256)) = 20
    }
    SubShader
    {
        LOD 200
        PASS 
        {
            Tags { "LightMode" = "ForwardBase" }
 
            CGPROGRAM
            #pragma vertex vert             //声明顶点着色器的函数
            #pragma fragment frag           //声明片段着色器的函数
            #include "UnityCG.cginc"
            #include "Lighting.cginc"
 
            fixed4 _DiffuseColor;
            fixed4 _SpecularColor;
            float _Gloss;
            sampler2D _RampTex;

            float4 _RampTex_ST;
            struct _2vert 
            {
                float4 vertex: POSITION;
                float3 normal: NORMAL;
                float4 texcoord: TEXCOORD0;
            };
            struct vert2frag 
            {
                float4 pos: SV_POSITION;
                float3 wPos: TEXCOORD0;
                float3 wNormal: TEXCOORD1;
                float2 uv: TEXCOORD2;
            };
 
            vert2frag vert(_2vert v) 
            {
                vert2frag o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.wNormal = normalize(mul(v.normal, (float3x3)unity_WorldToObject));
                o.wPos = mul(unity_ObjectToWorld, v.vertex).xyz;
                o.uv = TRANSFORM_TEX(v.texcoord, _RampTex);
                return o;
            }
            fixed4 frag(vert2frag i): SV_Target 
            {
                fixed3 wLightDir = normalize(UnityWorldSpaceLightDir(i.wPos));
                fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.wPos));

                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
                float uvSampler = 0.5 * dot(i.wNormal, wLightDir) + 0.5;
                fixed3 diffuse = _LightColor0.rgb * _DiffuseColor.rgb * tex2D(_RampTex, float2(uvSampler, uvSampler));      //因为渐变纹理是一维的,所以采样可以直接u=v
                
                fixed3 reflectDir = normalize(reflect(-wLightDir, i.wNormal));
                fixed3 specular = _LightColor0.rgb * _SpecularColor.rgb * pow(saturate(dot(reflectDir, viewDir)), _Gloss);
 
                return fixed4(ambient + diffuse + specular, 1.0); 
            } 
            ENDCG
        }
    }
    FallBack "Specular"
}

一个需要注意的点:

在计算时我们使用光照强度作为uv坐标,虽然它们的范围都是[0,1],但是在计算光照强度时,可能由于浮点数误差得到1.0001这样的值,它已经超过了[0,1]的范围,这个时候如果纹理环绕方式是 Repeat 而不是 Clamp,就会出现 1.0001 被映射到了 0.0001 从而得到了完全错误的采样结果

也会有下面这样的渐变纹理,可以看出它的纹理颜色并不是“渐变”的,这种往往用于实现一些非常简单的表面阴影效果

扫描二维码关注公众号,回复: 12476322 查看本文章

二、遮罩纹理

上面的渐变纹理是作用于漫反射光照的,而遮罩纹理一般是作用于镜面光照的,遮罩纹理目的更简单:逐像素控制镜面光照的强度,按照一定比例进行弱化

遮罩纹理也有一个特点:那就是它每个像素只需要一个值表示弱化程度就可以了,因此 rgb 三个值往往都相同,这就意味着:

  • 遮罩图和高度图一样,是张黑白图
  • 为了避免颜色通道被浪费,可以拿别的颜色通道去做其它事情,包括但不限于同时控制其它光照的强度

直接在上一章的基础上改,计算部分只需要改动两行就好了,非常好理解:

Shader "Jaihk662/NormalShader2"
{
    Properties
    {
        _DiffuseColor ("DiffuseColor", Color) = (1.0, 1.0, 1.0, 1.0)
        _SpecularColor ("SpecularColor", Color) = (1.0, 1.0, 1.0, 1.0)
        
        _MainTex ("MainTex", 2D) = "white" {}
        _NormalMap ("NormalMap", 2D) = "bump" {}        //bump为模型自带法线信息
        _NormalScale ("NormalScale", float) = 1.0       //控制法线贴图展现的凹凸效果,0~1
        _SpecularMask ("SpecularMask", 2D) = "white" {}
        _SpecularMaskScale ("SpecularMaskScale", float) = 1.0
        _Gloss ("Gloss", Range(8.0, 256)) = 20
    }
    SubShader
    {
        LOD 200
        PASS 
        {
            Tags { "LightMode" = "ForwardBase" }

            CGPROGRAM
            #pragma vertex vert             //声明顶点着色器的函数
            #pragma fragment frag           //声明片段着色器的函数
            #include "UnityCG.cginc"
            #include "Lighting.cginc"
 
            fixed4 _DiffuseColor;
            fixed4 _SpecularColor;
            sampler2D _MainTex;
            float _Gloss;
            sampler2D _NormalMap;
            float _NormalScale;
            sampler2D _SpecularMask;
            float _SpecularMaskScale;

            float4 _MainTex_ST;         //三张贴图用同一个纹理属性变量(ST缩放偏移)

            struct _2vert 
            {
                float4 vertex: POSITION;
                float3 normal: NORMAL;
                float4 tangent: TANGENT;                //targent.w表示副切线的方向
                float4 texcoord: TEXCOORD0;
            };
            struct vert2frag 
            {
                float4 pos: SV_POSITION;
                float2 uv: TEXCOORD0;
                float4 TtoW1: TEXCOORD1;
                float4 TtoW2: TEXCOORD2;
                float4 TtoW3: TEXCOORD3;
                //float3 wPos: TEXCOORD4;               //不需要了,把wPos的三个向量xyz分别放入TtoW1、TtoW2、TtoW3的第四个参数w中
            };
 
            vert2frag vert(_2vert v) 
            {
                vert2frag o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.uv = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw; 
                
                float3 wPos = mul(unity_ObjectToWorld, v.vertex).xyz;
                float3 wNormal = UnityObjectToWorldNormal(v.normal);
                float3 wTangent = UnityObjectToWorldDir(v.tangent);
                float3 wBinormal = cross(wNormal, wTangent) * v.tangent.w;
                o.TtoW1 = float4(wTangent.x, wBinormal.x, wNormal.x, wPos.x);
                o.TtoW2 = float4(wTangent.y, wBinormal.y, wNormal.y, wPos.y);
                o.TtoW3 = float4(wTangent.z, wBinormal.z, wNormal.z, wPos.z);
                return o;
            }
            fixed4 frag(vert2frag i): SV_Target 
            {
                float3 wPos = float3(i.TtoW1.w, i.TtoW2.w, i.TtoW3.w);
                fixed3 normal = UnpackNormal(tex2D(_NormalMap, i.uv));       //内置函数UnpackNormal自动帮我们将法线向量由[0,1]映射到[-1,1]
                normal.xy *= _NormalScale;
                normal.z = sqrt(1.0 - saturate(dot(normal.xy, normal.xy)));     //反正是标准化过的单位向量,可以自己由xy算出z的值
                normal = normalize(half3(dot(i.TtoW1.xyz, normal), dot(i.TtoW2.xyz, normal), dot(i.TtoW3.xyz, normal)));

                fixed3 wLightDir = normalize(UnityWorldSpaceLightDir(wPos));        //同等与normalize(_WorldSpaceLightPos0.xyz);
                fixed3 wViewDir = normalize(UnityWorldSpaceViewDir(wPos));          //同等于normalize(UnityWorldSpaceViewDir(i.wPos));
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * tex2D(_MainTex, i.uv).rgb * _DiffuseColor.rgb;
                fixed3 diffuse = _LightColor0.rgb * tex2D(_MainTex, i.uv).rgb * _DiffuseColor.rgb * saturate(dot(normal, wLightDir));
                
                fixed3 reflectDir = normalize(reflect(-wLightDir, normal));
                fixed specularMask = tex2D(_SpecularMask, i.uv).g * _SpecularMaskScale;
                fixed3 specular = specularMask * _LightColor0.rgb * _SpecularColor.rgb * pow(saturate(dot(reflectDir, wViewDir)), _Gloss);

                return fixed4(ambient + diffuse + specular, 1.0); 
            } 
            ENDCG
        }
    }
    FallBack "Specular"
}

猜你喜欢

转载自blog.csdn.net/Jaihk662/article/details/111701631