Files
2026-02-21 16:45:37 +08:00

423 lines
14 KiB
C#

using System;
using System.Threading;
using UnityEngine;
public class WaterRipples : MonoBehaviour
{
[Range(20f, 200f)]
public int UpdateFPS = 30;
public bool Multithreading = true;
public int DisplacementResolution = 128;
public float Damping = 0.005f;
[Range(0.0001f, 2f)]
public float Speed = 1.5f;
public bool UseSmoothWaves;
public bool UseProjectedWaves;
public Texture2D CutOutTexture;
private Transform t;
private float textureColorMultiplier = 10f;
private Texture2D displacementTexture;
private Vector2[,] waveAcceleration;
private Color[] col;
private Vector3[] wavePoints;
private Vector3 scaleBounds;
private float inversedDamping;
private float[] cutOutTextureGray;
private bool cutOutTextureInitialized;
private Thread thread;
private bool canUpdate = true;
private double threadDeltaTime;
private DateTime oldDateTime;
private Vector2 movedObjPos;
private Vector2 projectorPosition;
private Vector4 _GAmplitude;
private Vector4 _GFrequency;
private Vector4 _GSteepness;
private Vector4 _GSpeed;
private Vector4 _GDirectionAB;
private Vector4 _GDirectionCD;
private void OnEnable()
{
canUpdate = true;
Shader.EnableKeyword("editor_off");
Shader.EnableKeyword("ripples_on");
Renderer component = GetComponent<Renderer>();
_GAmplitude = component.sharedMaterial.GetVector("_GAmplitude");
_GFrequency = component.sharedMaterial.GetVector("_GFrequency");
_GSteepness = component.sharedMaterial.GetVector("_GSteepness");
_GSpeed = component.sharedMaterial.GetVector("_GSpeed");
_GDirectionAB = component.sharedMaterial.GetVector("_GDirectionAB");
_GDirectionCD = component.sharedMaterial.GetVector("_GDirectionCD");
t = base.transform;
scaleBounds = GetComponent<MeshRenderer>().bounds.size;
InitializeRipple();
if (Multithreading)
{
thread = new Thread(UpdateRipples);
thread.Start();
}
}
public Vector3 GetOffsetByPosition(Vector3 position)
{
Vector3 result = GerstnerOffset4(new Vector2(position.x, position.z), _GSteepness, _GAmplitude, _GFrequency, _GSpeed, _GDirectionAB, _GDirectionCD);
result.y += GetTextureHeightByPosition(position.x, position.z);
result.y += t.position.y;
return result;
}
public void CreateRippleByPosition(Vector3 position, float velocity)
{
position.x += scaleBounds.x / 2f - t.position.x;
position.z += scaleBounds.z / 2f - t.position.z;
position.x /= scaleBounds.x;
position.z /= scaleBounds.z;
position.x *= DisplacementResolution;
position.z *= DisplacementResolution;
SetRippleTexture((int)position.x, (int)position.z, velocity);
}
private void InitializeRipple()
{
inversedDamping = 1f - Damping;
displacementTexture = new Texture2D(DisplacementResolution, DisplacementResolution, TextureFormat.RGBA32, false);
displacementTexture.wrapMode = TextureWrapMode.Clamp;
displacementTexture.filterMode = FilterMode.Bilinear;
Shader.SetGlobalTexture("_WaterDisplacementTexture", displacementTexture);
wavePoints = new Vector3[DisplacementResolution * DisplacementResolution];
col = new Color[DisplacementResolution * DisplacementResolution];
waveAcceleration = new Vector2[DisplacementResolution, DisplacementResolution];
for (int i = 0; i < DisplacementResolution * DisplacementResolution; i++)
{
col[i] = new Color(0f, 0f, 0f);
wavePoints[i] = new Vector3(0f, 0f);
}
for (int j = 0; j < DisplacementResolution; j++)
{
for (int k = 0; k < DisplacementResolution; k++)
{
waveAcceleration[j, k] = new Vector2(0f, 0f);
}
}
if (CutOutTexture != null)
{
Texture2D texture2D = ScaleTexture(CutOutTexture, DisplacementResolution, DisplacementResolution);
Color[] pixels = texture2D.GetPixels();
cutOutTextureGray = new float[DisplacementResolution * DisplacementResolution];
for (int l = 0; l < pixels.Length; l++)
{
cutOutTextureGray[l] = pixels[l].r * 0.299f + pixels[l].g * 0.587f + pixels[l].b * 0.114f;
}
cutOutTextureInitialized = true;
}
}
private Texture2D ScaleTexture(Texture2D source, int targetWidth, int targetHeight)
{
Texture2D texture2D = new Texture2D(source.width, source.height, TextureFormat.ARGB32, false);
Color[] pixels = source.GetPixels();
texture2D.SetPixels(pixels);
TextureScale.Bilinear(texture2D, targetWidth, targetHeight);
texture2D.Apply();
return texture2D;
}
private void UpdateRipples()
{
oldDateTime = DateTime.UtcNow;
while (canUpdate)
{
threadDeltaTime = (DateTime.UtcNow - oldDateTime).TotalMilliseconds / 1000.0;
oldDateTime = DateTime.UtcNow;
int num = (int)((double)(1000f / (float)UpdateFPS) - threadDeltaTime);
if (num > 0)
{
Thread.Sleep(num);
}
RippleTextureRecalculate();
}
}
private void FixedUpdate()
{
if (!Multithreading)
{
RippleTextureRecalculate();
}
displacementTexture.SetPixels(col);
displacementTexture.Apply(false);
}
private void Update()
{
movedObjPos = new Vector2(t.position.x, t.position.z);
}
private void UpdateProjector()
{
int num = (int)((float)DisplacementResolution * movedObjPos.x / scaleBounds.x - projectorPosition.x);
int num2 = (int)((float)DisplacementResolution * movedObjPos.y / scaleBounds.z - projectorPosition.y);
projectorPosition.x += num;
projectorPosition.y += num2;
if (num == 0 && num2 == 0)
{
return;
}
if (num >= 0 && num2 >= 0)
{
for (int i = 1; i < DisplacementResolution; i++)
{
for (int j = 0; j < DisplacementResolution; j++)
{
if (i + num2 > 0 && i + num2 < DisplacementResolution && j + num > 0 && j + num < DisplacementResolution)
{
waveAcceleration[j, i] = waveAcceleration[j + num, i + num2];
wavePoints[j + i * DisplacementResolution] = wavePoints[j + num + (i + num2) * DisplacementResolution];
}
}
}
}
if (num >= 0 && num2 < 0)
{
for (int num3 = DisplacementResolution - 1; num3 >= 0; num3--)
{
for (int k = 0; k < DisplacementResolution; k++)
{
if (num3 + num2 > 0 && num3 + num2 < DisplacementResolution && k + num > 0 && k + num < DisplacementResolution)
{
waveAcceleration[k, num3] = waveAcceleration[k + num, num3 + num2];
wavePoints[k + num3 * DisplacementResolution] = wavePoints[k + num + (num3 + num2) * DisplacementResolution];
}
}
}
}
if (num < 0 && num2 >= 0)
{
for (int l = 0; l < DisplacementResolution; l++)
{
for (int num4 = DisplacementResolution - 1; num4 >= 0; num4--)
{
if (l + num2 > 0 && l + num2 < DisplacementResolution && num4 + num > 0 && num4 + num < DisplacementResolution)
{
waveAcceleration[num4, l] = waveAcceleration[num4 + num, l + num2];
wavePoints[num4 + l * DisplacementResolution] = wavePoints[num4 + num + (l + num2) * DisplacementResolution];
}
}
}
}
if (num < 0 && num2 < 0)
{
for (int num5 = DisplacementResolution - 1; num5 >= 0; num5--)
{
for (int num6 = DisplacementResolution - 1; num6 >= 0; num6--)
{
if (num5 + num2 > 0 && num5 + num2 < DisplacementResolution && num6 + num > 0 && num6 + num < DisplacementResolution)
{
waveAcceleration[num6, num5] = waveAcceleration[num6 + num, num5 + num2];
wavePoints[num6 + num5 * DisplacementResolution] = wavePoints[num6 + num + (num5 + num2) * DisplacementResolution];
}
}
}
}
Vector2 zero = Vector2.zero;
for (int m = 0; m < DisplacementResolution; m++)
{
waveAcceleration[0, m] = zero;
wavePoints[m * DisplacementResolution] = zero;
waveAcceleration[DisplacementResolution - 1, m] = zero;
wavePoints[DisplacementResolution - 1 + m * DisplacementResolution] = zero;
waveAcceleration[m, 0] = zero;
wavePoints[m] = zero;
waveAcceleration[m, DisplacementResolution - 1] = zero;
wavePoints[DisplacementResolution - 1 + m] = zero;
}
}
private void OnDestroy()
{
canUpdate = false;
}
private void OnDisable()
{
Shader.DisableKeyword("editor_off");
Shader.DisableKeyword("ripples_on");
canUpdate = false;
}
private void RippleTextureRecalculate()
{
if (UseProjectedWaves)
{
UpdateProjector();
}
int num = wavePoints.Length;
int num2 = DisplacementResolution + 1;
int num3 = DisplacementResolution - 2;
int num4 = num - (DisplacementResolution + 1);
for (int i = 0; i < num; i++)
{
if (i >= num2 && i < num4 && i % DisplacementResolution > 0)
{
int num5 = i % DisplacementResolution;
int num6 = i / DisplacementResolution;
float num7 = (wavePoints[i - 1].y + wavePoints[i + 1].y + wavePoints[i - DisplacementResolution].y + wavePoints[i + DisplacementResolution].y) / 4f;
waveAcceleration[num5, num6].y += num7 - waveAcceleration[num5, num6].x;
}
}
float num8 = Speed;
if (!Multithreading)
{
num8 *= Time.fixedDeltaTime * (float)UpdateFPS;
}
for (int j = 0; j < DisplacementResolution; j++)
{
for (int k = 0; k < DisplacementResolution; k++)
{
waveAcceleration[k, j].x += waveAcceleration[k, j].y * num8;
if (cutOutTextureInitialized)
{
waveAcceleration[k, j].x *= cutOutTextureGray[k + j * DisplacementResolution];
}
waveAcceleration[k, j].y *= inversedDamping;
waveAcceleration[k, j].x *= inversedDamping;
wavePoints[k + j * DisplacementResolution].y = waveAcceleration[k, j].x;
if (!UseSmoothWaves)
{
float num9 = waveAcceleration[k, j].x * textureColorMultiplier;
if (num9 >= 0f)
{
col[k + j * DisplacementResolution].r = num9;
}
else
{
col[k + j * DisplacementResolution].g = 0f - num9;
}
}
}
}
if (!UseSmoothWaves)
{
return;
}
for (int l = 2; l < num3; l++)
{
for (int m = 2; m < num3; m++)
{
float num9 = (wavePoints[m + l * DisplacementResolution - 2].y * 0.2f + wavePoints[m + l * DisplacementResolution - 1].y * 0.4f + wavePoints[m + l * DisplacementResolution].y * 0.6f + wavePoints[m + l * DisplacementResolution + 1].y * 0.4f + wavePoints[m + l * DisplacementResolution + 2].y * 0.2f) / 1.6f * textureColorMultiplier;
if (num9 >= 0f)
{
col[m + l * DisplacementResolution].r = num9;
}
else
{
col[m + l * DisplacementResolution].g = 0f - num9;
}
}
}
for (int n = 2; n < num3; n++)
{
for (int num10 = 2; num10 < num3; num10++)
{
float num9 = (wavePoints[num10 + n * DisplacementResolution - 2].y * 0.2f + wavePoints[num10 + n * DisplacementResolution - 1].y * 0.4f + wavePoints[num10 + n * DisplacementResolution].y * 0.6f + wavePoints[num10 + n * DisplacementResolution + 1].y * 0.4f + wavePoints[num10 + n * DisplacementResolution + 2].y * 0.2f) / 1.6f * textureColorMultiplier;
if (num9 >= 0f)
{
col[num10 + n * DisplacementResolution].r = num9;
}
else
{
col[num10 + n * DisplacementResolution].g = 0f - num9;
}
}
}
}
private void SetRippleTexture(int x, int y, float strength)
{
strength /= 100f;
if (x >= 2 && x < DisplacementResolution - 2 && y >= 2 && y < DisplacementResolution - 2)
{
waveAcceleration[x, y].y -= strength;
waveAcceleration[x + 1, y].y -= strength * 0.8f;
waveAcceleration[x - 1, y].y -= strength * 0.8f;
waveAcceleration[x, y + 1].y -= strength * 0.8f;
waveAcceleration[x, y - 1].y -= strength * 0.8f;
waveAcceleration[x + 1, y + 1].y -= strength * 0.7f;
waveAcceleration[x + 1, y - 1].y -= strength * 0.7f;
waveAcceleration[x - 1, y + 1].y -= strength * 0.7f;
waveAcceleration[x - 1, y - 1].y -= strength * 0.7f;
if (x >= 3 && x < DisplacementResolution - 3 && y >= 3 && y < DisplacementResolution - 3)
{
waveAcceleration[x + 2, y].y -= strength * 0.5f;
waveAcceleration[x - 2, y].y -= strength * 0.5f;
waveAcceleration[x, y + 2].y -= strength * 0.5f;
waveAcceleration[x, y - 2].y -= strength * 0.5f;
}
}
}
private float GetTextureHeightByPosition(float x, float y)
{
x /= scaleBounds.x;
y /= scaleBounds.y;
x *= (float)DisplacementResolution;
y *= (float)DisplacementResolution;
if (x >= (float)DisplacementResolution || y >= (float)DisplacementResolution || x < 0f || y < 0f)
{
return 0f;
}
return waveAcceleration[(int)x, (int)y].x * textureColorMultiplier;
}
private Vector3 GerstnerOffset4(Vector2 xzVtx, Vector4 _GSteepness, Vector4 _GAmplitude, Vector4 _GFrequency, Vector4 _GSpeed, Vector4 _GDirectionAB, Vector4 _GDirectionCD)
{
Vector3 result = default(Vector3);
float num = _GSteepness.x * _GAmplitude.x;
float num2 = _GSteepness.y * _GAmplitude.y;
Vector4 vector = new Vector4(num * _GDirectionAB.x, num * _GDirectionAB.y, num2 * _GDirectionAB.z, num2 * _GDirectionAB.w);
Vector4 vector2 = new Vector4(_GSteepness.z * _GAmplitude.z * _GDirectionCD.x, _GSteepness.z * _GAmplitude.z * _GDirectionCD.y, _GSteepness.w * _GAmplitude.w * _GDirectionCD.z, _GSteepness.w * _GAmplitude.w * _GDirectionCD.w);
float num3 = Vector2.Dot(new Vector2(_GDirectionAB.x, _GDirectionAB.y), xzVtx);
float num4 = Vector2.Dot(new Vector2(_GDirectionAB.z, _GDirectionAB.w), xzVtx);
float num5 = Vector2.Dot(new Vector2(_GDirectionCD.x, _GDirectionCD.y), xzVtx);
float num6 = Vector2.Dot(new Vector2(_GDirectionCD.z, _GDirectionCD.w), xzVtx);
Vector4 vector3 = new Vector4(num3 * _GFrequency.x, num4 * _GFrequency.y, num5 * _GFrequency.z, num6 * _GFrequency.w);
Vector4 vector4 = new Vector4(Time.time * _GSpeed.x % 6.2831f, Time.time * _GSpeed.y % 6.2831f, Time.time * _GSpeed.z % 6.2831f, Time.time * _GSpeed.w % 6.2831f);
Vector4 a = new Vector4(Mathf.Cos(vector3.x + vector4.x), Mathf.Cos(vector3.y + vector4.y), Mathf.Cos(vector3.z + vector4.z), Mathf.Cos(vector3.w + vector4.w));
Vector4 a2 = new Vector4(Mathf.Sin(vector3.x + vector4.x), Mathf.Sin(vector3.y + vector4.y), Mathf.Sin(vector3.z + vector4.z), Mathf.Sin(vector3.w + vector4.w));
result.x = Vector4.Dot(a, new Vector4(vector.x, vector.z, vector2.x, vector2.z));
result.z = Vector4.Dot(a, new Vector4(vector.y, vector.w, vector2.y, vector2.w));
result.y = Vector4.Dot(a2, _GAmplitude);
return result;
}
}