Height map mismatching with mesh that both use the same noise

Godot Version

v4.6.2.stable.official

Question

I’m working on a game in Godot and use FastNoiseLite to generate noise for elevation. I apply this noise to a plane mesh with a shader, the code listed below.

shader_type spatial;

uniform sampler2D noise;
uniform float height_scale;

void vertex()
{
	float height = texture(noise, UV).r*height_scale;

	VERTEX.y += height * height_scale;
}

void fragment()
{
	float height = texture(noise, VERTEX.xz/2.0+0.5).x;
	
	ALBEDO = vec3(0.06, 0.6, 0.0);
}

This works perfectly. However, when I apply this same logic to a HeightMap for collision, it doesn’t seem to match the mesh. It requires a higher amplification, and misaligns with the mesh.

Here’s the code for that.

func _generate_terrain_hitbox() -> void:
	var height_data = PackedFloat32Array();
	
	height_data.resize(GROUND_MESH_SUBDIVISIONS*GROUND_MESH_SUBDIVISIONS);
	
	var elevation_noise_image = elevation_noise.get_image();
	
	for z in range(GROUND_MESH_SUBDIVISIONS):
		for x in range(GROUND_MESH_SUBDIVISIONS):
			var pixel_color = elevation_noise_image.get_pixel(x, z);
			
			var height = pixel_color.r*ELEVATION_INTENSITY;
			
			height_data[x+z*GROUND_MESH_SUBDIVISIONS] = height;
	
	GROUND_HITBOX.shape.map_data = height_data;

And here’s the entire script if you need it for whatever reason.

Can you show some visuals?

Sure, I’m sorry. I was going to but forgot to add it in the end. Ignore the blue square, that’s for a tool system my fellow programmer is developing.

Can you pronounce the elevation more so the mismatch is better seen?

Here you go

That’s almost the same. I meant to really scale it up and zoom out the camera to observe it from a larger distance/elevation. Otherwise it’s hard to spot any eventual patterns.

Here you go. Contrast added to make it easier to see.

Can you scale them both vertically by the same amount and put them side by side, and draw the mesh as wireframe?

Alright, I’m sorry for all the trouble. I tried to get it as wireframe although couldn’t find a way to, I apologize.

Add render_mode wireframe; as the second line in the shader.

Didn’t do anything, I’m sorry for the inconvenience

The camera might be too far away to discern it a this mesh resoultion.

Lower the mesh resolution and also decrease the noise frequency so there are only few hills on the whole map. Easier to spot the nature of the missmatch that way.

Lower the mesh resolution even more. Make it 128x128 vertices or so.

Godot had a strange bug when trying to show both at the same time while it was reduced, so here’s two

You should be able to show them side by side.

I fixed the bug, here it is

There are multiple problems with your setup.

First make sure that your mesh and your collider grid topologies and sizes match. Since plane mesh subdivides faces, while heightmap collider counts vertices, your heightmap width/height will have to be +2 of the plane mesh subdivisions. Best to match them visually in editor or run your code without any height displacement and make sure they match. Draw the mesh as wireframe for visual feedback.

Once this is set up, read pixels from your image by remapping the heightmap index to pixels:

elevation_noise_image.get_pixel(roundi(remap(x, 0, MAP_SIZE - 1, 0, IMAGE_SIZE - 1)), roundi(remap(z, 0, MAP_SIZE - 1, 0, IMAGE_SIZE - 1)));

Lastly, in shader: You’re multiplying twice by height_scale. Do it only once. To ensure the texture is sampled the same way as reading the pixels in script, disable filtering and wrapping:

uniform sampler2D noise: filter_nearest, repeat_disable;