Problem with raycasting (floor tiles)

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
Post Reply
User avatar
Tabaqui
Prole
Posts: 34
Joined: Tue Mar 24, 2020 2:47 pm
Location: Italy

Problem with raycasting (floor tiles)

Post by Tabaqui »

EDIT: Solved, but i have another issue with floor rendering using meshes (found below)

Hi all!
I've started working on a raycasting engine in love2d but i'm facing my first problem: sometimes when i get too close to a wall the screen won't render correctly. I'm still at the untextured stage of the engine, but i think that adding textures won't solve the problem... It looks like the player's face is sinking in the wall :ehem:
screen.png
screen.png (14.76 KiB) Viewed 5606 times
Can anybody help me spotting the problem?

Thank you in advance!
Attachments
raycaster.love
(5.82 MiB) Downloaded 195 times
Last edited by Tabaqui on Sat Feb 06, 2021 7:37 pm, edited 2 times in total.
User avatar
pgimeno
Party member
Posts: 3550
Joined: Sun Oct 18, 2015 2:58 pm

Re: Problem with raycasting

Post by pgimeno »

It looks like a problem with Löve's line algorithm; not sure if it's a bug or just an expected loss of precision. Here is a test case, not sure if it can be reproduced everywhere:

Code: Select all

love.graphics.setLineStyle("rough")

function love.draw()
  love.graphics.line(70, 49138.421592350249, 70, -48658.421592350249)
  love.graphics.line(71, 49295.143758940096, 71, -48815.143758940096)
end
These two lines should be rendered as one thick line, but the one at x=70 is rendered at x=69, causing a black line in between.

You can fix it in two ways: by drawing the lines using the pixel centres (i.e. just add 0.5 to every x and y coordinate in love.graphics.line), or by drawing 1-pixel rectangles instead of lines, which will probably be less computationally expensive.

Code: Select all

-- option 1:
			love.graphics.line(x+.5, y1+.5, x+.5, y2+.5)
-- option 2:
			love.graphics.rectangle("fill", x, y1, 1, y2-y1)
Last edited by pgimeno on Fri Feb 05, 2021 1:28 pm, edited 1 time in total.
User avatar
Tabaqui
Prole
Posts: 34
Joined: Tue Mar 24, 2020 2:47 pm
Location: Italy

Re: Problem with raycasting

Post by Tabaqui »

pgimeno wrote: Fri Feb 05, 2021 1:17 pm It looks like a problem with Löve's line algorithm; not sure if it's a bug or just an expected loss of precision. Here is a test case, not sure if it can be reproduced everywhere:

Code: Select all

love.graphics.setLineStyle("rough")

function love.draw()
  love.graphics.line(70, 49138.421592350249, 70, -48658.421592350249)
  love.graphics.line(71, 49295.143758940096, 71, -48815.143758940096)
end
These two lines should be rendered as one thick line, but the one at x=70 is rendered at x=69, causing a black line in between.

You can fix it in two ways: by drawing the lines using the pixel centres (i.e. just add 0.5 to every x and y coordinate in love.graphics.line), or by drawing 1-pixel rectangles instead of lines, which will probably be less computationally expensive.

Code: Select all

-- option 1:
			love.graphics.line(x+.5, y1+.5, x+.5, y2+.5) -- or maybe y2-.5, otherwise it may be one pixel too high
-- option 2:
			love.graphics.rectangle("fill", x, y1, 1, y2-y1)
Oh God, you are right! I thought it was an error in the raycasting algorithm and i was going crazy! Thank you, pgimeno!
User avatar
Tabaqui
Prole
Posts: 34
Joined: Tue Mar 24, 2020 2:47 pm
Location: Italy

Re: Problem with raycasting

Post by Tabaqui »

Okay, i've successfully added textures for walls, but i'm having some troubles with the floor: the math is more or less done, but when i try to draw floor tiles (using meshes) i get this strange effect:
textured.png
textured.png (195.99 KiB) Viewed 5518 times
I've tried drawing a texture using meshes stripe by stripe and it's distorted too (but not as distorted as the floor):
texture.png
texture.png (3.12 KiB) Viewed 5518 times
Am i doing something wrong with the mesh?

I'll attach an updated .love file too.
Attachments
textured_raycaster.love
(13.61 KiB) Downloaded 200 times
User avatar
pgimeno
Party member
Posts: 3550
Joined: Sun Oct 18, 2015 2:58 pm

Re: Problem with raycasting (floor tiles)

Post by pgimeno »

That looks a lot like lack of perspective correction applied to the mesh. Squeezing a quad without applying perspective distorts the triangles that form the quad like that.

Edit: Here's an example to demonstrate the distortion, using your floor.png texture:

Code: Select all

love.graphics.setDefaultFilter("nearest", "nearest")
local tex = love.graphics.newImage("floor.png")
local mesh = love.graphics.newMesh(4, "strip", "stream")
mesh:setTexture(tex)

local grabbed = false

local vertices = {{100, 100, 0, 0}, {100, 400, 0, 1},
                  {400, 100, 1, 0}, {400, 400, 1, 1}}

function love.mousepressed(x, y, b)
  if b == 1 then
    local closestD, closestV
    for v = 1, 4 do
      local dx = vertices[v][1] - x
      local dy = vertices[v][2] - y
      local d2 = dx * dx + dy * dy
      if closestD == nil or closestD > d2 then
        closestD = d2
        closestV = v
      end
    end
    grabbed = closestV
    vertices[grabbed][1] = x
    vertices[grabbed][2] = y
  end
end

function love.mousemoved(x, y)
  if grabbed then
    vertices[grabbed][1] = x
    vertices[grabbed][2] = y
  end
end

function love.mousereleased(x, y, b)
  if grabbed then
    vertices[grabbed][1] = x
    vertices[grabbed][2] = y
  end
  if b == 1 then
    grabbed = false
  end
end

function love.update(dt)
  for i = 1, 4 do
    mesh:setVertex(i, vertices[i])
  end
end

function love.draw()
  love.graphics.draw(mesh)
end
(click and drag the vertices to see the issue)

I can think of two solutions: use the floor rendering technique used in ray casting for the floor too, instead of meshes, or ditch the ray casting completely and go full 3D. After all, Löve uses OpenGL, and for OpenGL it's probably cheaper to do perspective-correct 3D than to render one polygon per horizontal pixel column. Here's a quick demo I made (mainly intended to illustrate how to do 3D): https://love2d.org/forums/viewtopic.php ... 45#p219345

A third possibility would be to render the floor in full 3D and the walls with ray casting, but I'm not sure how that would work.

Edit: I forgot, there's a fourth possibility, which is to use this shader: https://love2d.org/forums/viewtopic.php?f=5&t=12483 which basically takes the four vertices of a quad as input, and applies perspective correction to them. However, it's pretty computationally expensive.
User avatar
Tabaqui
Prole
Posts: 34
Joined: Tue Mar 24, 2020 2:47 pm
Location: Italy

Re: Problem with raycasting (floor tiles)

Post by Tabaqui »

pgimeno wrote: Sun Feb 07, 2021 11:37 am That looks a lot like lack of perspective correction applied to the mesh. Squeezing a quad without applying perspective distorts the triangles that form the quad like that.

Edit: Here's an example to demonstrate the distortion, using your floor.png texture:

Code: Select all

love.graphics.setDefaultFilter("nearest", "nearest")
local tex = love.graphics.newImage("floor.png")
local mesh = love.graphics.newMesh(4, "strip", "stream")
mesh:setTexture(tex)

local grabbed = false

local vertices = {{100, 100, 0, 0}, {100, 400, 0, 1},
                  {400, 100, 1, 0}, {400, 400, 1, 1}}

function love.mousepressed(x, y, b)
  if b == 1 then
    local closestD, closestV
    for v = 1, 4 do
      local dx = vertices[v][1] - x
      local dy = vertices[v][2] - y
      local d2 = dx * dx + dy * dy
      if closestD == nil or closestD > d2 then
        closestD = d2
        closestV = v
      end
    end
    grabbed = closestV
    vertices[grabbed][1] = x
    vertices[grabbed][2] = y
  end
end

function love.mousemoved(x, y)
  if grabbed then
    vertices[grabbed][1] = x
    vertices[grabbed][2] = y
  end
end

function love.mousereleased(x, y, b)
  if grabbed then
    vertices[grabbed][1] = x
    vertices[grabbed][2] = y
  end
  if b == 1 then
    grabbed = false
  end
end

function love.update(dt)
  for i = 1, 4 do
    mesh:setVertex(i, vertices[i])
  end
end

function love.draw()
  love.graphics.draw(mesh)
end
(click and drag the vertices to see the issue)

I can think of two solutions: use the floor rendering technique used in ray casting for the floor too, instead of meshes, or ditch the ray casting completely and go full 3D. After all, Löve uses OpenGL, and for OpenGL it's probably cheaper to do perspective-correct 3D than to render one polygon per horizontal pixel column. Here's a quick demo I made (mainly intended to illustrate how to do 3D): https://love2d.org/forums/viewtopic.php ... 45#p219345

A third possibility would be to render the floor in full 3D and the walls with ray casting, but I'm not sure how that would work.

Edit: I forgot, there's a fourth possibility, which is to use this shader: https://love2d.org/forums/viewtopic.php?f=5&t=12483 which basically takes the four vertices of a quad as input, and applies perspective correction to them. However, it's pretty computationally expensive.
Thank you, pgimeno! I'll probably give your 3D example a try soon! I was trying raycasting instead of real 3D just for fun (and to learn something maybe).

By the way i've managed to draw floors using only scale, rotate, ox, oy and scissors but it's still buggy as you can see from the screenshot (still have to figure out why):
scale_scissor_method.png
scale_scissor_method.png (152.66 KiB) Viewed 5435 times
Basically, for each floor stripe i'm finding the points P1 and P2 where P1 is the first point of the tile encountered by the ray and P2 is the last one. I'll then calculate the angle between the two points (needed to rotate the texture) and the scale factor (thanks to the two perpendicular distances from the points to the projection plane) and then, using scissors, i'm drawing only the vertical stripe i need to view.

I'm leaving also the updated .love file in the attachments in case anybody wants to see how i've tried to implement such things.
Attachments
scaling_scissor_raycaster.love
(14.04 KiB) Downloaded 200 times
Post Reply

Who is online

Users browsing this forum: Bing [Bot] and 58 guests