Author Topic: "Real" 3D Rendering  (Read 8002 times)

0 Members and 1 Guest are viewing this topic.

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
"Real" 3D Rendering
« on: November 19, 2019, 07:03:59 pm »
This? It use hardware images + hardware MAPTRIANGLE https://www.qb64.org/forum/index.php?topic=300.msg102623#msg102623

Apologies to Steve for interrupting his question on how (where) to apply PAINT for Isometric Mappings but all this talk about 3D here PLUS what I saw at Syntax Bomb has me wondering about "real" 3D rendering.

Here is link that fits about 2 months ago: https://www.qb64.org/forum/index.php?topic=1759.0
with Petr starting and with STxAxTIC weighing in (with heavy stuff). It's not at all what I am remembering, I've still got to read it over again carefully. But doing things with MapTriangle seems right specially after what I have researched today on Internet.

I am looking for way to render a user's 2D screen view of a "Unit" or standard "pixel" like cube given it's (x,y,z) center in Euclidean space where the size of the cube is a function of it's distance from the user's view. I've come to realize that every standard cube is only the same size in a sphere whose origin is viewer eye. The same sphere intersects through the layers of z perpendicular of viewers eye line with circles of different radii.

So given the (x, y, z) of standard size cube, how big does it appear to viewer in his screen view?
At this point I don't care about rotating cubes, just would like to see where it fits in a cubic grid rendering it's faces properly.

Having that, we might be able to do "real" block lettering, among other starter things for 3D.
If the letters were lined up on a z plane level from left to right in users view the letters in middle should be larger than letters at extreme right or left. right?
« Last Edit: November 19, 2019, 07:06:15 pm by bplus »

Offline STxAxTIC

  • Library Staff
  • Forum Resident
  • Posts: 1091
  • he lives
    • View Profile
Re: "Real" 3D Rendering
« Reply #1 on: November 20, 2019, 06:19:34 am »
All great stuff bplus, good questions... I think I see exactly what you're asking for:

Quote
I am looking for way to render a user's 2D screen view of a "Unit" or standard "pixel" like cube given it's (x,y,z) center in Euclidean space where the size of the cube is a function of it's distance from the user's view. I've come to realize that every standard cube is only the same size in a sphere whose origin is viewer eye. The same sphere intersects through the layers of z perpendicular of viewers eye line with circles of different radii.

This is exactly, 100%, no bullshit, exactly the thing I was talking about with Petr at https://www.qb64.org/forum/index.php?topic=1759.msg110131#msg110131.

What you're basically asking for is a voxel engine with a locked perspective. There are two flavors though: (i) The kind where the user is immersed in the scene, like you'd get in a game, or, instead: (ii) the user views the scene "from infinity", as if viewed from a telescope. (The second method is what you'd get out of a 3D graphing calculator. You have 3D graphics but don't feel surrounded by them.) Both methods use the same math.

What is the math? This is the way to go (for example):

Code: QB64: [Select]
  1. vec3Ddotnhat = vec(i, 1) * nhat(1) + vec(i, 2) * nhat(2) + vec(i, 3) * nhat(3)
  2. vec2D(i, 1) = (vec(i, 1) * uhat(1) + vec(i, 2) * uhat(2) + vec(i, 3) * uhat(3)) * fovd / vec3Ddotnhat
  3. vec2D(i, 2) = (vec(i, 1) * vhat(1) + vec(i, 2) * vhat(2) + vec(i, 3) * vhat(3)) * fovd / vec3Ddotnhat

Here's what you're looking at:
vec(i,j) is an array holding all information on the objects in your world. Corners of a cube, for instance.
nhat(k) is a normalized vector pointing from your screen toward your eye
uhat(k) is a normalized vector pointing horizontally in your screen
vhat(k) is a normalized vector pointing vertically in your screen
fovd stands for field-of-view distance, just some number
vec2D(i,j) is what ends up on the user's screen

... and again, here's that damn drawing everyone hates by now:
« Last Edit: November 20, 2019, 06:37:27 am by STxAxTIC »
You're not done when it works, you're done when it's right.

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: "Real" 3D Rendering
« Reply #2 on: November 20, 2019, 11:24:13 am »
Hi STxAxTIC

First, thanks for going over this again!

Having worked through your PDF on vectors section I might be able use the vector math stuff I worked up. You are using arrays with x, y, z as 2nd dimension numbers 1, 2, 3 but I setup as v.x, v.y, v.z

Ha! I don't think everyone hates the picture so much as just don't understand all the symbols, plus it's too big to view the whole thing AND see the details both, BUT I just found a "view image in separate tab" browser option that works perfectly! Artwork and equations what a beauty!

I will review those PDF notes, I am thinking uhat, vhat and nhat are just 1,0,0 and 0,1,0 and 0,0,1 so nhat(1) = 0 nhat(2) = 0 nhat(3) = 1 the z-axis what I call eye-line, "point to your eye" that's right hand rule, thumb points x pos, index y pos, middle z pos so might have to reorient Basic's normal y pos pointing down.



« Last Edit: November 20, 2019, 11:28:49 am by bplus »

Offline Petr

  • Forum Resident
  • Posts: 1720
  • The best code is the DNA of the hops.
    • View Profile
Re: "Real" 3D Rendering
« Reply #3 on: November 20, 2019, 11:27:07 am »
Hm... Hi...

My survey how convert 2D to 3D is black era my programming life :)

As for the 3D letters, I wrote today a program that draws the letter H in 3D. Using MAPTRIANGLE3D. I proceeded by identifying the vertices in the letter H, where the first 4 are for the left part of the character (left leg), the next 4 for the right leg, and 4 for the comma in the middle of H. Thus 12 points. This creates a 2D letter H. For 3D I take these 12 points and in 3D is only the depth shift. In total, the letter H will contain 24 vertices. Using the 3D command, I avoided the conversion from 2D to 3D (only I converted the coordinates in the letter H to the OpenGL coordinate system at the beginning of the program). I suppose this would be the way to convert all characters to real 3D.


Example: HARDWARE 3D character "H"

Code: QB64: [Select]
  1. 'real HARDWARE 3D letter "H" (easy to do it)
  2.  
  3. 'letter is build as 2D map for vertexes. Vertex is "X" in DATA block, "O" is empty place
  4. 'DATA is not used, it is just for demonstration for you!
  5.  
  6. '1] DATA X,0,X,0,0,X,0,X: 'Vertex(0) is in position X = 1, Y = 1. Foreground Z is let say -5, Background coordinates are the same, just Z is -8, so depth is 3
  7. '   DATA 0,0,0,0,0,0,0,0: 'Vertex(1) is in position X = 1, Y = 3,
  8. '   DATA 0,0,0,0,0,0,0,0:
  9. '2] DATA 0,0,X,0,0,X,0,0:
  10. '3] DATA 0,0,X,0,0,X,0,0:
  11. '   DATA 0,0,0,0,0,0,0,0:
  12. '   DATA 0,0,0,0,0,0,0,0:
  13. '4] DATA X,0,X,0,0,X,0,X: 'Vertex(2) is here. Why? Because later is used MAPTRIANGLE. So, for better drawing i use triangles: Vertex(2) X = 1, Y = 8,
  14. '                                                                                                                           Vertex(3) X = 3, Y = 8
  15.  
  16.     X AS SINGLE
  17.     Y AS SINGLE
  18.     Z AS SINGLE
  19.     'used in phase 2:
  20.     Lenght AS SINGLE
  21.     Angle AS SINGLE
  22.  
  23.     rX AS SINGLE
  24.     rY AS SINGLE
  25.     rZ AS SINGLE
  26.  
  27. DIM Vertex(23) AS V 'DATA map is one side and contains 12 vertexes. 3D need two sides.
  28.  
  29. HChar: 'USED DATA for vertexes: X, Y, Z, X, Y, Z...      This is mask for foreground side. Background side is the same, just Z is not -9, but -9.6
  30. DATA 1,1,-5,3,1,-5,1,8,-5,3,8,-5: 'left 2 triangles
  31. DATA 6,1,-5,8,1,-5,6,8,-5,8,8,-5: ' right 2 triangles
  32. DATA 3,4,-5,6,4,-5,3,5,-5,6,5,-5: 'middle in H 2 triangles
  33.  
  34.  
  35. 'because is next used HARDWARE MAPTRIANGLE, and X, Y is not in OpenGL coordinate system, must this be recalculated
  36.  
  37. RESTORE HChar
  38. FOR LoadIt = 0 TO 11
  39.     READ X, Y, Z
  40.     Vertex(LoadIt).X = -4 + X '      H character foreground
  41.     Vertex(LoadIt).Y = -4 + Y
  42.     Vertex(LoadIt).Z = -9
  43.  
  44.     Vertex(LoadIt + 12).X = -4 + X ' H character background
  45.     Vertex(LoadIt + 12).Y = -4 + Y
  46.     Vertex(LoadIt + 12).Z = -9.6
  47. NEXT LoadIt
  48.  
  49. 'prepare texture:
  50. T& = _NEWIMAGE(100, 100, 32)
  51. CLS , &HDCFFFF11
  52. T2& = _NEWIMAGE(100, 100, 32)
  53. _DEST T2&
  54. CLS , &HFDFF0000
  55.  
  56. Ht& = _COPYIMAGE(T&, 33)
  57. Ht2& = _COPYIMAGE(T2&, 33)
  58.  
  59. 'And now.... draw it all.    DO NOT FORGOT TO _DISPLAY WHEN USE HARDWARE IMAGES!!!
  60.  
  61.     FOR D = 0 TO 22 STEP 4 'because we writed coordinates in corectness order in DATA for use with MAPTRIANGLE, this one loop draw it for us so easy:
  62.         _MAPTRIANGLE (0, 0)-(99, 0)-(0, 99), Ht& TO(Vertex(D).X, Vertex(D).Y, Vertex(D).Z)-(Vertex(D + 1).X, Vertex(D + 1).Y, Vertex(D + 1).Z)-(Vertex(D + 2).X, Vertex(D + 2).Y, Vertex(D + 2).Z)
  63.         _MAPTRIANGLE (99, 0)-(0, 99)-(99, 99), Ht& TO(Vertex(D + 1).X, Vertex(D + 1).Y, Vertex(D + 1).Z)-(Vertex(D + 2).X, Vertex(D + 2).Y, Vertex(D + 2).Z)-(Vertex(D + 3).X, Vertex(D + 3).Y, Vertex(D + 3).Z)
  64.     NEXT
  65.     LOCATE 1, 1: PRINT "Press any key..."
  66.     _DISPLAY
  67.  
  68. 'now is draw to screen FOREGROUND and BACKGROUND side H character. Vertexes in Z are not draw. Let show it for us:
  69. 'Because is used OpenGL coordinate system and H vertexes are calculated from 0, is center in point 0,0,-9.3
  70.  
  71. 'for rotating character in space is need:
  72. ' - calculating lenght for all vertexes from rotation center point  (using Pythagoran theorem)
  73. ' - calculating start angle between rotation point and vertex (using JK! function)
  74.  
  75. FOR LenghtCalc = 0 TO 23 '24 vertexes total. Rotation for X/Z:
  76.     Vertex(LenghtCalc).Lenght = SQR((0.3) ^ 2 + (Vertex(LenghtCalc).X ^ 2)) 'rotation center in axis Z: RADIUS is .3 (9.6 - 9) / 2
  77.     Vertex(LenghtCalc).Angle = JK!(0, -9.3, Vertex(LenghtCalc).X, Vertex(LenghtCalc).Z, Vertex(LenghtCalc).Lenght)
  78. NEXT LenghtCalc
  79.  
  80. 'now for rotating in every step must be new vertex position calculated and then be draw:
  81. 'backup old values:
  82. FOR b = 0 TO 23
  83.     Vertex(b).rX = Vertex(b).X
  84.     Vertex(b).rY = Vertex(b).Y
  85.     Vertex(b).rZ = Vertex(b).Z
  86.  
  87.  
  88.     pi = pi + .1
  89.     FOR Recalc = 0 TO 23
  90.         Vertex(Recalc).X = SIN(Vertex(Recalc).Angle + pi) * Vertex(Recalc).Lenght
  91.         Vertex(Recalc).Z = -9.3 + COS(Vertex(Recalc).Angle + pi) * Vertex(Recalc).Lenght
  92.     NEXT Recalc
  93.     LOCATE 1, 1: PRINT "As you see, here miss some sides! (Source line 91). Press key..."
  94.     FOR D = 0 TO 22 STEP 4 'because we writed coordinates in corectness order in DATA for use with MAPTRIANGLE, this one loop draw it for us so easy:
  95.         _MAPTRIANGLE (0, 0)-(99, 0)-(0, 99), Ht& TO(Vertex(D).X, Vertex(D).Y, Vertex(D).Z)-(Vertex(D + 1).X, Vertex(D + 1).Y, Vertex(D + 1).Z)-(Vertex(D + 2).X, Vertex(D + 2).Y, Vertex(D + 2).Z)
  96.         _MAPTRIANGLE (99, 0)-(0, 99)-(99, 99), Ht& TO(Vertex(D + 1).X, Vertex(D + 1).Y, Vertex(D + 1).Z)-(Vertex(D + 2).X, Vertex(D + 2).Y, Vertex(D + 2).Z)-(Vertex(D + 3).X, Vertex(D + 3).Y, Vertex(D + 3).Z)
  97.     NEXT
  98.     _DISPLAY
  99.     _LIMIT 25
  100. 'now we see, that us draw loop must be upgraded. Lets go:
  101. 'Foreground indexes are 0 to 11.  Background indexes are 12 to 23. Good order -> less work for us.
  102. 'copy previous correct draw loop
  103.  
  104.     pi = pi + .01
  105.     FOR Recalc = 0 TO 23
  106.         Vertex(Recalc).X = SIN(Vertex(Recalc).Angle + pi) * Vertex(Recalc).Lenght
  107.         Vertex(Recalc).Z = -9.3 + COS(Vertex(Recalc).Angle + pi) * Vertex(Recalc).Lenght
  108.     NEXT Recalc
  109.     LOCATE 1, 1: PRINT "Some sides added. But is this enought?  Press SPACE.                        "
  110.     FOR D = 0 TO 22 STEP 4
  111.         _MAPTRIANGLE (0, 0)-(99, 0)-(0, 99), Ht& TO(Vertex(D).X, Vertex(D).Y, Vertex(D).Z)-(Vertex(D + 1).X, Vertex(D + 1).Y, Vertex(D + 1).Z)-(Vertex(D + 2).X, Vertex(D + 2).Y, Vertex(D + 2).Z)
  112.         _MAPTRIANGLE (99, 0)-(0, 99)-(99, 99), Ht& TO(Vertex(D + 1).X, Vertex(D + 1).Y, Vertex(D + 1).Z)-(Vertex(D + 2).X, Vertex(D + 2).Y, Vertex(D + 2).Z)-(Vertex(D + 3).X, Vertex(D + 3).Y, Vertex(D + 3).Z)
  113.     NEXT
  114.  
  115.     'indexes diagram (in parentheses is background), without is foreground    (try draw virtual lines between numbers in order as are here)
  116.  
  117.     ' (12)   (13)               (16)      (17)
  118.     '  0      1                   4         5
  119.     '
  120.     '
  121.     '        (20)               (21)
  122.     '         8                  9
  123.     '        (22)               (23)
  124.     '         10                 11
  125.     '
  126.     '
  127.     ' (14)   (15)               (18)      (19)
  128.     '  2      3                   6         7
  129.  
  130.     RESTORE sides
  131.     FOR S = 1 TO 4
  132.         READ i1, i2, i3, i4, i5, i6
  133.         _MAPTRIANGLE (0, 0)-(99, 0)-(0, 99), Ht& TO(Vertex(i1).X, Vertex(i1).Y, Vertex(i1).Z)-(Vertex(i2).X, Vertex(i2).Y, Vertex(i2).Z)-(Vertex(i3).X, Vertex(i3).Y, Vertex(i3).Z)
  134.         _MAPTRIANGLE (99, 0)-(0, 99)-(99, 99), Ht& TO(Vertex(i4).X, Vertex(i4).Y, Vertex(i4).Z)-(Vertex(i5).X, Vertex(i5).Y, Vertex(i5).Z)-(Vertex(i6).X, Vertex(i6).Y, Vertex(i6).Z)
  135.     NEXT
  136.  
  137.     sides:
  138.     DATA 12,0,14,0,14,2: 'left - left side
  139.     DATA 13,1,15,1,15,3: 'left - right side
  140.     DATA 16,4,18,4,18,6: 'right - left side
  141.     DATA 17,5,19,5,19,7: 'right - right side
  142.  
  143.     _DISPLAY
  144.     _LIMIT 25
  145.  
  146. 'now again: copy previous code, but in this case do control in axis Y/Z:
  147.  
  148.  
  149. 'for rotating character in space is need:
  150. ' - calculating lenght for all vertexes from rotation center point  (using Pythagoran theorem)
  151. ' - calculating start angle between rotation point and vertex (using JK! function)
  152.  
  153.  
  154. 'restore original values
  155. FOR b = 0 TO 23
  156.     Vertex(b).X = Vertex(b).rX
  157.     Vertex(b).Y = Vertex(b).rY
  158.     Vertex(b).Z = Vertex(b).rZ
  159.  
  160. FOR LenghtCalc = 0 TO 23 '23 vertexes total. Rotation for Y/Z:
  161.     Vertex(LenghtCalc).Lenght = SQR((Vertex(LenghtCalc).Y ^ 2) + (0.3 ^ 2)) '(Vertex(LenghtCalc).Z ^ 2)) 'we are in OpenGL, middle is in 0,0
  162.     Vertex(LenghtCalc).Angle = JK!(0, -9.3, Vertex(LenghtCalc).Y, Vertex(LenghtCalc).Z, Vertex(LenghtCalc).Lenght)
  163. NEXT LenghtCalc
  164.  
  165.  
  166.     pi = pi + .01
  167.     FOR Recalc = 0 TO 23
  168.         Vertex(Recalc).Z = -8.1 - SIN(Vertex(Recalc).Angle + pi) * Vertex(Recalc).Lenght
  169.         Vertex(Recalc).Y = COS(Vertex(Recalc).Angle + pi) * Vertex(Recalc).Lenght
  170.     NEXT Recalc
  171.     LOCATE 1, 1: PRINT "  As you see, previous texturing is not enough. I use next texture for           last texturing. Press space..."
  172.     FOR D = 0 TO 22 STEP 4
  173.         _MAPTRIANGLE (0, 0)-(99, 0)-(0, 99), Ht& TO(Vertex(D).X, Vertex(D).Y, Vertex(D).Z)-(Vertex(D + 1).X, Vertex(D + 1).Y, Vertex(D + 1).Z)-(Vertex(D + 2).X, Vertex(D + 2).Y, Vertex(D + 2).Z)
  174.         _MAPTRIANGLE (99, 0)-(0, 99)-(99, 99), Ht& TO(Vertex(D + 1).X, Vertex(D + 1).Y, Vertex(D + 1).Z)-(Vertex(D + 2).X, Vertex(D + 2).Y, Vertex(D + 2).Z)-(Vertex(D + 3).X, Vertex(D + 3).Y, Vertex(D + 3).Z)
  175.     NEXT
  176.  
  177.     'indexes diagram (background)    (try draw virtual lines between numbers in order as are here)
  178.  
  179.     ' (12)   (13)               (16)      (17)
  180.     '  0      1                   4         5
  181.     '
  182.     '
  183.     '        (20)               (21)
  184.     '         8                  9
  185.     '        (22)               (23)
  186.     '         10                 11
  187.     '
  188.     '
  189.     ' (14)   (15)               (18)      (19)
  190.     '  2      3                   6         7
  191.  
  192.     RESTORE sid
  193.     FOR S = 1 TO 4
  194.         READ i1, i2, i3, i4, i5, i6
  195.         _MAPTRIANGLE (0, 0)-(99, 0)-(0, 99), Ht& TO(Vertex(i1).X, Vertex(i1).Y, Vertex(i1).Z)-(Vertex(i2).X, Vertex(i2).Y, Vertex(i2).Z)-(Vertex(i3).X, Vertex(i3).Y, Vertex(i3).Z)
  196.         _MAPTRIANGLE (99, 0)-(0, 99)-(99, 99), Ht& TO(Vertex(i4).X, Vertex(i4).Y, Vertex(i4).Z)-(Vertex(i5).X, Vertex(i5).Y, Vertex(i5).Z)-(Vertex(i6).X, Vertex(i6).Y, Vertex(i6).Z)
  197.     NEXT
  198.  
  199.     sid:
  200.     DATA 12,0,14,0,14,2: 'left - left side
  201.     DATA 13,1,15,1,15,3: 'left - right side
  202.     DATA 16,4,18,4,18,6: 'right - left side
  203.     DATA 17,5,19,5,19,7: 'right - right side
  204.  
  205.  
  206.     _DISPLAY
  207.     _LIMIT 25
  208.  
  209. 'last phase. Add missing textures. Again, for this example, copy previous code + add new DATAs for texturing.
  210.  
  211.  
  212.     pi = pi + .01
  213.     FOR Recalc = 0 TO 23
  214.         Vertex(Recalc).Z = -8.1 - SIN(Vertex(Recalc).Angle + pi) * Vertex(Recalc).Lenght
  215.         Vertex(Recalc).Y = COS(Vertex(Recalc).Angle + pi) * Vertex(Recalc).Lenght
  216.     NEXT Recalc
  217.     LOCATE 1, 1: PRINT "Press Space for end                                                                                            "
  218.     FOR D = 0 TO 22 STEP 4
  219.         _MAPTRIANGLE (0, 0)-(99, 0)-(0, 99), Ht& TO(Vertex(D).X, Vertex(D).Y, Vertex(D).Z)-(Vertex(D + 1).X, Vertex(D + 1).Y, Vertex(D + 1).Z)-(Vertex(D + 2).X, Vertex(D + 2).Y, Vertex(D + 2).Z)
  220.         _MAPTRIANGLE (99, 0)-(0, 99)-(99, 99), Ht& TO(Vertex(D + 1).X, Vertex(D + 1).Y, Vertex(D + 1).Z)-(Vertex(D + 2).X, Vertex(D + 2).Y, Vertex(D + 2).Z)-(Vertex(D + 3).X, Vertex(D + 3).Y, Vertex(D + 3).Z)
  221.     NEXT
  222.  
  223.     'indexes diagram (background)    (try draw virtual lines between numbers in order as are here)
  224.  
  225.     ' (12)   (13)               (16)      (17)
  226.     '  0      1                   4         5
  227.     '
  228.     '
  229.     '        (20)               (21)
  230.     '         8                  9
  231.     '        (22)               (23)
  232.     '         10                 11
  233.     '
  234.     '
  235.     ' (14)   (15)               (18)      (19)
  236.     '  2      3                   6         7
  237.  
  238.     RESTORE sid3
  239.     FOR S = 1 TO 10
  240.         READ i1, i2, i3, i4, i5, i6
  241.         _MAPTRIANGLE (0, 0)-(99, 0)-(0, 99), Ht2& TO(Vertex(i1).X, Vertex(i1).Y, Vertex(i1).Z)-(Vertex(i2).X, Vertex(i2).Y, Vertex(i2).Z)-(Vertex(i3).X, Vertex(i3).Y, Vertex(i3).Z)
  242.         _MAPTRIANGLE (99, 0)-(0, 99)-(99, 99), Ht2& TO(Vertex(i4).X, Vertex(i4).Y, Vertex(i4).Z)-(Vertex(i5).X, Vertex(i5).Y, Vertex(i5).Z)-(Vertex(i6).X, Vertex(i6).Y, Vertex(i6).Z)
  243.     NEXT
  244.  
  245.     sid3:
  246.     DATA 12,0,14,0,14,2: 'left - left side
  247.     DATA 13,1,15,1,15,3: 'left - right side
  248.     DATA 16,4,18,4,18,6: 'right - left side
  249.     DATA 17,5,19,5,19,7: 'right - right side
  250.     DATA 20,21,8,21,8,9: 'middle - up
  251.     DATA 22,23,10,23,10,11: ' middle - down
  252.     DATA 12,13,0,13,0,1: 'left ceil
  253.     DATA 16,17,4,17,4,5: 'right ceil
  254.     DATA 14,15,2,15,2,3: ' left bottom
  255.     DATA 18,19,6,19,6,7: 'right bottom
  256.  
  257.     'WORK DONE
  258.     _DISPLAY
  259.     _LIMIT 25
  260.  
  261.  
  262.  
  263.  
  264. FUNCTION JK! (cx, cy, px, py, R!)
  265.     LenX! = cx - px
  266.     LenY! = cy - py
  267.     jR! = 1 / R!
  268.  
  269.     jX! = LenX! * jR!
  270.     jY! = LenY! * jR!
  271.  
  272.     sinusAlfa! = jX!
  273.     Alfa! = ABS(_ASIN(sinusAlfa!))
  274.  
  275.     Q = 1
  276.     IF px >= cx AND py >= cy THEN Q = 1 ' select angle to quadrant
  277.     IF px >= cx AND py <= cy THEN Q = 2
  278.     IF px <= cx AND py <= cy THEN Q = 3
  279.     IF px <= cx AND py >= cy THEN Q = 4
  280.     SELECT CASE Q
  281.         CASE 1: alfaB! = Alfa!
  282.         CASE 2: alfaB! = _PI / 2 + (_PI / 2 - Alfa!)
  283.         CASE 3: alfaB! = _PI + Alfa!
  284.         CASE 4: alfaB! = _PI(1.5) + (_PI / 2 - Alfa!)
  285.     END SELECT
  286.     JK! = alfaB!
  287.     IF JK! = 0 THEN BEEP
  288.  

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: "Real" 3D Rendering
« Reply #4 on: November 20, 2019, 11:36:27 am »
Wow Petr, you are really coming along. I am not sure what you mean by term HARDWARE?

Offline Petr

  • Forum Resident
  • Posts: 1720
  • The best code is the DNA of the hops.
    • View Profile
Re: "Real" 3D Rendering
« Reply #5 on: November 20, 2019, 11:54:08 am »
HARDWARE = use hardware acceleration + hardware image type for texturing. It is MAPTRIANGLE 3D version, with OpenGL coordinate system.

Offline Qwerkey

  • Forum Resident
  • Posts: 755
    • View Profile
Re: "Real" 3D Rendering
« Reply #6 on: November 20, 2019, 12:39:57 pm »
The folk at QB64 who figured out and coded _MAPTRIANGLE(3D) are real geniuses.  I'm currently working on a graphics program using _MAPTRIANGLE(3D) everywhere (and Steve will be pleased that I'm also doing some _MEM work to alter graphics properties on-the-fly as bplus has already done).  The use of _MAPTRIANGLE(3D) where the coordinate z- is included gives a real 3D look, AND there's perspective included.  OpenGL is (I believe) beyond me - until Ashish finishes his studies and can get back to his OpenGL tutorials.  But _MAPTRIANGLE(3D) makes the dumb (in this case) coder look as if he has some skill.

I attach a portion of a screen from this work-in-progress progam: you can see the 3D pi image looping into the 3D annulus (it's more impressive with motion).  And all that without knowing anything about OpenGL.

Offline Petr

  • Forum Resident
  • Posts: 1720
  • The best code is the DNA of the hops.
    • View Profile
Re: "Real" 3D Rendering
« Reply #7 on: November 20, 2019, 12:47:12 pm »
It looks beautiful. With OpenGL you would have only a tenth of work compared to MAPTRIANGLE3D. In addition, OpenGL allows various effects, but they don't always work with QB64.

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: "Real" 3D Rendering
« Reply #8 on: November 20, 2019, 02:26:23 pm »
Maybe one of you 3D guys will take time and give us a few simple tutorials on making something 3D.  Start with something simple like a cube; and don't worry about filing in any sides or shading.  Just show the math behind taking the height = 1, width = 1, length = 1 and translating that into the 8 points needed to plot the cube at 0 rotation.  (Pretend it's just sitting flat on a glass table it that helps visualize it.)

Then, for an advanced lesson, show us how to rotate on the x-axis.  Keep it flat on that pretend table and just turn it 30 degrees, then 45 degrees, then 60 degrees for us, left or right.

After that, show us how to take the original cube, and then tilt it forward, or backwards, as it sits on that imaginary table.

I would think an example of just those things would be more than enough to get the people who are interested into 3D started on it, and it'd give them a point of reference so they'd know enough to at least know what questions to start asking to try and improve their knowledge past that point. 

The more you comment the code, the better it'll be as a learning too!

(Maybe The Librarian will open a section here on the forum for Tutorials, and it'd be an excellent piece of code to go there to begin with!)

https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

FellippeHeitor

  • Guest
Re: "Real" 3D Rendering
« Reply #9 on: November 20, 2019, 03:36:01 pm »
The folk at QB64 who figured out and coded _MAPTRIANGLE(3D) are real geniuses.  I'm currently working on a graphics program using _MAPTRIANGLE(3D) everywhere (and Steve will be pleased that I'm also doing some _MEM work to alter graphics properties on-the-fly as bplus has already done).  The use of _MAPTRIANGLE(3D) where the coordinate z- is included gives a real 3D look, AND there's perspective included.  OpenGL is (I believe) beyond me - until Ashish finishes his studies and can get back to his OpenGL tutorials.  But _MAPTRIANGLE(3D) makes the dumb (in this case) coder look as if he has some skill.

I attach a portion of a screen from this work-in-progress progam: you can see the 3D pi image looping into the 3D annulus (it's more impressive with motion).  And all that without knowing anything about OpenGL.

That looks promising!

Offline Petr

  • Forum Resident
  • Posts: 1720
  • The best code is the DNA of the hops.
    • View Profile
Re: "Real" 3D Rendering
« Reply #10 on: November 20, 2019, 03:47:15 pm »
Hi Steve. Ok. I start here CUBE with MAPTRIANGLE3D tutorial.

Basis: Paint a square with MAPTRIANGLE3D. Try using the + and - keys to see how the Z offset works.

You say yourself. Yeah, square. That's not what I need 3D for. Imagine that you are moving two squares with different depths (example 2)

Example 1:
Code: QB64: [Select]
  1. SCREEN _NEWIMAGE(1024, 768, 32)
  2.  
  3. 'STEP 1: HOW DRAW QUAD IN 3D?   OpenGL COORDINATES are negative in X axis from left to middle, in middle is 0. X Coordinates from left to right are positive. Its SINGLE type.
  4. '                                             Y coordinates are positive from ceiling to middle screen. In middle is 0. From 0 to bottom are values negative. Its SINGLE type.
  5. '                                             Z coordinates are negative to depth. Positive values are before scene (before monitor), so this are UNVISIBLE. Its SINGLE type.
  6.  
  7. 'create hardware texture for use:
  8. texture& = _NEWIMAGE(100, 100, 32)
  9. _DEST texture&
  10. CLS , &HF0785AC6
  11. PRINT "Text" 'this is placed to texture for show correct orientation
  12.  
  13. HardwareTexture& = _COPYIMAGE(texture&, 33)
  14. _FREEIMAGE texture&
  15.  
  16.  
  17.  
  18. TYPE Vertex
  19.     X AS SINGLE
  20.     Y AS SINGLE
  21.     Z AS SINGLE
  22.  
  23. DIM Vertex(4) AS Vertex
  24. 'now we can define 4 vertexes for quad. I do it centered to middle the screen.
  25.  
  26. '  Vertex(0)          Vertex(1)
  27. '     +-------------------+
  28. '     I                   I
  29. '     I                   I
  30. '     I                   I
  31. '     I                   I
  32. '     +-------------------+
  33. ' Vertex(2)           Vertex(3)
  34.  
  35. Vertex(0).X = -1
  36. Vertex(0).Y = 1
  37. Vertex(0).Z = -6
  38.  
  39. Vertex(1).X = 1
  40. Vertex(1).Y = 1
  41. Vertex(1).Z = -6
  42.  
  43. Vertex(2).X = -1
  44. Vertex(2).Y = -1
  45. Vertex(2).Z = -6
  46.  
  47. Vertex(3).X = 1
  48. Vertex(3).Y = -1
  49. Vertex(3).Z = -6
  50.  
  51. 'draw it
  52.     i$ = INKEY$
  53.     SELECT CASE i$
  54.         CASE "+"
  55.             FOR s = 0 TO 3
  56.                 Vertex(s).Z = Vertex(s).Z + .1
  57.             NEXT
  58.         CASE "-"
  59.             FOR s = 0 TO 3
  60.                 Vertex(s).Z = Vertex(s).Z - .1
  61.             NEXT
  62.     END SELECT
  63.     _MAPTRIANGLE (0, 0)-(99, 0)-(0, 99), HardwareTexture& TO(Vertex(0).X, Vertex(0).Y, Vertex(0).Z)-(Vertex(1).X, Vertex(1).Y, Vertex(1).Z)-(Vertex(2).X, Vertex(2).Y, Vertex(2).Z), 0
  64.     _MAPTRIANGLE (99, 0)-(0, 99)-(99, 99), HardwareTexture& TO(Vertex(1).X, Vertex(1).Y, Vertex(1).Z)-(Vertex(2).X, Vertex(2).Y, Vertex(2).Z)-(Vertex(3).X, Vertex(3).Y, Vertex(3).Z), 0
  65.     LOCATE 1, 1: PRINT "Press + or - for Z shift. Crrent Z:"; Vertex(0).Z; "      " 'all vertexes use the same z
  66.     _DISPLAY
  67.  


Example 2 (second quadric use transparent color, so first quadric, which is deeper is also visible)

Code: QB64: [Select]
  1. SCREEN _NEWIMAGE(1024, 768, 32)
  2.  
  3. 'STEP 1: HOW DRAW QUAD IN 3D?   OpenGL COORDINATES are negative in X axis from left to middle, in middle is 0. X Coordinates from left to right are positive. Its SINGLE type.
  4. '                                             Y coordinates are positive from ceiling to middle screen. In middle is 0. From 0 to bottom are values negative. Its SINGLE type.
  5. '                                             Z coordinates are negative to depth. Positive values are before scene (before monitor), so this are UNVISIBLE. Its SINGLE type.
  6.  
  7. 'create hardware texture for use:
  8. texture& = _NEWIMAGE(100, 100, 32)
  9. _DEST texture&
  10. CLS , &HFF33AA55
  11. PRINT "First" 'this is placed to texture for show correct orientation
  12.  
  13.  
  14. 'create hardware texture for use:
  15. texture2& = _NEWIMAGE(100, 100, 32)
  16. _DEST texture2&
  17. CLS , &HAFFFAA99
  18. PRINT "Second"
  19.  
  20. HardwareTexture& = _COPYIMAGE(texture&, 33)
  21. HardwareTexture2& = _COPYIMAGE(texture2&, 33)
  22. _FREEIMAGE texture&
  23. _FREEIMAGE texture2&
  24.  
  25.  
  26. TYPE Vertex
  27.     X AS SINGLE
  28.     Y AS SINGLE
  29.     Z AS SINGLE
  30.  
  31. DIM Vertex(7) AS Vertex '8 vertexes (2 x quadric)
  32. 'now we can define 4 vertexes for quad. I do it centered to middle the screen.
  33.  
  34. '  Vertex(0)          Vertex(1)
  35. '     +-------------------+
  36. '     I                   I
  37. '     I                   I
  38. '     I                   I
  39. '     I                   I
  40. '     +-------------------+
  41. ' Vertex(2)           Vertex(3)
  42.  
  43. Vertex(0).X = -1
  44. Vertex(0).Y = 1
  45. Vertex(0).Z = -6
  46.  
  47. Vertex(1).X = 1
  48. Vertex(1).Y = 1
  49. Vertex(1).Z = -6
  50.  
  51. Vertex(2).X = -1
  52. Vertex(2).Y = -1
  53. Vertex(2).Z = -6
  54.  
  55. Vertex(3).X = 1
  56. Vertex(3).Y = -1
  57. Vertex(3).Z = -6
  58.  
  59. 'second quadric
  60.  
  61. Vertex(4).X = -1
  62. Vertex(4).Y = 1
  63. Vertex(4).Z = -4
  64.  
  65. Vertex(5).X = 1
  66. Vertex(5).Y = 1
  67. Vertex(5).Z = -4
  68.  
  69. Vertex(6).X = -1
  70. Vertex(6).Y = -1
  71. Vertex(6).Z = -4
  72.  
  73. Vertex(7).X = 1
  74. Vertex(7).Y = -1
  75. Vertex(7).Z = -4
  76.  
  77. 'draw it
  78.     i$ = INKEY$
  79.     SELECT CASE i$
  80.         CASE "+"
  81.             FOR s = 0 TO 7
  82.                 Vertex(s).Z = Vertex(s).Z + .1
  83.             NEXT
  84.         CASE "-"
  85.             FOR s = 0 TO 7
  86.                 Vertex(s).Z = Vertex(s).Z - .1
  87.             NEXT
  88.     END SELECT
  89.  
  90.     FOR D = 0 TO 4 STEP 4
  91.         IF D < 4 THEN T& = HardwareTexture& ELSE T& = HardwareTexture2&
  92.         _MAPTRIANGLE (0, 0)-(99, 0)-(0, 99), T& TO(Vertex(0 + D).X, Vertex(0 + D).Y, Vertex(0 + D).Z)-(Vertex(1 + D).X, Vertex(1 + D).Y, Vertex(1 + D).Z)-(Vertex(2 + D).X, Vertex(2 + D).Y, Vertex(2 + D).Z), 0
  93.         _MAPTRIANGLE (99, 0)-(0, 99)-(99, 99), T& TO(Vertex(1 + D).X, Vertex(1 + D).Y, Vertex(1 + D).Z)-(Vertex(2 + D).X, Vertex(2 + D).Y, Vertex(2 + D).Z)-(Vertex(3 + D).X, Vertex(3 + D).Y, Vertex(3 + D).Z), 0
  94.     NEXT D
  95.  
  96.     LOCATE 1, 1: PRINT "Press + or - for Z shift. Crrent Z:"; Vertex(0).Z; Vertex(5).Z; "      "
  97.     _DISPLAY
  98.  

Notice in example 2 how the color of square 1 changes if you zoom in so much that you have square 2 behind, so square 2 will not be visible. The color becomes correct for square 1 because is not longer affects by the transparent color of square 2.

Since it is here again night, 23 hours, I will continue on this topic tomorrow.
« Last Edit: November 20, 2019, 04:59:15 pm by Petr »

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: "Real" 3D Rendering
« Reply #11 on: November 20, 2019, 10:01:09 pm »
I found the One Lone Coder 3D Engine quite interesting and informative. Maybe someone can work out a QB64 3D Engine.

Here is the video link (first of four videos):



Yeah, I saw that yesterday, Shiffman has one too.
&t=1696s
If we make it to rotations, I'm going back to this.

Here is my test of STxAxTIC code, the perspective is perfect but the cubes are stretched too l-o-n-g on Z axis:
Code: QB64: [Select]
  1. _TITLE "3D Render" ' B+ started 2019-10-20
  2. ' Based on notes provided to QB64 forum by William F Barnes, on 2019-10-19
  3. ' https://www.qb64.org/forum/index.php?topic=1782.0
  4. ' A vector's dimension is the number of components it has.
  5. ' Here is code for processing 2 and 3 dimension vectors.
  6.  
  7. '2019-11-20 add STxAxTIC's conversion code for new sub screenXY
  8. ' Nice cube corners maker and nice wireframe cube
  9.  
  10.  
  11. CONST xmax = 700, ymax = 700
  12. CONST tlx = -100, tly = 100, brx = 100, bry = -100 ' Cartesian Coordinate System corners for WINDOW command
  13. ' to convert mouse coordinates to WINDOW after call look up PMAP
  14.  
  15. TYPE xyType
  16.     x AS SINGLE
  17.     y AS SINGLE
  18.  
  19. TYPE xyzType
  20.     x AS SINGLE
  21.     y AS SINGLE
  22.     z AS SINGLE
  23.  
  24.  
  25. ' notation 0 w/arrowHat  (no way of telling if 2, 3 or more dimensions)
  26. DIM SHARED v2zero AS xyType, v3zero AS xyzType
  27. v2zero.x = 0: v2zero.y = 0
  28. v3zero.x = 0: v3zero.y = 0: v3zero.z = 0
  29.  
  30. 'Basis Vectors, isolate components e sub x Dot V w/arrowHat = V sub x
  31. DIM SHARED v2e(1 TO 2) AS xyType, v3e(1 TO 3) AS xyzType
  32. v2e(1).x = 1: v2e(1).y = 0
  33. v2e(2).x = 0: v2e(2).y = 1
  34. v3e(1).x = 1: v3e(1).y = 0: v3e(1).z = 0
  35. v3e(2).x = 0: v3e(2).y = 1: v3e(2).z = 0
  36. v3e(3).x = 0: v3e(3).y = 0: v3e(3).z = 1
  37.  
  38. DIM SHARED fovd AS DOUBLE 'for screenXY of (x, y, z) point in real space
  39. fovd = 10 '???
  40.  
  41.  
  42. '==================================================================================== test area 2019-11-20
  43.  
  44. SCREEN _NEWIMAGE(xmax, ymax, 32) 'square screen
  45. _SCREENMOVE 300, 40
  46. WINDOW (tlx, tly)-(brx, bry) ' <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< get a Cartesian Coordinate System started
  47. ' >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>   to convert mouse coordinates to WINDOW after call look up PMAP
  48.  
  49. REDIM testCube(0) AS xyzType, y AS INTEGER, x AS INTEGER, z AS INTEGER, i AS INTEGER
  50.  
  51. 'all z's must be negative!!!! there seems a flip flop mirror upside down image passing zero top pos hit inf at 0!!!!!!!!!
  52. FOR y = -50 TO 50 STEP 10
  53.     FOR x = -50 TO 50 STEP 50
  54.         FOR z = -11 TO -31 STEP -10
  55.             newCube x, y, z, 10, testCube()
  56.             'FOR i = 0 TO 7
  57.             '    PRINT v3$(testCube(i))
  58.             'NEXT
  59.             'good!  now convert these to screen projection points
  60.             REDIM screenTest(0 TO 7) AS xyType
  61.             FOR i = 0 TO 7
  62.                 screenXY testCube(i), screenTest(i)
  63.                 'PRINT screenTest(i).x, screenTest(i).y
  64.             NEXT
  65.             drawWireCube screenTest()
  66.         NEXT
  67.     NEXT
  68. WHILE _KEYDOWN(27) = 0
  69.     CLS
  70.     PRINT "Press any to wakeup from sleep, esc to quit..."
  71.     newCube RND * 100 - 50, RND * 100 - 50, -30 * RND - 6, 10, testCube()
  72.     FOR i = 0 TO 7
  73.         PRINT v3$(testCube(i))
  74.     NEXT
  75.     'good!  now convert these to screen projection points
  76.     REDIM screenTest(0 TO 7) AS xyType
  77.     FOR i = 0 TO 7
  78.         screenXY testCube(i), screenTest(i)
  79.         PRINT screenTest(i).x, screenTest(i).y
  80.     NEXT
  81.     drawWireCube screenTest()
  82.     SLEEP
  83.  
  84. SUB drawWireCube (corners() AS xyType)
  85.     'front face
  86.     LINE (corners(0).x, corners(0).y)-(corners(1).x, corners(1).y)
  87.     LINE -(corners(2).x, corners(2).y)
  88.     LINE -(corners(3).x, corners(3).y)
  89.     LINE -(corners(0).x, corners(0).y)
  90.     'back face
  91.     LINE (corners(4).x, corners(4).y)-(corners(5).x, corners(5).y)
  92.     LINE -(corners(6).x, corners(6).y)
  93.     LINE -(corners(7).x, corners(7).y)
  94.     LINE -(corners(4).x, corners(4).y)
  95.     'connect from to back
  96.     LINE (corners(0).x, corners(0).y)-(corners(4).x, corners(4).y)
  97.     LINE (corners(1).x, corners(1).y)-(corners(5).x, corners(5).y)
  98.     LINE (corners(2).x, corners(2).y)-(corners(6).x, corners(6).y)
  99.     LINE (corners(3).x, corners(3).y)-(corners(7).x, corners(7).y)
  100.  
  101. SUB newCube (cx, cy, cz, side, cubeCorners() AS xyzType)
  102.     DIM sd2, lx, rx, ty, by, fz, bz
  103.     REDIM cubeCorners(0 TO 7) AS xyzType
  104.     sd2 = side / 2
  105.     rx = cx + sd2: lx = cx - sd2
  106.     ty = cy + sd2: by = cy - sd2
  107.     fz = cz + sd2: bz = cz - sd2
  108.     cubeCorners(0).x = lx: cubeCorners(0).y = ty: cubeCorners(0).z = fz
  109.     cubeCorners(1).x = rx: cubeCorners(1).y = ty: cubeCorners(1).z = fz
  110.     cubeCorners(2).x = rx: cubeCorners(2).y = by: cubeCorners(2).z = fz
  111.     cubeCorners(3).x = lx: cubeCorners(3).y = by: cubeCorners(3).z = fz
  112.     cubeCorners(4).x = lx: cubeCorners(4).y = ty: cubeCorners(4).z = bz
  113.     cubeCorners(5).x = rx: cubeCorners(5).y = ty: cubeCorners(5).z = bz
  114.     cubeCorners(6).x = rx: cubeCorners(6).y = by: cubeCorners(6).z = bz
  115.     cubeCorners(7).x = lx: cubeCorners(7).y = by: cubeCorners(7).z = bz
  116.  
  117. ' project (x, y, z) point in real space to screenXY of user's eye-line
  118. SUB screenXY (xyzReal AS xyzType, xyScreen AS xyType)
  119.     'convert STxAxTIC's code to my code here
  120.     ' https://www.qb64.org/forum/index.php?topic=1904.msg111304#msg111304
  121.  
  122.     ' vec3Ddotnhat = vec(i, 1) * nhat(1) + vec(i, 2) * nhat(2) + vec(i, 3) * nhat(3)
  123.     ' vec2D(i, 1) = (vec(i, 1) * uhat(1) + vec(i, 2) * uhat(2) + vec(i, 3) * uhat(3)) * fovd / vec3Ddotnhat
  124.     ' vec2D(i, 2) = (vec(i, 1) * vhat(1) + vec(i, 2) * vhat(2) + vec(i, 3) * vhat(3)) * fovd / vec3Ddotnhat
  125.  
  126.     'my comments and conversion
  127.     'fovd seems like a variable that should be globally shared, maybe constant?
  128.     DIM vec3Ddotnhat
  129.     vec3Ddotnhat = v3DotProduct(xyzReal, v3e(3))
  130.     xyScreen.x = v3DotProduct(xyzReal, v3e(1)) * fovd / vec3Ddotnhat
  131.     xyScreen.y = v3DotProduct(xyzReal, v3e(2)) * fovd / vec3Ddotnhat
  132.  
  133. '================================================= subs and fuctions  from Vector Math.bas 2019-10-20
  134. SUB setV3 (x, y, z, setMe AS xyzType)
  135.     setMe.x = x: setMe.y = y: setMe.z = z
  136.  
  137. FUNCTION v3$ (showMeInnards AS xyzType)
  138.     v3$ = "[" + ts$(showMeInnards.x) + ", " + ts$(showMeInnards.y) + ", " + ts$(showMeInnards.z) + "]"
  139.  
  140. FUNCTION ts$ (number)
  141.     ts$ = _TRIM$(STR$(number))
  142.  
  143. 'notation UppercaseLetter w/arrowhat + uppercase Letter w/arrowHat
  144. SUB v2Add (A AS xyType, B AS xyType, Sum AS xyType)
  145.     Sum.x = A.x + B.x
  146.     Sum.y = A.y + B.y
  147. SUB v3Add (A AS xyzType, B AS xyzType, Sum AS xyzType)
  148.     Sum.x = A.x + B.x
  149.     Sum.y = A.y + B.y
  150.     Sum.z = A.z + B.z
  151.  
  152. 'notation UppercaseLetter w/arrowHat - UppercaseLetter w/arrowHat
  153. SUB v2Subtr (A AS xyType, B AS xyType, Sum AS xyType)
  154.     Sum.x = A.x - B.x
  155.     Sum.y = A.y - B.y
  156. SUB v3Subtr (A AS xyzType, B AS xyzType, Sum AS xyzType)
  157.     Sum.x = A.x - B.x
  158.     Sum.y = A.y - B.y
  159.     Sum.z = A.z - B.z
  160.  
  161. 'notation lowercaseletter (for a number next to (times)) UppercaseLetter w/arrowHat
  162. SUB v2Scale (mult AS SINGLE, A AS xyType, Scale AS xyType) 'parallels
  163.     Scale.x = mult * A.x
  164.     Scale.y = mult * A.y
  165. SUB v3Scale (mult AS SINGLE, A AS xyzType, Scale AS xyzType) 'parallels
  166.     Scale.x = mult * A.x
  167.     Scale.y = mult * A.y
  168.     Scale.z = mult * A.z
  169.  
  170. 'notation the inverse of A w/arrowHat is -A w/arrowHat
  171. SUB v2Inverse (A AS xyType, Inverse AS xyType) ' A + InverseOfA = 0
  172.     Inverse.x = -A.x
  173.     Inverse.y = -A.y
  174. SUB v3Inverse (A AS xyzType, Inverse AS xyzType) ' A + InverseOfA = 0
  175.     Inverse.x = -A.x
  176.     Inverse.y = -A.y
  177.     Inverse.z = -A.z
  178.  
  179. 'notation: A w/arrowHat Dot B w/arrowHat v2 Dot Product is a number, v3 Dot Product is a vector
  180. FUNCTION v2DotProduct (A AS xyType, B AS xyType) 'shadow or projection  if A Dot B = 0 then A , B are perpendicular
  181.     v2DotProduct = A.x * B.x + A.y * B.y
  182. FUNCTION v3DotProduct (A AS xyzType, B AS xyzType) 'shadow or projection  if A Dot B = 0 then A , B are perpendicular
  183.     v3DotProduct = A.x * B.x + A.y * B.y + A.z * B.z
  184.  
  185. 'notation absolute value bars about A w/arrowHat OR just an UppercaseLetter (with no hat), its just a number
  186. FUNCTION v2Magnitude (A AS xyType) 'hypotenuse of right triangle
  187.     v2Magnitude = SQR(v2DotProduct(A, A))
  188. FUNCTION v3Magnitude (A AS xyzType) 'hypotenuse of cube
  189.     v3Magnitude = SQR(v3DotProduct(A, A))
  190.  
  191. 'notation: A w/arrowHat X B w/arrowHat, X is a Cross get it?
  192. FUNCTION v2CrossProduct (A AS xyType, B AS xyType) ' a vector perpendicular to both A and B, v2 is a magnitude
  193.     v2CrossProduct = A.x * B.y - A.y * B.x
  194. SUB v3CrossProduct (A AS xyzType, B AS xyzType, Cross AS xyzType) ' v3 cross product is a 3d vector perpendicular to A and B
  195.     'notice x has no x components, y no y componets, z no z components
  196.     Cross.x = A.y * B.z - A.z * B.y
  197.     Cross.y = A.z * B.x - A.x * B.z
  198.     Cross.z = A.x * B.y - A.y * B.x
  199.  
  200. 'notation: A w/caratHat = A w/arrowHat divided by A (UppercaseLetter) or scaled by 1/A magnitude (no hats)
  201. SUB v2Unit (A AS xyType, Unit AS xyType)
  202.     DIM m AS SINGLE
  203.     m = v2Magnitude(A)
  204.     v2Scale 1 / m, A, Unit
  205. SUB v3Unit (A AS xyzType, Unit AS xyzType)
  206.     DIM m AS SINGLE
  207.     m = v3Magnitude(A)
  208.     v3Scale 1 / m, A, Unit
  209.  

BTW, it's disaster if corner on z = 0 and the image flip-flops if corner slips past 0 into positive range.
« Last Edit: November 20, 2019, 11:02:41 pm by bplus »

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: "Real" 3D Rendering
« Reply #12 on: November 20, 2019, 11:33:54 pm »
My go at sorting out a 3D cube:

Code: QB64: [Select]
  1. SCREEN _NEWIMAGE(640, 480, 32)
  2. CONST Left = 19200
  3. CONST Right = 19712
  4. CONST Up = 18432
  5. CONST Down = 20480
  6.  
  7. DEFLNG A-Z
  8.  
  9. TYPE pnt 'type for each 3D point
  10.     x AS INTEGER 'x coord (horizontal)
  11.     y AS INTEGER 'y coord (vertical)
  12.     Z AS INTEGER 'z coord (into the screen)
  13.  
  14. DIM P(7) AS pnt, PR(7) AS pnt 'our original points, and then their rotated values
  15.  
  16. DIM scrX(7) 'screen x coord for my 7 points of my cube
  17. DIM scrY(7) 'screen y coord
  18.  
  19. DIM s(359) AS _FLOAT, c(359) AS _FLOAT 'trig tables
  20.  
  21. FOR i = 0 TO 359 'create sine and cosine
  22.     s(i) = SIN(i * (_PI / 180)) 'look up tables to speed up
  23.     c(i) = COS(i * (_PI / 180)) 'the math
  24.  
  25.  
  26.  
  27. 'My cube is going to be 100x100x100 pixels in size
  28. 'But to rotate it, I want the dimensions from the center.
  29. 'And I need 8 points to make my cube
  30. '(It's 4 points for a box on one plane, and another 4 points for the box on the second plane.
  31. ' And all the other sides are connected to those points.)
  32.  
  33. 'If you look at my data, you'll see that I've positioned the coordinates to be equally distant from the center point.
  34. DATA -50,50,50
  35. DATA 50,50,50
  36. DATA 50,-50,50
  37. DATA -50,-50,50
  38. DATA -50,50,-50
  39. DATA 50,50,-50
  40. DATA 50,-50,-50
  41. DATA -50,-50,-50
  42.  
  43. FOR i = 0 TO 7
  44.     READ P(i).x, P(i).y, P(i).Z
  45.  
  46. xCenter = 320: yCenter = 240: zCenter = 256
  47.  
  48.     CLS
  49.     FOR i = 0 TO 7 'calculate our points
  50.         ' Rotate the points on each axis.
  51.         PR(i).x = P(i).x * s(theta) + P(i).y * c(theta)
  52.         PR(i).y = P(i).x * c(theta) * s(phi) - P(i).y * s(theta) * s(phi) - P(i).Z * c(phi)
  53.         PR(i).Z = P(i).x * c(theta) * c(phi) - P(i).y * s(theta) * c(phi) + P(i).Z * s(phi)
  54.  
  55.         ' Translate both points from 3D to 2D.
  56.         IF (P(i).Z + zCenter) <> 0 THEN
  57.             scrX(i) = 256 * (PR(i).x / (PR(i).Z + zCenter)) + xCenter
  58.             scrY(i) = 256 * (PR(i).y / (PR(i).Z + zCenter)) + yCenter
  59.         END IF
  60.     NEXT
  61.  
  62.     'Top of Cube
  63.     LINE (scrX(0), scrY(0))-(scrX(1), scrY(1)), _RGB32(255, 255, 255)
  64.     LINE -(scrX(2), scrY(2)), _RGB32(255, 255, 255)
  65.     LINE -(scrX(3), scrY(3)), _RGB32(255, 255, 255)
  66.     LINE -(scrX(0), scrY(0)), _RGB32(255, 255, 255)
  67.  
  68.  
  69.  
  70.     'Bottom of Cube
  71.     LINE (scrX(4), scrY(4))-(scrX(5), scrY(5)), _RGB32(255, 255, 255)
  72.     LINE -(scrX(6), scrY(6)), _RGB32(255, 255, 255)
  73.     LINE -(scrX(7), scrY(7)), _RGB32(255, 255, 255)
  74.     LINE -(scrX(4), scrY(4)), _RGB32(255, 255, 255)
  75.  
  76.     'Right of Cube
  77.     LINE (scrX(0), scrY(0))-(scrX(4), scrY(4)), _RGB32(255, 255, 255)
  78.     LINE -(scrX(5), scrY(5)), _RGB32(255, 255, 255)
  79.     LINE -(scrX(1), scrY(1)), _RGB32(255, 255, 255)
  80.     LINE -(scrX(0), scrY(0)), _RGB32(255, 255, 255)
  81.  
  82.     'Front of Cube
  83.     LINE (scrX(1), scrY(1))-(scrX(2), scrY(2)), _RGB32(255, 255, 255)
  84.     LINE -(scrX(6), scrY(6)), _RGB32(255, 255, 255)
  85.     LINE -(scrX(5), scrY(5)), _RGB32(255, 255, 255)
  86.     LINE -(scrX(1), scrY(1)), _RGB32(255, 255, 255)
  87.  
  88.     'Left of Cube
  89.     LINE (scrX(2), scrY(2))-(scrX(6), scrY(6)), _RGB32(255, 255, 255)
  90.     LINE -(scrX(7), scrY(7)), _RGB32(255, 255, 255)
  91.     LINE -(scrX(3), scrY(3)), _RGB32(255, 255, 255)
  92.     LINE -(scrX(2), scrY(2)), _RGB32(255, 255, 255)
  93.  
  94.     'Back of Cube
  95.     LINE (scrX(3), scrY(3))-(scrX(0), scrY(0)), _RGB32(255, 255, 255)
  96.     LINE -(scrX(4), scrY(4)), _RGB32(255, 255, 255)
  97.     LINE -(scrX(7), scrY(7)), _RGB32(255, 255, 255)
  98.     LINE -(scrX(3), scrY(3)), _RGB32(255, 255, 255)
  99.  
  100.  
  101.     k = _KEYHIT
  102.     SELECT CASE k
  103.         CASE 27: SYSTEM
  104.         CASE Left: theta = theta + 2
  105.         CASE Right: theta = theta - 2
  106.         CASE Up: phi = phi + 2
  107.         CASE Down: phi = phi - 2
  108.     END SELECT
  109.     theta = (theta + 360) MOD 360
  110.     phi = (phi + 360) MOD 360
  111.     _LIMIT 60
  112.     _DISPLAY
  113.     SLEEP

Left/Right to rotate on the X-axis.
Up/Down to rotate on the Y-axis.

The reason why I was trying to sort out the points like this, is so that I could _MAPTRIANGLE along any side, so I could cover it with a nice texture, like the pips of a dice.  ;)
« Last Edit: November 20, 2019, 11:37:49 pm by SMcNeill »
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline STxAxTIC

  • Library Staff
  • Forum Resident
  • Posts: 1091
  • he lives
    • View Profile
Re: "Real" 3D Rendering
« Reply #13 on: November 21, 2019, 07:07:02 am »
Quote
BTW, it's disaster if corner on z = 0 and the image flip-flops if corner slips past 0 into positive range.

This is why I need a different job - I covet the free time I see some people have here and want to explain my take on this much better.

So yes - bplus, killer job at implementing the vectorized formulation of this. I like to avoid trig functions and wonky theta/phi polar coordinate systems in favor of this way... because you walked perfectly into the next challenge, which I tend to call "plane clipping". Bear with me: When I code something in 3D, I pretend it's the old days where the compiler doesn't want to plot anything off screen - but nowadays, QB64 quietly ignores off-screen points for you, so the knowledge on this has been basically lost... to our peril though... because without a good understanding of intersecting planes, the negative-z problem you point out is not automatically solved.

Unfortunately, my sketch doesn't explain the math on plane clipping, but my Sanctum code does. At the beginning, I have:

Code: QB64: [Select]
  1. ' Clipping planes.
  2. DIM nearplane(4), farplane(4), rightplane(4), leftplane(4), topplane(4), bottomplane(4)

... and in the body of the code I have:

Code: QB64: [Select]
  1. calculate.clippingplanes:
  2. ' Calculate normal vectors to all clipping planes.
  3. h2 = screenheight / 2
  4. w2 = screenwidth / 2
  5. nearplane(1) = -nhat(1)
  6. nearplane(2) = -nhat(2)
  7. nearplane(3) = -nhat(3)
  8. farplane(1) = nhat(1)
  9. farplane(2) = nhat(2)
  10. farplane(3) = nhat(3)
  11. rightplane(1) = h2 * fovd * uhat(1) - h2 * w2 * nhat(1)
  12. rightplane(2) = h2 * fovd * uhat(2) - h2 * w2 * nhat(2)
  13. rightplane(3) = h2 * fovd * uhat(3) - h2 * w2 * nhat(3)
  14. mag = SQR(rightplane(1) * rightplane(1) + rightplane(2) * rightplane(2) + rightplane(3) * rightplane(3))
  15. rightplane(1) = rightplane(1) / mag
  16. rightplane(2) = rightplane(2) / mag
  17. rightplane(3) = rightplane(3) / mag
  18. leftplane(1) = -h2 * fovd * uhat(1) - h2 * w2 * nhat(1)
  19. leftplane(2) = -h2 * fovd * uhat(2) - h2 * w2 * nhat(2)
  20. leftplane(3) = -h2 * fovd * uhat(3) - h2 * w2 * nhat(3)
  21. mag = SQR(leftplane(1) * leftplane(1) + leftplane(2) * leftplane(2) + leftplane(3) * leftplane(3))
  22. leftplane(1) = leftplane(1) / mag
  23. leftplane(2) = leftplane(2) / mag
  24. leftplane(3) = leftplane(3) / mag
  25. topplane(1) = w2 * fovd * vhat(1) - h2 * w2 * nhat(1)
  26. topplane(2) = w2 * fovd * vhat(2) - h2 * w2 * nhat(2)
  27. topplane(3) = w2 * fovd * vhat(3) - h2 * w2 * nhat(3)
  28. mag = SQR(topplane(1) * topplane(1) + topplane(2) * topplane(2) + topplane(3) * topplane(3))
  29. topplane(1) = topplane(1) / mag
  30. topplane(2) = topplane(2) / mag
  31. topplane(3) = topplane(3) / mag
  32. bottomplane(1) = -w2 * fovd * vhat(1) - h2 * w2 * nhat(1)
  33. bottomplane(2) = -w2 * fovd * vhat(2) - h2 * w2 * nhat(2)
  34. bottomplane(3) = -w2 * fovd * vhat(3) - h2 * w2 * nhat(3)
  35. mag = SQR(bottomplane(1) * bottomplane(1) + bottomplane(2) * bottomplane(2) + bottomplane(3) * bottomplane(3))
  36. bottomplane(1) = bottomplane(1) / mag
  37. bottomplane(2) = bottomplane(2) / mag
  38. bottomplane(3) = bottomplane(3) / mag
  39.  

That gosub must be run every time the perspective changes... So in a 3D engine you're doing it every loop. For platformer game, or anything else with a locked perspective, you only need to do it once.

Finally, this code actually decides whether or not the point is in view:

Code: QB64: [Select]
  1. clip.project.vectors: ' requires i
  2. vec(i, 1) = vec3Dpos(i, 1) - camx
  3. vec(i, 2) = vec3Dpos(i, 2) - camy
  4. vec(i, 3) = vec3Dpos(i, 3) - camz
  5. fogswitch = -1
  6. vec3Dvis(i) = 0
  7. vectorinview = 1
  8. ' Perform view plane clipping.
  9. IF vec(i, 1) * nearplane(1) + vec(i, 2) * nearplane(2) + vec(i, 3) * nearplane(3) - nearplane(4) < 0 THEN vectorinview = 0
  10. IF vec(i, 1) * farplane(1) + vec(i, 2) * farplane(2) + vec(i, 3) * farplane(3) - farplane(4) < 0 THEN vectorinview = 0
  11. IF vec(i, 1) * farplane(1) + vec(i, 2) * farplane(2) + vec(i, 3) * farplane(3) - farplane(4) * .85 < 0 THEN fogswitch = 1
  12. IF vec(i, 1) * rightplane(1) + vec(i, 2) * rightplane(2) + vec(i, 3) * rightplane(3) - rightplane(4) < 0 THEN vectorinview = 0
  13. IF vec(i, 1) * leftplane(1) + vec(i, 2) * leftplane(2) + vec(i, 3) * leftplane(3) - leftplane(4) < 0 THEN vectorinview = 0
  14. IF vec(i, 1) * topplane(1) + vec(i, 2) * topplane(2) + vec(i, 3) * topplane(3) - topplane(4) < 0 THEN vectorinview = 0
  15. IF vec(i, 1) * bottomplane(1) + vec(i, 2) * bottomplane(2) + vec(i, 3) * bottomplane(3) - bottomplane(4) < 0 THEN vectorinview = 0
  16. IF vectorinview = 1 THEN
  17.     vec3Dvis(i) = 1
  18.     ' Project vectors onto the screen plane.
  19.     vec3Ddotnhat = vec(i, 1) * nhat(1) + vec(i, 2) * nhat(2) + vec(i, 3) * nhat(3)
  20.     vec2D(i, 1) = (vec(i, 1) * uhat(1) + vec(i, 2) * uhat(2) + vec(i, 3) * uhat(3)) * fovd / vec3Ddotnhat
  21.     vec2D(i, 2) = (vec(i, 1) * vhat(1) + vec(i, 2) * vhat(2) + vec(i, 3) * vhat(3)) * fovd / vec3Ddotnhat
  22.     IF fogswitch = 1 THEN vec2Dcolor(i) = Gray ELSE vec2Dcolor(i) = vec3Dcolor(i)

This makes sure everything is plotted within your cone (a squared-off cone) of vision.

... Anyway, I can see how this becomes too much without a good explanation. Last thing I want is people blindly following equations just because I said so... I'll work on a real document on this one day...
« Last Edit: November 21, 2019, 07:19:24 am by STxAxTIC »
You're not done when it works, you're done when it's right.

Offline STxAxTIC

  • Library Staff
  • Forum Resident
  • Posts: 1091
  • he lives
    • View Profile
Re: "Real" 3D Rendering
« Reply #14 on: November 21, 2019, 07:31:14 am »
Ah crap, I misled you a little yesterday, or two days ago, bplus. Lookie here:

Code: QB64: [Select]
  1. vec(i, 1) = vec3Dpos(i, 1) - camx
  2. vec(i, 2) = vec3Dpos(i, 2) - camy
  3. vec(i, 3) = vec3Dpos(i, 3) - camz

I stupidly said that vec(i,j) holds the original position info. Not true. The original position info is stored in vec3Dpos(i,j), whereas you can see vec(i,j) subtracts off the position of the camera, or player, or whatever word you like. It shouldn't have caused a mistake in your demo though, its an easy blunder.
You're not done when it works, you're done when it's right.