Part isn't alligned with fov

I am trying to make a part fit the screen of the player perfectly. The part contains a SurfaceGui with a vignette filter. In another script I have the FOV being tweened and adjusted based on conditions. When zooming out, the visor gui is a frame delayed.

I have tried chagning Renderpriority, removing the fov tween, parenting to the camera and Heartbeat.

All do not work, except for increasing the size, to where the delay isn’t visible. It isn’t ideal to have the bigger size because a large part of the gui is cut off.

Any input is great, thanks for reading.


    RunService:BindToRenderStep("UpdateVisor", Enum.RenderPriority.Last.Value - 1, function()
  local Camera = workspace.CurrentCamera

        local cameraFOV = Camera.FieldOfView
        local viewportSize = Camera.ViewportSize
        local aspectRatio = viewportSize.X / viewportSize.Y

        local fovRadians = math.rad(cameraFOV)
        local depth = 1

        local height = 2 * math.tan(fovRadians / 2) * depth
        local width = height * aspectRatio
        
        VisorGui.Size = Vector3.new(width + 0.15, height + 0.15, 0.1)
        VisorGui.CFrame = Camera.CFrame * CFrame.new(0, 0, -depth)
end)

I played around with the code and it seems like the issue is the lack of precision in either of the calculations (due to floating point error) and to fix it I simply increased the depth by a big number.
However I do have to note that there seems to be shadows popping in and out despite me toggling “CastShadow” off in the part properties.
Here’s the code I tested it with:

local Players = game:GetService("Players")
local RunService = game:GetService("RunService")

local Camera = workspace.CurrentCamera
local VisorGui = script.VisorGui

VisorGui.Parent = workspace

RunService:BindToRenderStep("UpdateVisor", Enum.RenderPriority.Last.Value + 1, function()
	local Camera = workspace.CurrentCamera

	local cameraFOV = Camera.FieldOfView
	local viewportSize = Camera.ViewportSize
	local aspectRatio = viewportSize.X / viewportSize.Y

	local fovRadians = math.rad(cameraFOV)
	local depth = 100

	local height = 2 * math.tan(fovRadians / 2) * depth
	local width = height * aspectRatio
	
	local partDepth = 1

	VisorGui.Size = Vector3.new(width, height, partDepth)
	VisorGui.CFrame = Camera.CFrame * CFrame.new(0, 0, -depth - (partDepth / 2))
end)

RunService:BindToRenderStep("UpdateFOV", Enum.RenderPriority.First.Value, function()
	local m = (math.sin(time()) + 1) / 2
	Camera.FieldOfView = 40 + (m * 80) 
end)

I think, you should try changing Enum.RenderPriority.Last.Value + 1 to Enum.RenderPriority.Camera.Value + 1, so it’s certain that the change will happen right after the FOV updates.

I have already tried this, I couldn’t get it to work using this.

I couldn’t get this to work. I don’t think it’s a floating point error.

After furthing testing, I concluded that: Even when keeping the parts size static, in my case I used 100x100x0.1, and only modified the SurfaceGuiSize and PixelsPerStus, It still hasthis delay. I don’t know how I can counteract this, seems kind of impossible.

Maybe It’s an engine bug, but I honestly don’t know. I will try some other things but, this is what I concluded.

I’m so genuinely frustrated at this. I wish roblox had a feature to where you could make SurfaceGUIS appear above ScreenGUIS.

After a bit of research I think it’s because of the way SurfaceGui are updated. I think they’re updated one frame behind the camera, so inherintly the surfacegui will always be a frame behind the fov update.

Code I used:

    local Camera = workspace.CurrentCamera
    local CamCFrame = Camera:GetRenderCFrame()
  
    local cameraFOV = Camera.FieldOfView
    local viewportSize = Camera.ViewportSize
    local aspectRatio = viewportSize.X / viewportSize.Y

    local depth = 1
    
    local partDepth = 0.1
    VisorGui.SurfaceGui.PixelsPerStud = 100
    VisorGui.SurfaceGui.Visor.Size = UDim2.new(0,viewportSize.X,0,viewportSize.Y)
    VisorGui.Size = Vector3.new(100, 100, partDepth)
    
    local fovRadians = math.rad(cameraFOV)
    local hFOV   = 2 * math.atan(math.tan(fovRadians/2) * aspectRatio)

    local W_s = VisorGui.SurfaceGui.Visor.Size.X.Offset / VisorGui.Size.X
    local H_s = VisorGui.SurfaceGui.Visor.Size.Y.Offset / VisorGui.Size.Y

    local d_v = (H_s/2) / math.tan(fovRadians/2)
    local d_h = (W_s/2) / math.tan(hFOV/2)
    local d   = math.max(d_v, d_h)
    

    local partDepth = VisorGui.Size.Z
    VisorGui.CFrame = CamCFrame * CFrame.new(0, 0, -d -partDepth/2)