Procedural Terrain Generation

Hi, can anyone recommend how I can best manage procedural generation. for example, do something with the noiseScale and occupancies value so that it can be customized

local TerrainGeneratorService = {}
TerrainGeneratorService.__index = TerrainGeneratorService

local UserInputService = game:GetService("UserInputService")

local MAP_SIZE = Vector3.new(6144, 0, 6144)
local RESOLUTION = 4
local CHUNK_SIZE = 256

function TerrainGeneratorService.new()
	local self = setmetatable({}, TerrainGeneratorService)
	
	self.services = {
		Workspace = game:GetService("Workspace")
	}
	
	self.terrain = self.services.Workspace.Terrain
	self:ConnectEvents()
	self:Generate()
	return self
end

function TerrainGeneratorService:ConnectEvents()
	UserInputService.InputBegan:Connect(function(input, gameProcessed)
		if input.UserInputType == Enum.UserInputType.Keyboard and not gameProcessed then
			if input.KeyCode == Enum.KeyCode.F then
				self:Generate()
			end
		end
	end)
end

function TerrainGeneratorService:Generate()
	local seed = math.random(-os.time(), os.time())
	math.randomseed(seed)

	local noiseScale = 0.01

	self.terrain:Clear()
	for x = -MAP_SIZE.X, MAP_SIZE.X, CHUNK_SIZE do
		for z = -MAP_SIZE.Z, MAP_SIZE.Z, CHUNK_SIZE do

			local voxelsPerChunk = CHUNK_SIZE / RESOLUTION
			local materials = {}
			local occupancies = {}

			for i = 1, voxelsPerChunk do
				materials[i] = {}
				occupancies[i] = {}
				materials[i][1] = {}
				occupancies[i][1] = {}

				for j = 1, voxelsPerChunk do
					local worldX = x + (i - 1) * RESOLUTION
                    local worldZ = z + (j - 1) * RESOLUTION
                    
                    local noiseValue = math.noise(
                        worldX * noiseScale, 
						worldZ * noiseScale,
						seed
                    )

                    local material
                    if noiseValue >= 0.5 then
                        material = Enum.Material.Grass
                    else
                        material = Enum.Material.Ground
                    end

                    materials[i][1][j] = material
                    occupancies[i][1][j] = 0.5
				end
			end

			local minPos = Vector3.new(x, 0, z)
			local maxPos = Vector3.new(x + CHUNK_SIZE, RESOLUTION, z + CHUNK_SIZE)
			local region = Region3.new(minPos, maxPos):ExpandToGrid(RESOLUTION)

			self.terrain:WriteVoxels(region, RESOLUTION, materials, occupancies)
		end
	end
end

return TerrainGeneratorService