Unity shader 水波 ,模擬雨滴落的水波

前言

盡管已經(jīng)有很多關(guān)于水波的shader了,原理就是通過sin函數(shù)的波動,在圖片的uv上做手腳,從而實現(xiàn)波紋。對于多個波紋的處理并沒有很好地實現(xiàn)。本篇文章盡量以最簡潔的方式,為大家講解多個水波的實現(xiàn)過程。

實現(xiàn)原理

1.

對于水波而言, 他的波涌主要是靠sin來完成的。(如下圖)那么我們就可以通過sin(距離)來完成水的波動效果。

sin函數(shù)

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容