How can i improve my terrain generator

Recently i made this terrain generator that uses fractal noise to generate terrain with trees, hills, rocks and river. i was wondering how i can improve it by adding some area with less trees and some gigantic mountains, or anything you guys suggest.

here is what a gameplay looks like

here is the code:

WIDTH, DEPTH, HEIGHT = 159, 150, 30

local function Round(num, mult)
	return math.floor(num / mult + 0.5) * mult
end

FractalNoise = require(script.FractalNoise)

PART_SCALE = 5
NOISE_SCALE = 250
HEIGHT_SCALE = 120



OCTAVES = 25
LACUNARITY = 3
PERSISTENCE = .1
SEED = math.random(1,999999)
FeatureSpawns = {}


function DeletePickedSpot(Position, Distance)
	
	for i, v in ipairs(FeatureSpawns) do
		if v then
			if (v.Position - Position).Magnitude < Distance then
				return true
			end
		end
	end
	
	return false
end

for x=0,WIDTH*PART_SCALE,PART_SCALE do
	for z=0,DEPTH*PART_SCALE,PART_SCALE do

		
		
		
		
		local TerrainNoise = FractalNoise["2D"](x, z,
			OCTAVES,
			LACUNARITY,
			PERSISTENCE,
			NOISE_SCALE,
			SEED
		) * HEIGHT_SCALE
		
		TerrainNoise = Round(TerrainNoise, PART_SCALE)
		TerrainNoise += (HEIGHT*PART_SCALE) - HEIGHT_SCALE
		
		local SecondTerrainNoise = FractalNoise["2DModified"](x, z,
			35,
			3,
			.2,
			100,
			SEED * 2
		) * HEIGHT_SCALE

		SecondTerrainNoise = Round(SecondTerrainNoise, PART_SCALE)
		SecondTerrainNoise += (HEIGHT*PART_SCALE) - HEIGHT_SCALE
		
		local VariationNoise = FractalNoise["2D"](x, z,
			OCTAVES,
			LACUNARITY,
			PERSISTENCE,
			NOISE_SCALE,
			SEED / 2
		) * HEIGHT_SCALE
		VariationNoise = Round(VariationNoise, PART_SCALE)
		VariationNoise += (HEIGHT*PART_SCALE) - HEIGHT_SCALE
		
		local SecondVariationNoise = FractalNoise["2D"](x, z,
			OCTAVES,
			LACUNARITY,
			PERSISTENCE,
			190,
			SEED / 3
		) * HEIGHT_SCALE
		SecondVariationNoise = Round(SecondVariationNoise, PART_SCALE)
		SecondVariationNoise += (HEIGHT*PART_SCALE) - HEIGHT_SCALE
		
		local MountainNoise = FractalNoise["2DModified"](x, z,
			60,
			3,
			.8,
			300,
			SEED * 255
		) * 400

		MountainNoise = Round(MountainNoise, PART_SCALE)
		MountainNoise += (HEIGHT*PART_SCALE) - HEIGHT_SCALE
		
		
		
		
		

		for y=0,HEIGHT*PART_SCALE,PART_SCALE do
			
			if y > TerrainNoise or y > SecondTerrainNoise and y * 2 > MountainNoise then continue end		
			
			
			
			
			local Material = Enum.Material.Grass
			
			
			
			
			if y > VariationNoise and y < SecondVariationNoise then
				Material = Enum.Material.Rock
			elseif y > SecondVariationNoise then
				Material = Enum.Material.Grass
			elseif y < VariationNoise and y > SecondVariationNoise then
				Material = Enum.Material.Rock
			end
			
			local RiverNoise = FractalNoise["2D"](x, z,
				3,
				2,
				.35,
				1000,
				SEED
			) * 50

			RiverNoise = Round(RiverNoise, PART_SCALE)
			RiverNoise += (HEIGHT*PART_SCALE) - HEIGHT_SCALE
			
			
			if y <= 24 then
				Material = Enum.Material.Ground
			end
			if y <= 30 and y >= 25 and RiverNoise <= 40 then
				Material = Enum.Material.Water
			end
			
			
			
			
			
			
			
			workspace.Terrain:FillBall(Vector3.new(x,y,z), PART_SCALE, Material)
		
		
		
			if Material == Enum.Material.Grass then
				if y >= 30 and y <= 90 then

					local FeatureNoise = math.noise(x / 25, y / 25, SEED) * 30
					-- FeatureNoise >= -1 and FeatureNoise <= 3 and 
					if math.random(1,3) == 2 and VariationNoise == 35 then

						if not DeletePickedSpot(Vector3.new(x + FeatureNoise / math.random(-2.00,2.00),y + 200, z + FeatureNoise / math.random(-2.00,2.00)), math.clamp(FeatureNoise * 2, 0, 1000)) then
							if math.random(1,6) == 6 then

								local RaycastPart = Instance.new("Part", workspace)
								RaycastPart.Anchored = true
								RaycastPart.Position = Vector3.new(x + FeatureNoise * 2,y + 200, z + FeatureNoise * 2)
								RaycastPart.Size = Vector3.new(1,1,1)
								RaycastPart.Rotation = Vector3.new(-90,0,0)
								task.wait()
								local RaycastResult = workspace:Raycast(RaycastPart.Position, RaycastPart.CFrame.LookVector * 1000)
								if RaycastResult then
									if RaycastResult.Material == Enum.Material.Grass then
										local Tree = game.ServerStorage.Features.Trees:GetChildren()[math.random(1,#game.ServerStorage.Features.Trees:GetChildren())]:Clone()
										Tree.Parent = workspace
										Tree:PivotTo(CFrame.new(RaycastResult.Position.X,RaycastResult.Position.Y - 3,RaycastResult.Position.Z) * CFrame.Angles(0,math.rad(math.random(-180,180)),0))

										RaycastPart:Destroy()
									else
										RaycastPart:Destroy()
									end

								else
									RaycastPart:Destroy()
								end
							end
						end


					end
				end
			end

		end
	end
	
	task.wait()
end


and the module code:

FractalNoise = {}

FractalNoise["2D"] = function(x, y, octaves, lacunarity, persistence, scale, seed)
	local value = 0 
	local x1 = x 
	local y1 = y
	local amplitude = 1
	for i = 1, octaves, 1 do
		value += math.abs(math.noise(x1 / scale, y1 / scale, seed)) * amplitude
		y1 *= lacunarity
		x1 *= lacunarity
		amplitude *= persistence
	end
	value = value ^ 2
	return math.clamp(value, -1, 1)
end

FractalNoise["2DModified"] = function(x, y, octaves, lacunarity, persistence, scale, seed)
	local value = 0 
	local x1 = x 
	local y1 = y
	local amplitude = 1
	for i = 1, octaves, 1 do
		value += math.abs(math.noise(x1 / scale, y1 / scale, seed)) * amplitude
		y1 *= lacunarity
		x1 *= lacunarity
		amplitude *= persistence
	end
	return math.clamp(value, -1, 1)
end

FractalNoise["3D"] = function(x, y, z, octaves, lacunarity, persistence, scale, seed)
	local value = 0 
	local x1 = x 
	local y1 = y
	local z1 = z
	local amplitude = 1
	for i = 1, octaves, 1 do
		value += math.noise(x1 / scale, y1 / scale, z1 / scale, seed) * amplitude
		y1 *= lacunarity
		x1 *= lacunarity
		z1 *= lacunarity
		amplitude *= persistence
	end
	return math.clamp(value, -1, 1)
end

return FractalNoise

3 Likes

these videos might be useful.
Now you donโ€™t need to limit yourself to how minecraft does it because it can become quite complex.
A simple solution for mountains is to have a different noise map and multiply your existing noise with that new noise map. You could also exponentiate the noise map to make the peaks higher and the valleys lower.

You also donโ€™t need to limit yourself to perlin noise. you could make your own worley noise to make biomes or regions where only trees grow in that worley noise cell or something.

Hope this helps.

2 Likes