2
\$\begingroup\$

hopefully this isn't too difficult to answer, but I'm stuck and could at least use some input. I'm working on simulating water for the first time with basic wave functionality. As in I click at a point on the quad, and it will generate a ripple throughout the quad.

I've been following two tutorials:

  1. Mesh Deformation Tutorial
  2. GDC 2008 Water Simulation (Page 14)

The mesh deformation tutorial is done and works just as expected, giving me the ability to deform a mesh with a mouse click. Unfortunately, the GDC presentation isn't as clear cut as I would have liked. Implementing the code on page 14 doesn't seem to do much, and messing around with the algorithm produces some results that make me think I'm in the right direction.

Here's what I have so far:

void UpdateVertex(int i)
{
    // Handles deformation from mouse click
    Vector3 velocity = vertexVelocities[i]; // Velocity for given vertex
    displacedVertices[i] += velocity * Time.deltaTime; // Moves vertex based on velocity
    Vector3 displacement = displacedVertices[i] - originalVertices[i]; // Gets how much the vertex was moved
    displacement *= uniformScale; // Ensures forces act consistent across all scales
    velocity -= displacement * springForce * Time.deltaTime; // Applies spring force so vertex will bounce back to its original position
    velocity *= 1f - damping * Time.deltaTime; // Applies dampening value to vertex won't "explode"
    vertexVelocities[i] = velocity; // Updates vertex velocity
    displacedVertices[i] += velocity * (Time.deltaTime / uniformScale); // Applies velocity to vertex position based on scale


    // Supposed to handle wave-ripple effect
    nHeight = 0; // North Vertex Height
    eHeight = 0; // East Vertex Height
    sHeight = 0; // South Vertex Height
    wHeight = 0; // West Vertex Height

    // Simple boundary checks
    if (i + 100 >= 0 && i + 100 < 10000)
        nHeight = displacedVertices[i + 100].y;
    if (i - 100 >= 0 && i - 100 < 10000)
        sHeight = displacedVertices[i - 100].y;
    if (i + 1 >= 0 && i + 1 < 10000)
        eHeight = displacedVertices[i + 1].y;
    if (i - 1 >= 0 && i - 1 < 10000)
        wHeight = displacedVertices[i - 1].y;

    // Struggle to understand how this is supposed to work; water ripple algorithm
    float f = (waveSpeed * waveSpeed) * ((nHeight + sHeight + eHeight + wHeight) - 4f*displacedVertices[i].y) / 4f;
    float fdt = f * Time.deltaTime;
    velocity.y += fdt;
    vertexVelocities[i] = velocity;
    displacedVertices[i].y += vertexVelocities[i].y * Time.deltaTime * waveSpeed * 2;
}

Even with this current code I have to give the program specific variable parameters.

Mouse Click Force is set to 40, and Wave Speed is set to 5 in this picture. Weird Oval Rippling

Not only are the ripples extending out in an oval pattern, but the rippling effect takes a while to start up, which is obviously not how water works.

If anyone can offer insight into these issues, that would be greatly appreciated. Please let me know if I should post further code or information.

I forgot to mention the quad I'm dealing with is 100x100 vertices

UPDATE

I've made some good progress, however I'm now running into two issues:

  1. the algorithm is computationally expensive (which is weird since it was given in a GDC presentation). Am I not implementing it right? Can optimizations be made
  2. I've got some boundary issues I'm dealing with.

    using UnityEngine;

[RequireComponent(typeof(MeshFilter))] public class WaterWaves : MonoBehaviour {

public float c = 10f;
public float h = 4f;
public int height, width;

Mesh mesh;
int n, s, e, w = 0;
float[] originalVerts;
float[] u, v;
float[] newVerts;
Vector3[] vertices;

float dt;

void Start()
{
    mesh = GetComponent<MeshFilter>().mesh;
    originalVerts = new float[mesh.vertices.Length];
    u = new float[mesh.vertices.Length];
    v = new float[mesh.vertices.Length];
    newVerts = new float[mesh.vertices.Length];
    vertices = new Vector3[mesh.vertices.Length];

    for (int i = 0; i < mesh.vertices.Length; i++)
    {
        originalVerts[i] = mesh.vertices[i].y;
        newVerts[i] = originalVerts[i];
        u[i] = newVerts[i];
        v[i] = 0;
    }

    v[337] = -5;
    Debug.Log("v[337]: " + v[337]);
}

void Update()
{
    Debug.Log("u[337]: "+u[337]);
    dt = Time.deltaTime;
    for(int i = 0; i < originalVerts.Length; i++)
    {
        BoundaryCheck(i);
        float f = (c * c) * (u[n] + u[s] + u[e] + u[w] - 4*u[i]) / (h * h);
        v[i] += f * dt;
        v[i] -= 0.05f * (newVerts[i] - originalVerts[i]);
        newVerts[i] = u[i] + v[i] * dt;
        newVerts[i] *= 0.99f;
    }
    for(int i = 0; i < originalVerts.Length; i++)
    {
        vertices[i] = new Vector3(mesh.vertices[i].x, newVerts[i]+mesh.vertices[i].y, mesh.vertices[i].z);
        u[i] = newVerts[i];
    }
    mesh.vertices = vertices;
    mesh.RecalculateNormals();
}

void BoundaryCheck(int i)
{
    n = i - height;
    s = i + height;
    e = i + 1;
    w = i - 1;

    if (n < 0 || n >= originalVerts.Length)
    {
        n = i;
    }
    if (s < 0 || s >= originalVerts.Length)
    {
        s = i;
    }
    if (e < 0 || e >= originalVerts.Length)
    {
        e = i;
    }
    if (w < 0 || w >= originalVerts.Length)
    {
        w = i;
    }
}

}

Here's the full file I'm working with. I'm dealing with a 1D array, and that makes testing the boundary issues, kind of difficult. I'm arbitrarily choosing a point and applying velocity to get the algorithm going (at this point I just want to get it to work; interactivity comes later).

enter image description here

Lastly, for anyone looking to help, but unsure of where to start, my brother made the astute point that what I'm trying to do is essentially a JavaScript program by Mr. Doob.

His code is a bit more abstract than I'd like, so I have trouble understanding how it works.

Mr. Doob Liquid Voxels

UPDATE 2

Super close to figuring it out I think. Here's the good news: Thanks to Stephen's insight, I realized that I was handling the vertices incorrectly which was making some weird patterns and waves. Fixing that actually makes it work SO CLOSELY. Next up, I redid the boundary constraints based on Mr. Doob's code, and it appears to be working well!

Now the newest and hopefully last issue, is that my spring doesn't seem to be working properly. I'm using Hooke's Law F = -kX, but when the spring comes to a rest, the restored mesh is not a flat quad.

Here's the relevant code:

BoundaryCheck(i);
newVerts[i] += v[i] * dt;
float f = (c * c) * (n + s + e + w - 4*u[i]) / (h * h);
v[i] += f * dt;
v[i] -= springConstant * (newVerts[i] - originalVerts[i]); // SPRING
v[i] *= 1f - damping * dt;
newVerts[i] = u[i] + v[i] * dt;
\$\endgroup\$
  • \$\begingroup\$ How are you applying the force? If you aren't making sure it's straight down, you can get oval propagation. What boundary issues are you getting? Specific questions get specific answers. \$\endgroup\$ – Stephan Jan 2 '18 at 18:28
  • \$\begingroup\$ Thank you for the response. I guess part of my issue is that I don't exactly know what might be causing the problem which I know isn't helpful for anyone looking to answer. For example, to help understand how the algorithm works, I did a 1D implementation of it in a 2D space and I after tweaking it for quite a while, I found out I didn't have my spring equation implemented properly For the boundary issues, as you can see in my newest photo, you get those double dips on the border. Based on my BoundaryCheck() code, that shouldn't be happening. \$\endgroup\$ – David King Jan 2 '18 at 22:33
  • \$\begingroup\$ After doing some basic testing, I think I might know what's causing one of the issues. I set up a 3x3 quad and extract the vertices from the mesh. Now, naturally I'd expect to get an array of size 9, but instead I get one of 16. Which is strange because this is not how many vertices were used to create the mesh. Meaning that I think Unity recreates the vertex array to in relation to the triangle array. So the vertex array unity spits out is how many vertices are being used, rather than the true number of vertices. This means I'll likely need to rebuild the mesh every frame. \$\endgroup\$ – David King Jan 2 '18 at 23:08
  • \$\begingroup\$ 3x3 quads will have 16 verts. We're you thinking you were creating 3x3 verts? That'd be 2x2 quads. \$\endgroup\$ – Stephan Jan 3 '18 at 1:16
  • \$\begingroup\$ Oh jeeze. How embarrassing. \$\endgroup\$ – David King Jan 3 '18 at 2:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.