Infinite_Map Perlin Noise -- Question

#1

User britzl has a demo project of using Perlin Noise to generate an infinite map, found here:

I’ve looked through the source code of actually interacting with the ‘perlin’ object, and it looks pretty simple. The only functions called were “perlin.init()” then “perlin.noise(,,_).”

My question is: what exactly is supposed to go in the “perlin.noise(,,_)” parameters? I don’t understand the function at all, but it looks like britzl is using the tilemap position and player position as arguments, along with a “/ 10” for some reason, and then “0.3” for the third argument.

Could someone explain what’s going on here? Thank you. I’m new to Perlin Noise and am looking to generate a simple wavy line for the “top” of my terrain. I’ll fill in the pixels below that “top” wave.

1 Like

#2

The algorithm expects three values to calculate the noise for any coordinate for a 3 dimensional point (x, y, z). The z value is fixed and hardcoded while the x and y values are what matters since its a 2D tilemap.

In your case it sounds like you could use a 1D noise function instead where you give it the horizontal position and it returns the vertical value for the “wavy line” you mention. But using the noise function from my example and hardcoding both y and z will give the same end result.

1 Like

#3

Thank you, I’ve been playing with it and got it to work. Currently I’m drawing a rectangular region 1024 x 512 to an atlas and displaying it on the screen. I have a few questions, if you don’t mind answering them:

  1. Using the perlin code in your project, how can I alter the more specific values, like octaves, wavelength, amplitude, etc? Changing x, y, and z don’t seem to have much of an affect on the image, other than scaling it.

  2. Hardcoding the y and z arguments gives me a bunch of horizontal bars, rather than a wavy line. I’m still trying to wrap my head around the concept of Perlin Noise, so maybe this is to be expected?

Thanks for your time, it’s appreciated.

0 Likes

#4

What you’re after is probably Fractal Brownian Motion.
This allows you to combine multiple noise function calls, using amplitude and octaves.

Example (from quick googling): https://thebookofshaders.com/13/

2 Likes

#5

Thanks! After studying the Fractal Motion code, I decided to write my own version with my own little hacks and tweaks. The terrain generation is looking interesting, even at this early stage.

2 Likes

#6

Cool! How are you drawing the map?

0 Likes

#7

I actually initially followed your “draw_pixels” example, which took a while to study and understand. I’ve made some modifications to it, along with other algorithms like flood_fill(), etc. Here’s the bulk of it. Thanks for making those examples, by the way. Some of them have come in handy!

----------------------------------------------------------------
-- CONSTANTS
----------------------------------------------------------------

-- Size of canvas (pixels)
local WIDTH = 1024
local HEIGHT = 512

-- Buffer constructor info for convenience
local BUFFER_SIZE = WIDTH * HEIGHT
local BUFFER_INFO = { {
		name = hash("rgba"),
		type = buffer.VALUE_TYPE_UINT8,
		count = 4
} }

-- Texture header for convenience
local HEADER = {
	width = WIDTH,
	height = HEIGHT,
	type = resource.TEXTURE_TYPE_2D,
	format = resource.TEXTURE_FORMAT_RGBA,
	num_mip_maps = 1
}

----------------------------------------------------------------
-- HELPER FUNCTIONS
----------------------------------------------------------------

-- Paints a single pixel on the canvas
local function paint(self, x, y, r, g, b, a)
	assert(0 <= x and x < WIDTH and 0 <= y and y < HEIGHT)
	local index = y * WIDTH * 4 + x * 4 + 1
	self.stream[index + 0] = r
	self.stream[index + 1] = g
	self.stream[index + 2] = b
	self.stream[index + 3] = a
	self.dirty = true
end

-- Replicates a grid of pixels by copying it to the canvas
local function replicate(self, grid, grid_width, grid_height)
	assert(grid_width <= WIDTH and grid_height <= HEIGHT)
	for y = 0, grid_height - 1 do
		for x = 0, grid_width - 1 do
			local index = y * grid_width + x + 1
			paint(self, x, y, grid[index].x, grid[index].y, grid[index].z, grid[index].w)
		end
	end
end

----------------------------------------------------------------
-- ENGINE FUNCTIONS
----------------------------------------------------------------

-- Init canvas with a buffer and a stream with which to edit the buffer
function init(self)
	self.buffer = buffer.create(BUFFER_SIZE, BUFFER_INFO)
	self.stream = buffer.get_stream(self.buffer, hash("rgba"))
end

-- Update canvas with new pixels when dirty
function update(self, dt)
	if self.dirty then
		local path = go.get("#sprite", "texture0")
		resource.set_texture(path, HEADER, self.buffer)
		self.dirty = true
	end
end

-- Process paint request messages
function on_message(self, message_id, message, sender)
	if message_id == REPLICATE then
		replicate(self, message.grid, message.grid_width, message.grid_height)
	end
end
2 Likes