32.渐变纹理基础实现

32.纹理-渐变纹理-基础实现


32.1 知识点

导入测试资源

在进行渐变纹理基础Shader开发之前,首先需要准备一些测试资源,例如机器人模型和渐变纹理。我们将使用这些资源进行渲染效果的测试。

渐变纹理基础Shader实现

关键步骤

在实现Shader时,我们需要关注以下几个关键步骤:

  1. 属性相关

    • 漫反射颜色
    • 渐变纹理
    • 高光反射颜色
    • 光泽度
  2. 结构体相关

    • 顶点着色器中传入:
      • 可以使用 UnityCG.cginc 中的 appdata_base,其中包含了我们需要的顶点、法线相关数据。
    • 片元着色器中传入:
      • 自定义一个结构体,其中包含裁剪空间下的坐标、世界空间下的顶点坐标、世界空间下的法线方向。
  3. 顶点着色器回调函数中

    • 顶点坐标从模型空间转裁剪空间。
    • 顶点坐标从模型空间转世界空间。
    • 法线从模型空间转世界空间。
  4. 片元着色器回调函数中

    • 计算光的方向。
    • 计算半兰伯特光照后半部分公式值:
      • diffuseColor = 光源的颜色 * 材质的漫反射颜色 * ((标准化后物体表面法线向量· 标准化后光源方向向量) * 0.5 + 0.5)
    • 利用该值从渐变纹理中取出对应颜色,参与漫反射光照计算,得到漫反射颜色:
      • diffuseColor = 光源的颜色 * 材质的漫反射颜色 * 渐变纹理中取出的颜色
    • 计算Blinn-Phong光照模型,其中的漫反射光照颜色使用上一步计算出来的颜色。

写出Shader骨架

Shader "Unlit/Lesson32_Gradient_Texture_Base"
{
    Properties
    {

    }
    SubShader
    {
        Pass
        {
            Tags
            {
                "LightMode" = "ForwardBase"
            }
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #include "Lighting.cginc"

            v2f vert(appdata_base appdata_base)
            {

            }

            fixed4 frag(v2f v2f) : SV_Target
            {

            }
            ENDCG
        }
    }
}

声明属性相关和属性映射

定义Shader中的属性,包括漫反射颜色、渐变纹理、高光反射颜色和光泽度:

Properties
{
    _MainColor("MainColor", Color) = (1,1,1,1) // 漫反射颜色
    _RampTex("RampTex", 2D) = ""{} // 渐变纹理
    _SpecularColor("SpecularColor", Color) = (1,1,1,1) // 高光反射颜色
    _SpecularNum("SpecularNum", Range(8, 256)) = 18 // 光泽度
}
fixed4 _MainColor; // 漫反射颜色
sampler2D _RampTex; // 渐变纹理
float4 _RampTex_ST; // 渐变纹理的缩放和偏移
fixed4 _SpecularColor; // 高光反射颜色
float _SpecularNum; // 光泽度

声明结构体

我们需要定义一个结构体,用于存储裁剪空间下的顶点坐标、世界空间下的顶点坐标以及法线方向:

struct v2f
{
    //裁剪空间下顶点坐标
    float4 pos:SV_POSITION;
    //世界空间下顶点坐标
    float3 worldPos:TEXCOORD0;
    //世界空间下法线
    float3 worldNormal:TEXCOORD1;
};

顶点着色器

在顶点着色器中,将顶点坐标从模型空间转换为裁剪空间,并将法线从模型空间转换为世界空间:

v2f vert(appdata_base appdata_base)
{
    v2f v2f;
    v2f.pos = UnityObjectToClipPos(appdata_base.vertex);
    v2f.worldPos = mul(unity_ObjectToWorld, appdata_base.vertex);
    v2f.worldNormal = UnityObjectToWorldNormal(appdata_base.normal);
    return v2f;
}

片元着色器

在片元着色器中,计算半兰伯特光照后半部分的值,利用该值从渐变纹理中取出颜色,计算漫反射光照,并加入高光反射计算:

fixed4 frag(v2f v2f) : SV_Target
{
    //光的方向
    float3 lightDir = normalize(_WorldSpaceLightPos0);

    //diffuseColor = 光源的颜色 * 材质的漫反射颜色 *((标准化后物体表面法线向量· 标准化后光源方向向量)* 0.5 + 0.5)

    //计算半兰伯特光照后半部分公式值  ((标准化后物体表面法线向量· 标准化后光源方向向量)* 0.5 + 0.5)
    fixed halfLambertNum = dot(normalize(v2f.worldNormal), lightDir) * 0.5 + 0.5;

    //diffuseColor = 漫反射颜色 = 光的颜色 * 漫反射颜色 * 渐变纹理中取出的颜色
    fixed3 diffuseColor = _LightColor0.rgb * _MainColor.rgb *
        tex2D(_RampTex, fixed2(halfLambertNum, halfLambertNum));

    //高光反射颜色
    
    //视角方向
    float3 viewDir = normalize(UnityWorldSpaceViewDir(v2f.worldPos));
    
    //半角向量
    float3 halfDir = normalize(lightDir + viewDir);

    //color = 光源颜色 * 材质高光反射颜色 * pow( max(0, dot(视角单位向量, 光的反射单位向量)), 光泽度 )
    fixed3 specularColor = _LightColor0.rgb * _SpecularColor.rgb * pow(max(0, dot(v2f.worldNormal, halfDir)), _SpecularNum);

    //布林方 公式
    //物体表面光照颜色 = 环境光颜色 + 兰伯特光照模型所得颜色 + Phong式高光反射光照模型所得颜色
    fixed3 color = UNITY_LIGHTMODEL_AMBIENT.rgb + diffuseColor + specularColor;

    return fixed4(color.rgb, 1);
}

创建材质并赋值给模型

将上面的Shader编译后,创建一个新的材质,将材质赋值给模型,进行效果测试。你将能够看到基于渐变纹理的半兰伯特光照和高光反射效果。



修改渐变纹理设置避免黑点出现

为了避免渐变纹理在接缝处出现黑点,我们需要将 Wrap Mode(循环模式)切换为 Clamp(拉伸模式):

  • 原因:浮点数计算可能存在误差,会出现超过1的值(例如1.00001)。如果使用 Repeat(重复模式),会舍弃整数部分,保留小数部分0.00001,这时对应的颜色会是渐变纹理最左边的值,因此会出现黑色。




32.2 知识点代码

Lesson32_纹理_渐变纹理_基础实现

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

public class Lesson32_纹理_渐变纹理_基础实现 : MonoBehaviour
{
    void Start()
    {
        #region 知识点一 导入测试资源

        #endregion

        #region 知识点二 渐变纹理基础Shader实现

        //关键点:
        //1.属性相关
        //  漫反射颜色
        //  渐变纹理
        //  高光反射颜色
        //  光泽度

        //2.结构体相关
        //  顶点着色器中传入:
        //  可以使用 UnityCG.cginc 中的 appdata_base
        //  其中包含了我们需要的顶点、法线相关数据
        //
        //  片元着色器中传入:
        //  自定义一个结构体
        //  其中包含 裁剪空间下坐标、世界空间下顶点坐标、世界空间下法线方向

        //3.顶点着色器回调函数中
        //  3-1 顶点坐标模型转裁剪
        //  3-2 顶点坐标模型转世界
        //  3-3 法线从模型转世界

        //4.片元着色器回调函数中
        //  4-1 计算光的方向
        //  4-2 计算半兰伯特光照后半部分公式值
        //      diffuseColor = 光源的颜色 * 材质的漫反射颜色 *((标准化后物体表面法线向量· 标准化后光源方向向量)* 0.5 + 0.5)
        //  4-3 利用该值从渐变纹理中取出对应颜色,参与漫反射光照计算 得出漫反射颜色
        //      diffuseColor = 光源的颜色 * 材质的漫反射颜色 * 渐变纹理中取出的颜色
        //  4-4 计算Blinn Phong光照模型,其中的漫反射光照颜色使用上一步计算出来的颜色

        #endregion

        #region 知识点三 修改渐变纹理设置 避免黑点出现

        //避免渐变纹理接缝处有黑点
        //我们需要将 Wrap Mode(循环模式)切换为 Clamp(拉伸模式)

        //出现黑点的原因是:
        //浮点数计算可能存在误差,会出现超过1的值(1.00001)
        //如果使用Repeat(重复模式),会舍弃整数部分,保留小数0.00001
        //这时对应的颜色会是最左边的值,因此会出现黑色

        #endregion
    }
}

Lesson32_Gradient_Texture_Base.shader

Shader "Unlit/Lesson32_Gradient_Texture_Base"
{
    Properties
    {
        _MainColor("MainColor", Color) = (1,1,1,1) // 漫反射颜色
        _RampTex("RampTex", 2D) = ""{}// 渐变纹理
        _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"

            fixed4 _MainColor; // 漫反射颜色
            sampler2D _RampTex; // 渐变纹理
            float4 _RampTex_ST; // 渐变纹理的缩放和偏移
            fixed4 _SpecularColor; // 高光反射颜色
            float _SpecularNum; // 光泽度

            struct v2f
            {
                //裁剪空间下顶点坐标
                float4 pos:SV_POSITION;
                //世界空间下顶点坐标
                float3 worldPos:TEXCOORD0;
                //世界空间下法线
                float3 worldNormal:TEXCOORD1;
            };

            v2f vert(appdata_base appdata_base)
            {
                v2f v2f;
                v2f.pos = UnityObjectToClipPos(appdata_base.vertex);
                v2f.worldPos = mul(unity_ObjectToWorld, appdata_base.vertex);
                v2f.worldNormal = UnityObjectToWorldNormal(appdata_base.normal);
                return v2f;
            }

            fixed4 frag(v2f v2f) : SV_Target
            {
                //光的方向
                float3 lightDir = normalize(_WorldSpaceLightPos0);

                //diffuseColor = 光源的颜色 * 材质的漫反射颜色 *((标准化后物体表面法线向量· 标准化后光源方向向量)* 0.5 + 0.5)

                //计算半兰伯特光照后半部分公式值  ((标准化后物体表面法线向量· 标准化后光源方向向量)* 0.5 + 0.5)
                fixed halfLambertNum = dot(normalize(v2f.worldNormal), lightDir) * 0.5 + 0.5;

                //diffuseColor = 漫反射颜色 = 光的颜色 * 漫反射颜色 * 渐变纹理中取出的颜色
                fixed3 diffuseColor = _LightColor0.rgb * _MainColor.rgb *
                    tex2D(_RampTex, fixed2(halfLambertNum, halfLambertNum));

                //高光反射颜色
                
                //视角方向
                float3 viewDir = normalize(UnityWorldSpaceViewDir(v2f.worldPos));
                
                //半角向量
                float3 halfDir = normalize(lightDir + viewDir);

                //color = 光源颜色 * 材质高光反射颜色 * pow( max(0, dot(视角单位向量, 光的反射单位向量)), 光泽度 )
                fixed3 specularColor = _LightColor0.rgb * _SpecularColor.rgb * pow(max(0, dot(v2f.worldNormal, halfDir)), _SpecularNum);

                //布林方 公式
                //物体表面光照颜色 = 环境光颜色 + 兰伯特光照模型所得颜色 + Phong式高光反射光照模型所得颜色
                fixed3 color = UNITY_LIGHTMODEL_AMBIENT.rgb + diffuseColor + specularColor;

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


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

×

喜欢就点赞,疼爱就打赏