49.滚动的2D河流具体实现

49.动态效果-顶点动画-滚动的2D河流-具体实现


49.1 知识点

知识回顾

实现 2D 河流效果的关键公式:
某轴位置偏移量 = sin(_Time.y * 波动频率 + 顶点某轴坐标 * 波长的倒数) * 波动幅度

知识补充:关闭批处理

渲染标签设置

"DisableBatching" = "True"

主要作用

  • 是否对 SubShader 关闭批处理。
  • 在制作顶点动画时,可能需要关闭 Shader 的批处理:
    • 因为顶点动画经常使用模型空间下的数据。
    • 批处理会合并所有相关模型,导致模型空间数据丢失,无法正确应用。
  • 在实现 2D 河流效果时,需要让顶点在模型空间下偏移,因此需使用该标签关闭 Shader 的批处理。

导入资源 观察资源

导出测试用资源,观察资源模型空间轴向。该模型的模型空间坐标并不符合Unity轴向标准,它的上下是x轴 左右是z轴 前后是y轴。旋转他子对象让他符合标准

流动的 2D 河流效果:具体实现

主要步骤

  1. 新建 Shader 文件并清理掉无用代码,保留必要的框架结构。
  2. 声明属性和映射属性,包括:
    • 主纹理 (_MainTex)
    • 叠加颜色 (_Color)
    • 波动幅度 (_WaveAmplitude)
    • 波动频率 (_WaveFrequency)
    • 波长的倒数 (_InvWaveLength)
  3. 配置透明 Shader 的相关参数:
    • 渲染标签设置:
      Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "DisableBatching"="True"}
      
    • 深度写入与透明混合设置:
      ZWrite Off
      Blend SrcAlpha OneMinusSrcAlpha
      
  4. 定义结构体,包含顶点位置和 UV 坐标。
  5. 编写顶点着色器:
    • 使用理论公式计算指定轴的偏移量。
    • 注意偏移操作需在模型空间中进行。
  6. 编写片元着色器:
    • 对纹理颜色进行采样。
    • 将采样的颜色与叠加颜色进行融合并输出。

创建 Shader 骨架

Shader "Unlit/Lesson49_2DWater"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {

        Tags {  }

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;

            ENDCG
        }
    }
}

声明属性、映射属性包括主纹理(_MainTex),叠加的颜色(_Color),波动幅度(_WaveAmplitude),波动频率(_WaveFrequency)和波长的倒数(_InvWaveLength),也可以加上纹理移动速度,让纹理和顶点都动,效果更好

Properties
{
    _MainTex ("Texture", 2D) = "white" {}
    _Color("Color", Color) = (1,1,1,1)
    //波动幅度
    _WaveAmplitude("WaveAmplitude", Float) = 1
    //波动频率
    _WaveFrequency("WaveFrequency", Float) = 1
    //波长的倒数
    _InvWaveLength("InvWaveLength", Float) = 1

    //纹理变化速度
    _Speed("Speed", Float) = 1
}
// 2D 纹理采样器
sampler2D _MainTex;
// 纹理的缩放和平移信息
float4 _MainTex_ST;
// 颜色值,用于调整纹理颜色
float4 _Color;
// 波浪的振幅
float _WaveAmplitude;
// 波浪的频率
float _WaveFrequency;
// 波浪的倒数波长
float _InvWaveLength;
// 纹理移动速度
float _Speed;

设置透明Shader相关,设置相关渲染标签,关闭深度写入开启透明混合

// 透明 Shader 相关渲染标签 + 关闭批处理标签
Tags
{
    // 设置渲染类型为透明 因为可能会在水中悬浮
    "RenderType"="Transparent"
    // 设置渲染队列顺序为透明队列
    "Queue"="Transparent"
    // 忽略投影器
    "IgnoreProjector"="True"
    // 关闭批处理
    "DisableBatching"="True"
}

Pass
{
    // 关闭深度写入
    ZWrite Off
    // 设置混合模式为源颜色的透明度作为混合因子,目标颜色的(1 - 源颜色透明度)作为混合因子
    Blend SrcAlpha OneMinusSrcAlpha            
    //...
}

结构体无需改动,包括纹理坐标和裁剪坐标即可

struct v2f
{
    // 纹理坐标,用于传递给片元着色器进行纹理采样
    float2 uv : TEXCOORD0;
    // 顶点在裁剪空间中的位置
    float4 vertex : SV_POSITION;
};

顶点函数中,利用理论中讲解的公式,计算对应轴向偏移位置。注意,是在模型空间中偏移。为了效果更好可以加上时间对纹理的移动

// 顶点着色器函数
v2f vert(appdata_base appdata_base)
{
    v2f v2f;

    // 模型空间下的偏移位置
    float4 offset;

    // 让它在模型空间的 x 轴方向进行偏移 因为x是长的横的 基于预制体定的
    // 河流公式:某轴位置偏移量 = sin(_Time.y * 波动频率 + 顶点某轴坐标 * 波长的倒数) * 波动幅度
    offset.x = sin(_Time.y * _WaveFrequency + appdata_base.vertex.z * _InvWaveLength) * _WaveAmplitude;
    offset.yzw = float3(0, 0, 0);

    // 将顶点从模型空间转换到裁剪空间,并加上偏移量
    v2f.vertex = UnityObjectToClipPos(appdata_base.vertex + offset);

    // 计算纹理坐标,结合纹理的缩放和平移信息以及模型的原始纹理坐标
    v2f.uv = appdata_base.texcoord * _MainTex_ST.xy + _MainTex_ST.zw;

    // 根据时间和速度在垂直方向上移动纹理坐标
    v2f.uv += float2(0, _Time.y * _Speed);

    return v2f;
}

片元函数中采样乘上我们设置好的颜色并返回

// 片元着色器函数
fixed4 frag(v2f v2f) : SV_Target
{
    // 对纹理进行采样
    fixed4 color = tex2D(_MainTex, v2f.uv);
    
    // 将采样得到的颜色的 RGB 分量乘以指定的颜色值,实现颜色调整
    color.rgb *= _Color.rgb;
    
    return color;
}

创建材质赋值可以查看水流的效果


49.2 知识点代码

Lesson49_动态效果_顶点动画_滚动的2D河流_具体实现.cs

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

public class Lesson49_动态效果_顶点动画_滚动的2D河流_具体实现 : MonoBehaviour
{
    void Start()
    {
        #region 知识回顾

        //实现2D河流效果的关键公式:
        //某轴位置偏移量 = sin(_Time.y * 波动频率 + 顶点某轴坐标 * 波长的倒数) * 波动幅度

        #endregion

        #region 知识补充 关闭批处理

        //渲染标签
        //"DisableBatching" = "True"
        //主要作用:
        //是否对SubShader关闭批处理
        //我们在制作顶点动画时,有时需要关闭该Shader的批处理
        //因为我们在制作顶点动画时,有时需要使用模型空间下的数据
        //而批处理会合并所有相关的模型,这些模型各自的模型空间会丢失,导致我们无法正确使用模型空间下相关数据

        //在实现流程的2D河流效果时,我们就需要让顶点在模型空间下进行偏移
        //因此我们需要使用该标签,为该Shader关闭批处理

        #endregion

        #region 知识点一 导入资源 观察资源

        //1.导出测试用资源
        //2.观察资源模型空间轴向
        //  该模型的模型空间坐标并不符合Unity轴向标准
        //  它的上下是x轴 左右是z轴 前后是y轴

        #endregion

        #region 知识点二 流动的2D河流效果 具体实现

        //1.新建Shader,删除无用代码
        //2.声明属性、映射属性
        //  主纹理(_MainTex)
        //  叠加的颜色(_Color)
        //  波动幅度(_WaveAmplitude)
        //  波动频率(_WaveFrequency)
        //  波长的倒数(_InvWaveLength)
        //3.透明Shader相关
        //  渲染标签相关
        //  Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "DisableBatching"="True"}
        //  深度写入、透明混合相关
        //  ZWrite Off
        //  Blend SrcAlpha OneMinusSrcAlpha
        //4.结构体相关
        //  顶点和uv
        //5.顶点着色器
        //  利用理论中讲解的公式,计算对应轴向偏移位置
        //  注意,在模型空间中偏移
        //6.片元着色器
        //  直接进行颜色采样,颜色叠加

        #endregion
    }
}

Lesson49_2DWater.shader

Shader "Unlit/Lesson49_2DWater"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _Color("Color", Color) = (1,1,1,1)
        //波动幅度
        _WaveAmplitude("WaveAmplitude", Float) = 1
        //波动频率
        _WaveFrequency("WaveFrequency", Float) = 1
        //波长的倒数
        _InvWaveLength("InvWaveLength", Float) = 1

        //纹理变化速度
        _Speed("Speed", Float) = 1
    }
    SubShader
    {
        // 透明 Shader 相关渲染标签 + 关闭批处理标签
        Tags
        {
            // 设置渲染类型为透明 因为可能会在水中悬浮
            "RenderType"="Transparent"
            // 设置渲染队列顺序为透明队列
            "Queue"="Transparent"
            // 忽略投影器
            "IgnoreProjector"="True"
            // 关闭批处理
            "DisableBatching"="True"
        }

        Pass
        {
            // 关闭深度写入
            ZWrite Off
            // 设置混合模式为源颜色的透明度作为混合因子,目标颜色的(1 - 源颜色透明度)作为混合因子
            Blend SrcAlpha OneMinusSrcAlpha

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            // 2D 纹理采样器
            sampler2D _MainTex;
            // 纹理的缩放和平移信息
            float4 _MainTex_ST;
            // 颜色值,用于调整纹理颜色
            float4 _Color;
            // 波浪的振幅
            float _WaveAmplitude;
            // 波浪的频率
            float _WaveFrequency;
            // 波浪的倒数波长
            float _InvWaveLength;
            // 纹理移动速度
            float _Speed;


            struct v2f
            {
                // 纹理坐标,用于传递给片元着色器进行纹理采样
                float2 uv : TEXCOORD0;
                // 顶点在裁剪空间中的位置
                float4 vertex : SV_POSITION;
            };

            // 顶点着色器函数
            v2f vert(appdata_base appdata_base)
            {
                v2f v2f;

                // 模型空间下的偏移位置
                float4 offset;

                // 让它在模型空间的 x 轴方向进行偏移 因为x是长的横的 基于预制体定的
                // 河流公式:某轴位置偏移量 = sin(_Time.y * 波动频率 + 顶点某轴坐标 * 波长的倒数) * 波动幅度
                offset.x = sin(_Time.y * _WaveFrequency + appdata_base.vertex.z * _InvWaveLength) * _WaveAmplitude;
                offset.yzw = float3(0, 0, 0);

                // 将顶点从模型空间转换到裁剪空间,并加上偏移量
                v2f.vertex = UnityObjectToClipPos(appdata_base.vertex + offset);

                // 计算纹理坐标,结合纹理的缩放和平移信息以及模型的原始纹理坐标
                v2f.uv = appdata_base.texcoord * _MainTex_ST.xy + _MainTex_ST.zw;

                // 根据时间和速度在垂直方向上移动纹理坐标
                v2f.uv += float2(0, _Time.y * _Speed);

                return v2f;
            }

            // 片元着色器函数
            fixed4 frag(v2f v2f) : SV_Target
            {
                // 对纹理进行采样
                fixed4 color = tex2D(_MainTex, v2f.uv);
                
                // 将采样得到的颜色的 RGB 分量乘以指定的颜色值,实现颜色调整
                color.rgb *= _Color.rgb;
                
                return color;
            }
            ENDCG
        }
    }
}


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

×

喜欢就点赞,疼爱就打赏