• Godot Help
  • Why is FastNoiseLite producing oddly-tiled output?

I'm trying to use FastNoiseLite to create heightmaps for procedurally-generated terrain. I'm generating the noise in chunks so that I can do infinite terrain generation. I'm running into this weird bug where the noise it outputs has weird tiling in the output. It doesn't at all look like the smooth noise map I'd expect (I'm expecting something more like this).

Here's a minimum reproducible code example (Using Godot 4.1.1 on W11):

extends Node

# Set this to true to save individual heightmap files
const save_individual_heightmaps := false

func _ready():
	# Initialize noise
	var noise = FastNoiseLite.new()
	noise.noise_type = FastNoiseLite.TYPE_SIMPLEX
	
	# Initialize full image
	DirAccess.make_dir_absolute("res://heightmap")
	var full_img = Image.create(64, 64, false, Image.FORMAT_L8)
	
	# Create and save a heightmap for each sector in range
	for x in range(-32, 32, 16):
		for z in range(-32, 32, 16):
			# Create heightmap
			noise.offset = Vector3(x, z, 0)
			var heightmap = noise.get_seamless_image(16, 16)
			
			# Save individual file (if desired)
			if save_individual_heightmaps:
				heightmap.save_png("res://heightmap/" + str(x) + "_" + str(z) + ".png")
			
			# Fill in appropriate seciton of full image
			var src_rect = Rect2i(0, 0, 16, 16).abs()
			var dst = Vector2i(x + 32, z + 32)
			full_img.blit_rect(heightmap, src_rect, dst)
	
	# Save full image
	var path = "res://heightmap/full_img.png"
	full_img.save_png(path)
	
	print("Done! File saved at " + ProjectSettings.globalize_path(path))

Here's what the output of that code looks like:

An individual 16x16 tile looks like this:

Any idea what I'm doing wrong?

  • xyz replied to this.

    It certainly does not look like "seamless image" it should produce. I'd say there is a bug that needs to be reported; at very least the API should be rearranged in a way that allows getting predictable result out of it.

    Just a guess, try disabling normalization in get_seamless_image.

    The docs mention that TYPE_VALUE_CUBIC should be used for bump/height maps (doc).

    You could use the output in an image and tweak the settings until you get the desired results, then drop it into your code (or just save the resource).

    Dacromir Increase the optional skirt parameter when calling get_seamless_image(). The default value is .1 which makes the transition zone rather narrow, causing the seam to stay visible for low frequency noises. Try to set it to maximal 1.0.

    However get_seamless_image() is not what you need here. It's for making a single self-repeating tile. Since each of your tiles samples a different noise area, making it self-repeating would in fact produce seams between adjacent tiles.

    You don't actually need to do anything about tiling because the noise function is infinitely continuous. Just adjust the offsets properly (which you're already doing) and sample using get_image().

    It's also important to disable the normalization so the amplitude ranges stay the same across the tiles.

    So instead of:

    var heightmap = noise.get_seamless_image(16, 16)

    try:

    var heightmap = noise.get_image(16, 16, false, false, false)