Rendering Viewport Frames with Region3

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

Edit: it should be noted the examples used in this thread clones and destroys parts every frame, I highly recommend not doing that, and instead updating the CFrames of dynamic parts every frame, this is just a quick example I threw together to showcase this.

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)

or here

46 Likes

EUREKA!!

This is awesome!!!

2 Likes

Solid solution for limited rendering, though as many people stated the main purpose of these frames doesn’t seem to be large scale rendering. I thought about Region3s and it’s a very nice use of them, so kudos on making it and tossing it up open source for the community.

4 Likes

This is super cool, thanks for making and sharing!

1 Like

Amazing! Thanks for sharing it

1 Like

Try using a large anchored/invisible/nocollide sphere and using GetTouchingParts to test in a radius… I’m not sure what difference in performance that will have but it would be good to find out! Hopefully it’s fast if not faster. Also you would need to connect a .Touched event to the sphere for this to work but an empty function works.

yeah but using region3 doesnt even have any events, which means it causes less updates

1 Like

Is this really good for performance?

No, when I made this it was only a way to get a baseline of the parts that should be rendered in an area, the best way to do this would to be using a combination of this and cframe updating, so you dont clone and destroy every frame, like was done in this example

1 Like