35.遮罩纹理综合实现

35.纹理-遮罩纹理-综合实现


35.1 知识点

复用凹凸纹理切线空间下的法线纹理贴图Shader代码

  • 拷贝凹凸纹理切线空间下实现的代码,再进行修改
Properties
{
    _MainColor("MainColor", Color) = (1,1,1,1)//  漫反射颜色 
    _MainTex("MainTex", 2D) = ""{}//  单张纹理
    _BumpMap("BumpMap", 2D) = ""{}//  法线纹理
    _BumpScale("BumpScale", Range(0,1)) = 1//  凹凸程度
    _SpecularColor("SpecularColor", Color) = (1,1,1,1)//  高光反射颜色
    _SpecularNum("SpecularNum", Range(0,20)) = 18//  光泽度
}
SubShader
{
    Pass
    {
        Tags
        {
            "LightMode"="ForwardBase"
        }
        CGPROGRAM
        #pragma vertex vert
        #pragma fragment frag

        #include "UnityCG.cginc"
        #include "Lighting.cginc"


        float4 _MainColor; //漫反射颜色
        sampler2D _MainTex; //颜色纹理
        float4 _MainTex_ST; //颜色纹理的缩放和平移
        sampler2D _BumpMap; //法线纹理
        float4 _BumpMap_ST; //法线纹理的缩放和平移
        float _BumpScale; //凹凸程度
        float4 _SpecularColor; //高光颜色
        fixed _SpecularNum; //光泽度

        struct v2f
        {
            //裁剪空间下坐标
            float4 pos:SV_POSITION;


            //纹理uv

            //我们可以单独的声明两个float2的成员用于记录 颜色和法线纹理的uv坐标
            //float2 uvTex:TEXCOORD0;//颜色纹理
            //float2 uvBump:TEXCOORD1;//法线纹理

            //也可以直接声明一个float4的成员 xy用于记录颜色纹理的uv,zw用于记录法线纹理的uv
            float4 uv:TEXCOORD0; //纹理变量


            //光的方向 相对于切线空间下的
            float3 lightDir:TEXCOORD1;

            //视角的方向 相对于切线空间下的
            float3 viewDir:TEXCOORD2;
        };


        //  顶点着色器中传入:
        //  可以使用 UnityCG.cginc 中的 appdata_full
        //  其中包含了我们需要的顶点、法线、切线、纹理坐标相关数据
        v2f vert(appdata_full appdata_full)
        {
            v2f v2f;

            //把模型空间下的顶点转到裁剪空间下
            v2f.pos = UnityObjectToClipPos(appdata_full.vertex);

            //单张纹理和法线纹理 UV坐标缩放偏移计算
            v2f.uv.xy = appdata_full.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
            v2f.uv.zw = appdata_full.texcoord.xy * _BumpMap_ST.xy + _BumpMap_ST.zw;

            //在顶点着色器当中 得到 模型空间到切线空间的 转换矩阵
            //切线、副切线、法线
            //计算副切线 计算叉乘结果后 垂直与切线和法线的向量有两条 通过乘以 切线当中的w,就可以确定是哪一条(确定副切线方向)
            //cross是叉乘 appdata_full.tangent是切线 appdata_full.normal是法线 appdata_full.tangent.w代表叉乘结果方向
            float3 binormal = cross(normalize(appdata_full.tangent), normalize(appdata_full.normal)) * appdata_full.
                tangent.w;

            //构建模型空间到切线空间的变换矩阵
            float3x3 rotation = float3x3(appdata_full.tangent.xyz,
                                    binormal,
                                    appdata_full.normal);

            //模型空间下的光的方向
            //v2f.lightDir = ObjSpaceLightDir(appdata_full.vertex);

            //乘以模型空间到切线空间的转换矩阵 就可以得到切线空间下的 光的方向了
            v2f.lightDir = mul(rotation, ObjSpaceLightDir(appdata_full.vertex));

            //模型空间下的视角的方向
            //v2f.viewDir = ObjSpaceViewDir(appdata_full.vertex);

            //乘以模型空间到切线空间的转换矩阵 就可以得到切线空间下的 视角的方向了
            v2f.viewDir = mul(rotation, ObjSpaceViewDir(appdata_full.vertex));

            return v2f;
        }


        //  片元着色器中传入:
        //  自定义一个结构体
        //  其中包含 裁剪空间下坐标、uv坐标、光的方向、视角的方向
        fixed4 frag(v2f v2f) : SV_Target
        {
            //纹理采样函数tex2D
            //通过纹理采样函数 取出法线纹理贴图当中的数据
            float4 packedNormal = tex2D(_BumpMap, v2f.uv.zw);

            //利用内置的UnpackNormal函数对法线信息进行逆运算以及可能的解压
            //将我们取出来的法线数据 进行逆运算并且可能会进行解压缩的运算,最终得到切线空间下的法线数据
            float3 tangentNormal = UnpackNormal(packedNormal);
            
            //用得到的切线空间的法线数据 乘以 BumpScale 来控制凹凸程度
            //注意 tangentNormal不要进行单位化 直接用即可 因为乘了BumpScale就不是单位向量了
            tangentNormal *= _BumpScale;
            tangentNormal.z = sqrt(1.0 - saturate(dot(tangentNormal.xy, tangentNormal.xy)));

            //接下来就来处理 带颜色纹理的 布林方光照模型计算
            
            //颜色纹理和漫反射颜色的 叠加
            fixed3 albedo = tex2D(_MainTex, v2f.uv.xy) * _MainColor.rgb;
            
            //兰伯特漫反射颜色 = 光的颜色 * 漫反射材质的颜色 * max(0, dot(切线坐标系下的法线, 光的方向))
            fixed3 lambertColor = _LightColor0.rgb * albedo.rgb * max(0, dot(tangentNormal, normalize(v2f.lightDir)));
            
            //半角向量 = 视角方向 + 光的方向
            float3 halfA = normalize(normalize(v2f.viewDir) + normalize(v2f.lightDir));
            
            //高光反射的颜色 = 光的颜色 * 高光反射材质的颜色 * pow(max(0, dot(切线坐标系下的法线, 半角向量)), 光泽度)
            fixed3 specularColor = _LightColor0.rgb * _SpecularColor.rgb * pow(max(0, dot(tangentNormal, halfA)), _SpecularNum);
            
            //布林方光照颜色 = 环境光颜色 + 兰伯特漫反射颜色 + 高光反射的颜色
            fixed3 color = UNITY_LIGHTMODEL_AMBIENT.rgb * albedo + lambertColor + specularColor;

            return fixed4(color.rgb, 1);
        }
        ENDCG
    }
}

将高光遮罩纹理实现融入其中

主要步骤

  1. 从纹理中取出对应的遮罩掩码值(颜色的RGB值都可以使用)。
  2. 用该掩码值和遮罩系数相乘,得到遮罩值。
  3. 用该遮罩值和高光反射计算出来的颜色相乘。

声明高光遮罩纹理和遮罩系数的属性和属性映射

Properties
{
    _MainColor("MainColor", Color) = (1,1,1,1)
    _MainTex("MainTex", 2D) = ""{}
    _BumpMap("BumpMap", 2D) = ""{}
    _BumpScale("BumpScale", Range(0,1)) = 1
    
    _SpecularMask("SpecularMask", 2D) = ""{}//高光遮罩纹理
    _SpecularScale("SpecularScale", Float) = 1 //遮罩系数
    
    _SpecularColor("SpecularColor", Color) = (1,1,1,1)
    _SpecularNum("SpecularNum", Range(8,256)) = 18
}
float4 _MainColor; //漫反射颜色
sampler2D _MainTex; //颜色纹理
float4 _MainTex_ST; //颜色纹理的缩放和平移
sampler2D _BumpMap; //法线纹理
float4 _BumpMap_ST; //法线纹理的缩放和平移
float _BumpScale; //凹凸程度

sampler2D _SpecularMask; //高光遮罩纹理
float4 _SpecularMask_ST; //高光遮罩纹理的缩放和平移
float _SpecularScale; //遮罩系数

float4 _SpecularColor; //高光颜色
fixed _SpecularNum; //光泽度

修改片元函数

修改片元函数,去除遮罩掩码值乘以遮罩系数得到遮罩值后,让高光反射乘上遮罩值

fixed4 frag(v2f v2f) : SV_Target
{
    //通过纹理采样函数 取出法线纹理贴图当中的数据
    float4 packedNormal = tex2D(_BumpMap, v2f.uv.zw);

    //将我们取出来的法线数据 进行逆运算并且可能会进行解压缩的运算,最终得到切线空间下的法线数据
    float3 tangentNormal = UnpackNormal(packedNormal);

    //乘以凹凸程度的系数
    tangentNormal.xy *= _BumpScale;
    tangentNormal.z = sqrt(1.0 - saturate(dot(tangentNormal.xy, tangentNormal.xy)));

    //接下来就来处理 带颜色纹理的 布林方光照模型计算

    //颜色纹理和漫反射颜色的 叠加
    fixed3 albedo = tex2D(_MainTex, v2f.uv.xy) * _MainColor.rgb;

    //兰伯特
    fixed3 lambertColor = _LightColor0.rgb * albedo.rgb
        * max(0, dot(tangentNormal, normalize(v2f.lightDir)));

    //半角向量
    float3 halfA = normalize(normalize(v2f.viewDir) + normalize(v2f.lightDir));

    //1.从纹理中取出对应的遮罩掩码值(颜色的RGB值都可以使用)
    // fixed maskNum = tex2D(_SpecularMask, v2f.uv.xy).r;

    //2.用该掩码值和遮罩系数(我们自己定义的)相乘得到遮罩值
    // fixed specularMaskNum = maskNum * _SpecularScale;
    fixed specularMaskNum = tex2D(_SpecularMask, v2f.uv.xy).r * _SpecularScale;
    
    //3.用该遮罩值和高光反射计算出来的颜色相乘 * specularMaskNum
    //高光反射
    fixed3 specularColor = _LightColor0.rgb * _SpecularColor.rgb
        * pow(max(0, dot(tangentNormal, halfA)), _SpecularNum) * specularMaskNum;

    //布林方
    fixed3 color = UNITY_LIGHTMODEL_AMBIENT.rgb * albedo + lambertColor + specularColor;

    return fixed4(color.rgb, 1);
}

创建材质球,查看效果

创建材质球,赋值Road纹理。并复制凹凸纹理切线下的材质球赋值Road纹理,进行对比。可以明显看到遮罩纹理抑制了高光



遮罩纹理中的RGBA值

对于高光遮罩纹理中的 RGBA 值,是非常浪费的,因为我们只使用其中一个值就可以得到我们想要的数据。

因此对于遮罩纹理来说,我们可以合理地利用其中的每一个值来存储我们想要的数据。

随着以后的学习,我们可以在遮罩纹理当中存储更多信息,比如:

  • R 值代表高光遮罩数据;
  • G 值代表透明遮罩数据;
  • B 值代表特效遮罩数据;等等。

甚至可以用 n 张遮挡纹理存储 4×n 个会参与每个片元渲染计算的值。


35.2 知识点代码

Lesson35_纹理_遮罩纹理_综合实现

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Lesson35_纹理_遮罩纹理_综合实现 : MonoBehaviour
{
    void Start()
    {
        #region 知识点一 复用法线纹理贴图Shader代码(切线空间下的)

        #endregion

        #region 知识点二 将高光遮罩纹理实现融入其中

        //1.从纹理中取出对应的遮罩掩码值(颜色的RGB值都可以使用)
        //2.用该掩码值和遮罩系数(我们自己定义的)相乘得到遮罩值
        //3.用该遮罩值和高光反射计算出来的颜色相乘

        #endregion

        #region 知识点三 遮罩纹理中的RGBA值

        //对于高光遮罩纹理中的RGBA值,是非常浪费的
        //因为我们只使用其中一个值就可以得到我们想要的数据

        //因此对于遮罩纹理来说
        //我们可以合理的利用其中的每一个值来存储我们想要的数据

        //随着以后的学习
        //我们可以在遮罩纹理当中存储更多信息
        //比如:
        //R值代表高光遮罩数据
        //G值代表透明遮罩数据
        //B值代表特效遮罩数据
        //等等
        //甚至可以用 n 张遮挡纹理存储 4xn 个会参与 每个片元渲染计算的值

        #endregion
    }
}

Lesson35_Mask_Texture_Comprehensive.shader

Shader "Unlit/Lesson35_Mask_Texture_Comprehensive"
{
    Properties
    {
        _MainColor("MainColor", Color) = (1,1,1,1)
        _MainTex("MainTex", 2D) = ""{}
        _BumpMap("BumpMap", 2D) = ""{}
        _BumpScale("BumpScale", Range(0,1)) = 1

        _SpecularMask("SpecularMask", 2D) = ""{}//高光遮罩纹理
        _SpecularScale("SpecularScale", Float) = 1 //遮罩系数

        _SpecularColor("SpecularColor", Color) = (1,1,1,1)
        _SpecularNum("SpecularNum", Range(8,256)) = 18
    }
    SubShader
    {
        Pass
        {
            Tags
            {
                "LightMode"="ForwardBase"
            }
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"
            #include "Lighting.cginc"


            float4 _MainColor; //漫反射颜色
            sampler2D _MainTex; //颜色纹理
            float4 _MainTex_ST; //颜色纹理的缩放和平移
            sampler2D _BumpMap; //法线纹理
            float4 _BumpMap_ST; //法线纹理的缩放和平移
            float _BumpScale; //凹凸程度

            sampler2D _SpecularMask; //高光遮罩纹理
            float4 _SpecularMask_ST; //高光遮罩纹理的缩放和平移
            float _SpecularScale; //遮罩系数

            float4 _SpecularColor; //高光颜色
            fixed _SpecularNum; //光泽度

            struct v2f
            {
                float4 pos:SV_POSITION;
                //float2 uvTex:TEXCOORD0;
                //float2 uvBump:TEXCOORD1;
                //我们可以单独的声明两个float2的成员用于记录 颜色和法线纹理的uv坐标
                //也可以直接声明一个float4的成员 xy用于记录颜色纹理的uv,zw用于记录法线纹理的uv
                float4 uv:TEXCOORD0;
                //光的方向 相对于切线空间下的
                float3 lightDir:TEXCOORD1;
                //视角的方向 相对于切线空间下的
                float3 viewDir:TEXCOORD2;
            };


            v2f vert(appdata_full appdata_full)
            {
                v2f v2f;
                //把模型空间下的顶点转到裁剪空间下
                v2f.pos = UnityObjectToClipPos(appdata_full.vertex);
                //计算纹理的缩放偏移
                v2f.uv.xy = appdata_full.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
                v2f.uv.zw = appdata_full.texcoord.xy * _BumpMap_ST.xy + _BumpMap_ST.zw;

                //在顶点着色器当中 得到 模型空间到切线空间的 转换矩阵
                //切线、副切线、法线
                //计算副切线 计算叉乘结果后 垂直与切线和法线的向量有两条 通过乘以 切线当中的w,就可以确定是哪一条
                float3 binormal = cross(normalize(appdata_full.tangent), normalize(appdata_full.normal)) * appdata_full.
                    tangent.w;
                //转换矩阵
                float3x3 rotation = float3x3(appdata_full.tangent.xyz,
                                             binormal,
                                             appdata_full.normal);
                //模型空间下的光的方向
                //data.lightDir = ObjSpaceLightDir(v.vertex);
                //乘以模型空间到切线空间的转换矩阵 就可以得到切线空间下的 光的方向了
                v2f.lightDir = mul(rotation, ObjSpaceLightDir(appdata_full.vertex));

                //模型空间下的视角的方向
                //data.viewDir = ObjSpaceViewDir(v.vertex);
                v2f.viewDir = mul(rotation, ObjSpaceViewDir(appdata_full.vertex));

                return v2f;
            }

            fixed4 frag(v2f v2f) : SV_Target
            {
                //通过纹理采样函数 取出法线纹理贴图当中的数据
                float4 packedNormal = tex2D(_BumpMap, v2f.uv.zw);

                //将我们取出来的法线数据 进行逆运算并且可能会进行解压缩的运算,最终得到切线空间下的法线数据
                float3 tangentNormal = UnpackNormal(packedNormal);

                //乘以凹凸程度的系数
                tangentNormal.xy *= _BumpScale;
                tangentNormal.z = sqrt(1.0 - saturate(dot(tangentNormal.xy, tangentNormal.xy)));

                //接下来就来处理 带颜色纹理的 布林方光照模型计算

                //颜色纹理和漫反射颜色的 叠加
                fixed3 albedo = tex2D(_MainTex, v2f.uv.xy) * _MainColor.rgb;

                //兰伯特
                fixed3 lambertColor = _LightColor0.rgb * albedo.rgb
                    * max(0, dot(tangentNormal, normalize(v2f.lightDir)));

                //半角向量
                float3 halfA = normalize(normalize(v2f.viewDir) + normalize(v2f.lightDir));

                //1.从纹理中取出对应的遮罩掩码值(颜色的RGB值都可以使用)
                // fixed maskNum = tex2D(_SpecularMask, v2f.uv.xy).r;

                //2.用该掩码值和遮罩系数(我们自己定义的)相乘得到遮罩值
                // fixed specularMaskNum = maskNum * _SpecularScale;
                fixed specularMaskNum = tex2D(_SpecularMask, v2f.uv.xy).r * _SpecularScale;
                
                //3.用该遮罩值和高光反射计算出来的颜色相乘 * specularMaskNum
                //高光反射
                fixed3 specularColor = _LightColor0.rgb * _SpecularColor.rgb
                    * pow(max(0, dot(tangentNormal, halfA)), _SpecularNum) * specularMaskNum;

                //布林方
                fixed3 color = UNITY_LIGHTMODEL_AMBIENT.rgb * albedo + lambertColor + specularColor;

                return fixed4(color.rgb, 1);
            }
            ENDCG
        }
    }
}


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏