Help generating HeightMapShape3D that matches Vertex Shader
Asked Answered
A

2

0

Hi all! It's nice to be here 🙂

I am working on some terrain generation in Godot 4.0.3. I have a vertex shader that deforms a mesh, and I am generating a HeightMapShape3D for a CollisionShape3D. I am loosely following this tutorial by devmar.

I am having trouble getting the HeightMapShape3D to match the shape of the mesh. The HeightMapShape3D is much flatter than the Mesh and does not have the same shape.

I have experimented with the following:

  • Normalization of the noise function I am using
  • Using get_image on the noise texture and using get_pixel instead of sampling with get_noise_2d
  • Importing the noise as an image instead of using NoiseTexture2D
  • rotating the mesh in each of the four possible orientations (0, 90, 180, and 270-degree rotations)
  • Iterating over different ranges of x and y values (-range to zero, -range/2 to range/2, 0 to range, and most combinations of those, as well as most of those combinations with +1s thrown around)
  • dividing by xz_scale instead of multiplying by it, many other misc scaling tweaks

The result looks something like this, where the mesh undulates above and below the collider, instead of the collider having roughly the same shape as the mesh.

I've also attached a minimum repro project if you'd like to take a look at my setup.

terraincollidertest.zip
14MB

Based on how long I've been trial-and-erroring this, I think I'm just misunderstanding something fundamental. Any ideas on where I'm going wrong? I am also open to other strategies for programmatically creating collision shapes for terrain if this isn't a good strategy.

Thank you!

Anthocyanin answered 2/7, 2023 at 17:59 Comment(0)
A
0

In case you don't want to download the project:

I have the following shader to deform the mesh, based loosely on "Your first shader" in the Godot docs:

shader_type spatial;

uniform float height_scale = 0.5;
uniform float xz_scale = 0.1;

uniform sampler2D heightmap;
uniform sampler2D normalmap;

varying vec2 texture_position;

void vertex() {
  texture_position = xz_scale * VERTEX.xz;
  vertex_y_position = VERTEX.y + height_scale * texture(heightmap, texture_position).r;
  VERTEX.y = vertex_y_position;
}

void fragment() {
	NORMAL_MAP = texture(normalmap, texture_position).xyz;
}

I am using this GDScript to create the HeightMapShape3D, based on the devmar tutorial:

@tool
extends Node3D

@onready var mesh: PlaneMesh = $Ground/Mesh.mesh
@onready var material: ShaderMaterial = $Ground/Mesh.get_active_material(0)
@onready var collision: CollisionShape3D = $Ground/Shape

func _ready():
	var heightmap = material.get("shader_parameter/heightmap")
	var height_scale: float = material.get("shader_parameter/height_scale")
	var xz_scale: float = material.get("shader_parameter/xz_scale")

	var map_data = []
	for y in range(mesh.size.y + 1):
		for x in range(mesh.size.x + 1):
			var x_scaled: float = x * xz_scale
			var y_scaled: float = y * xz_scale
			var height = height_scale * heightmap.noise.get_noise_2d(x_scaled, y_scaled)
			map_data.push_back(height)

	var shape = HeightMapShape3D.new()
	shape.map_width = mesh.size.x + 1
	shape.map_depth = mesh.size.y + 1
	shape.map_data = map_data
	collision.shape = shape
Anthocyanin answered 2/7, 2023 at 17:59 Comment(0)
C
0

Anthocyanin You need to match readout of the noise in the shader and in the script. Since shader reads it from the texture I'd do the same from the script. Sample the texture pixels with the same uv coordinates (not pixel coordinates) you're using in the shader.

Caenogenesis answered 3/7, 2023 at 12:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.