I keep seeing people making these limited viewport renderers using a for loop that loops through the workspace to detect if a part is on a camera’s screen, this is very bad for performance, so I went looking for a better solution and I found one. This thread is meant for helping people learn to efficiently render their viewports
Region3 is useful for rendering, its not nearly as expensive to get parts that are on a camera as looping through workspace, here is a video using this method:
as seen in the video, it runs just fine, now the video didn’t capture at 60 fps, but the game was running at it, I have also tested this on some mobile devices to ensure it runs good.
The methods used for rendering that i have seen all over here on the dev forums include looping through the workspace and testing if a part is on screen to render it, rather than using a Region3
Basic example of looping through the workspace:
local camera = workspace.CurrentCamera -- This would likely be a camera other than this local viewportFrame = script.Parent.ViewportFrame game:GetService("RunService").RenderStepped:Connect(function() for i,v in pairs(workspace:GetChildren()) do if v:IsA("BasePart") then local _,visible = camera:WorldToScreenPoint(v.Position) if visible then local c = v:Clone() c.Parent = viewportFrame end end end end)
Example of using a region3:
local function renderDynamic(camera,viewport,characters,list,renderDistance) --Characters is a folder i parented all the player's characters to when they joined to make rendering easier --List is the whitelist used to render dynamic parts --I also put a folder in the viewportframe named "Dynamic" --Create the region3 to detect the parts local center = (camera.CFrame.LookVector*renderDistance)/2 local corner = Vector3.new(renderDistance/2,renderDistance/4,renderDistance/2) local renderRegion = Region3.new(center - corner, center + corner) --Get all parts within the region local parts = workspace:FindPartsInRegion3WithWhiteList(renderRegion, list, PartLimit) --Loop through the parts for i,v in pairs(parts) do --check if the part is part of a character if v.Name == "HumanoidRootPart" then local function renderCharacter(chr) if not chr.Archivable then chr.Archivable = true end local c = chr:Clone() for i,v in pairs(c:GetDescendants()) do if v:IsA("Script") or v:IsA("Sound") then v:Destroy() end end c.Parent = viewport.Dynamic end if not(viewport.Dynamic:FindFirstChild(v.Parent.Name)) then v.Parent.Archivable = true renderCharacter(v.Parent) v.Parent.Archivable = false end elseif not(v:IsDescendantOf(characters)) then local c = v:Clone() for i,v2 in pairs(c:GetDescendants()) do -- Remove things we dont want if v2:IsA("Script") or v2:IsA("Sound") then v2:Destroy() end end c.Parent = viewport.Dynamic end end end
you would want an inverse of the function above that runs less often to render the static parts.
Rendering parts with the workspace loop often results in framerates below 30 fps, but so far from my testing with the region3, the frames havent dipped below 53 fps on any device i run it on, running at an average of 59 fps.
I will be testing this in more intense situations for better benchmarks soon.
Of course people have come up with better methods for rendering viewport frames with the workspace loop, as can be found here: [Release] ViewportFrame limited-rendering (I have not checked if their new version also uses a loop similar to this so I will not include it here)
you can explore the place I made for this here:
Region3Viewports.rbxl (562.4 KB)