Perlin Noise and Seeds

Keeping it short, I’m curious of how to implement custom Perlin Noise of which is generated from a seed and can have modified amplitude and frequency.

Roblox’s Perlin Noise appears to be only this (but 3D instead of 2D):

Roblox Perlin Noise

The issue is that this is only one seed. The most you can do is apply some offset based on a seed, but it’s the same noise map in the end. You can also divide the coordinates to give smoother results, but that’s not entirely ideal either. I want to create truly unique Perlin Noise from each unique seed.

2 Likes

Perlin noise is self-similar, which means that picking a random offset is the same as picking a random seed. What are you planning to use this for? There is a chance you don’t need the “true random” property you’re looking for, most things that aren’t cryptography don’t.

1 Like

I’m sure that’s the case, or at least I would think there would be a larger noise map. For example, Minecraft has 18Q unique worlds. Now I’m sure that accounts for all of the different possible combinations of noise maps that they use, but can that really add up to that big of a number?

Your asking a way broader question about the “size” of a random generator. Minecraft has about that many possible worlds because the size of the input seed is 64 bits, so the number of worlds is 2^64 assuming no seeds produce the same world. In cryptography this would be called the “space” of the random function. There is no guarantee however that no two of those minecraft worlds are almost identical, it is possible to make a “bad” random function with many repeated or missing possible outputs.
There are many properties that random generators can have, most of them are required for cryptography use, but only some of them are needed for non security related use in games. For example Minecrafts world generator is not cryptographically secure, since if someone sees a screenshot of your bedrock pattern and knows the world seed, they can calculate what position you took it at.
Can you find a more specific property that you want your noise to have?

2 Likes

Well I’ll be blunt then I guess. I’m attempting my own version of Minecraft. I’ve got most of it figured out now, but the part that worries me is how unique each seed really is. Because once you go far enough in a direction, it’ll loop around to the other side of the noise. Obviously there are multiple noise maps at play here so there would be different types of terrain and biomes, but it’s still essentially repeating the same noise.

What I mean to say is that it’s similar to starting in the same world, but in a different spot in that world (kind of). Is there a better way to diversify seeds for the entire process?

1 Like

You can hash the input to the noise function which will “scramble” the points inside the function for the same position. I can give you a basic example of this bit it’ll be a while, so let me know if you want that.

I would appreciate that. This whole project has been giving me a headache (feels like I’m relearning math from my schooldays all over again).

Also, is interpolation of positions less computational than reading from multiple noise maps per block? Actually I don’t know if that really even matters. The actual generating of blocks have been the bottleneck rather than computation (especially if using parallel processing).

Ok well let me give you some more info first that might end up helping you later. You’ve recognized that the numbers will loop around after a while, and thats because all numbers on a computer are actually numbers in some modulus 2^N where N is the number of bits that number contains. Because these possible numbers form a “loop” there are some differences between real life integers that have unlimited size.
A language like Lua does you a bit of a disservice here by hiding how this works behind the scenes.
You can make functions that take advantage of this looping in order to not have different behavior “far from zero” or even when jumping around the far end (overflow). Something like math.sin doesn’t provide this, since its input is a float, and the further you get from zero the “chunkier” the output will be until it just becomes a constant value.

1 Like

What do you mean by this exactly? Or do you just mean that the looping is a good thing? Is there a way to scale this up at all or do I just have to rely on the different combinations of noise maps for the diversity?

I’m saying you don’t have to use perlin noise (or any real noise function like Simplex) specifically. Perlin noise just gives you smoothness, the random component of perlin noise is actually just a fixed list of random numbers that gets read in different orders, the actual output is interpolation between these points. The reason they are desirable for Minecraft-like world generation is this smoothness property. If you placed ore by giving every underground block a 1% chance of being iron, then you’d have a bunch of single block ore veins with extremely low chance (about 0.01 x 0.01 x 26) chance of being two block veins, which wouldn’t look good. With a noise function, you can “shave off” the top of to create smooth clumps:


I think this is the property, “clumpiness” (not a real term I think?) that you are looking for?

2 Likes

Ah, that’s what you meant. I guess I’ll just keep going with what I’ve got so far and see if it seems diverse enough. There’s like 6 different noise maps and all of these spline point things. Just trying to wrap my head around all of it. I’m just trying to see if I can figure out a decent seed system so I don’t have to redo much.

This also doesn’t account for 3D noise which I can’t figure out how to apply in relation to the existing height generation. It’s easy enough for caves and the like, but I mean for surface generation like overhangs and things above ground. I can do that in a vacuum, but I don’t see how to correlate it to the existing 2D height map.

1 Like

For terrain height you can just scale a 2D noise. For example if you want height to vary between 60 and 100, you could do something like height = noise*40 + 60. Remember that you can get a 2D noise by taking a slice of 3D noise, that might help you later for making different features match up.

I’m using this as a reference for the 2D part of the generation. Where continentalness, erosion, and peaks & valleys are all height factors. Then temperature, humidity, and weirdness (which isn’t shown there) would help determine the type of biome (also using the other 3 noise maps).

Now the part that confuses me is how to apply 3D noise to it like this:

I can generate that alone, but I don’t understand how to apply it in relation to all of the other noise maps listed above. Because normally when working with a seed offset, you use one unused axis (of the 3D noise, because you’d be using 2D for the heightmap) for that. But with 3D noise, all 3 are used. I feel like I’m missing something obvious here…

I think whats going on here in the bottom picture is they are using 2 dimensions of a noise function to get an initial height, then using the third axis to create the “scooping” effect. You would take the initial Height value and plug it back into the noise function like Noise(X, Height, Z), then if the new noise value is higher, generate the block, and if its lower, don’t generate. This would allow there to be variation in the height axis. I’m just kind of guessing here so let me know how it looks.

1 Like

I threw this together quickly and I think I’m getting the effect you want

--Terrain size
local Sx = 40
local Sy = 16
local Sz = 40

local partPrototype = Instance.new("Part")
partPrototype.Anchored = true
partPrototype.Size = Vector3.one
partPrototype.TopSurface = Enum.SurfaceType.Smooth
partPrototype.BottomSurface = Enum.SurfaceType.Smooth

--Scale of terrain in XZ direction
local scale = 4.0

--Scale of undercut effect
local qscale = 6.0

--How strong the undercut effect is
local qpower = 0.75

--Raise terrain this much extra
local yoffset = 5.0

for x = 1, Sx do
	for z = 1, Sz do
		
		local height = (Sy * math.noise(x / Sx * scale, 0, z / Sz * scale) + Sy) / 2.0
		
		for y = 1, Sy do
			
			local height3d = (Sy * math.noise(x / Sx * scale, y / Sy * qscale, z / Sz * scale) + Sy) / 2.0
			
			if height - height3d * qpower + yoffset > y then
				local part = partPrototype:Clone()
				part.Position = Vector3.new(x, y, z)
				part.Parent = workspace
				part.Color = Color3.fromRGB(x / Sx * 255, y / Sy * 255, z / Sz * 255)
			end
		end
	end
end

2 Likes

I’ll give you a better response a bit later, I’m currently rewriting everything from scratch all into one proper system. Everything I’ve done has been split up into different saved projects in their own vacuum. I hope I get everything right this first attempt…

I’m not sure if this would even be necessary, but doesn’t this mean I can just create my own noise function by generating a 3D matrix of random points? Then with the position, I essentially just Lerp between the closest points? Though I’m guessing this probably more inefficient than just using Roblox’s built-in noise function.

Because I’m wondering if Minecraft uses the seed to generate their own 3D matrix for noise.

Yes, if you interpolate between random vectors in the same way Perlin does (using the dot product), the resulting function will be smooth. Which random vectors you choose to interpolate between doesn’t really matter.

1 Like

Wouldn’t it matter for consistency? I would think it should pull from points the same way every time for the same seed, right?

Wouldn’t this be a better way to handle seeds rather than using Roblox’s noise system, or would it not really matter?

It would give you more control over the random vectors as you could make the random function depend on any values you want instead of just x y and z. By consistency I assume you mean the same seed always giving you the same value for the same inputs. This is guaranteed to be the case if your random vector output is only a function of your input coordinates and the seed.

1 Like