前言
盡管已經(jīng)有很多關(guān)于水波的shader了,原理就是通過sin函數(shù)的波動,在圖片的uv上做手腳,從而實現(xiàn)波紋。對于多個波紋的處理并沒有很好地實現(xiàn)。本篇文章盡量以最簡潔的方式,為大家講解多個水波的實現(xiàn)過程。
實現(xiàn)原理
1.
對于水波而言, 他的波涌主要是靠sin來完成的。(如下圖)那么我們就可以通過sin(距離)來完成水的波動效果。

2.
其次就是多點同時出現(xiàn)的效果(如下圖),假設(shè)一個shader同時只有一個水波出現(xiàn),呢么原理其實也很簡單,就是每一個UV點都計算出 到原點的距離,然后套用sin(距離)這個公式就能出現(xiàn)水波了,但是,一個小水面,可以同時出現(xiàn)好幾個水波(也是我們要實現(xiàn)的效果),這就比較麻煩了。

思路1,在cpu中做一個噪點圖,噪點圖里面存儲幾個水波產(chǎn)生的波紋。---能實現(xiàn),但是對cpu消耗太大,畢竟每次水波變化的時候都要將噪點圖更新一下。
思路2,通過在c#代碼中傳入不同的水波的初始點, 然后在Shader的算法中對每一個uv點做插值。
但是水波的點不是固定的,可能有一兩個,也有可能是七八個,所以對于Shader來說他不是一個固定的。要使用到for循環(huán)這種方式去動態(tài)更新。運氣比較好,我們的Shader在4.0版本之后也是支持for循環(huán)了。
https://answers.unity.com/questions/59563/for-loop-in-shader.html
效果展示


c#代碼
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
public class CreatNodes : MonoBehaviour
{
? ? public float _speed = 3;
? ? public float forward_speed= 0.006f;
? ? public float back_speed = 0.0045f;
? ? float max_dis = 1;
? ? public float width = 0.15f;
? ? /// <summary>
? ? /// 當(dāng)前的材質(zhì)
? ? /// </summary>
? ? private Material currentMaterial;
? ? public const int max_click_count = 10;
? ? //shader最大同時水波的數(shù)量是10,要修改請到Wave.shader里面相關(guān)代碼一起修改
? ? public Vector4[] uis = new Vector4[max_click_count];
? ? void Awake()
? ? {
? ? ? ? currentMaterial = transform.GetComponent<Renderer>().sharedMaterial;
? ? ? ? currentMaterial.SetVectorArray("_ArrayParams", uis);
? ? }
? ? private Ray ray;
? ? private RaycastHit hit;
? ? bool can_add;
? ? Vector3 vector3;
? ? 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)
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? //將物體坐標(biāo)轉(zhuǎn)換成uv坐標(biāo)
? ? ? ? ? ? ? ? ? ? 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))
? ? ? ? {
? ? ? ? ? ? // 主相機屏幕點轉(zhuǎn)換為射線
? ? ? ? ? ? ray = Camera.main.ScreenPointToRay(Input.mousePosition);
? ? ? ? ? ? //射線碰到了物體
? ? ? ? ? ? if (Physics.Raycast(ray, out hit))
? ? ? ? ? ? {
? ? ? ? ? ? ? ? if (hit.transform == transform)
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? vector3 = transform.InverseTransformPoint(hit.point);
? ? ? ? ? ? ? ? ? ? can_add = true;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
? ? }
}
Shader的代碼
Shader "Unlit/Wave"
{
? ? 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
? ? ? ? ? ? #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]標(biāo)簽,編譯的時候會把for展開成靜態(tài)代碼。相對于流式,運行時的性能會高很多。
結(jié)束
一開始我使用思路一的,但是發(fā)現(xiàn)走到后面發(fā)現(xiàn)卡的飛起,每一幀里面要遍歷圖片所有像素賦值,然后存下載交給GPU。如此之大費周章不是我的風(fēng)格。
通過測試得出第二種無論性能還是內(nèi)存都要比第一種好。所以還是要使用第二種。
這個是我臨時做的,有問題可以者加群問我哦:870332020