Raycasting to vertices.

Hi! My game has a few moments where the player walks on water, the game really doesn’t have many moments where you fully interact with water, so I thought making water collision wasn’t really necessary, although, I wish I could raycast below my character to verify if there’s any vertice from the water to play water-splash particles.

My water has waves, I made a simple shader on shadergraph to make the water pretty, but is there a solution to spawn the particles only when my character step on the water itself and not on the mesh plane (as it sometimes the water is not there because of the waves)?

I made this sketch to show you all my idea lol

In my opinion the simplest way to do this is use a raycast to the ground and save the y value. Then use the same math that you’re using within shader graph and execute it for just that single raycast hit x/z position and compare. There’s no simple way to cast against a non-static mesh that’s deformed in a vertex shader that I know of.

Worth noting that C# Time and the Shader Graph Time node are in sync - so for example if you’re animating waves using sine you can calculate whether a footstep would be in the water during any given frame for wherever it is. Just create a function that does the same as the portion of the graph affecting vertex position - then you can get the current wave height for any given Vector3 position(s).

Your solution sounds good, although, would that be applicable on the calculation with time instead of sine time? Also I’m wondering if it’s possible to use my shader to make the effect, as my shader has random generated gradient noise.

I’m quite behind when it comes to shader math, but I think it would be impossible to calculate the position as the waves are always random right?

I believe Time.time and the Time node’s time output always have the same value - so you’d just do the same in C# as your shader. Re randomness, you’d probably have to base your approach to noise on texture instead and sample from the same one(s) with GetPixel (or flattened index within raw image data) in your function. That way you’d have a fixed seed to write corresponding logic for. Another thought… as it’s possible to render a material to a texture, you could sample from that the same way, but in practice I think it might be too costly. However, if you did go about it that way, I’d try to keep the render reasonably low-res and a put a limiter on updates (i.e. once every 100ms or something / whatever precision you’d need), just sample from the render of the previous update until the next as needed.

1 Like

There is no such thing as randomness when it comes to programs. The gradient noise function is just complicated enough so it looks sufficiently random. That means you can replicate exactly the same behaviour of the shader math in CPU (C#) land. To make things simpler, first change the input of the gradient noise from “UV0” to world coordinates like so:

6558433--742900--Bildschirmfoto 2020-11-25 um 09.04.20.png

This way, you don’t have to figure out the UV coordinates of the object your player is touching. You probably have to adjust the scale so it fits your original look. Next, right click the gradient noise node and select “Show generated Code”:
6558433--742909--Bildschirmfoto 2020-11-25 um 09.10.22.png

It’ll open the HLSL code behind the Gradient Noise node:

float2 Unity_GradientNoise_Dir_float(float2 p)
    {
        // Permutation and hashing used in webgl-nosie goo.gl/pX7HtC
        p = p % 289;
        float x = (34 * p.x + 1) * p.x % 289 + p.y;
        x = (34 * x + 1) * x % 289;
        x = frac(x / 41) * 2 - 1;
        return normalize(float2(x - floor(x + 0.5), abs(x) - 0.5));
    }

    void Unity_GradientNoise_float(float2 UV, float Scale, out float Out)
    {
        float2 p = UV * Scale;
        float2 ip = floor(p);
        float2 fp = frac(p);
        float d00 = dot(Unity_GradientNoise_Dir_float(ip), fp);
        float d01 = dot(Unity_GradientNoise_Dir_float(ip + float2(0, 1)), fp - float2(0, 1));
        float d10 = dot(Unity_GradientNoise_Dir_float(ip + float2(1, 0)), fp - float2(1, 0));
        float d11 = dot(Unity_GradientNoise_Dir_float(ip + float2(1, 1)), fp - float2(1, 1));
        fp = fp * fp * fp * (fp * (fp * 6 - 15) + 10);
        Out = lerp(lerp(d00, d01, fp.y), lerp(d10, d11, fp.y), fp.x) + 0.5;
    }

Now you need to convert this code to C#:

  • float2 → Vector2
  • floor → Mathf.Floor
  • frac(x) → x - Mathf.Floor(x) // the decimal value of a float, eg. 50.23f → 0.23f
  • dot → Vector2.Dot
  • float2(1, 0) → Vector2.up
  • float2(0, 1) → Vector2.right
  • lerp → Mathf.Lerp

As you can see, the noise function is really just some code. No actual noise there. I hope that gives enough pointers for you to go on by yourself :slight_smile:

2 Likes