Perlin noise terrain gen chunks


This is an image from my terrain generator, and as you can see the chunks in it are very noticable.
image
Bigger chunks, but still noticable. I am trying to get good caves, and smooth chunking to the point where its seamless between chunks. I chose not to go through a walkthrough, nor guide to learn better. Code:

i = 50
v = 50
--size
ctmsx = 1
ctmxz = 1
--chunk placement
h = 15
p = 20
mh = 5
hlim = 30
--terrain instructions
chunkcount = 0
chunkmax = 10
chunkmulti = 4
--chunk settings
cavegen = 10
stonelayeram = 3
caveres = 6
chunkdivisor = 4
--caves
seed = math.random(1,100000)
--vars end
while chunkcount < chunkmax do
	chunkcount += 1
	if chunkcount < math.round(chunkmax/chunkdivisor)+1 then
		ctmxz += 1
	end
	if chunkcount > math.round(chunkmax/chunkdivisor) and chunkcount < (math.round(chunkmax/chunkdivisor)*2)+1 then
		if ctmsx == 1 then
			ctmsx += 1
			ctmxz -= math.round(chunkmax/chunkdivisor)
		end	
		if ctmsx > 1 then
			ctmxz += 1
		end	
	end 
	if chunkcount > (math.round(chunkmax/chunkdivisor)*2) and chunkcount < chunkmax then
		if ctmsx == 2 then
			ctmsx += 1
			ctmxz -= math.round(chunkmax/chunkdivisor)
		end	
		if ctmsx > 2 then
			ctmxz += 1
		end	
	end
	seed/=math.random(1,2)
	if chunkcount < chunkmax then
		grid = {}
		for x = 1, i do
			grid[x] = {}	
			for z = 1, v do	
				grid[x][z] = math.noise(x/h,seed,z/h)*p
			end
		end
		for x = 1, i do
			game["Run Service"].Heartbeat:Wait()
			for z = 1, v do
				local y = grid[x][z]
				local part = game.ServerStorage.Part:Clone()
				part.Parent = workspace.terrain
				if y >  hlim then
					y = hlim
				end
				part.CFrame = CFrame.new((x*4)+(i*(ctmsx*(chunkmulti))),y,(z*4)+(v*(ctmxz*(chunkmulti))))
				part.BrickColor = BrickColor.new("Forest green")
				part.Material = Enum.Material.Grass
				part.Name = "Grass"
				if part.CFrame.Y < 0 then
					part.BrickColor = BrickColor.new("Pastel yellow")
					part.Material = Enum.Material.Sand
					part.Name = "Sand"
					local water = game.ServerStorage.Part:Clone()
					local mag = 2
					water.Parent = workspace.terrain
					local magnitude = math.abs((Vector3.new(part.Position.X,2,part.Position.Z) - Vector3.new(part.Position.X,part.Position.Y+2,part.Position.Z)).Magnitude)
					water.Size = Vector3.new(4,magnitude,4)
					water.CFrame = CFrame.new((x*4)+(i*(ctmsx*(chunkmulti))),0,(z*4)+(v*(ctmxz*(chunkmulti))))
					water.Material = Enum.Material.SmoothPlastic
					water.Transparency = 0.5
					water.BrickColor = BrickColor.new("Bright bluish green")
					water.Name = "Water"
				end
				if part.CFrame.Y < -2 then
					part.BrickColor = BrickColor.new("Smoky grey")
					part.Material = Enum.Material.Slate
					part.Name = "Stone"
				end 
				if part.CFrame.Y > 25 then
					part.BrickColor = BrickColor.new("Smoky grey")
					part.Material = Enum.Material.Slate
					part.Name = "Stone"
				end
				local randofort = math.random(1,75)
				if randofort == 25 and part.CFrame.Y < 12 and part.CFrame.Y > 1 then
					local cloi = game.ServerStorage["Old roblox tree"]:Clone()
					cloi.Parent = workspace
					local y2 = y+7.181
					cloi:MoveTo(Vector3.new((x*4)+(i*(ctmsx*(chunkmulti))),y,(z*4)+(v*(ctmxz*(chunkmulti)))))
				end
			end
		end
		for x = 1, i do
			game["Run Service"].Heartbeat:Wait()
			for z = 1, v do
				local y = grid[x][z]
				y-=4
				if y >  hlim-4 then
					y = hlim-4
				end
				local part = game.ServerStorage.Part:Clone()
				part.Parent = workspace.terrain
				part.CFrame = CFrame.new((x*4)+(i*(ctmsx*(chunkmulti))),y,(z*4)+(v*(ctmxz*(chunkmulti))))
				part.BrickColor = BrickColor.new("Brown")
				part.Material = Enum.Material.Slate
				part.Name = "Dirt"
			end
		end
		for x = 1, i do
			game["Run Service"].Heartbeat:Wait()
			for z = 1, v do
				local y = grid[x][z]
				y-=8
				if y >  hlim-8 then
					y = hlim-8
				end
				local part = game.ServerStorage.Part:Clone()
				part.Parent = workspace.terrain
				part.CFrame = CFrame.new((x*4)+(i*(ctmsx*(chunkmulti))),y,(z*4)+(v*(ctmxz*(chunkmulti))))
				part.BrickColor = BrickColor.new("Brown")
				part.Material = Enum.Material.Slate
				part.Name = "Dirt"
			end
		end
		local minran = 1
		local maxran = 1
		local stolayer = 0
		local ynegative = 12
		while stolayer ~= stonelayeram do
			for x = 1, i do
				game["Run Service"].Heartbeat:Wait()
				for z = 1, v do
					local y2 = grid[x][z]
					y2-=ynegative
					if y2 >  hlim-ynegative then
						y2 = hlim-ynegative
					end
					local part = game.ServerStorage.Part:Clone()
					part.Parent = workspace.terrain
					part.CFrame = CFrame.new((x*4)+(i*(ctmsx*(chunkmulti))),y2,(z*4)+(v*(ctmxz*(chunkmulti))))
					part.BrickColor = BrickColor.new("Smoky grey")
					local random = math.random(minran,maxran)
					part.Material = Enum.Material.Slate
					part.Name = "Stone"
					if random == minran then
						part:Destroy()
						part.Material = Enum.Material.Slate
						local part = game.ServerStorage.Part:Clone()
						part.Parent = workspace.terrain
						part.CFrame = CFrame.new((x*4)+(i*(ctmsx*(chunkmulti))),y2,(z*4)+(v*(ctmxz*(chunkmulti))))
						part.BrickColor = BrickColor.new("Brown")
						maxran += 0.05
						part.Name = "Dirt"
					end 
				end
			end
			stolayer += 1
			ynegative += 4
		end
	end
end
local Resolution = caveres
local NumWorm = 1
while NumWorm ~= cavegen do
	local Seed = math.random(1,10)
	NumWorm = NumWorm+1
	local sX = math.noise(NumWorm/Resolution+.1,Seed)
	local sY = math.noise(NumWorm/Resolution+sX+.1,Seed)
	local sZ = math.noise(NumWorm/Resolution+sY+.1,Seed)
	local WormCF = CFrame.new((i+v)*NumWorm,0,(i+v)*NumWorm)
	print("Worm "..NumWorm.." spawning at "..WormCF.X..", "..WormCF.Y..", "..WormCF.Z)
	local Dist = (math.noise(NumWorm/Resolution+WormCF.p.magnitude,Seed)+.5)*500
	for i = 1,Dist do
		wait()
		local X,Y,Z = math.noise(WormCF.X/Resolution+.1,Seed),math.noise(WormCF.Y/Resolution+.1,Seed),math.noise(WormCF.Z/Resolution+.1,Seed)
		WormCF = WormCF*CFrame.Angles(X*2,Y*2,Z*2)*CFrame.new(0,0,-Resolution)
		local Part = Instance.new("Part")
		Part.Anchored = true
		Part.CFrame = CFrame.new(WormCF.Position)
		Part.Parent = workspace.worms
		Part.CanCollide = true
		Part.CanTouch = true
		Part.Size = Vector3.new(8,8,8)
		Part.Name = "Wormpart"
		local tabe = Part:GetTouchingParts()
		for k in pairs(tabe) do
			if tabe[k].Name == "Water" or tabe[k].Name == "Sand" or tabe[k].Name == "Wormpart" then
			else
				local box = workspace:GetPartBoundsInBox(tabe[k].CFrame,Vector3.new(8,8,8))
				for p in pairs(box) do
					box[p].BrickColor = BrickColor.new("Smoky grey")
					box[p].Material = Enum.Material.Slate
					box[p].Name = "Stone"
				end
				tabe[k]:Destroy()
			end
		end
	end
end
workspace.worms:ClearAllChildren()
script:Destroy()

Thoughts?

The black rocky parts on the surface of the second image are supposed to be caves.

Honestly I like it, of course youll need to fix up the chunk smoothing, placement but otherwise I like it

Your chunks are noticeable because you’re calling math.noise() with arguments per chunk.

For coordinates that are close to each other, the return values will also be close to each other.

While you gradually change the input over a chunk, there will be a much bigger gap in input values between the end of one chunk and the beginning of the next. You should instead use arguments that gradually change over the entire map, not per chunk.

Yeah im kind of an idiot sometimes.