Espace d'Asher256

Le graphisme 3D dans le QBasic





Écrit par Aaron Severn le 19 décembre 1997
Traduit par Asher256 en 1999

Pour beaucoup de gens, le concept mathématique de l’espace 3D est peu familier,
bien que nous y évoluions au quotidien. La notion la plus importante à retenir
en 3D est la suivante : la taille apparente des objets varie en fonction de leur
distance (éloignés ou proches). Si notre cerveau interprète cela naturellement,
ce n’est pas le cas d’un ordinateur. Il faut donc un certain travail pour
transformer un écran 2D en un monde 3D.

Tout d’abord, examinons les repères dans l’espace. Graphiquement, l’espace 3D
(R3) ressemble à ceci :

            						       z     y
                      		Y est l'axe horizontal          \    |
              y      z  	x est l'axe vertical 		 \   |
              |     /   	z est l'axe de profondeur         \  |
	      |    /		qui sort de l'écran                \ | é
              |   /		é est l'angle XY                    \|________
              |  /              et est l'angle de l'axe Z                    x
            é | /
      ________|/
      x					Et un cube en 3D

    				     ______
				   /|      /|
				  / |     / |
				 /__|____/  |
				|   |    |  |
				|   |____|__|
				|  /     |  /
				| /      | /
				|/_______|/

Les notations ci-dessus seront utilisées tout au long de ce texte.

Les deux problèmes que nous devons résoudre sont :

  • Comment créer l’effet de profondeur sur un écran plat ?
  • Comment faire pivoter des points dans l’espace ?

Maintenant que nous avons les bases, répondons à la première question.

Le dessin d’un objet 3D sur un moniteur 2D

La solution à ce problème est très simple. Pensez-y un instant. Supposons que
nous ayons une boîte transparente à l’écran ; nous voulons que la face la plus
proche de nous paraisse plus grande que la face la plus éloignée. Comment
procéder ? Il suffit de diviser les coordonnées des points par rapport à leur
distance (la profondeur).

Voici ce que cela signifie pour l’affichage de points en 3D. Nous partons de
trois coordonnées, mais nous n’en avons besoin que de deux pour l’écran. Il faut
donc diviser deux des coordonnées par la troisième. Voici les formules :

x2D = 256 * (x3D / (z3D + zCenter)) + xCenter
y2D = 256 * (y3D / (z3D + zCenter)) + yCenter

Dans ces formules, (x2D​,y2D​) représente le point qui sera affiché à l’écran, et
(x3D​,y3D​,z3D​) est le point dans l’espace 3D. Les variables xCenter​ et yCenter​
représentent les coordonnées du centre de l’objet à l’écran, tandis que zCenter​
représente la position de l’observateur (cette valeur est généralement de 256).
Les deux équations sont multipliées par 256 pour ajuster l’objet à la
perspective de l’observateur.

Vous pouvez utiliser ces formules pour dessiner n’importe quel objet 3D sur un
écran 2D.

La rotation 3D

Un objet 3D immobile à l’écran n’a que peu d’intérêt. Il est bien plus captivant
de le faire bouger ou pivoter. Comment y parvenir ? Les mathématiques impliquées
ici sont assez complexes. N’étant pas moi-même un expert en mathématiques, je ne
vous ferai pas perdre de temps avec de mauvaises explications. Je vais
simplement vous donner les formules finales, avec lesquelles vous pourrez faire
tourner des objets toute la nuit.

Tout d’abord, vous aurez besoin des deux angles illustrés dans le diagramme de
la première section : θ (theta) et ϕ (phi). Nous devrons également utiliser ρ
(rho), qui représente la distance entre le point et l’origine (0,0,0). Passons
directement aux formules. Ci-dessous, (xO​,yO​,zO​) représente la position initiale
du point dans l’espace 3D et (xR​,yR​,zR​) représente le point après rotation :

xR = -xO * SIN(é) + yO * COS(é)
yR = -xO * COS(é) * SIN(í) - yO * SIN(é) * SIN(í) - zO * COS(í) + p
zR = -xO * COS(é) * COS(í) - yO * SIN(é) * COS(í) + zO * SIN(í)

Si vous implémentez ces formules correctement, vous serez capable d’écrire un
programme générant de belles rotations 3D. Pour voir un cas concret, consultez
le code source ci-dessous.

Programme d’exemple

DEFINT A-Z

TYPE pnt                                        'type for each 3D point
    x AS INTEGER                                'x coord (horizontal)
    y AS INTEGER                                'y coord (vertical)
    Z AS INTEGER                                'z coord (into the screen)     p AS INTEGER                                'dist from center of object END TYPE

numLines = 12 - 1

DIM lO(numLines, 1) AS pnt                      'Original line coords
DIM lR(numLines, 1) AS pnt                      'Rotated coords
DIM scrX(numLines, 1)                           'screen x coord
DIM scrY(numLines, 1)                           'screen y coord
DIM oldX(numLines, 1)                           'old x coord for erasing DIM oldY(numLines, 1)                           'old y coord for erasing

DIM s!(359)                                     'trig tables
DIM c!(359)

CONST PI = 3.141592

FOR i = 0 TO 359                                'create sine and cosine    s!(i) = SIN(i * (PI / 180))                  'look up tables to speed up    c!(i) = COS(i * (PI / 180))                  'the math
NEXT

' Read two points instead of one.
FOR i = 0 TO numLines
   READ lO(i, 0).x, lO(i, 0).y, lO(i, 0).Z, lO(i, 0).p
   READ lO(i, 1).x, lO(i, 1).y, lO(i, 1).Z, lO(i, 1).p
NEXT

SCREEN 13
CLS

xCenter = 160: yCenter = 100: zCenter = 256
theta = 0: phi = 0
thetaRot = 2: phiRot = 2

justStarted = 1
DO
  FOR i = 0 TO numLines
     ' Save the old values of x and y so we can erase the balls later.      oldX(i, 0) = scrX(i, 0): oldY(i, 0) = scrY(i, 0)
     oldX(i, 1) = scrX(i, 1): oldY(i, 1) = scrY(i, 1)

     ' Rotate both points on each axis.
     lR(i, 0).x = -lO(i, 0).x * s!(theta) + lO(i, 0).y * c!(theta)
     lR(i, 0).y = -lO(i, 0).x * c!(theta) * s!(phi) - lO(i, 0).y * s!(theta)            * s!(phi) - lO(i, 0).Z * c!(phi) + lO(i, 0).p
     lR(i, 0).Z = -lO(i, 0).x * c!(theta) * c!(phi) - lO(i, 0).y * s!(theta)            * c!(phi) + lO(i, 0).Z * s!(phi)

     lR(i, 1).x = -lO(i, 1).x * s!(theta) + lO(i, 1).y * c!(theta)
     lR(i, 1).y = -lO(i, 1).x * c!(theta) * s!(phi) - lO(i, 1).y * s!(theta)            * s!(phi) - lO(i, 1).Z * c!(phi) + lO(i, 1).p
     lR(i, 1).Z = -lO(i, 1).x * c!(theta) * c!(phi) - lO(i, 1).y * s!(theta)            * c!(phi) + lO(i, 1).Z * s!(phi)

     ' Translate both points from 3D to 2D.
     IF (lR(i, 0).Z + zCenter) <> 0 THEN
       scrX(i, 0) = 256 * (lR(i, 0).x / (lR(i, 0).Z + zCenter)) + xCenter        scrY(i, 0) = 256 * (lR(i, 0).y / (lR(i, 0).Z + zCenter)) + yCenter      END IF

     IF (lR(i, 1).Z + zCenter) <> 0 THEN
       scrX(i, 1) = 256 * (lR(i, 1).x / (lR(i, 1).Z + zCenter)) + xCenter        scrY(i, 1) = 256 * (lR(i, 1).y / (lR(i, 1).Z + zCenter)) + yCenter      END IF
  NEXT i

  ' Erase the old lines.
  WAIT &H3DA, 8
  IF justStarted = 0 THEN
    FOR i = 0 TO numLines
       LINE (oldX(i, 0), oldY(i, 0))-(oldX(i, 1), oldY(i, 1)), 0
    NEXT i
  END IF

  ' Draw the new lines.
  FOR i = 0 TO numLines
     LINE (scrX(i, 0), scrY(i, 0))-(scrX(i, 1), scrY(i, 1)), 11
  NEXT i

  theta = (theta + thetaRot) MOD 360
  phi = (phi + phiRot) MOD 360

  justStarted = 0
LOOP UNTIL INKEY$ = CHR$(27)

' Lines are stored in format (X1,Y1,Z1,p1)-(X2,Y2,Z2,p2)
DATA -50,50,50,1,50,50,50,1
DATA 50,-50,50,1,50,50,50,1
DATA 50,50,-50,1,50,50,50,1
DATA -50,-50,50,1,-50,50,50,1
DATA -50,50,-50,1,-50,50,50,1
DATA -50,-50,50,1,50,-50,50,1
DATA -50,50,-50,1,50,50,-50,1
DATA -50,-50,-50,1,50,-50,-50,1
DATA -50,-50,-50,1,-50,50,-50,1
DATA 50,-50,-50,1,50,-50,50,1
DATA 50,-50,-50,1,50,50,-50,1
DATA -50,-50,-50,1,-50,-50,50,1