How do I generate islands with Perlin Noise?

While I have been able to use perlin noise to create terrain as in the image below, I am struggling to think how I can apply it to an island in a 3D format.

I am mostly wondering how I can:

  • Make it an island (or a few islands very close together);
  • Make the terrain elevations.

For further reference as to what I am trying to do, see the image below:

heres a bit of a base

local X,Z = 10,10
for x=1,X do
for z=1,Z do
local y = math.noise(x,z)/15
end
end

I made this using a roblox dev hub post

just added 1 line lol

local Players = game:GetService("Players")

------------------------------------------------------------------------------------------------------------------------------------------------

local BASE_HEIGHT 		= 10				-- The main height factor for the terrain.
local CHUNK_SCALE 		= 3 				-- The grid scale for terrain generation. Should be kept relatively low if used in real-time.
local RENDER_DISTANCE 	= 120 / 4 			-- The length/width of chunks in voxels that should be around the player at all times
local X_SCALE 			= 90 / 4			-- How much we should strech the X scale of the generation noise
local Z_SCALE 			= 90 / 4			-- How much we should strech the Z scale of the generation noise
local GENERATION_SEED	= math.random() 	-- Seed for determining the main height map of the terrain.

------------------------------------------------------------------------------------------------------------------------------------------------

local chunks = {}

local function chunkExists(chunkX, chunkZ)
	if not chunks[chunkX] then
		chunks[chunkX] = {}
	end
	return chunks[chunkX][chunkZ]
end

local function mountLayer(x, heightY, z, material)
	local beginY = -BASE_HEIGHT
	local endY = heightY
	local cframe = CFrame.new(x * 4 + 2, (beginY + endY) * 4 / 2, z * 4 + 2)
	local size = Vector3.new(4, (endY - beginY) * 4, 4)
	workspace.Terrain:FillBlock(cframe, size, material)	
end

function makeChunk(chunkX, chunkZ)
	local rootPosition = Vector3.new(chunkX * CHUNK_SCALE, 0, chunkZ * CHUNK_SCALE)
	chunks[chunkX][chunkZ] = true -- Acknowledge the chunk's existance.
	for x = 0, CHUNK_SCALE - 1 do
		for z = 0, CHUNK_SCALE - 1 do
			local cx = (chunkX * CHUNK_SCALE) + x
			local cz = (chunkZ * CHUNK_SCALE) + z
			local noise = math.noise(GENERATION_SEED, cx / X_SCALE, cz / Z_SCALE)
			local cy = noise * BASE_HEIGHT
			if cy <= 0 then return end
			mountLayer(cx, cy, cz, Enum.Material.Grass)
		end
	end
end

function checkSurroundings(location)
	local chunkX, chunkZ = math.floor(location.X / 4 / CHUNK_SCALE), math.floor(location.Z / 4 / CHUNK_SCALE)
	local range = math.max(1, RENDER_DISTANCE / CHUNK_SCALE)
	for x = -range, range do
		for z = -range, range do
			local cx, cz = chunkX + x
			local cz = chunkZ + z
			if not chunkExists(cx, cz) then
				makeChunk(cx, cz)
			end
		end
	end
end

while true do
	for _, player in pairs(Players:GetPlayers()) do
		if player.Character then
			local humanoidRootPart = player.Character:FindFirstChild("HumanoidRootPart")
			if humanoidRootPart then
				checkSurroundings(humanoidRootPart.Position)
			end
		end
	end
	wait(1)
end

Hi I would advise a 2d falloff, And to first generate the basic heightmap, Then apply the 3d stuff later

While this helps with the height, I am still not sure how I can make it an island.

i guess add big spaces? using offset?

Ill look into it!
(char limit)

I am pretty sure that is not how Perlin Noise works…

well its not but you do have to position it right? so yea add offset and big spaces in there

Hi i gound a code that i used


local function sineEase(val)
	return math.sin(val * math.pi / 2)
end

local function cosEase(val)
	return 1 - math.cos(val * math.pi / 2)
end

-- input values between -1 and 1, output value between 0 and 1
function CircularGrayscale.GetVal(xScale, yScale)
	xScale, yScale = math.abs(xScale), math.abs(yScale)
	local squaredDistToEdgeInXZDir = 1 + math.min(xScale, yScale)^2
	local linear = 1 - math.sqrt( (xScale^2 + yScale^2) / squaredDistToEdgeInXZDir )
	
	local val = linear ^ squaredDistToEdgeInXZDir
	--val = sineEase(val)
	--val = cosEase(val)
	return val
end

return CircularGrayscale 

The code is not mine you basically get the vale from this module script and add it with perlin 2d then you add 3d perlin

1 Like

Ohhhh this is really smart! Thank you very much!

For the elevations would you just generate based on the noise value or some other method?
For example, height in this coordinate = math.Perlin(x,z, seed) * 30.

height in this coordinate = math.Perlin(x,z, seed) * 30 + CircularGrayscale.GetVal(x, z) this will allow for the heightmap, Also remember to posibly modify the function to make it accepts size value
Modified code for my plugin although i recommend you to modify it to work with no value in workspace

local CircularGrayscale = {}

local function sineEase(val)
	return math.sin(val * math.pi / 2)
end

local function cosEase(val)
	return 1 - math.cos(val * math.pi / 2)
end

-- input values between -1 and 1, output value between 0 and 1
function CircularGrayscale.applyGradient(voronoipart, x, z,heightMapSize)
	if heightMapSize == nil then
		heightMapSize = workspace:WaitForChild("Voronoi"):WaitForChild("Size").Value
	end
	local voronoiPos = voronoipart.Position
	local distance_x = math.abs(x - voronoiPos.X * 0.5)
	local distance_y = math.abs(z - voronoiPos.Z * 0.5)
	local distance = math.sqrt(distance_x*distance_x + distance_y*distance_y)

	local max_width = heightMapSize * 0.5 - 10.0
	local delta = distance / max_width
	local gradient = delta * delta
	if voronoipart:FindFirstChild("Multiplyer") then
		return math.max(0.0, 1.0 - gradient)*voronoipart:FindFirstChild("Multiplyer").Value
	else
		return math.max(0.0, 1.0 - gradient)*workspace:WaitForChild("Voronoi"):WaitForChild("Multiplyer").Value
	end
end

return CircularGrayscale
2 Likes