Unity Shader 水波漣漪效果


正弦函數/正弦波 是最基礎的波形

在游戲中通常使用 正弦函數/正弦波 來逼近真實世界中的漣漪效果

漣漪效果

有了波形並不意味着就能產生漣漪的效果

往往還需要在畫面中添加折射、反射、扭曲等效果

看圖中的漣漪效果

之所以人眼看起來像漣漪

是因為在漣漪處的空間發生了輕微的扭曲,而 “空間扭曲”,也就是貼圖偏移

Shader

Shader "Effect/ripple"
{
    Properties
    {
        _MainTex("Texture", 2D) = "white" {}
        _Ctor("Ctor", float) = 84
        _timeCtor("timector",float) = 60
        _max_dis("maxdis",Range(0,1)) = 0.5
    }

    SubShader
    {
        Tags { "RenderType" = "Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_fog
            #pragma target 4.0

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                UNITY_FOG_COORDS(1)
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            float4 _ArrayParams[10];
            float _Ctor;
            float _timeCtor;
            float _max_dis;

            v2f vert(appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                UNITY_TRANSFER_FOG(o,o.vertex);
                return o;
            }

            fixed4 frag(v2f i) : SV_Target
            {
                float2 uv = float2(0,0);
                [unroll]
                for (int j = 0; j < 10; j++)
                {
                    float2 dv = float2(_ArrayParams[j].x,_ArrayParams[j].y) - i.uv;
                    float dis = sqrt(dv.x * dv.x + dv.y * dv.y);
                    float sinFactor = sin(dis * _Ctor + _Time.y * _timeCtor);
                    float2 dv1 = normalize(dv);
                    float2 offset = dv1 * sinFactor * max(0,_max_dis - dis) * step(dis,_ArrayParams[j].z) * step(_ArrayParams[j].w,dis);
                    uv += offset;
                }
                uv = i.uv + uv / 10;
                return tex2D(_MainTex, uv);
            }
            ENDCG
        }
    }
}

Shader 代碼里面使用了 for,上面打上 [unroll] 標簽,編譯的時候會把 for 展開成靜態代碼

通過在 C# 代碼中傳入不同水波漣漪的初始點

然后在 Shader 的算法中對每一個uv點做插值來實現多個的漣漪效果

using UnityEngine;

/// <summary>
/// 根據位置信息,在指定位置生成水波
/// </summary>
public class CreatNodes : MonoBehaviour
{
    // 水波參數
    public float _speed = 3;
    public float forward_speed = 0.006f;
    public float back_speed = 0.003f;
    public float width = 0.1f;

    // shader最多的水波數量是10(同時)
    public const int max_click_count = 10;
    public Vector4[] uis = new Vector4[max_click_count];

    // 最大距離
    private float max_dis = 1;
    // 當前材質球
    private Material currentMaterial;

    private Ray ray;
    private RaycastHit hit;
    private bool can_add;
    private Vector3 vector3;

    private void Awake()
    {
        currentMaterial = transform.GetComponent<Renderer>().sharedMaterial;
        currentMaterial.SetVectorArray("_ArrayParams", uis);
    }

    private void FixedUpdate()
    {
        for (int i = 0; i < uis.Length; i++)
        {
            if (uis[i].z > max_dis)
                uis[i].Set(0, 0, 0, 0);
            if (uis[i].x == 0 && uis[i].y == 0)
            {
                if (can_add)
                {
                    // 將物體坐標轉換成uv坐標
                    uis[i].x = vector3.x + 0.5f;
                    uis[i].y = vector3.y + 0.5f;
                    // 頭與尾巴的寬度
                    uis[i].z = width;
                    // 尾巴的開始點
                    uis[i].w = 0;
                    can_add = false;
                }
            }
            else
            {
                uis[i].z += forward_speed * _speed;
                uis[i].w += back_speed * _speed;
            }

        }
        currentMaterial.SetVectorArray("_ArrayParams", uis);
    }

    private void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            // 主相機屏幕點轉換為射線
            ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            // 射線碰撞檢測
            if (Physics.Raycast(ray, out hit))
            {
                if (hit.transform == transform)
                {
                    vector3 = transform.InverseTransformPoint(hit.point);
                    can_add = true;
                }
            }
        }
    }
}

通過修改 C# 腳本上的參數也能改變生成的漣漪效果

水波漣漪效果

 

 

 

 

 

👉 | Shader 水波漣漪效果原文 | 👈

👉 |  以上內容僅為學習參考、學習筆記使用  | 👈


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM