Heightmap issues

I’m trying to create a mountain system from a heightmap, based on this example: https://www.youtube.com/watch?v=fEG_cnRQ1HI

The idea is simple:

  1. Create a MeshInstance3D with a PlaneMesh of WidthDepth vertices (8080 in my case), create a Shader for the mesh, read the heightmap image from it, and for each level of grey, modify the vertex Y coordinates
  2. In the MeshInstance3D (as a child of it), create a StaticBody3D with a child CollisionShape3D, add a script to the parent MeshInstance3D, which will build a collision shape, based on the same heightmap image as used in the Shader

But in my project, this not works as expected. I have indeed a more or less matching collision shape, but it contains several important differences, which turn it unusable because not reliable (see screenshot below).

Below is the code of my script:

extends MeshInstance3D

@onready var colShape = $StaticBody3D/CollisionShape3D

@export var chunk_size = 2.0
@export var height_ratio = 0.5
@export var colShape_size_ratio = 0.5#0.1

var img = Image.new()
var shape = HeightMapShape3D.new()

# Called when the node enters the scene tree for the first time.
func _ready():
	colShape.shape = shape
	mesh.size = Vector2(chunk_size, chunk_size)
	update_terrain(height_ratio, colShape_size_ratio)

func update_terrain(_height_ratio, _colShape_size_ratio):
	mesh.material.set("shader_parameters/height", _height_ratio)

	img.load("res://assets/models/mountains/heightmap.png")
	img.convert(Image.FORMAT_RF)
	img.resize(img.get_width() * _colShape_size_ratio, img.get_height() * _colShape_size_ratio)

	var data = img.get_data().to_float32_array()

	for i in range(0, data.size()):
		data[i] *= _height_ratio

	shape.map_width = img.get_width()
	shape.map_depth = img.get_height()
	shape.map_data  = data

	var scale_ratio = chunk_size / float(img.get_width())
	colShape.scale  = Vector3(scale_ratio, 1, scale_ratio)

And here is the heightmap image I’m using:

I suspect that the ghost artifacts in my collision shape may come from a conversion issue, something like the collision shape is created 3 times due to the RGB values, but I cannot find how and where. I tried to convert the image to grayscale, or use a .exr format as suggested in the above video, but no way.

Can someone explain me what I’m doing wrong?

Note that PNG images are low quality for heightmaps in Godot. For the full 32 bit height data (FORMAT_RF) you need to use an e.g. EXR file format, not PNG.

That tutorial is old. There is a dedicated function by now on the HeightMapShape3D to create the height points from a grayscale image in all 3 common 1 channel image formats.

See HeightMapShape3D — Godot Engine (stable) documentation in English for the update_map_data_from_image(), also with a script example in the HeightMapShape3D class description at the begin of the page.

@smix8 Thank you for the explanations. However your solution is correct only since Godot 4.3 and higher. But as I’m still using Godot 4.2, the update_map_data_from_image() function does not exist on this version.

Anyway I could find what was my issue: I wrongly used the same script in 2 meshes on 2 different scenes, and contrary to what I thought, turning a scene hidden on the editor does not deactivate its collider, only the mesh visibility. For that reason, I had 2 superposed colliders, which were generated in different configuration, it was the reason why I had such ghosts when I ran my project.

But once again, thank you very much for the time you took to answer my question.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.