How to Script Buoyancy with a Water Shader

How do I get objects to float on my shader’s waves?

I have a water shader I made in Shader Forge and I want to make objects float on the waves. From my understanding I need to calculate my shader’s wave functions in C#, because they are not accessible through script, and sort of mimic what is happening in my shader to calculate the distance from my boat to the wave surface.

If this is correct, the how would I achieve this? Or, if not, what is a good method of doing this? I could really use some help wrapping my head around this. Thanks!

I’m also confused for a long time, good question @coling1
_
If you have buoyancy physics and waves, you need a function that returns height of water of any point:
_

public float GetWaterHeight(Vector3 pos)

_

I know 3 ways to make buoyancy work:

  1. Implement wave function in shader and duplicate the function in C# with all parameters (Wave Length, Speed, etc)

  2. Implement wave function in C# using mesh manipulation

  3. Pass all water mesh vertices to find the closest to selected point and use this vertex’s height (or approximate it)
    _
    The first way is fast but dangerous in usage (easy to mistake). For example, if you want to use sine wave function, you can generate wave in shader like this ([tutorial how it works][1]):
    _

    void vert(inout appdata_full vertexData) {
    float3 p = vertexData.vertex.xyz;

     float k = 2 * UNITY_PI / _Wavelength;
     float f = k * (p.x - _Speed * _Time.y);
     p.y = _Amplitude * sin(f);
    
     float3 tangent = normalize(float3(1, k * _Amplitude * cos(f), 0));
     float3 normal = float3(-tangent.y, tangent.x, 0);
    
     vertexData.vertex.xyz = p;
     vertexData.normal = normal;
    

    }
    _

Then you can find water height in C# like this:
_

public Transform waterTransform;
public float GetWaterHeight(Vector3 pos)
{
    var localPoint = waterTransform.InverseTransformPoint(pos);
    var func = _waveNumber * (localPoint.x - speed * Time.time);
    var height = amplitude * Mathf.Sin(func);
    return height;
}

_

The second way is what @coling1 assumed. [See documentation of mesh manipulation][2] and implement wave function right in C# (_mesh is water mesh):
_

void Update()
{
    var vertices = _mesh.vertices;
    var normals = _mesh.normals;

    for (int i = 0; i < _mesh.vertices.Length; i++)
    {
        var vertex = _mesh.vertices*;*

var func = waveNumber * (vertex.x - speed * Time.time);
vertex.y = amplitude * Mathf.Sin(func);
var tangent = new Vector3(1, waveNumber * amplitude * Mathf.Cos(func), 0);
tangent.Normalize();
var normal = new Vector3(-tangent.y, tangent.x, 0);
vertices = vertex;
normals = normal;
}

_mesh.vertices = vertices;
_mesh.normals = normals;
}
_
Find water height after that is not too hard (for example, you can create vertices map). My implementation have very bad perfomance. If there is 10000 vertices in water mesh, there is 4 second for 1 Update call.
If someone know, how to fix bad perfomance, help please.
_
The third method was mentioned on [Unite 2015][3]. It is good when you don’t know how waves work or haven’t access to them.
_
*[1]: https://catlikecoding.com/unity/tutorials/flow/waves/*_
[2]: https://docs.unity3d.com/ScriptReference/Mesh.html*_
_[3]: Unite 2015 - A Little Math for Your Big Ideas - YouTube

I don’t know if you can but I sure wouldn’t. It seems like you want to distort the actual shape of the object rather than use a shader, which only governs how an object looks (as far as I know).

So, if it were me, I’d pull the wave part out of the shader and write something that actually distorts the surface of the water with the shape of the waves I want. Maybe render something into some texture that the shader uses to tell what part of the wave it’s rendering (whitewater, tube, lip, face, etc).

Someone who really knows about shaders - like in a deep way - might have a more satisfactory answer but I’ve had bad luck asking shader-related questions through this medium.