How do I fix this "curve distortion" in my 2D Raycast Renderer GUI?

So, I am trying to make a Wolfenstein-styled 2D Raycasting Renderer, but I am having issues with visuals.

The obvious issue is distortion. Some examples are below:

Side of a wall

Corner of a block

Does anyone know how to fix this?

Help will be greatly appreciated as always!


Raycast project.rbxl (32.9 KB)


local Gui = script.Parent
local MainFrame = Gui:WaitForChild("MainFrame")
local Canvas = MainFrame:WaitForChild("Canvas") -- Our container for the frames

local RS = game:GetService("RunService")

local CameraPart = workspace:WaitForChild("CameraPart") -- The player
local LevelWorkspace = workspace:WaitForChild("LevelWorkspace") -- The 2D map

local RenderDistance = 100
local FieldOfView = 170
local ViewDistance = 30 -- Fog?

local Resolution = 100 -- the columns. Can run up to 60FPS from 0-200 frames/columns

local function GenerateFrames() -- Creates all the frames that will be used
	for i = 1, Resolution do
		local Frame = Instance.new("Frame")

		Frame.LayoutOrder = i
		Frame.Name = "Frame" .. i
		Frame.BorderSizePixel = 0
		Frame.Size = UDim2.fromScale(1/Resolution, 1)
		Frame.Parent = Canvas
	end
end

local function ClearCanvas() -- Clears any old frames
	for i, Frame in ipairs(Canvas:GetChildren()) do
		if Frame:IsA("GuiObject") then
			Frame:Destroy()
		end
	end
end

local function UpdateFrames() -- Renders the picture
	local Count = -Resolution/2 
	for i, Frame in ipairs(Canvas:GetChildren()) do
		if Frame:IsA("GuiObject") then
			
			local Params = RaycastParams.new()
			Params.FilterType = Enum.RaycastFilterType.Whitelist
			Params.FilterDescendantsInstances = LevelWorkspace:GetChildren()
			
			local ViewRayOrigin = CameraPart.CFrame.Position
			local ViewRayDirection = (CameraPart.CFrame * CFrame.new((Count * FieldOfView) / Resolution, 0, -RenderDistance)).Position
			
			local ViewRay = workspace:Raycast(ViewRayOrigin, ViewRayDirection, Params)
			if ViewRay and ViewRay.Instance and ViewRay.Instance:IsA("BasePart") then

				--print(ViewRay.Instance)
				--print(ViewRay.Position)
				--print(Count)
				
				local Distance = (ViewRayOrigin - ViewRay.Position).Magnitude
				Frame.BackgroundColor3 = ViewRay.Instance.Color
				Frame.BackgroundTransparency = 1 * (Distance / ViewDistance)
				Frame.Size = UDim2.fromScale(1 / Resolution, 1 / Distance)
				
				--local DebugLaser = Instance.new("Part") -- Useful for visualizing the rays
				--DebugLaser.Anchored = true
				--DebugLaser.Size = Vector3.new(0.2, 0.2, Distance)
				--DebugLaser.CFrame = CFrame.new(ViewRayOrigin, ViewRay.Position) * CFrame.new(0, 0, -Distance/2)
				--DebugLaser.Parent = workspace

				Count = Count + 1
			end
		end
	end
end

ClearCanvas()

GenerateFrames()

UpdateFrames()

CameraPart.Changed:Connect(UpdateFrames) -- Render the picture when the player moves (not good if your world has moving objects)

--RS.RenderStepped:Connect(function() -- Always render the picture
	--UpdateFrames()
--end)
4 Likes

Hey!

This is really cool, nice job on it so far.

I’ve messed around a bit with your project and I was able to significantly reduce the distortion by reducing the FOV to 70.

Too big of a FOV also distorts the image on the normal Roblox renderer:

image
(this is the max of 120, imagine it at 170!)

The controls are a bit weird now though :smile:

1 Like

I found a video where this exact problem is discussed. https://youtu.be/vYgIKn7iDH8?t=1475

1 Like

That fixes the distortion issue, but now there is this new issue. where sometimes holding W to walk forwards, makes it look like you are walking diagonally.

Its very trippy and I don’t know why.

The issue with is, is that i’m not really using any angles in my renderer.

Yeah, that’s the effect I was talking about. I suspect that what is shown on the screen isn’t what is straight ahead of the player, but rather what is on the side. The rays seem to be shot at wrong angles

1 Like

Omg, your right.

They are offset!!

I wonder how that happened. And how could I fix that?

I see no reason for this to even happen in the first place

1 Like

From this image, it looks like only half of the rays are fired. There is probably something wrong with your loop. It may have been the case before, but since it was a way larger FOV (170), it wasn’t that noticeable

Ok, here are some things that I’ve discovered.

It appears that there is nothing wrong with my RayOrigin and RayDirection values, but more with the rays themselves:

Visualizing the ray’s Origin and Direction:

Visualizing the rays:

Can someone explain?

I’ve fixed that issue above. I was offsetting the rays in worldspace.

I’ve also switched to angles instead of offsets too.

Now on to fixing the “fisheye” effect @crimcat’s method.

Apparently the easiest fix is to simply divide by cos(angle of ray - viewAngle) and use that as the distance.

Iv’e almost fixed this issue, but there is still a slight curve that is noticeable.

How do I fix this?

image

here’s the current code for calculating range:

local RayAngleX, RayAngleY, RayAngleZ = RayAngle:ToEulerAnglesXYZ() -- RayAngle is a CFrame value
local Distance = ((ViewRayOrigin - ViewRay.Position).Magnitude) * math.cos(RayAngleY)

Try normalizing the fragment ray, I did it in my shader and it stopped it from being “thicc”