How can I optimise this chunk-based fog system

So I have been trying to make a particle-based fog system for large maps, now since particles and computers have many limits to massive amounts of particles, i came up with this a solution. A chunk-based rendering system that enables and emits fog particles in front of the camera, and disables and clears fog particles outside of the region. All though this may be a smart solution, it is also pretty damn laggy for mid-range computers.

How can I stop or reduce lag from this system?

local Camera = workspace.CurrentCamera
local RS = game:GetService("ReplicatedStorage")

local FogPartTemplate = RS:WaitForChild("VolumetricFog")

local FogPartsFolder = Instance.new("Folder")
FogPartsFolder.Name = "FogParts"
FogPartsFolder.Parent = workspace

local RunService = game:GetService("RunService")

local RegionModule = require(script:WaitForChild("RotatedRegion3")) -- Is like Region3.new(), but with shapes and easier constructors

local PosY = 20

local AreaSize = 2000
local RenderRegionSize = Vector3.new(50, 50, 75)
local MaxRenderedParts = 20

local UpdateRate = 0.05
local CurrentUpdateRateTimer = os.clock()

local ChunkSize = 30

FogPartTemplate.Size = Vector3.new(1, 1, 1) * ChunkSize

for x = 1, AreaSize, ChunkSize do
	RunService.RenderStepped:Wait()
	x = x - AreaSize/2
	for z = 1, AreaSize, ChunkSize do
		z = z - AreaSize/2
		local NewFogPart = FogPartTemplate:Clone()
		NewFogPart.Position = Vector3.new(x, PosY, z)
		NewFogPart.Parent = FogPartsFolder
	end
end

RunService.RenderStepped:Connect(function()
	if os.clock() > CurrentUpdateRateTimer + UpdateRate then
		CurrentUpdateRateTimer = os.clock()
		local CurrentRegion = RegionModule.Block(Camera.CFrame * CFrame.new(0, 0, -((RenderRegionSize.Z/2) - 5)), RenderRegionSize)
		local FogParts = CurrentRegion:FindPartsInRegion3WithWhiteList(FogPartsFolder:GetChildren(), MaxRenderedParts)
		
		for i, OldFogPart in ipairs(FogPartsFolder:GetChildren()) do
			if OldFogPart.FogParticle.Enabled and not table.find(FogParts, OldFogPart) and OldFogPart.TimeToUpdate.Value <= os.clock() then
				RunService.RenderStepped:Wait()
				--OldFogPart.Transparency = 1
				OldFogPart.FogParticle:Clear()
				OldFogPart.FogParticle.Enabled = false
			end
		end
		
		for i, RenderedFogPart in ipairs(FogParts) do
			if not RenderedFogPart.FogParticle.Enabled and RenderedFogPart.TimeToUpdate.Value <= os.clock() then
				RunService.RenderStepped:Wait()
				RenderedFogPart.TimeToUpdate.Value = os.clock() + RenderedFogPart.FogParticle.Lifetime.Min
				--RenderedFogPart.Transparency = 0.5
				RenderedFogPart.FogParticle.Enabled = true
				RenderedFogPart.FogParticle:Emit(40)
			end
		end
	end
end)
1 Like

I surmise that the primary cause of your lag is coming from running relatively expensive code in RenderStepped particularly in terms of using the RegionModule. It’s a good catch to want to update the map before render tasks execute but it can cause notable slowdowns. This is typically the reason why you’re encouraged to use RenderStepped only for things like camera updates.

If the main issue is optimisation then you will really find the MicroProfiler helpful. This will allow you to debug what processes are taking exceptionally long during each frame and you can hone in on those labels specifically to see what needs work on your code. From a glance it’s difficult to provide optimisation tips besides yielding less (i.e. get rid of the signal waits in your for loops) so adding some labels and time checks around your code can help you diagnose where the slowdowns come from.

Try opening a few profiles around your code. I’d recommend having them around every major operation of code and then focusing in on each process as needed.

1 Like