Window/Portal Viewport frame

I’m trying to achieve something like a “window to a different dimension” using viewportframes. I have read every single article that I found about this, I even checked and tried EgoMoose’s portal effect and watched this video that explains how portals are created in games, but I’m still stuck.

What I have so far:

local cam = workspace.CurrentCamera;

local ui = script.Parent.Parent;
local fram = script.Parent:WaitForChild("Viewport");
local vCam = Instance.new("Camera");
vCam.CameraType = Enum.CameraType.Scriptable;
fram.CurrentCamera = vCam;
vCam.Parent = fram.Parent;

local screen = ui.Adornee;

local rs = game:GetService("RunService");
rs.RenderStepped:Connect(function(dt)
	local dist = (cam.CFrame.Position - screen.Position).Magnitude;
	if dist < 20 then
		vCam.CFrame = cam.CFrame;
	end
end

And there are no errors, but the whole camera view is stretched down to the part’s surface so obviously it doesn’t look like a portal. If I use a ScreenGui instead of a SurfaceGui, the parts appear where they should be and look perfect.

As the video explained too (4:07), all I would have to do is stretch up the ViewportFrame to my screen’s size so it fits into the world around it, but I couldn’t find a way to achieve that. Maybe, if I could use a ScreenGui and mask it out so only the part’s surface is visible, it would work, but I have no idea how I could mask out a surface.

I also tried to take inspiration from EgoMoose but I barely understand anything of his code. I tried to just copy it, but nothing happened.

local rs = game:GetService("RunService");
rs.RenderStepped:Connect(function(dt)
	local dist = (cam.CFrame.Position - ui.Adornee.Position).Magnitude;
	if dist < 20 then
		local camCF = cam.CFrame
		local surfaceCF = screen.CFrame * CFrame.new(0, 0, -screen.Size.z/2)

		local pDist = screen.Size.y/2 / math.tan(math.rad(vCam.FieldOfView)/2)
		local rDist = (camCF.p - surfaceCF.p):Dot(surfaceCF.LookVector)

		local adjust = rDist / pDist

		local p = surfaceCF:PointToObjectSpace(camCF.p)
		local s = p / screen.Size

		fram.Position = UDim2.new(0.5 - s.x, 0, 0.5 - s.y, 0)
		fram.Size =  UDim2.new(4, 0, adjust, 0)

		vCam.CFrame = CFrame.new(camCF.p) * (surfaceCF - surfaceCF.p) * CFrame.Angles(0, math.pi, 0)
	end
end)

How could I achieve this window effect?
Note: The screen is a cylinder, so the surface is round.
Thanks for any help!

1 Like

you will have to clone the visible parts to a viewport frame.
to get a more circular window you can use an UICorner.
you need to use a surfaceGUI, for the stretching part I am not sure what to do, but you could mess with the window’s camera FOV to align it to the player’s camera FOV

1 Like

Yes, the parts were put inside the viewport frame. I solved it by using the newer version of EgoMoose’s portal, which worked without a problem.

Thanks for the reply!