前言
盡管已經有很多關于水波的shader了,原理就是通過sin函式的波動,在圖片的uv上做手腳,從而實作波紋,對于多個波紋的處理并沒有很好地實作,
本篇文章盡量以最簡潔的方式,為大家講解多個水波的實作程序,
實作原理
1.
對于水波而言, 他的波涌主要是靠sin來完成的,(如下圖)那么我們就可以通過sin(距離)來完成水的波動效果,
sin函式
2.
其次就是多點同時出現的效果,假設一個shader同時只有一個水波出現,呢么原理其實也很簡單,就是每一個UV點都計算出 到原點的距離,然后套用sin(距離)這個公式就能出現水波了,但是,一個小水面,可以同時出現好幾個水波,這就比較麻煩了,

多個水波
?
思路1,在cpu中做一個噪點圖,噪點圖里面存盤幾個水波產生的波紋,—能實作,但是對cpu消耗太大,畢竟每次水波變化的時候都要將噪點圖更新一下,
思路2,通過在c#代碼中傳入不同的水波的初始點, 然后在Shader的演算法中對每一個uv點做插值,
但是水波的點不是固定的,可能有一兩個,也有可能是七八個,所以對于Shader來說他不是一個固定的,要使用到for回圈這種方式去動態更新,運氣比較好,我們的Shader在4.0版本之后也是支持for回圈了,
?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>
/// 當前的材質
/// </summary>
private Material currentMaterial;
public const int max_click_count = 10;
//shader最大同時水波的數量是10,要修改請到Wave.shader里面相關代碼一起修改
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)
{
//將物體坐標轉換成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;
}
}
}
}
}
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
#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展開成靜態代碼,相對于流式,運行時的性能會高很多,
結束
一開始我使用思路一的,但是發現走到后面發現卡的飛起,每一幀里面要遍歷圖片所有像素賦值,然后存下載交給GPU,如此之大費周章不是我的風格,
通過測驗得出第二種的性能要比第一種太很多,就用了第二種,
這個是我臨時做的,有問題可以者加群問我哦:870332020
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/328209.html
標籤:其他
下一篇:unity
