Raycaster: fish eye effect past 60 degrees
Posted: Mon Aug 20, 2018 6:50 am
I'm trying to make a dungeon crawler type game and I thought a nice way to do the game screen would be raycasting.
I've been following this awesome tutorial: https://permadi.com/1996/05/ray-casting ... f-contents
Unfortunately, the raycaster I have built only works at an FOV of 60 degrees of less, while most dungeon crawlers show more than that.
After some experimentation, I found that an FOV of 120 degrees includes everything I want on the screen.
There's just one thing. Even with fish eye correction, the screen begins warping at anything higher than 60 degrees.
If I had to guess, I would say the code causing the malfunction is here, as that's where I start doing calculations to actually draw the walls.
Thank you for any help! A readme is included that lists the controls.
I've been following this awesome tutorial: https://permadi.com/1996/05/ray-casting ... f-contents
Unfortunately, the raycaster I have built only works at an FOV of 60 degrees of less, while most dungeon crawlers show more than that.
After some experimentation, I found that an FOV of 120 degrees includes everything I want on the screen.
There's just one thing. Even with fish eye correction, the screen begins warping at anything higher than 60 degrees.
Code: Select all
-- raycaster.lua
sin = math.sin
cos = math.cos
tan = math.tan
rad = math.rad
Newcaster = {}
Newcaster.map = {{1, 1, 1, 1, 1},
{1, 0, 0, 0, 1},
{1, 0, 1, 0, 1},
{1, 0, 0, 0, 1},
{1, 1, 1, 1, 1}}
Newcaster.player = {angle = 360, x = 2, y = 2}
Newcaster.highlightRay = 1
Newcaster.displayRay = true -- horizontal intercepts
--[[ ANGLES
/- 090 -\
/ | \
180 --+-- 360
\ | /
\- 270 -/
360 is EAST
360 -> 1 instead of 359 -> 0
Blocks are 100 wide
]]
-- center of projection plane: 250, 150
screenWidth = 500
screenHeight = 300
FOV = 120
function Newcaster:raycast()
distToPlane = math.floor((screenWidth / 2) / tan(rad( FOV / 2 )))
subAngle = FOV / screenWidth -- angle of subsequent rays
rayAngle = self.player.angle + FOV / 2
rayAngle = rayAngle + subAngle -- Because we subtract from it once the loop starts
if self.player.angle > 360 then self.player.angle = 1 end
if self.player.angle < 1 then self.player.angle = 360 end
if self.highlightRay > 500 then self.highlightRay = 1 end
if self.highlightRay < 1 then self.highlightRay = 500 end
for ray=1, screenWidth do
rayAngle = rayAngle - subAngle
if rayAngle < 1 then rayAngle = rayAngle + 360 end
if rayAngle > 360 then rayAngle = rayAngle - 360 end
startCoords = {x = (self.player.x-1)*100+50,
y = (self.player.y-1)*100+50}
dx = 100 / tan(rad(rayAngle)) -- The amount that x changes for every horizontal intercept
dy = 100 * tan(rad(rayAngle)) -- The amount that y changes for every vertical intercept
-- If the ray shoots straight up or straight down, dx will be infinite
if rayAngle == 360 or rayAngle == 180 then
dx = math.huge
-- If the ray shoots directly left or right, dy will be infinite
elseif rayAngle == 90 or rayAngle == 270 then
dy = math.huge
end
-- Find Horizontal Intercepts ------------------------------
local upOrDown = 100
if rayAngle < 180 then upOrDown = -100 end
if rayAngle > 180 and rayAngle < 360 then
dx = -dx
end
curPointHoriz = {x = startCoords.x + dx/2,
y = startCoords.y + upOrDown/2}
local findHorizIntercepts = true
for intercept=1, 7 do -- Range of 7 blocks
testRow = curPointHoriz.y / 100
testCol = 1 + ((curPointHoriz.x - (curPointHoriz.x % 100)) / 100)
if curPointHoriz.y <= 0 or curPointHoriz.y >= #self.map*100 or
curPointHoriz.x <= 0 or curPointHoriz.x >= #self.map[1]*100 then -- It's outta da map!
findHorizIntercepts = false
end
if findHorizIntercepts then
if self.map[testRow][testCol] > 0 then
findHorizIntercepts = false
elseif self.map[testRow+1][testCol] > 0 then
findHorizIntercepts = false
else -- Increment them, we didn't hit nothin'!
curPointHoriz.x = curPointHoriz.x + dx
curPointHoriz.y = curPointHoriz.y + upOrDown
end
end
end
-- Find Vertical Intercepts ------------------------------
local leftOrRight = 100
if rayAngle < 270 and rayAngle > 90 then leftOrRight = -100 end
if rayAngle > 270 or rayAngle < 90 then dy = -dy end
curPointVert = {x = startCoords.x + leftOrRight/2,
y = startCoords.y + dy/2 }
local findVertIntercepts = true
for intercept=1, 7 do -- Range of 7 blocks
testRow = 1 + ((curPointVert.y - (curPointVert.y % 100)) / 100)
testCol = curPointVert.x / 100
if curPointVert.y <= 0 or curPointVert.y >= #self.map*100 or
curPointVert.x <= 0 or curPointVert.x >= #self.map[1]*100 then -- It's outta da map!
findVertIntercepts = false
end
if findVertIntercepts then
if self.map[testRow][testCol] > 0 then
findVertIntercepts = false
elseif self.map[testRow][testCol+1] > 0 then
findVertIntercepts = false
else -- Increment them, we didn't hit nothin' (again)!
curPointVert.x = curPointVert.x + leftOrRight
curPointVert.y = curPointVert.y + dy
end
end
end
-- Find the shortest line
horizLineDist = math.sqrt( (startCoords.x - curPointHoriz.x)^2 + (startCoords.y - curPointHoriz.y)^2 )
vertLineDist = math.sqrt( (startCoords.x - curPointVert.x)^2 + (startCoords.y - curPointVert.y)^2 )
drawAngle = self.player.angle - rayAngle
if horizLineDist < vertLineDist then
correctedDistance = math.abs(horizLineDist * cos(rad(drawAngle)))
else
correctedDistance = math.abs(vertLineDist * cos(rad(drawAngle)))
end
local drawHeight = math.floor((100 / correctedDistance * distToPlane))
love.graphics.setColor(1, 1, 1)
if self.highlightRay == ray then
if love.keyboard.isDown('l') then
print(drawAngle)
end
love.graphics.setColor(0, 1, 0)
end
love.graphics.rectangle('fill', ray+50, love.graphics.getHeight()/2 - drawHeight/2, 1, drawHeight)
--[[ Draw the rays, infinitely helpful for debugging ------------------------------]
love.graphics.setColor(1, 0, 0) -- Horizontal Intercept Lines
if self.highlightRay == ray then highlightCoords = {x = curPointHoriz.x, y = curPointHoriz.y} end
love.graphics.line(startCoords.x, startCoords.y, curPointHoriz.x, curPointHoriz.y)
love.graphics.setColor(0, 0, 1)
if self.highlightRay == ray then highlightCoords = {x = curPointVert.x, y = curPointVert.y} end
love.graphics.line(startCoords.x, startCoords.y, curPointVert.x, curPointVert.y)
--]]
if self.highlightRay == ray then
if love.keyboard.isDown('p') then
print('Ray: '..ray..'\tRayangle: '..rayAngle)
end
end
end
-- Print some easy debug information
love.graphics.setColor(0, 1, 0)
love.graphics.print(self.highlightRay, 0, 12)
love.graphics.setColor(1, 1, 1)
love.graphics.print(self.player.angle, 0, 0)
end
return Newcaster
Code: Select all
--raycaster.lua (subsection)
drawAngle = self.player.angle - rayAngle
if horizLineDist < vertLineDist then
correctedDistance = math.abs(horizLineDist * cos(rad(drawAngle)))
else
correctedDistance = math.abs(vertLineDist * cos(rad(drawAngle)))
end
local drawHeight = math.floor((100 / correctedDistance * distToPlane))
love.graphics.setColor(1, 1, 1)
love.graphics.rectangle('fill', ray+50, love.graphics.getHeight()/2 - drawHeight/2, 1, drawHeight)