# How can I correct an unwanted fisheye effect when drawing a scene with raycasting?

Hello I have a game using raycasting for the TI Nspire in Lua, and when the player faces a wall so that their degree they are facing is perpendicular to the length of the wall, I am having what is described as the fishbowl/fisheye effect.

How can I fix this? I have already looked here, and none of that makes sense to me. Here is my code:

map = {{0, 0, 50, 0},{50, 0, 50, 50},{50, 50, 0, 50},{0, 50, 0, 0}}

facing = 45 --"player rotation"
rays = {}
x, y = 25, 25

function raycast()
for i,v in pairs(rays) do
rays[i] = nil
end

rayf = facing + 30

for i = 1, 320, 1 do --64, 5
x_ray, y_ray = x, y
ray()
rayf = rayf - (60/320)
end
platform.window:invalidate()
end

function ray()
for n = 1, 50, 1 do -- increase of testdot
x_ray = math.floor(x + (radius * math.cos(rayf * 3.141592653/180)))
y_ray = math.floor(y - (radius * math.sin(rayf * 3.141592653/180)))
for k,v in pairs(map) do
if (
math.min(v[1],v[3]) <= x_ray and x_ray <= math.max(v[1],v[3])
) and (
math.min(v[2],v[4]) <= y_ray and y_ray <= math.max(v[2],v[4])
) then
--print("Collision")
return true
end
end
end
end

function on.paint(gc)
gc:setColorRGB(0,200,255)
gc:fillRect(0, 0, 320, 108)
gc:setColorRGB(100,100,100)
gc:fillRect(0, 109, 320, 109)
gc:setColorRGB(100,0,100)
for i,v in ipairs(rays) do
gc:drawLine(i, 108.5 - ((100/v * 20)/2), i, 108.5 + ((100/v * 20)/2))
end
end

function on.arrowKey(key)
if key == 'left' then
facing = facing + 3
elseif key == 'right' then
facing = facing - 3
end
if key == 'up' then
y = y - 1
elseif key == 'down' then
y = y + 1
end
raycast()
end


I have also tried it like this:

map = {{0, 0, 50, 0},{50, 0, 50, 50},{50, 50, 0, 50},{0, 50, 0, 0}}

facing = 45 --"player rotation"
rays = {}
x, y = 25, 25

function raycast()
for i,v in pairs(rays) do
rays[i] = nil
end

rayf = facing + 30

for i = 1, 320, 1 do --64, 5
x_ray, y_ray = x, y
ray()
----------------
----------------
rayf = rayf - (60/320)
end
platform.window:invalidate()
end

function ray()
for n = 1, 50, 1 do -- increase of testdot
x_ray = math.floor(x + (radius * math.cos(rayf * 3.141592653/180)))
y_ray = math.floor(y - (radius * math.sin(rayf * 3.141592653/180)))
for k,v in pairs(map) do
if (
math.min(v[1],v[3]) <= x_ray and x_ray <= math.max(v[1],v[3])
) and (
math.min(v[2],v[4]) <= y_ray and y_ray <= math.max(v[2],v[4])
) then
--print("Collision")
return true
end
end
end
end

function on.paint(gc)
gc:setColorRGB(0,200,255)
gc:fillRect(0, 0, 320, 106)
gc:setColorRGB(100,100,100)
gc:fillRect(0, 106, 320, 106)
gc:setColorRGB(100,0,100)
for i,v in ipairs(rays) do
gc:drawLine(i, 108.5 - ((100/v * 20)/2), i, 108.5 + ((100/v * 20)/2))
end
end

function on.arrowKey(key)
if key == 'left' then
facing = facing + 3
elseif key == 'right' then
facing = facing - 3
end
if key == 'up' then
y = y - 1
elseif key == 'down' then
y = y + 1
end
raycast()
end


The wall originally looked like this:

But I have made changes around the ---------------, and now the wall looks like this:

I this supposed to occur?

I actually had to solve this some time ago. The easiest fix is to simply divide by cos(angle of ray - viewAngle) and use that as the distance

• the viewAngle is the angle of the player? – crazicrafter1 Mar 26 '18 at 0:36
• And is it the distance of a ray that I divide by cos(angle of ray - viewAngle)? – crazicrafter1 Mar 26 '18 at 0:37
• @crazicrafter1 Yes and yes – Bálint Mar 26 '18 at 6:00
• It looks like in the code above, you forgot to convert from degrees to radians, which is why you're getting that wrap-around/repeating effect. You're spinning around the whole circle more times than you intend. – DMGregory Mar 26 '18 at 11:59
• rayf * 3.141592653/180 is the part that converts to degrees to radians – crazicrafter1 Mar 27 '18 at 1:11

It looks like you're firing out your rays at evenly spaced angular intervals.

rayf = facing + 30
...
x_ray = math.floor(x + (radius * math.cos(rayf * 3.141592653/180)))
y_ray = math.floor(y - (radius * math.sin(rayf * 3.141592653/180)))
...
rayf = rayf - (60/320)


This makes a kind of intuitive sense - you want to sweep rays across the field of view - but if you're mapping each ray to (a column of) pixels on a flat screen, then it's a mistake.

When the player looks at the grid of pixels on a flat screen, the angles from their eye to each column of pixels from left to right are not evenly spaced. They're spread apart widest in the middle of the screen, and bunch together toward the sides as the screen slopes away from perpendicular to the view ray, foreshortening in the player's view.

When you use rays fired off at even angular intervals, you're effectively stating that the rendering intent is to be displayed on a cylindrical or spherical screen that wraps around the player's head. And for such a screen, this pinched lens shape is indeed the correct appearance for a flat wall.

So, to ensure your rendering looks good on a flat screen, fire your rays based on the flat screen. Imagine you have an image plane hovering in front of the player, pick evenly spaced points on that plane, and fire your rays toward those points.

For example...

forward_x = cos(facing * pi/180)
forward_y = -sin(facing * pi/180)  -- following the convention you use for rays

right_x = -forward_y
right_y = forward_x

for i = 1, 320, 1 do
-- step from -0.5 to +0.5 in equal increments
progress = (i - 1)/319 - 0.5

-- aim at a position ahead of the player and slightly to the left or right
step_x = (forward_x + progress * right_x) * radius
step_y = (forward_y + progress * right_y) * radius

• What is the forward_x and forward_y variables for? The right_x, right_y? Where do I put this in the code? Sorry if I'm asking for too much – crazicrafter1 Mar 27 '18 at 2:05
• forward x & y are the components of a unit vector pointing in the direction the player is looking. Right x & y are the components of a unit vector pointing 90 degrees to the player's right. We use this coordinate basis to pick the points to fire rays toward. – DMGregory Mar 27 '18 at 2:09