Help with Matching Vertex Position in CPU/GPU

Godot Version

4.5

Question

I am having difficulty implementing a wave function on both the GPU and CPU. I am using shaders to displace the vertices of a PlaneMesh and matching CPU functionality to calculate basic physics. I have confirmed that both implementations of the wave offset function return the same output given the same input. However, I cannot get my input vertices/positions to line up in World Space. My simplified code is below.

GDScript Code:

@onready var ocean : Ocean = get_node("/root/Main/Ocean")
@onready var start : Vector3 = global_position;

func _process(delta: float) -> void:
	global_position = start + ocean.get_ocean_height(global_position)

GDShader Code (simplified):

void vertex() {
    vec3 offset = vec3(0.0);
    VERTEX = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
    for (int i = 0; i < numWaves; i++) {
        WaveProps w;
        w.dir = normalize(pm_direction(wind, float(i), 1.2));
        float k = choose_k(float(i));
        float k_next = choose_k(float(i+1));
        float delta = k_next - k;
        w.k = k;
        w.amplitude = 3.0 * sqrt(2.0 * pierson_moskowitz(w.k, length(wind)) * delta);
        w.steepness = 1.0 / (w.k * w.amplitude * float(numWaves));
        offset += wave_offset(VERTEX.xz, w);
    }
    VERTEX += offset;
    VERTEX = (inverse(MODEL_MATRIX) * vec4(VERTEX, 1.0)).xyz;
	
}

Is there any way to determine what exact vertex position I am passing? I am sure that I am in some sort of world space, but I can’t determine if the positions are identical. I would post a video of what I see, but this thread won’t let me as I am a new user. I appreciate any advice of how to fix this.

VERTEX in vertex function is in object space. If the transform on your mesh instance node is identity, VERTEX value will match global space. Otherwise it won’t.

1 Like

What is the best practice here to get VERTEX into world space? I tried using render_mode world_vertex_coords and operating on the vertex directly, but that didn’t produce the result I was expecting either.

Simply multiply it with MODEL_MATRIX which transforms from object space to global space.

Does my code not do that? I’m confused

It does, but it also does the reverse. So if model matrix is not identity, there will be discrepancies anyway because your script code assumes identity. Your code should not be doing any matrix multiplications.

I was under the impression that spatial shaders automatically perform the world space transformation after vertex(), so I thought that it would end up multiplying by MODEL_MATRIX twice.

They do. But why are you multiplying in the first place?

I need to use the World coordinates as an input into my offset function so I don’t get repeated scrolling if using multiple objects which use the same shader. I guess I need to applying the offset at the end of the shader while in world coords without it transforming again until view-projection?

Then just transform the sampling position to global space:

vec3 vertex_global = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
float vertical_offset = sample_wave(vertex_global);
VERTEX.y += vertical_offset;

I will give this a go when I get the chance. Thanks for the help!

You didn’t understand what I wrote there, did you?
Who wrote that shader code?

Uhh, I did? I’m just not at my PC at the moment

I was able to get my code fixed through a combination of using world_vertex_coords, as well as tweaking my hashing algorithm for random wave directions. Turns out GDScript uses 64-bit floating point by default and calls it Float, when GLSL uses 32-bit. Thanks for the help though

Only thing you need to do is transform your sampling coordinate into world space if your sampling code works in world space. world_vertex_coords can be used but it’s not necessary.
Float precision has nothing to do with this.