Why is my terrain generation not working properly?

Hello there im trying to make a terrain generator with biomes but its not working properly. The generation is very wonky(image below). How can i fix this?


The diffrent types of biomes i have are as listed :

  • Mountain : snow and rock very hilly
  • Desert : small hills and sand
  • Grassland : small hills and grass
  • Ice : medium hills and snow
    I also want the biomes to be decent sized too.
    the script i have is
local X, Z = 20,20
BiomeScaleA = 40*4*3/2/8/8
BiomeScaleB = 30*4*3/2/8/8
BiomeScaleC = 30*4*2/2/8/8
BiomeScaleD = 20*4*1/2/8/8
BiomeScaleE = 20*4/2/8/8

BiomeStrengthA = 1 * 1.5
BiomeStrengthB = .5 * 1.5
BiomeStrengthC = -0.5 * 1.5
BiomeStrengthD = .5 * 1.5
BiomeStrengthE = -0.5 * 1.5

BiomeZoneMin = -200000
BiomeZoneMax = 200000
Rvar = math.random(BiomeZoneMin,BiomeZoneMax)
Gvar = math.random(BiomeZoneMin,BiomeZoneMax)
Bvar = math.random(BiomeZoneMin,BiomeZoneMax)
Svar = math.random(BiomeZoneMin,BiomeZoneMax)
TempGrid = {}
WetGrid = {}
Final = {}
local Budget = 1/60 -- seconds

local expireTime = 0
-- Call at start of process.
function ResetTimer()
	expireTime = tick() + Budget
end

-- Call where appropriate, such as at the top of loops.
function MaybeYield()
	if tick() >= expireTime then
		game:GetService("RunService").Heartbeat:Wait() -- insert preferred yielding method
		ResetTimer()
	end
end
function sig(x, k)
	return (x-k*x)/(k-2*k*math.abs(x) + 1)
end
ResetTimer()
for x = -X, X do
	MaybeYield()
	TempGrid[x] = {}
	for z = -Z, Z do
		local y = (math.noise(x/30,0,z/30)) * 0.5 + (math.noise(x/40,0,z/40)) * 0.3 + (math.noise(x/7,0,z/7)) * 0.2
		local r = (math.noise(x/BiomeScaleA + Rvar, 0, z/BiomeScaleA + Rvar)) * BiomeStrengthA + (math.noise(x/BiomeScaleB + Rvar, 0, z/BiomeScaleB + Rvar)) * BiomeStrengthB + (math.noise(x/BiomeScaleC + Rvar, 0, z/BiomeScaleC + Rvar)) * BiomeStrengthC + (math.noise(x/BiomeScaleD + Rvar, 0, z/BiomeScaleD + Rvar)) * BiomeStrengthD + (math.noise(x/BiomeScaleE + Rvar, 0, z/BiomeScaleE + Rvar)) * BiomeStrengthE
		local g = (math.noise(x/BiomeScaleA + Gvar, 0, z/BiomeScaleA + Gvar)) * BiomeStrengthA + (math.noise(x/BiomeScaleB + Gvar, 0, z/BiomeScaleB + Gvar)) * BiomeStrengthB + (math.noise(x/BiomeScaleC + Gvar, 0, z/BiomeScaleC + Gvar)) * BiomeStrengthC + (math.noise(x/BiomeScaleD + Gvar, 0, z/BiomeScaleD + Gvar)) * BiomeStrengthD + (math.noise(x/BiomeScaleE + Gvar, 0, z/BiomeScaleE + Gvar)) * BiomeStrengthE
		local b = (math.noise(x/BiomeScaleA + Bvar, 0, z/BiomeScaleA + Bvar)) * BiomeStrengthA + (math.noise(x/BiomeScaleB + Bvar, 0, z/BiomeScaleB + Bvar)) * BiomeStrengthB + (math.noise(x/BiomeScaleC + Bvar, 0, z/BiomeScaleC + Bvar)) * BiomeStrengthC + (math.noise(x/BiomeScaleD + Bvar, 0, z/BiomeScaleD + Bvar)) * BiomeStrengthD + (math.noise(x/BiomeScaleE + Bvar, 0, z/BiomeScaleE + Bvar)) * BiomeStrengthE
		local s = (math.noise(x/BiomeScaleA*3 + Svar, 0, z/BiomeScaleA*3 + Svar)) * BiomeStrengthA + (math.noise(x/BiomeScaleB*3 + Svar, 0, z/BiomeScaleB*3 + Svar)) * BiomeStrengthB + (math.noise(x/BiomeScaleC*3 + Svar, 0, z/BiomeScaleC*3 + Svar)) * BiomeStrengthC + (math.noise(x/BiomeScaleD*3 + Svar, 0, z/BiomeScaleD*3 + Svar)) * BiomeStrengthD + (math.noise(x/BiomeScaleE*3 + Svar, 0, z/BiomeScaleE*3 + Svar)) * BiomeStrengthE
		local yVar = (1 + math.noise(x/200 + 999,0,z/200 + 999))*5
		y = (y*yVar*(r*4+g*7-b*6)+sig(s*3,-0.4)*16*r * g * 2)
		local val = ""
		if y < 0 then
			val = "High"
		elseif y > 0 then
			val = "Low"
		end
		TempGrid[x][z] = val
	end
end
ResetTimer()
Rvar = math.random(BiomeZoneMin,BiomeZoneMax)
Gvar = math.random(BiomeZoneMin,BiomeZoneMax)
Bvar = math.random(BiomeZoneMin,BiomeZoneMax)
Svar = math.random(BiomeZoneMin,BiomeZoneMax)
for x = -X, X do
	MaybeYield()
	WetGrid[x] = {}
	for z = -Z, Z do
		local y = (math.noise(x/30,0,z/30)) * 0.5 + (math.noise(x/40,0,z/40)) * 0.3 + (math.noise(x/7,0,z/7)) * 0.2
		local r = (math.noise(x/BiomeScaleA + Rvar, 0, z/BiomeScaleA + Rvar)) * BiomeStrengthA + (math.noise(x/BiomeScaleB + Rvar, 0, z/BiomeScaleB + Rvar)) * BiomeStrengthB + (math.noise(x/BiomeScaleC + Rvar, 0, z/BiomeScaleC + Rvar)) * BiomeStrengthC + (math.noise(x/BiomeScaleD + Rvar, 0, z/BiomeScaleD + Rvar)) * BiomeStrengthD + (math.noise(x/BiomeScaleE + Rvar, 0, z/BiomeScaleE + Rvar)) * BiomeStrengthE
		local g = (math.noise(x/BiomeScaleA + Gvar, 0, z/BiomeScaleA + Gvar)) * BiomeStrengthA + (math.noise(x/BiomeScaleB + Gvar, 0, z/BiomeScaleB + Gvar)) * BiomeStrengthB + (math.noise(x/BiomeScaleC + Gvar, 0, z/BiomeScaleC + Gvar)) * BiomeStrengthC + (math.noise(x/BiomeScaleD + Gvar, 0, z/BiomeScaleD + Gvar)) * BiomeStrengthD + (math.noise(x/BiomeScaleE + Gvar, 0, z/BiomeScaleE + Gvar)) * BiomeStrengthE
		local b = (math.noise(x/BiomeScaleA + Bvar, 0, z/BiomeScaleA + Bvar)) * BiomeStrengthA + (math.noise(x/BiomeScaleB + Bvar, 0, z/BiomeScaleB + Bvar)) * BiomeStrengthB + (math.noise(x/BiomeScaleC + Bvar, 0, z/BiomeScaleC + Bvar)) * BiomeStrengthC + (math.noise(x/BiomeScaleD + Bvar, 0, z/BiomeScaleD + Bvar)) * BiomeStrengthD + (math.noise(x/BiomeScaleE + Bvar, 0, z/BiomeScaleE + Bvar)) * BiomeStrengthE
		local s = (math.noise(x/BiomeScaleA*3 + Svar, 0, z/BiomeScaleA*3 + Svar)) * BiomeStrengthA + (math.noise(x/BiomeScaleB*3 + Svar, 0, z/BiomeScaleB*3 + Svar)) * BiomeStrengthB + (math.noise(x/BiomeScaleC*3 + Svar, 0, z/BiomeScaleC*3 + Svar)) * BiomeStrengthC + (math.noise(x/BiomeScaleD*3 + Svar, 0, z/BiomeScaleD*3 + Svar)) * BiomeStrengthD + (math.noise(x/BiomeScaleE*3 + Svar, 0, z/BiomeScaleE*3 + Svar)) * BiomeStrengthE
		local yVar = (1 + math.noise(x/200 + 999,0,z/200 + 999))*5
		y = (y*yVar*(r*4+g*7-b*6)+sig(s*3,-0.4)*16*r * g * 2)
		local val = ""
		if y < 0 then
			val = "High"
		elseif y > 0 then
			val = "Low"
		end
		WetGrid[x][z] = val
	end
end
ResetTimer()
for x = -X, X do
	MaybeYield()
	Final[x] = {}
	for z = -Z, Z do
		local val = ""
		local firstvalue = TempGrid[x][z]
		local secondvalue = WetGrid[x][z]
		if firstvalue == "High" and secondvalue == "Low" then
			val = "Desert"
		elseif firstvalue == "Low" and secondvalue == "Low" then
			val = "Mountains"
		elseif firstvalue == "High" and secondvalue == "High" then
			val = "Grass"
		elseif firstvalue == "Low" and secondvalue == "High" then
			val = "Ice"
		end
		Final[x][z] = val
	end
end
local wedge = Instance.new("WedgePart");
wedge.Anchored = true;
wedge.TopSurface = Enum.SurfaceType.Smooth;
wedge.BottomSurface = Enum.SurfaceType.Smooth;
local function draw3dTriangle(a, b, c, part)
	local ab, ac, bc = b - a, c - a, c - b;
	local abd, acd, bcd = ab:Dot(ab), ac:Dot(ac), bc:Dot(bc);

	if (abd > acd and abd > bcd) then
		c, a = a, c;
	elseif (acd > bcd and acd > abd) then
		a, b = b, a;
	end

	ab, ac, bc = b - a, c - a, c - b;

	local right = ac:Cross(ab).unit;
	local up = bc:Cross(right).unit;
	local back = bc.unit;

	local height = math.abs(ab:Dot(up));

	local w1 = wedge:Clone();
	w1.Size = Vector3.new(2, height, math.abs(ab:Dot(back)));
	w1.CFrame = CFrame.fromMatrix((a + b)/2, right, up, back);
	w1.Parent = workspace.BlockTerrain;

	local w2 = wedge:Clone();
	w2.Size = Vector3.new(2, height, math.abs(ac:Dot(back)));
	w2.CFrame = CFrame.fromMatrix((a + c)/2, -right, up, -back);
	w2.Parent = workspace.BlockTerrain;
	local s= w1.Size+Vector3.new(20,0,0)
	local v = w2
	if part == "Desert" then
		workspace.Terrain:FillBlock(w1.CFrame,s,Enum.Material.Sand)
		w1:Destroy()
		workspace.Terrain:FillBlock(v.CFrame,s,Enum.Material.Sand)
		v:Destroy()
	else
		if w1.Position.Y < -3 then
			workspace.Terrain:FillBlock(w1.CFrame,s,Enum.Material.Sand)
			w1:Destroy()
		elseif w1.Position.Y > -5 and w1.Position.Y < 100 then
			local terraintype = math.random(1,10)
			if terraintype == 1 then
				workspace.Terrain:FillBlock(w1.CFrame,s,Enum.Material.Ground)
				w1:Destroy()
			elseif terraintype == 2 then
				workspace.Terrain:FillBlock(w1.CFrame,s,Enum.Material.LeafyGrass)
				w1:Destroy()
			else
				workspace.Terrain:FillBlock(w1.CFrame,s,Enum.Material.Grass)
				w1:Destroy()
			end
		else
			local terraintype = math.random(1,10)
			if terraintype == 1 then
				workspace.Terrain:FillBlock(w1.CFrame,s,Enum.Material.Rock)
				w1:Destroy()
			else
				workspace.Terrain:FillBlock(w1.CFrame,s,Enum.Material.Snow)
				w1:Destroy()
			end
		end
		local s= v.Size+Vector3.new(20,0,0)
		if v.Position.Y < -3 then
			workspace.Terrain:FillBlock(v.CFrame,s,Enum.Material.Sand)
			v:Destroy()
		elseif v.Position.Y > -5 and v.Position.Y < 100 then
			local terraintype = math.random(1,10)
			if terraintype == 1 then
				workspace.Terrain:FillBlock(v.CFrame,s,Enum.Material.Ground)
				v:Destroy()
			elseif terraintype == 2 then
				workspace.Terrain:FillBlock(v.CFrame,s,Enum.Material.LeafyGrass)
				v:Destroy()
			else
				workspace.Terrain:FillBlock(v.CFrame,s,Enum.Material.Grass)
				v:Destroy()
			end
		else
			local terraintype = math.random(1,10)
			if terraintype == 1 then
				workspace.Terrain:FillBlock(v.CFrame,s,Enum.Material.Rock)
				v:Destroy()
			else
				workspace.Terrain:FillBlock(v.CFrame,s,Enum.Material.Snow)
				v:Destroy()
			end
		end
	end
	return w1, w2;
end

local positionGrid = {}
local count = 0
ResetTimer()
Rvar = math.random(BiomeZoneMin,BiomeZoneMax)
Gvar = math.random(BiomeZoneMin,BiomeZoneMax)
Bvar = math.random(BiomeZoneMin,BiomeZoneMax)
Svar = math.random(BiomeZoneMin,BiomeZoneMax)
for x = -X, X do
	MaybeYield()
	positionGrid[x] = {}
	for z = -Z, Z do
		local biome = Final[x][z]
		if biome == "Mountains" then
			BiomeScaleA = 40*4*3/2/5
			BiomeScaleB = 30*4*3/2/5
			BiomeScaleC = 30*4*2/2/5
			BiomeScaleD = 20*4*1/2/5
			BiomeScaleE = 20*4/2/5
		elseif biome == "Grass" then
			BiomeScaleA = 40*4*3/2
			BiomeScaleB = 30*4*3/2
			BiomeScaleC = 30*4*2/2
			BiomeScaleD = 20*4*1/2
			BiomeScaleE = 20*4/2
		elseif biome == "Desert" then
			BiomeScaleA = 40*4*3/2
			BiomeScaleB = 30*4*3/2
			BiomeScaleC = 30*4*2/2
			BiomeScaleD = 20*4*1/2
			BiomeScaleE = 20*4/2
		elseif biome == "Ice" then
			BiomeScaleA = 40*4*3/2
			BiomeScaleB = 30*4*3/2
			BiomeScaleC = 30*4*2/2
			BiomeScaleD = 20*4*1/2
			BiomeScaleE = 20*4/2
		end 
		local y = (math.noise(x/30,0,z/30)) * 0.5 + (math.noise(x/40,0,z/40)) * 0.3 + (math.noise(x/7,0,z/7)) * 0.2
		local r = (math.noise(x/BiomeScaleA + Rvar, 0, z/BiomeScaleA + Rvar)) * BiomeStrengthA + (math.noise(x/BiomeScaleB + Rvar, 0, z/BiomeScaleB + Rvar)) * BiomeStrengthB + (math.noise(x/BiomeScaleC + Rvar, 0, z/BiomeScaleC + Rvar)) * BiomeStrengthC + (math.noise(x/BiomeScaleD + Rvar, 0, z/BiomeScaleD + Rvar)) * BiomeStrengthD + (math.noise(x/BiomeScaleE + Rvar, 0, z/BiomeScaleE + Rvar)) * BiomeStrengthE
		local g = (math.noise(x/BiomeScaleA + Gvar, 0, z/BiomeScaleA + Gvar)) * BiomeStrengthA + (math.noise(x/BiomeScaleB + Gvar, 0, z/BiomeScaleB + Gvar)) * BiomeStrengthB + (math.noise(x/BiomeScaleC + Gvar, 0, z/BiomeScaleC + Gvar)) * BiomeStrengthC + (math.noise(x/BiomeScaleD + Gvar, 0, z/BiomeScaleD + Gvar)) * BiomeStrengthD + (math.noise(x/BiomeScaleE + Gvar, 0, z/BiomeScaleE + Gvar)) * BiomeStrengthE
		local b = (math.noise(x/BiomeScaleA + Bvar, 0, z/BiomeScaleA + Bvar)) * BiomeStrengthA + (math.noise(x/BiomeScaleB + Bvar, 0, z/BiomeScaleB + Bvar)) * BiomeStrengthB + (math.noise(x/BiomeScaleC + Bvar, 0, z/BiomeScaleC + Bvar)) * BiomeStrengthC + (math.noise(x/BiomeScaleD + Bvar, 0, z/BiomeScaleD + Bvar)) * BiomeStrengthD + (math.noise(x/BiomeScaleE + Bvar, 0, z/BiomeScaleE + Bvar)) * BiomeStrengthE
		local s = (math.noise(x/BiomeScaleA*3 + Svar, 0, z/BiomeScaleA*3 + Svar)) * BiomeStrengthA + (math.noise(x/BiomeScaleB*3 + Svar, 0, z/BiomeScaleB*3 + Svar)) * BiomeStrengthB + (math.noise(x/BiomeScaleC*3 + Svar, 0, z/BiomeScaleC*3 + Svar)) * BiomeStrengthC + (math.noise(x/BiomeScaleD*3 + Svar, 0, z/BiomeScaleD*3 + Svar)) * BiomeStrengthD + (math.noise(x/BiomeScaleE*3 + Svar, 0, z/BiomeScaleE*3 + Svar)) * BiomeStrengthE
		local yVar = (1 + math.noise(x/200 + 999,0,z/200 + 999))*5
		y = (y*yVar*(r*4+g*7-b*6)+sig(s*3,-0.4)*16*r * g * 2) + 3
		if y < -1 then
			y = sig(y, -0.02)
		end
		positionGrid[x][z] = x*15 .. ":" ..  y * 10 .. ":" .. z*15 .. ":".. biome
	end
end
ResetTimer()
for x = -X, X-1 do
	MaybeYield()
	for z = -Z, Z-1 do
		local a = positionGrid[x][z]
		local info = string.split(a, ":")
		local one = info[1]
		local two = info[2]
		local three = info[3]
		local part = info[4]
		a = Vector3.new(one, two, three)
		local b = positionGrid[x+1][z]
		local info = string.split(b, ":")
		local one = info[1]
		local two = info[2]
		local three = info[3]
		b = Vector3.new(one, two, three)
		local c = positionGrid[x][z+1]
		local info = string.split(c, ":")
		local one = info[1]
		local two = info[2]
		local three = info[3]
		c = Vector3.new(one, two, three)
		local d = positionGrid[x+1][z+1]
		local info = string.split(d, ":")
		local one = info[1]
		local two = info[2]
		local three = info[3]
		d = Vector3.new(one, two, three)

		draw3dTriangle(a, b, c, part)
		draw3dTriangle(b, c, d, part)
	end
end
print(Final)

I can’t tell exactly what’s going wrong, but first of all it seems the biomes are way too small. Didn’t look through all of it but it looked like you were generating 40 * 40? If that’s the case then each biome is sized at a couple voxels, so you may want to change that.

i tried generating 100*100 this is what i got

It’s still not clear whether this is about biome borders. Could you raise the biome scales by 5X or so?

Still the same result MASSIVE stick hills and little patches of desert