Author Topic: Raycasting  (Read 4110 times)

0 Members and 1 Guest are viewing this topic.

Offline TerryRitchie

  • Seasoned Forum Regular
  • Posts: 495
  • Semper Fidelis
    • View Profile
Raycasting
« on: July 13, 2020, 01:56:42 pm »
Sorry I've been scarce this past month. The wife was getting upset that the honey do list was not getting done. Had to limit my time on computer.

Any way, I've been trying to learn ray casting for another topic in my tutorial. I found a pretty good video on YouTube explaining the process. The narrator uses OpenGL and C. I converted it to QB64 as I watched the video. If you watch the video and look at the code below you'll be amazed at just how easy it is to do ray casting.

The code is sloppy. I threw it together as the narrator wrote his code. But it works. :-)



Code: QB64: [Select]
  1. '  [youtube]https://www.youtube.com/watch?v=gYRrGTC7GtA[/youtube]
  2.  
  3.  
  4.  
  5. CONST DARKGRAY = _RGB32(76, 76, 76)
  6. CONST WHITE = _RGB32(255, 255, 255)
  7. CONST BLACK = _RGB32(0, 0, 0)
  8. CONST YELLOW = _RGB32(255, 255, 0)
  9. CONST GREEN = _RGB32(0, 255, 0)
  10. CONST RED = _RGB32(255, 0, 0)
  11. CONST DARKRED = _RGB32(128, 0, 0)
  12. CONST PI = 3.1415926
  13. CONST P2 = PI / 2
  14. CONST P3 = 3 * PI / 2
  15. CONST DR = .0174533
  16.  
  17. DIM SHARED px AS SINGLE '  player position
  18. DIM SHARED pdx AS SINGLE ' delta X of player
  19. DIM SHARED pdy AS SINGLE ' delta Y of player
  20. DIM SHARED pa AS SINGLE '  angle of player
  21.  
  22. mapX = 8
  23. mapY = 8
  24. mapS = 64
  25. FOR i = 0 TO 63: READ map(i): NEXT i
  26.  
  27.  
  28. Init
  29.     _LIMIT 60
  30.     CLS , DARKGRAY
  31.     drawMap2D
  32.     drawRays2D
  33.     DrawPlayer
  34.     Buttons
  35.  
  36.  
  37.     _DISPLAY
  38.  
  39. DATA 1,1,1,1,1,1,1,1
  40. DATA 1,0,1,0,0,0,0,1
  41. DATA 1,0,1,0,0,0,0,1
  42. DATA 1,0,1,0,0,0,0,1
  43. DATA 1,0,0,0,0,0,0,1
  44. DATA 1,0,0,0,0,1,0,1
  45. DATA 1,0,0,0,0,0,0,1
  46. DATA 1,1,1,1,1,1,1,1
  47.  
  48.  
  49. FUNCTION dist (ax AS SINGLE, ay AS SINGLE, bx AS SINGLE, by AS SINGLE, ang AS SINGLE)
  50.  
  51.     dist = SQR((bx - ax) * (bx - ax) + (by - ay) * (by - ay))
  52.  
  53.  
  54.  
  55. SUB drawRays2D ()
  56.  
  57.     DIM r AS INTEGER
  58.     DIM mx AS INTEGER
  59.     DIM my AS INTEGER
  60.     DIM mp AS INTEGER
  61.     DIM dof AS INTEGER
  62.     DIM rx AS SINGLE
  63.     DIM ry AS SINGLE
  64.     DIM ra AS SINGLE
  65.     DIM xo AS SINGLE
  66.     DIM yo AS SINGLE
  67.     DIM aTan AS SINGLE
  68.     DIM nTan AS SINGLE
  69.     DIM disH AS SINGLE
  70.     DIM hx AS SINGLE
  71.     DIM hy AS SINGLE
  72.     DIM disV AS SINGLE
  73.     DIM vx AS SINGLE
  74.     DIM vy AS SINGLE
  75.     DIM disTT AS SINGLE
  76.     DIM lineH AS SINGLE
  77.     DIM lineO AS SINGLE
  78.     DIM ca AS SINGLE
  79.     DIM clr AS _UNSIGNED LONG
  80.  
  81.     ra = pa - DR * 30
  82.     IF ra < 0 THEN ra = ra + 2 * PI
  83.     IF ra > 2 * PI THEN ra = ra - 2 * PI
  84.  
  85.     FOR r = 0 TO 59
  86.         '--- Check Horizontal Lines ---
  87.         dof = 0
  88.         disH = 1000000
  89.         hx = px
  90.         hy = py
  91.         aTan = -1 / TAN(ra)
  92.         IF ra > PI THEN
  93.             'ry = ((INT(py) / 64) * 64) - .0001
  94.  
  95.             ry = INT(py / 64) * 64 - .0001
  96.  
  97.  
  98.             rx = (py - ry) * aTan + px
  99.             yo = -64
  100.             xo = -yo * aTan
  101.         END IF
  102.         IF ra < PI THEN
  103.             'ry = ((INT(py) / 64) * 64) + 64
  104.  
  105.             ry = INT(py / 64) * 64 + 64
  106.  
  107.  
  108.             rx = (py - ry) * aTan + px
  109.             yo = 64
  110.             xo = -yo * aTan
  111.         END IF
  112.         IF (ra = 0) OR (ra = PI) THEN
  113.             rx = px
  114.             ry = py
  115.             dof = 8
  116.         END IF
  117.         WHILE dof < 8
  118.             'mx = INT(rx) / 64
  119.             'my = INT(ry) / 64
  120.  
  121.             mx = INT(rx / 64)
  122.             my = INT(ry / 64)
  123.  
  124.             mp = my * mapX + mx
  125.  
  126.             'LOCATE 1, 1: PRINT mp; pa;
  127.             '_DISPLAY
  128.  
  129.             IF mp < 0 THEN mp = 0
  130.             IF mp > 63 THEN mp = 63
  131.  
  132.             'IF (mp < mapX * mapY) AND map(mp) = 1 THEN ' -- hit wall
  133.             IF map(mp) = 1 THEN
  134.                 hx = rx
  135.                 hy = ry
  136.                 disH = dist(px, py, hx, hy, ra)
  137.                 dof = 8
  138.             ELSE '                                       -- next line
  139.                 rx = rx + xo
  140.                 ry = ry + yo
  141.                 dof = dof + 1
  142.             END IF
  143.         WEND
  144.  
  145.  
  146.         '--- Check Vertical Lines ---
  147.         dof = 0
  148.         disV = 1000000
  149.         vx = px
  150.         vy = py
  151.  
  152.         nTan = -TAN(ra)
  153.         IF ra > P2 AND ra < P3 THEN
  154.             'ry = ((INT(py) / 64) * 64) - .0001
  155.  
  156.             rx = INT(px / 64) * 64 - .0001
  157.  
  158.  
  159.             ry = (px - rx) * nTan + py
  160.             xo = -64
  161.             yo = -xo * nTan
  162.         END IF
  163.         IF ra < P2 OR ra > P3 THEN
  164.             'ry = ((INT(py) / 64) * 64) + 64
  165.  
  166.             rx = INT(px / 64) * 64 + 64
  167.  
  168.  
  169.             ry = (px - rx) * nTan + py
  170.             xo = 64
  171.             yo = -xo * nTan
  172.         END IF
  173.         IF (ra = 0) OR (ra = PI) THEN
  174.             rx = px
  175.             ry = py
  176.             dof = 8
  177.         END IF
  178.         WHILE dof < 8
  179.             'mx = INT(rx) / 64
  180.             'my = INT(ry) / 64
  181.  
  182.             mx = INT(rx / 64)
  183.             my = INT(ry / 64)
  184.  
  185.             mp = my * mapX + mx
  186.  
  187.             'LOCATE 1, 1: PRINT mp; pa;
  188.             '_DISPLAY
  189.  
  190.             IF mp < 0 THEN mp = 0
  191.             IF mp > 63 THEN mp = 63
  192.  
  193.             'IF (mp < mapX * mapY) AND map(mp) = 1 THEN ' -- hit wall
  194.             IF map(mp) = 1 THEN
  195.                 vx = rx
  196.                 vy = ry
  197.                 disV = dist(px, py, vx, vy, ra)
  198.                 dof = 8
  199.             ELSE '                                       -- next line
  200.                 rx = rx + xo
  201.                 ry = ry + yo
  202.                 dof = dof + 1
  203.             END IF
  204.         WEND
  205.  
  206.         IF disV < disH THEN ' -- Vertical wall hit
  207.             rx = vx
  208.             ry = vy
  209.             disTT = disV
  210.             clr = RED
  211.         END IF
  212.         IF disH < disV THEN ' -- Horizontal wall hit
  213.             rx = hx
  214.             ry = hy
  215.             disTT = disH
  216.             clr = DARKRED
  217.         END IF
  218.  
  219.         LINE (px, py)-(rx, ry), RED
  220.  
  221.         '--- Draw 3D Walls ---
  222.  
  223.         ca = pa - ra
  224.         IF ca < 0 THEN ca = ca + 2 * PI
  225.         IF ca > 2 * PI THEN ca = ca - 2 * PI
  226.         disTT = disTT * COS(ca) '          -- fix fish eye
  227.  
  228.         lineH = mapS * 320 / disTT
  229.         IF lineH > 320 THEN lineH = 320 ' -- Line height
  230.         lineO = 160 - lineH / 2 '         -- Line offset
  231.         LINE (r * 8 + 530 - 4, lineO)-(r * 8 + 530 + 4, lineH + lineO), clr, BF
  232.  
  233.  
  234.  
  235.         ra = ra + DR
  236.         IF ra < 0 THEN ra = ra + 2 * PI
  237.         IF ra > 2 * PI THEN ra = ra - 2 * PI
  238.  
  239.  
  240.  
  241.     NEXT r
  242.  
  243.  
  244.  
  245.  
  246.  
  247. SUB drawMap2D ()
  248.  
  249.     DIM x AS INTEGER
  250.     DIM y AS INTEGER
  251.     DIM xo AS INTEGER
  252.     DIM yo AS INTEGER
  253.     DIM clr AS _UNSIGNED LONG
  254.  
  255.     FOR y = 0 TO mapY - 1
  256.         FOR x = 0 TO mapX - 1
  257.             IF map(y * mapX + x) = 1 THEN clr = WHITE ELSE clr = BLACK
  258.             xo = x * mapS
  259.             yo = y * mapS
  260.             LINE (xo + 1, yo + 1)-(xo + mapS - 1, yo + mapS - 1), clr, BF
  261.         NEXT x
  262.     NEXT y
  263.  
  264.  
  265.  
  266. SUB Buttons ()
  267.  
  268.     IF _KEYDOWN(97) THEN
  269.         pa = pa - .1
  270.         IF pa < 0 THEN pa = pa + 2 * PI
  271.     END IF
  272.     IF _KEYDOWN(100) THEN
  273.         pa = pa + .1
  274.         IF pa > 2 * PI THEN pa = pa - 2 * PI
  275.     END IF
  276.     pdx = COS(pa) * 5
  277.     pdy = SIN(pa) * 5
  278.     IF _KEYDOWN(119) THEN px = px + pdx: py = py + pdy
  279.     IF _KEYDOWN(115) THEN px = px - pdx: py = py - pdy
  280.  
  281.  
  282.  
  283. SUB DrawPlayer ()
  284.  
  285.     LINE (px - 4, py - 4)-(px + 4, py + 4), YELLOW, BF
  286.  
  287.     LINE (px, py)-(px + pdx * 5, py + pdy * 5), YELLOW
  288.  
  289.  
  290.  
  291.  
  292. SUB Init ()
  293.  
  294.  
  295.     SCREEN _NEWIMAGE(1024, 512, 32)
  296.     CLS , DARKGRAY
  297.     px = 300
  298.     py = 300
  299.     pdx = COS(pa) * 5
  300.     pdy = SIN(pa) * 5
  301.  
  302.  
  303.  
« Last Edit: July 13, 2020, 04:17:03 pm by TerryRitchie »
In order to understand recursion, one must first understand recursion.

FellippeHeitor

  • Guest
Re: Raycasting
« Reply #1 on: July 13, 2020, 02:39:50 pm »
Wow!

Offline Petr

  • Forum Resident
  • Posts: 1720
  • The best code is the DNA of the hops.
    • View Profile
Re: Raycasting
« Reply #2 on: July 13, 2020, 02:53:54 pm »
Excellent!

Offline SierraKen

  • Forum Resident
  • Posts: 1454
    • View Profile
Re: Raycasting
« Reply #3 on: July 13, 2020, 04:05:33 pm »
I knew if I waited long enough someone would figure this out here! Thank you! Back in the mid 90's someone posted something just like this for QBasic on the newsgroup comp.lang.basic.misc and it was tons of code if I recall and they also used DATA for their map. It worked very slowly though back then. I wish I still had it but I think I lost it over time. I've wanted to learn this ever since and now that you made an example, I'm going to study like crazy. :)) It's a wish come true, thank you!

Offline TerryRitchie

  • Seasoned Forum Regular
  • Posts: 495
  • Semper Fidelis
    • View Profile
Re: Raycasting
« Reply #4 on: July 13, 2020, 04:14:48 pm »
Yeah, I always viewed ray tracing as coding voodoo, probably because Wolfenstein 3D came out right around the time I was really getting into coding. I figured the math alone behind this was something to tackle. Turns out it's a very simple effect to achieve.

The narrator in the video does a good job of explaining each part of the code. I kept the variable names, functions, and subroutines named the same as well. I had to make a few changes here and there because of the funky way C handles IF statements but for the most part it's a one to one conversion.
In order to understand recursion, one must first understand recursion.

Offline SierraKen

  • Forum Resident
  • Posts: 1454
    • View Profile
Re: Raycasting
« Reply #5 on: July 13, 2020, 04:48:32 pm »
I watched the video and it's a bit beyond me for now. He did say we can change the map so I made the map bigger with more walls, etc. Doing that we could even remove the 2D map and make it as large as you want I would think, just keep all of the 2D code so it knows where the walls are, but not the graphic. I also noticed that you can walk through the walls. I tried scanning through the code to see if there was a simple way to say something like hit = 1 when the wall is too close and you can't move anymore, but I can't really find it. Maybe you can add it sometime so people can't walk through them. But still an awesome job!

FellippeHeitor

  • Guest
Re: Raycasting
« Reply #6 on: July 13, 2020, 06:31:54 pm »
Finally got around to watching the video too. The guy's really funny, which is a bonus.

Offline johnno56

  • Forum Resident
  • Posts: 1270
  • Live long and prosper.
    • View Profile
Re: Raycasting
« Reply #7 on: July 13, 2020, 07:09:22 pm »
Cool video. I like the way he walks (no pun intended) you through the process. I have keyed in raycasters before but none of the listings really explained 'how' it all works. Knowledge of OpenGL, in this case, seems to be a per-requisite. Trigonometry was never by best subject, so I found the math, somewhat intimidating. The video was easy to follow - understanding completely will take some time - but the results are  there to see... Wow is right.

I hope he does do texture; collision; objects etc videos. Wolf3D. Watch out!

Thank you, Terry!!
Logic is the beginning of wisdom.

Offline TempodiBasic

  • Forum Resident
  • Posts: 1792
    • View Profile
Re: Raycasting
« Reply #8 on: July 13, 2020, 07:16:03 pm »
Cool!
I find it more fun and clearer than this one of Biswit that I saw some year ago and I posted the first part (that written in Qbasic) in [abandoned, outdated and now likely malicious qb64 dot net website - don’t go there] for some issue about runtime errors.
https://www.youtube.com/watch?v=HQYsFshbkYw
LOL I find at the bottom of comments just 3 years ago a comment of an unknown TempodiBasic Programming

Thanks to share this resource !
Great translation into QB64!
Programming isn't difficult, only it's  consuming time and coffee

Offline OldMoses

  • Seasoned Forum Regular
  • Posts: 469
    • View Profile
Re: Raycasting
« Reply #9 on: July 13, 2020, 07:36:35 pm »

Offline Ashish

  • Forum Resident
  • Posts: 630
  • Never Give Up!
    • View Profile
Re: Raycasting
« Reply #10 on: July 14, 2020, 01:48:10 am »
Amazing! :D
if (Me.success) {Me.improve()} else {Me.tryAgain()}


My Projects - https://github.com/AshishKingdom?tab=repositories
OpenGL tutorials - https://ashishkingdom.github.io/OpenGL-Tutorials