Author Topic: Problem with Pseudo 3D Projection  (Read 1691 times)

0 Members and 1 Guest are viewing this topic.

Offline Zentek

  • Newbie
  • Posts: 2
Problem with Pseudo 3D Projection
« on: May 10, 2021, 01:12:39 pm »
Hello to all of you,
I am new here and wanted to ask something specific in this forum about my code in QB64.
So my code for a simple pseudo 3D projection is the following:
Code: QB64: [Select]
  1. REM 3D Test Suite
  2. REM (C) Christoph Petsch
  3. REM TODO: clipping of lines
  4.  
  5.  
  6. mdx = 160: mdy = 100 ' The Middle of the screen
  7. wheight = 80 ' our wall height
  8.  
  9. ' lines start and end + player position
  10. px = 0: py = 0
  11. ' lines will be in an array
  12. DIM wall(5, 4) AS INTEGER
  13. wall(1, 1) = -10: wall(1, 2) = 10: wall(1, 3) = 1: wall(1, 4) = 10
  14. wall(2, 1) = 10: wall(2, 2) = 10: wall(2, 3) = 10: wall(2, 4) = -10
  15. wall(3, 1) = 10: wall(3, 2) = -10: wall(3, 3) = 5: wall(3, 4) = -15
  16. wall(4, 1) = -5: wall(4, 2) = -15: wall(4, 3) = -10: wall(4, 4) = -10
  17. wall(5, 1) = -10: wall(5, 2) = -10: wall(5, 3) = -10: wall(5, 4) = 10
  18. ' players angle
  19. angle = 0
  20. scale = 5
  21. pi = 3.14159
  22.  
  23.     FOR i = 1 TO 5
  24.         ' here goes the action now
  25.         ' transform the wall into players position
  26.         wx1 = wall(i, 1): wy1 = wall(i, 2): wx2 = wall(i, 3): wy2 = wall(i, 4)
  27.         tx1 = wx1 - px: ty1 = wy1 - py: tx2 = wx2 - px: ty2 = wy2 - py
  28.         ' rotate this
  29.         rx1 = tx1 * COS(-angle) - ty1 * SIN(-angle)
  30.         ry1 = tx1 * SIN(-angle) + ty1 * COS(-angle)
  31.         rx2 = tx2 * COS(-angle) - ty2 * SIN(-angle)
  32.         ry2 = tx2 * SIN(-angle) + ty2 * COS(-angle)
  33.         ' now we know the start and end point
  34.         PSET (mdx, mdy), 15
  35.         LINE (mdx + rx1, mdy + ry1)-(mdx + rx2, mdy + ry2), 15
  36.         ' draw the line around the player
  37.         ' now we have our static player and the rotated map
  38.         ' next thing to do is now project the 2d point to a 3d space
  39.  
  40.         REM ******************** 3D Projection ************************
  41.         REM Formula is quite simple
  42.         REM Use ry as scaling factor ... zx1 = rx1/ry1
  43.         REM upper and lower will be calculated with zu1 = wheight / ry1
  44.  
  45.  
  46.         ' render nothing behind us
  47.         IF rx1 <= 0 AND rx2 <= 0 THEN GOTO notrender
  48.         ' Clip the lines if one is partially behind the player
  49.         ' this will be done with intersection calculation
  50.  
  51.         IF rx1 <= 0 OR rx2 <= 0 THEN
  52.             ' clip the line at y-axis
  53.             iy = (rx1 * ry2 - rx2 * ry1) / (rx1 - rx2)
  54.             IF rx1 <= 0 THEN rx1 = 0.01: ry1 = iy
  55.             IF rx2 <= 0 THEN rx2 = 0.01: ry2 = iy
  56.         END IF
  57.  
  58.         ' Calculations here
  59.         zx1 = -ry1 * scale / rx1
  60.         zu1 = wheight / rx1
  61.         zd1 = -zu1
  62.         zx2 = -ry2 * scale / rx2
  63.         zu2 = wheight / rx2
  64.         zd2 = -zu2
  65.  
  66.         ' draw the lines
  67.         LINE (mdx + zx1, mdy + zu1)-(mdx + zx2, mdy + zu2), 14
  68.         LINE (mdx + zx1, mdy + zd1)-(mdx + zx2, mdy + zd2), 14
  69.         LINE (mdx + zx1, mdy + zu1)-(mdx + zx1, mdy + zd1), 6
  70.         LINE (mdx + zx2, mdy + zu2)-(mdx + zx2, mdy + zd2), 6
  71.  
  72.         notrender:
  73.     NEXT i
  74.     a$ = INKEY$
  75.     SELECT CASE a$
  76.         CASE "q"
  77.             END
  78.         CASE "w"
  79.             CLS
  80.             px = px + 0.2 * COS(angle)
  81.             py = py + 0.2 * SIN(angle)
  82.         CASE "s"
  83.             CLS
  84.             px = px - 0.2 * COS(angle)
  85.             py = py - 0.2 * SIN(angle)
  86.         CASE "a"
  87.             CLS
  88.             angle = angle + 0.01
  89.         CASE "d"
  90.             CLS
  91.             angle = angle - 0.01
  92.         CASE "j"
  93.             ' Strafe
  94.             CLS
  95.             px = px - 0.2 * COS(-angle + 0.5 * pi)
  96.             py = py - 0.2 * SIN(-angle + 0.5 * pi)
  97.         CASE "l"
  98.             CLS
  99.             px = px + 0.2 * COS(-angle + 0.5 * pi)
  100.             py = py + 0.2 * SIN(-angle + 0.5 * pi)
  101.         CASE ELSE
  102.             'do nothing
  103.     END SELECT
  104.  
  105.  
  106.  

So this little program just projects a 2d map with some sense of depth onto the screen. It works in fact but it renders a terrible picture...
It seems like some warp effect I can't get rid of (you will see it yourself) also the intersection calculation is just a snipped from github and I don't really understand what it does... (if someone has an idea, what this actually does in terms of math (it just calculates the intersection point with the y axis))

So thank you if you find the time to answer my (maybe stupid) question.

Greetings

Offline _vince

  • Seasoned Forum Regular
  • Posts: 422
Re: Problem with Pseudo 3D Projection
« Reply #1 on: May 10, 2021, 06:42:41 pm »
I tried it, pretty interesting

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
Re: Problem with Pseudo 3D Projection
« Reply #2 on: May 13, 2021, 09:05:36 pm »
Code: QB64: [Select]
  1. '_FullScreen   >> no full screen until debugged
  2. 'If _FullScreen = 0 Then _FullScreen _Off
  3.  
  4. mdx = 160: mdy = 100 ' The Middle of the screen >> not for screen 12 maybe screen 13?

Hi Zentek welcome to forum, I just took a look at your code.

Warping might be from poor scaling but middle doesn't look right for screen 12, looks like screen 13.

Offline Zentek

  • Newbie
  • Posts: 2
Re: Problem with Pseudo 3D Projection
« Reply #3 on: May 17, 2021, 04:10:02 pm »
Yeah excuse me, I did some errors.
I also already solved the problem by simply using my good ol' math skillz ;)
Its just the intersection with the y-axis and in fact this can be calculated by the slope (you calculate for a linear function y = mx+n the case x = 0 => n = intersection point)

You will find a first working demo. At the moment I implement PAINT to work correctly and then clipping, also sectors for different heights are implemented at the moment)
Code: QB64: [Select]
  1. SCREEN _NEWIMAGE(1280, 720, 256)
  2.  
  3. mdx = 1280 / 2: mdy = 720 / 2 ' The Middle of the screen
  4. wheight = 500 ' our wall height
  5. pheight = 0
  6.  
  7. ' lines start and end + player position
  8. px = 0: py = 0
  9. ' lines will be in an array
  10. DIM wall(5, 6) AS INTEGER
  11. wall(1, 1) = -10: wall(1, 2) = 10: wall(1, 3) = 10: wall(1, 4) = 10: wall(1, 5) = 10: wall(1, 6) = -1000
  12. wall(2, 1) = 10: wall(2, 2) = 10: wall(2, 3) = 10: wall(2, 4) = -10: wall(2, 5) = 20: wall(2, 6) = 0
  13. wall(3, 1) = 10: wall(3, 2) = -10: wall(3, 3) = 5: wall(3, 4) = -15: wall(3, 5) = -20: wall(3, 6) = 0
  14. wall(4, 1) = -5: wall(4, 2) = -15: wall(4, 3) = -10: wall(4, 4) = -10: wall(4, 5) = -5: wall(4, 6) = 0
  15. wall(5, 1) = -10: wall(5, 2) = -10: wall(5, 3) = -10: wall(5, 4) = 10: wall(5, 5) = 10: wall(5, 6) = 0
  16. ' players angle
  17. angle = 0
  18. scale = 100
  19. pi = 3.14159
  20. fwd = 0: bwd = 0: left = 0: right = 0: strafel = 0: strafer = 0: scaler = 0
  21.     FOR i = 1 TO 5
  22.         ' here goes the action now
  23.         ' transform the wall into players position
  24.         wx1 = wall(i, 1): wy1 = wall(i, 2): wx2 = wall(i, 3): wy2 = wall(i, 4)
  25.         tx1 = wx1 - px: ty1 = wy1 - py: tx2 = wx2 - px: ty2 = wy2 - py
  26.         ' rotate this
  27.         rx1 = tx1 * COS(-angle) - ty1 * SIN(-angle)
  28.         ry1 = tx1 * SIN(-angle) + ty1 * COS(-angle)
  29.         rx2 = tx2 * COS(-angle) - ty2 * SIN(-angle)
  30.         ry2 = tx2 * SIN(-angle) + ty2 * COS(-angle)
  31.         ' now we know the start and end point
  32.         PSET (mdx, mdy), 15
  33.         LINE (mdx + rx1, mdy + ry1)-(mdx + rx2, mdy + ry2), 15
  34.         ' draw the line around the player
  35.         ' now we have our static player and the rotated map
  36.         ' next thing to do is now project the 2d point to a 3d space
  37.  
  38.         REM ******************** 3D Projection ************************
  39.         REM Formula is quite simple
  40.         REM Use ry as scaling factor ... zx1 = rx1/ry1
  41.         REM upper and lower will be calculated with zu1 = wheight / ry1
  42.  
  43.  
  44.         ' render nothing behind us
  45.         IF rx1 <= 0 AND rx2 <= 0 THEN GOTO notrender
  46.         ' Clip the lines if one is partially behind the player
  47.         ' this will be done with intersection calculation
  48.  
  49.         IF rx1 <= 0 OR rx2 <= 0 THEN
  50.             ' clip the line at y-axis
  51.             iy = ry1 - (ry1 - ry2) / (rx1 - rx2) * rx1 ' line intersection calculation with simple linear function y = 0
  52.             IF rx1 <= 0 THEN rx1 = 0.01: ry1 = iy
  53.             IF rx2 <= 0 THEN rx2 = 0.01: ry2 = iy
  54.         END IF
  55.  
  56.         ' Calculations here
  57.         zx1 = -ry1 * scale / rx1
  58.         zd1 = -wheight / rx1 - wall(i, 5) / rx1 - pheight
  59.         zu1 = wheight / rx1 - wall(i, 6) / rx1 - pheight
  60.         zx2 = -ry2 * scale / rx2
  61.         zd2 = -wheight / rx2 - wall(i, 5) / rx2 - pheight
  62.         zu2 = wheight / rx2 - wall(i, 6) / rx2 - pheight
  63.  
  64.         ' draw the lines
  65.         'LINE (mdx * zx1 / 2 + mdx, mdy * zu1 / 2 + mdy)-(mdx * zx2 / 2 + mdx, mdy * zu2 / 2 + mdy), 14
  66.         'LINE (mdx * zx1 / 2 + mdx, mdy * zd1 / 2 + mdy)-(mdx * zx2 / 2 + mdx, mdy * zd2 / 2 + mdy), 14
  67.         'LINE (mdx * zx1 / 2 + mdx, mdy * zu1 / 2 + mdy)-(mdx * zx1 / 2 + mdx, mdy * zd1 / 2 + mdy), 6
  68.         'LINE (mdx * zx2 / 2 + mdx, mdy * zu2 / 2 + mdy)-(mdx * zx2 / 2 + mdx, mdy * zd2 / 2 + mdy), 6
  69.         ' draw the lines
  70.         LINE (zx1 + mdx, zu1 + mdy)-(zx2 + mdx, zu2 + mdy), 14
  71.         LINE (zx1 + mdx, zd1 + mdy)-(zx2 + mdx, zd2 + mdy), 14
  72.         LINE (zx1 + mdx, zu1 + mdy)-(zx1 + mdx, zd1 + mdy), 6
  73.         LINE (zx2 + mdx, zu2 + mdy)-(zx2 + mdx, zd2 + mdy), 6
  74.  
  75.         notrender:
  76.     NEXT i
  77.     LOCATE 1, 1: PRINT "Scale factor = "; scale
  78.  
  79.     ' modified key input to detect several keypresses
  80.     _LIMIT 20
  81.     kpress = _KEYHIT
  82.     IF kpress = 18432 THEN fwd = 1
  83.     IF kpress = -18432 THEN fwd = 0
  84.     IF kpress = 20480 THEN bwd = 1
  85.     IF kpress = -20480 THEN bwd = 0
  86.     IF kpress = 19200 THEN left = 1
  87.     IF kpress = -19200 THEN left = 0
  88.     IF kpress = 19712 THEN right = 1
  89.     IF kpress = -19712 THEN right = 0
  90.     IF kpress = 97 THEN strafel = 1
  91.     IF kpress = -97 THEN strafel = 0
  92.     IF kpress = 100 THEN strafer = 1
  93.     IF kpress = -100 THEN strafer = 0
  94.     IF kpress = 27 THEN END
  95.     IF kpress = 43 THEN scaler = 1
  96.     IF kpress = -43 THEN scaler = 0
  97.     IF kpress = 45 THEN scaler = -1
  98.     IF kpress = -45 THEN scaler = 0
  99.     IF fwd = 1 THEN px = px + 0.2 * COS(angle): py = py + 0.2 * SIN(angle): CLS
  100.     IF bwd = 1 THEN px = px - 0.2 * COS(angle): py = py - 0.2 * SIN(angle): CLS
  101.     IF left = 1 THEN angle = angle + 0.05: CLS
  102.     IF right = 1 THEN angle = angle - 0.05: CLS
  103.     IF strafel = 1 THEN px = px + 0.2 * COS(angle + 0.5 * pi): py = py + 0.2 * SIN(angle + 0.5 * pi): CLS
  104.     IF strafer = 1 THEN px = px - 0.2 * COS(angle + 0.5 * pi): py = py - 0.2 * SIN(angle + 0.5 * pi): CLS
  105.     IF scaler > 0 THEN scale = scale + 50: CLS
  106.     IF scaler < 0 THEN scale = scale - 50: CLS
  107.  
  108. REM  (rx1 * ry2 - rx2 * ry1)/(rx1 - rx2)
  109.  
  110.  

But obviously there are some problems with PAINT as when something is off the screen, it won't be rendered correctly ...

Have fun and stay tuned :)

Offline OldMoses

  • Seasoned Forum Regular
  • Posts: 469
Re: Problem with Pseudo 3D Projection
« Reply #4 on: May 18, 2021, 09:05:56 am »
I suspect the PAINT issue is with borders that don't "seal" correctly. I tried PAINT with a shape that was partially offscreen and it worked alright, although I found the border color parameter is not so "optional" as the wiki suggests. If I didn't control the border colors, it would paint the whole screen.

Code: QB64: [Select]
  1. SCREEN _NEWIMAGE(400, 400, 32)
  2. LINE (-200, -200)-(200, 200), &HFFFFFFFF, B
  3. PAINT STEP(-20, -20), &HFFFF0000, &HFFFFFFFF
  4. LINE (250, 250)-(300, 300), &HFFFFFFFF, B
  5. PAINT STEP(-10, -10), &HFFFF0000, &HFFFFFFFF
  6.  
  7.