Renderer 2.5D, Mode 7 et Bresenham

Semaine du 26 novembre au 1er décembre 2018 :
Finalement, cette semaine je n’ai pas trouvé l’envie d’avancer le développement de mon robot trader. J’ai décidé que cette semaine y sera consacrée. A la place, j’ai avancé sur mon moteur de rendu 2.5D. Je ne vais pas m’attarder sur ce que j’y ai fait.

Renderer 2.5D

J’ai continué à réfléchir et tester des choses sur le rendu. Notamment, mon objectif est, comme je l’ai indiqué dans la semaine précédente, de n’utiliser que des entiers et aucun float. Pour des raisons d’optimisation considérable, même si les ordinateurs actuels n’en ont plus tellement besoin. Cependant, j’ai remarqué qu’il y avait des petits soucis de performance dans le nombre d’image par secondes. Ceci s’explique par la nature logiciel du rendu. Je n’utilise pas le matériel pour calculer le graphisme. Entendez par là, la carte graphique. Ce rendu utilise le CPU, et reproduit donc les méthodes des anciens ordinateurs. Bien entendu, pour afficher, il faut la carte vidéo, mais tout le reste est purement CPU. Même les processeurs modernes ont une unité spécialisée pour gérer les floats, contrairement à avant le milieu des années 90. Mais, ce type de donnée reste lent. Quand je sais que les jeux actuels ont tendance à s’en servir plus massivement, je me demande si les jeux vendus chers en AAA par exemple, ne pourraient pas être meilleurs encore. Je ne sais pas, donc si vous avez une idée là-dessus, n’hésitez pas à en parler dans les commentaires 🙂 Bref, j’ai donc cherché à améliorer mes algorithmes pour accélérer le rendu et aussi mieux penser le code. Et c’est en faisant autre chose que j’ai appris des méthodes qui m’ont, en plus, permis de comprendre totalement comment le moteur Build de Ken Silverman fonctionne. Toutes les parts d’ombre à son sujet sont, à mon avis, dissipées 🙂

Mode 7 et Bresenham

Je me suis lancée dans l’expérimentation du mode 7 sur ordinateur. Le mode 7 est un terme qui vient d’une capacité matérielle de la super nintendo. Il s’agit d’une technologie qui permettaient de distordre, produire des rotations, et d’autres effets sur les sprites dans les jeux. A l’époque, cette technique était révolutionnaire et a permis de faire de la pseudo-3D (le 2.5D en fait parti). Le mode 7 est le terme qui est utilisé aussi par abus de langage pour désigner la distorsion d’une image donnant l’illusion d’une projection perspective. On retrouve cela dans des jeux comme F-Zero, Mariokart, Hyperzone, Pilot Wings, ou encore dans l’introduction de Zelda 3 avec la carte qui arrive en perspective avant d’entrer dans la partie.

Comme vous pouvez le voir dans la capture ci-dessous, j’ai fait une génération procédurale d’une image (texture) sous forme de grillage de tubes de deux couleurs. Ensuite, j’ai divisé l’écran en deux parties : haute et basse. Pour le bas de l’écran, j’ai intervertis les couleurs de sorte à avoir le Red Green Blue qui soit disposé en Green Blue Red, et donne ce vert et cyan à la place du violet et magenta. J’ai appliqué un calcul de perspective sur les coordonnées des pixels de l’image et dessinés aux coordonnées normales de l’écran. Ce qui a donné cette distorsion géométrique qui crée l’illusion de 3D. Enfin, pour éviter les effets moches à l’horizon, j’ai ajouté un ombrage de profondeur qui obscurcit les couleurs au fur et à mesure qu’on se rapproche du milieu de la hauteur de l’écran, ou si vous préférez, la profondeur de l’illusion d’espace tridimensionnelle.

ShadeAdditionInTheProject_2018-11-30_05-22-16

Une fois cela fait, j’avais les bases pour créer un sol et un plafond pour mon moteur de rendu 2.5D, facilement texturable, et surtout ne nécessitant pas de lancer de rayon. Cet algorithme nécessite cependant à première vue d’utiliser quelques floats. Mais ce n’est qu’à première vue, car j’ai découvert qu’auparavant, les nombres réels étaient représentés par des entiers spéciaux : fixed point integer. Il existe une libraire pour utiliser ce type de donnée et qui propose les outils de base comme les cosinus, sinus, racine carrée, etc… sans utiliser de floats ou de doubles. Cette technique est encore utilisée pour faire tourner des programmes sur du matériel dépourvu d’un FPU (Float Point Unit), car moins cher, ou tout simplement pour augmenter la vitesse de calcul (optimisation). Finalement, le peu de nombre réel nécessaire à cet algorithme et l’indispensable trigonométrie sont possible par des entiers. Le lancer de rayon nécessite plus de nombres réels et de calculs pour générer les rayons pour chaque pixels de l’écran. Le mode 7 offre des calculs plus modestes, pour peu qu’on prépare bien ces variables (pour réduire les besoins en calcul) avant les boucles de rendu.

Parmi les autres tâches du moteur de rendu qui nécessitait le lancer de rayon, il y avait le dessin des murs et au minimum la délimitation de ces murs lors du dessin des sols et plafonds. Lorsque les murs ne sont pas alignés à l’horizontale devant notre caméra, les hauts et les bas des murs forment des obliques. Pour dessiner cela, correctement, j’ai eu l’idée de découper le dessin en deux parties. La première partie consiste à dessiner le plafond ou le sol jusqu’au premier point le plus haut ou le plus bas du mur. Ainsi, l’image se remplis dans un rectangle. Dans la deuxième partie, il reste un triangle à dessiner. L’hypoténuse de ce triangle est la délimitation entre le plafond ou sol et le mur. En somme, c’est une ligne. Et pour dessiner une ligne, avec uniquement des entiers (pas de floats), il existe un algorithme : l’algorithme de tracer de ligne de Bresenham. Je l’utilisais déjà pour dessiner les lignes de la vue 2D de mon moteur. Pour faire mon rendu qui doit remplir les vides et pas uniquement dessiner la délimitation sous forme de ligne, j’ai codé une structure (une classe aurait aussi été très bien) qui décompose la fonction de dessin de ligne. Les variables qui changent avec le temps sont devenu des attributs, et la fonction de dessin a perdu ses boucles, et s’est découpée en une fonction d’initialisation, une fonction de calcul des points, et deux fonctions qui renvoies le X et le Y du point calculé actuel. Ainsi, je peux reprendre le calcul de chaque point après chaque dessin d’une horizontale pour le sol ou le plafond. Au bout de la boucle de rendu du sol ou plafond, on obtient un triangle qui reproduit parfaitement ma texture et avec la distorsion souhaitée.

BresenhamToDrawFloorAndCeil+Mode7_2018-12-01_23-53-12

Dans cette image, j’ai mis le fond noir en gris foncé pour distinguer les zones de dessin en rectangle des sols et murs. En gris clair, ce sont les dessins en triangle avec Bresenham qui détermine la limite du dessin au bord du mur. En blanc, c’est le mur, ou plus tôt l’absence de mur. J’ai mis le fond de l’écran en blanc, et n’ai pas encore codé le rendu du mur. Ce rendu se fera simplement sur un occlusion buffer qui enregistre les hauteurs de chaque Y du sol et du plafond et permet de savoir ou commencer et finir le dessin du mur pour chaque colonne sur chaque X. Le plafond et le sol sont dessinés à l’horizontale, alors que les murs sont dessinées à la verticale (d’où les colonnes). Sur les côtés j’ai simplement dessinés le sol et plafond normalement pour le test.

Je suis contente du résultat et surtout contente d’avoir enfin compris de quelle manière Ken Silverman a pu réaliser, Build, son moteur de rendu, avec les contraintes de l’époque. D’ailleurs à ce propos, finalement mon système basé sur un depth buffer n’est pas nécessaire, et l’utilisation du système de bunchs sera bien plus pratique avec ce genre de technique de rendu. Le depth buffer reste plus intéressant pour de la 3D, comme dans Quake ou les autres jeux qui ont suivit, comme ceux de nos jours. Éventuellement, il est aussi très intéressant dans un moteur utilisant le lancer de rayon 🙂

Si vous avez aimé cet article, vous pouvez me faire un don en Ethereum 🙂
Ethereum : 0xab7dD988aD7348C75db90343591596974A435803

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *