1 水波効果の原理
ウォーター ウェーブ エフェクトは、Unity3D の後処理の特殊効果です。その原理は、レンダリングされたテクスチャに対してローカルの押し出しとリフティング トランスフォームを実行することです。つまり、ローカルの UV 座標で周期的なオフセット移動を実行して波及効果を実現します。
1) 波形方程式
水波モデルを単純化するために、水波が機械波 (正弦波または余弦波) として伝播し、時間 0 での水面波形関数が次のようになると仮定します。
このうち、rは水波の中心からの粒子の半径長さ、w1は波形の角周波数(値が大きいほど波紋が密になる)、Aは粒子の水平振動の振幅です。 、offset はパーティクルの水平オフセットです。
2) 振動方程式
時間 t における、半径 r の粒子の水平オフセットは次のとおりです。
ここで、w2 は粒子の水平振動の角周波数です (値が大きいほど、粒子の振動は速くなります)。
この記事のコード リソースについては、Unity3D Water Wave Effectを参照してください。
2 コードの実装
WaterWaveEffect.cs
using UnityEngine;
[RequireComponent(typeof(Camera))] // 屏幕后处理特效一般都需要绑定在像机上
public class WaterWaveEffect : MonoBehaviour {
public float A = 0.01f; // 水面质点水平波动振幅
public float w1 = 60; // 水波截面波形角频率(值越大, 波纹越密)
public float w2 = 30; // 水面质点水平波动角频率(值越大, 水波质点振动越快)
public float waveWidth = 0.3f; // 水波宽度(水波在扩散时, 后波会消失)
public float waveSpeed = 0.3f; // 水波传播的速度
private float waveTime; // 水波传播时间
private Vector4 waveCenter; // 水波中心
private Material waveMaterial; // 水波材质
private bool enabledWave = false; // 水波开关
private void Awake() {
waveMaterial = new Material(Shader.Find("Custom/WaterWave"));
waveMaterial.hideFlags = HideFlags.DontSave;
}
private void Update() {
if (Input.GetMouseButton(0)) {
waveCenter = new Vector2(Input.mousePosition.x / Screen.width, Input.mousePosition.y / Screen.height); // 屏幕坐标归一化
enabledWave = true;
waveTime = 0;
}
}
void OnRenderImage (RenderTexture source, RenderTexture destination) {
if (enabledWave) {
SetWaveMaterialParams();
Graphics.Blit (source, destination, waveMaterial);
waveTime += Time.deltaTime;
if (waveTime > 2 / waveSpeed) { // 水波传播到屏幕外面, 结束水波特效
enabledWave = false;
}
} else {
Graphics.Blit (source, destination);
}
}
private void SetWaveMaterialParams() { // 设置水波材质参数
waveMaterial.SetFloat("_A", A); // 水面质点水平波动振幅
waveMaterial.SetFloat("_w1", w1); // 水波截面波形角频率(值越大, 波纹越密)
waveMaterial.SetFloat("_w2", w2); // 水面质点水平波动角频率(值越大, 水波质点振动越快)
waveMaterial.SetFloat("_t", waveTime); // 水波传播时间
waveMaterial.SetVector("_o", waveCenter); // 水波中心
waveMaterial.SetFloat("_waveDist", waveTime * waveSpeed); // 水波传播距离
waveMaterial.SetFloat("_waveWidth", waveWidth); // 水波宽度(水波在传播时, 后波会消失)
}
}
注: WaterWaveEffect スクリプト コンポーネントは、カメラの下に吊るす必要があります。
WaveShader.shader
Shader "Custom/WaterWave"
{
Properties
{
_MainTex ("mainTex", 2D) = "white" {}
}
SubShader
{
Pass
{
ZTest Always
Cull Off
ZWrite Off
Fog { Mode off }
CGPROGRAM
#pragma vertex vert_img // UnityCG.cginc中定义了vert_img方法, 对vertex和texcoord进行了处理, 输出v2f_img中的pos和uv
#pragma fragment frag
#pragma fragmentoption ARB_precision_hint_fastest
#include "UnityCG.cginc"
sampler2D _MainTex;
float _A; // 水面质点水平波动振幅
float _w1; // 水波截面波形角频率(值越大, 波纹越密)
float _w2; // 水面质点水平波动角频率(值越大, 水波质点振动越快)
float _t; // 水波传播时间
float2 _o; // 水波中心坐标
float _waveDist; // 水波传播距离
float _waveWidth; // 水波宽度(水波在传播时, 后波会消失)
fixed4 frag(v2f_img i) : SV_Target // 水波uv坐标的计算不能在顶点着色器中进行, 因为屏后处理的顶点只有屏幕的4个角顶点
{
float2 vec = i.uv - _o.xy;
vec.x *= _ScreenParams.x / _ScreenParams.y; // 按照屏幕长宽比进行缩放
float radius = length(vec); // 距离波中心的半径长度
float leng = abs(radius - _waveDist);
float offset = 0;
if (leng < _waveWidth)
{
offset = _A * sin(_w1 * radius - _w2 * _t) * (1 - leng / _waveWidth);
}
return tex2D(_MainTex, i.uv + offset * 0.707); // offset是一维的, uv是二维的, 需要除以根号2, 即乘以0.707
}
ENDCG
}
}
Fallback off
}
3 ランニング効果
画面上の任意の場所をクリックすると、水の波が次のように表示されます。地面と空の接合部の直線に注意してください。正弦波がはっきりと見えます。