I’ve been tinkering with fog for a while now in my game and I’ve noticed something less than desirable with a recent script I’ve designed.
[This] script is what I’m using in my game. It creates a large inverse sphere mesh and CFrames it to your camera every frame. Since the way it works is so simple, here’s the main function:
game:GetService("RunService").RenderStepped:Connect(function (Delta)
if workspace.CurrentCamera then
local FogDist = Lighting.FogEnd
BallMesh.Scale = Vector3.new(FogDist, FogDist, FogDist) * -2
BallPart.Color = Lighting.FogColor
BallPart.CFrame = workspace.CurrentCamera.CFrame
end
end)
This works great for all practical purposes and in testing it’s been more than great cosmetically. There’s just one issue: I’ve noticed an issue of rather harsh frame drops using this code (Framerate can go as low as 20FPS, I’m using a GTX 1070).
Given what I know about Roblox’s engine, I have two guesses for the cause of the lag. The first guess relates to the large part size. Since the ball scales to be accurate to the fog distance (to avoid making the ball visible), it can get quite large (well over 2048 studs) and may have adverse effects due to its size and how rendering is handled. The second guess relates to the constant CFraming. Although CFrame is generally fast, in this case it’s a huge render update. I make this guess because standing still for a short time (not moving my camera) will allow the game to return to 60 FPS, though it takes a couple of seconds.
Is there a better way to go about making realistic fog? What are other solutions people have had to this problem?
On line 4 of the function I gave, you can see that I am indeed using a mesh (BallMesh.Scale = Vector3.new(FogDist, FogDist, FogDist) * -2). I already factored in the lag caused by large parts. Additionally, parts cannot exceed 2048 on any axis for their size, so a mesh was absolutely necessary.
I already have collision off (Given that it’s a small, anchored part, the collision shouldn’t be a problem anyway as there’s no interactions it has to do, and there’s no unanchored parts anywhere nearby with the exception of my avatar).
I’ll tinker with #2 and #3 to see what I can get to happen. I’ll edit my post after testing.
Update: Neither of those two solutions were successful. Good insight nonetheless.
You could try and decrease how fast your fog updates. Since you are updating on RenderStepped, you are updating every frame. Try decreasing the amount of time your fog updates (probably using a while loop)
The transparency of the part is only >0 and <1 when transitioning from one state to another. Any other fog state will have the transparency at 0 or 1, nothing in between.
A bit of an update, using a plane in front of the camera has notable performance increases though they could use to be perfected.
In order to determine the size of the plane, I use this function:
function GetPlaneSize(Distance)
local VPSize = workspace.CurrentCamera.ViewportSize
local Aspect = VPSize.X / VPSize.Y
local vFOV = math.rad(workspace.CurrentCamera.FieldOfView)
local Y = 2 * math.tan(vFOV / 2) * math.abs(Distance)
local X = Y * Aspect
return X, Y
end
I should be able to make use of this by giving it some extra size to compensate for the slight delay from moving when it’s not bound to RenderStepped
You don’t need the whole sphere. You should only use the section of the sphere that the user can see. Roblox could be trying to render shadows or lighting from all around the sphere every time it is updated which could be the cause of your increased frame times.
Edit: combing this with your GetPlaneSize function could look just as good as using a whole sphere, although you may have to tweak it slightly to accommodate for the different shape
A bit of an update, I have come up with probably one of the single hackiest solutions I’ve made to-date.
I was able to get around this lag by attaching a BillboardGui to Terrain. The BillboardGui’s size is set to UDim2.new(W, 0, H, 0) where W and H are the return values from GetPlaneSize(Distance) (See response #12).
There is a Frame in this BillboardGui that has its color set to the fog’s color. The BillboardGui’s StudsOffsetWorldSpace property is set to the position of where the part would normally be, and the rotation is handled automatically by the UI element.
This isn’t really the best way in the aspect of long-term support (for instance, Roblox may deem it a good idea to not have fog affect BillboardGui, although I doubt this will be the case given that there was an update to add this a while ago), but it is most certainly functional and lagless.
This is what I’m currently doing, except I’m adjusting the container part’s position on RenderStep, instead of StudsOffsetWorldSpace. I think your method is better performance wise, so I think ill use that instead
This is a bit of a necro bump, but scaling up meshes or parts in Roblox to very large sizes always causes a lot of lag, instead scale the mesh inside blender or some other 3d modelling software before importing it. You won’t have any frame drops with it. It’s weird.
What do you mean ‘StudsOffsetWorldSpace property is set to the position of where the part would normally be’ ; I’m setting StudsOffsetWorldSpace to game.Workspace.CurrentCamera.CFrame.Position . Is that correct? It isn’t working correctly for me