Manipulate the CurrentCamera to mirror the size and positioning of a viewport and its camera

In short I’m trying to replicate the behaviour of viewports but with the CurrentCamera while looking onto the real Workspace (and surrounded by UI elements to give the impression you’re using a ViewportFrame).

Viewports don’t support particle effects and many important lighting features hence the reason for doing this.

This is my first attempt. It mostly works but is incredibly hacky and doesn’t work perfectly across different devices and screen sizes. I’m ideally looking for a solution grounded in proper math instead of trial and error:

function CameraUtil.matchViewport(viewport, viewportCamera)
	-- This modifies and offsets the workspaceCamera so that it
	-- appears identical when overlayed with the viewport; effectively
	-- it allows us to create a mock viewport using the real workspace
	-- (by having borders around the rest of the screen). This is
	-- desirable due to current limitations with viewports (such as
	-- poorer lighting, lack of support for particle effects, etc).
	-- This function works by considering the size and position of the
	-- viewport relative to the rest of the world and screen, then manipulating
	-- the properties of the viewportCamera such as CFrame and FieldOfView.
	local framePos = viewport.AbsolutePosition
	local frameSize = viewport.AbsoluteSize
	local frameMidPos = framePos + (frameSize/2)
	local screenPos = app.AbsolutePosition
	local screenSize = app.AbsoluteSize
	local screenMidPos = screenPos + (screenSize/2)
	local xyRatio = frameSize.X / frameSize.Y
	local distanceFromCentre = screenMidPos - frameMidPos
	local yDistanceRatio = distanceFromCentre.Y / screenSize.Y
	local clampedXYRatioA = math.clamp(xyRatio, 0.8, 1.4) - 0.8
	local clampedXYRatioB = math.clamp(xyRatio, 0.6, 1.2) - 0.6
	local clampedYDisRatio = math.clamp(yDistanceRatio, 0, 0.17)
	local disRemainder = math.clamp(yDistanceRatio - clampedYDisRatio, 0, 0.25)
	local transformation = CFrame.Angles(math.rad(1 + (16 * -clampedXYRatioA) + (37.5 * -clampedYDisRatio)) + (-0.125 * disRemainder), 0, 0)
	local transformedCFrame = viewportCamera.CFrame * transformation
	currentCamera.CFrame = transformedCFrame
	currentCamera.FieldOfView = viewportCamera.FieldOfView + (48 * clampedXYRatioB)
end

Tapping the star icon toggles the transparency of the viewport (therefore revealing the workspace and CurrentCamera behind).

For those wanting to play around you can find an uncopylocked version of the place here (just search for ‘CameraUtil’):

I’m willing to reward 100,000 Robux to anyone who can perfectly solve this task:

Well there’s a few things to unpack here. The traditional way that viewport frame would handle this would look like this:

However, you’re playing around w/ the aspect ratio here so you may actually want to do something more like this where nothing gets stretch and pulled and the same aspect ratio as the “compressed view” is maintained.

This means when you increase the size of the view frame the width is maintained (since the viewport stayed the same width), but you get a lot more vertical space b/c the height of the frame increased drastically.

Happy to hand this demo off to you for the 100k, but I am about to leave my PC for the weekend so we may need to continue this convo next monday.

2 Likes

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.