0 votes

So, I'm currently working on a water shader for some boat stuff, and you can't have boats without buoyancy. I've got an idea of how I can implement it, but for it to work I need to either now when an object is touching the water, or I need the height of a vertex in the given location.

I'm quite new to shaders in Godot, so I have no idea of how to get said information. I know it's possible to make variables uniform so that I can access them in a script, but I can't really see a way to use that.

Also, here's my shader if it's needed...

shader_type spatial;
render_mode cull_back, diffuse_toon, specular_toon, depth_draw_always, blend_mix;

const float PI = 3.14159265359;

uniform vec4 _WaterColor : hint_color = vec4(0.1, 0.3, 0.5, 1.0);
uniform vec4 _FoamColor : hint_color = vec4(1.0);
uniform float _FoamStrength : hint_range(0, 10) = 1;

uniform float _Speed = 0.25;

// Direction, steepness, wavelength
uniform vec4 _WaveA = vec4(1, 0, 0.5, 10);
uniform vec4 _WaveB = vec4(0, 1, 0.25, 20);
uniform vec4 _WaveC = vec4(1, 1, 0.15, 10);

uniform float _Metallic : hint_range(0, 1);
uniform float _Roughness = 0.01;
uniform float _Rim = 0.2;

uniform float _PlaneScale = 10;
uniform float _NoiseSpeed = 0.1;
uniform float _HeightScale = 0.5;
uniform float _NormalStrength = 1.25;
uniform sampler2D _FoamNoise;
uniform sampler2D _Noise;
uniform sampler2D _NormalMap;

varying vec2 tex_position;
varying float vertex_height;

vec3 GerstnerWave(vec4 wave, vec3 v, inout vec3 tangent, inout vec3 binormal, float _Time) {
    float steepness = wave.z;
    float wavelength = wave.w;

    float k = 2.0 * PI / wavelength;
    float c = sqrt(9.8 / k) * _Speed;
    vec2 d = normalize(wave.xy);
    float f = k * (dot(d, v.xz) + c * _Time);
    float a = steepness / k;

    tangent += vec3 (
        -d.x * d.x * (steepness * sin(f)),
        d.x * (steepness * cos(f)),
        -d.x * d.y * (steepness * sin(f))
    );

    binormal += vec3 (
        -d.x * d.y * (steepness * sin(f)),
        d.y * (steepness * cos(f)),
        -d.y * d.y * (steepness * sin(f))
    );

    return vec3 (
        d.x * (a * cos(f)),
        a * sin(f),
        d.y * (a * cos(f))
    );
}

float calc_height(vec2 position, float time) {
    vec2 offset = _NoiseSpeed * cos(position + time);
    return texture(_Noise, (position / _PlaneScale) - offset).x;
}

void vertex() {
    vec3 gridPoint = VERTEX.xyz;
    vec3 tangent = vec3(1, 0, 0);
    vec3 binormal = vec3(0, 0, 1);
    vec3 v = gridPoint;

    v += GerstnerWave(_WaveA, gridPoint, tangent, binormal, TIME);
    v += GerstnerWave(_WaveB, gridPoint, tangent, binormal, TIME);
    v += GerstnerWave(_WaveC, gridPoint, tangent, binormal, TIME);

    tex_position = VERTEX.xz / 2.0 + 0.5;
    vec2 pos = VERTEX.xz;
    float h = calc_height(pos, TIME) * _HeightScale;
    vertex_height = h;
    v.y += h;

    vec3 normal = normalize(cross(binormal, tangent));
    VERTEX.xyz = v;
    NORMAL = normal;
}

void fragment() {
    float fresnel = sqrt(1.0 - dot(NORMAL, VIEW));
    vec3 water_color = _WaterColor.rgb + (0.1 * fresnel);

    float depth = texture(DEPTH_TEXTURE, SCREEN_UV).x;
    vec3 ndc = vec3(SCREEN_UV, depth) * 2.0 - 1.0;
    vec4 view = INV_PROJECTION_MATRIX * vec4(ndc, 1.0);
    view.xyz /= view.w;
    float linear_depth = -view.z;

    if (linear_depth + VERTEX.z < vertex_height-0.1) {
            vec2 offset = _PlaneScale * cos(tex_position + TIME);
            float foam_noise = clamp(pow(texture(_FoamNoise, (tex_position / _NoiseSpeed) - offset).r, 30.0) * 40.0, 0.0, 0.2);
            float foam_mix = clamp(pow((1.0-(depth + VERTEX.z) + foam_noise), _FoamStrength*10.0) * foam_noise * 0.4, 0.0, 1.0);
            water_color = mix(water_color, _FoamColor.rgb, foam_mix);
    }

    ALBEDO = water_color;
    ALPHA = _WaterColor.a;
    NORMALMAP = texture(_NormalMap, tex_position).xyz;
    NORMALMAP_DEPTH = _NormalStrength;

    METALLIC = _Metallic;
    ROUGHNESS = _Roughness * (1.0 - fresnel);
    RIM = _Rim;
}

Now, I know I could (in theory) recreate all the math in a script and calculate it from that, but if was lazy then I would be making this post.

Also I want to emphasize that I'm fairly new to Godot, and therefore I could be tackling this problem in a different way.

And of course, thank you beforehand for taking your time to read my post.

Godot version 3.2.3
in Engine by (17 points)

Hi,
its not quite clear what you are trying to achieve.

height of a vertex in the given location

Do you mean the y position of a vertex on a x/z location? What do you need it for?

Yes exactly, sorry for not being clear enough.
I need it to detect when an object is hitting the water, so that I can make a buoyancy system

I think this would require the use of compute-shader wich godot does not support.

I see, thanks anyways.
Also I had an idea, I just need the height to know if an object and the water is touching... So do you know of another way to do this, like with a collision box of some sort?

This is highly dependent on how the water and your object is shaped.

Looking in your shadrcode i suggest there are rolling waves on the water surface.
I suggest your have a static 3d model of a boat.
The water surface height should be computable on any point in 3D Space. If you step through any vertex of your boat you then can calculate if the vertex is above or in the water.
With surfacegetarrays you can access all vertieces of a mesh. You will have to transform the coordinates to global space to use them. (with toglobal or xform)
https://docs.godotengine.org/en/stable/classes/class_mesh.html#class-mesh-method-surface-get-arrays

You should consider using a simplified mesh for computation to keep up the performance.
I also would advise you to do it in a child thread to not freeze the application while calculating. But that is a whole different story and should be considerd when everything works but performance is poor.

Makes sense, again thanks a lot I'll have a look at it once I have the time.
And I'll definetly look into this child thread thing.

Please log in or register to answer this question.

Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read How to use this Q&A? before posting your first questions.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to webmaster@godotengine.org with your username.