Aller au contenu

Les cartes graphiques/Les cartes accélératrices 2D

Un livre de Wikilivres.

Dans le chapitre précédent, nous avons vu les CRTC. Il est maintenant temps de voir les Video Interface Controlers, des VDC qui gèrent des fonctionnalités de rendu 2D avancées, voire plus. Avec l'arrivée des interfaces graphiques (celles des systèmes d'exploitation, notamment) et des jeux vidéo 2D, les cartes graphiques ont pu s'adapter. Les cartes graphiques 2D ont d'abord commencé par accélérer le tracé et coloriage de figures géométriques simples, tels les lignes, segments, cercles et ellipses, afin d’accélérer les premières interfaces graphiques. Par la suite, diverses techniques d'accélération de rendu 2D se sont fait jour.

La base d'un rendu en 2D est de superposer des images 2D pré-calculées les unes au-dessus des autres. Par exemple, on peut avoir une image pour l’arrière-plan (le décor), une image pour le monstre qui vous fonce dessus, une image pour le dessin de votre personnage, etc. On distingue généralement l'image pour l'arrière-plan, qui prend tout l'écran, des images plus petites qu'on superpose dessus et qui sont appelées des sprites. Le rendu des sprites doit s'effectuer de l'image la plus profonde (l'arrière-plan), vers les plus proches (les sprites qui se superposent sur les autres) : on parle d'algorithme du peintre.

Exemple de rendu 2D utilisant l'algorithme du peintre.

Il existe plusieurs techniques d'accélération graphique pour le rendu en 2D :

  • L'accélération des copies dans la mémoire vidéo grâce à un circuit dédié. Elle aide à implémenter de manière rapide le défilement ou les sprites, ou encore le tracé de certaines figures géométriques. Mais elle est moins performante que les trois suivantes, bien qu'elle lui soit complémentaire.
  • L'accélération matérielle des sprites, où les sprites sont stockés dans des registres dédiés et où la carte graphique les gère séparément de l'arrière-plan.
  • L'accélération matérielle du défilement, une opération très couteuse.
  • L'accélération matérielle du tracé de lignes/segments/figures géométriques simples.

Les quatre techniques ne sont pas exclusives, mais complémentaires. Elles ne sont pas utiles que pour les jeux vidéo, mais peuvent aussi servir à accélérer le rendu d'une interface graphique. Après tout, les lettres, les fenêtres d'une application ou le curseur de souris sont techniquement du rendu 2D. C'est ainsi que les cartes graphiques actuelles supportent des techniques d’accélération du rendu des polices d'écriture, une accélération du défilement ou encore un support matériel du curseur de la souris, toutes dérivées des techniques d'accélération de rendu 2D.

Le circuit de Blitter : les copies en mémoire

[modifier | modifier le wikicode]

Les cartes 2D ont introduit un circuit pour accélérer les copies d'images en mémoire, appelé le blitter. Les copies de données en mémoire vidéo sont nécessaires pour ajouter les sprites sur l'arrière-plan, mais aussi lorsqu'un objet 2D se déplace sur l'écran. Déplacer une fenêtre sur votre bureau est un bon exemple : le contenu de ce qui était présent sur l'écran doit être déplacé vers le haut ou vers le bas. Dans la mémoire vidéo, cela correspond à une copie des pixels correspondant de leur ancienne position vers la nouvelle.

Sans blitter, les copies étaient donc à la charge du processeur, qui devait déplacer lui-même les données en mémoire. Le blitter est conçu pour ce genre de tâches, sauf qu'il n'utilise pas le processeur. Pour ceux qui ont quelques connaissances avancées en architecture des ordinateurs, on peut le voir comme une sorte de contrôleur DMA amélioré. D'ailleurs, il est souvent combiné à un contrôleur DMA, voire fusionné avec lui. Il pouvait non seulement faire des copies, mais aussi appliquer des opérations bit à bit sur les données copiées.

La superposition des sprites sur l'arrière-plan

[modifier | modifier le wikicode]

Les cartes 2D sans sprites matériels effectuent leur rendu en deux étapes : elles copient l'image d'arrière-plan dans le framebuffer, puis chaque sprite est copié à la bonne place en mémoire pour le placer au bon endroit sur l'écran. Les copies de sprites sont généralement prises en charge par le blitter, qui est spécialement optimisé pour cela. L'optimisation principale est le fait que le blitter peut effectuer une opération bit à bit entre les données à copier et une donnée fournie par le programmeur appelé un masque.

Pour voir à quoi cela peut servir, rappelons que les sprites sont souvent des images rectangulaires sans transparence ! Le sans transparence est très important pour ce qui va suivre. Idéalement, les sprites devraient contenir des zones transparentes pour laisser la place à l'arrière-plan, mais le hardware ne gère pas forcément des pixels transparents à l'intérieur des sprites.

La transparence peut s'émuler facilement en utilisant un masque, une donnée qui indique quelles parties de l'image sont censées être transparentes. Par exemple, prenons l'image ci-dessous à gauche et son masque à droite. Les parties blanches du masque sont censées être transparentes et ne pas être copiées au-dessus de l'arrière-plan, il ne faut le faire que pour les pixels noirs. Le masque indique quels pixels sont à copier ou non, ce qui demande simplement 1 bit par pixel. Le pixel est associé dans le masque à une couleur noire ou blanche, pas de niveaux de gris permis.

Image de Pacman.
Masque de Pacman.

Le blitter combine le sprite, l'arrière-plan et le masque. Pour chaque pixel, il effectue l'opération suivante : ((arrière-plan) AND (masque)) OR (image de pacman).

Sprite rendering by binary image mask

Imaginons qu'on veut placer l'image ci-dessus (le point vert), en plusieurs endroits de l'arrière-plan.

Image et masque.

L'application d'un ET logique entre masque et arrière-plan met à zéro les pixels modifiés par le sprite et uniquement ceux-ci, ils sont mis à la couleur noire. Le OU copie le sprite dans le vide laissé, les parties noires sont ignorées. Au final, l'image finale est bel et bien celle qu'on attend.

Arrière-plan de l'exemple.
Application du masque - ET.
Application des sprites - OU.

Les sprites matériels

[modifier | modifier le wikicode]

Il existe des cartes 2D sur lesquelles les sprites sont gérés directement en matériel, sans passer par un blitter, sans être copiés sur un arrière-plan préexistant. À la place, l'image est rendue pixel par pixel, à la volée. La carte graphique décide, à chaque envoi de pixel, s'il faut afficher les pixels de l’arrière-plan ou du sprite pendant l'accès au framebuffer par le CRTC.

Il faut noter que les sprites matériel ont une taille assez petite. Ils sont typiquement des carrés de 8 pixels de côté, rarement plus. Par contre, les sprites utilisés dans les jeux vidéos sont souvent plus grands, certains font 16 pixels de côtés, parfois plus. Aussi, les sprites d'un jeu vidéo sont parfois composés de plusieurs sprites matériels placés les uns à côté des autres. Par exemple, un sprite de 16 pixels de hauteur et de 8 pixels de largeur est composé de deux sprites 8 par 8, placés l'un au-dessus de l'autre.

Le support des sprites est parfois utilisé dans un cadre particulièrement spécialisé : la prise en charge du curseur de la souris, ou le rendu de certaines polices d'écritures ! Le curseur de souris est alors traité comme un sprite spécialisé, surimposé au-dessus de tout le reste. Les cartes graphiques modernes gèrent un ou plusieurs sprites, qui représentent chacun un curseur de souris, et deux registres pour repsectivement les coordonnées x et y du curseur. Ainsi, pas besoin de redessiner l'image à envoyer à l'écran à chaque fois que l'on bouge la souris : il suffit de modifier le contenu des deux registres, la carte graphique place le curseur sur l'écran automatiquement. Pour en avoir la preuve, testez une nouvelle machine sur laquelle les drivers ne sont pas installés, et bougez le curseur : lag garantit !

Les circuits d'accélération matérielle des sprites

[modifier | modifier le wikicode]

Sur ces cartes 2D, les sprites sont stockés dans des registres, alors que l'image d'arrière-plan est stockée dans un framebuffer, dans une mémoire RAM. La RAM pour l'arrière-plan est généralement assez grosse, car l'arrière-plan a la même résolution que l'écran. Pour les sprites, la mémoire est généralement très petite, ce qui fait que les sprites ont une taille limitée.

Pour chaque sprite, on trouve deux registres permettant de mémoriser la position du sprite à l'écran : un pour sa coordonnée X, un autre pour sa coordonnée Y. Lorsque le CRTC demande à afficher le pixel à la position (X , Y), chaque triplet de registres de position est comparé à la position X,Y fournie par le CRTC. Si aucun sprite ne correspond, les mémoires des sprites sont déconnectées du bus et le pixel affiché est celui de l'arrière-plan. Dans le cas contraire, la RAM du sprite est connectée sur le bus et son contenu est envoyé au RAMDAC.

Sprites matériels.

Les sprites allaient presque toujours de pair avec une gestion de la transparence. Dans le cas le plus simple, la transparence permet de ne pas afficher certaines portions d'un sprite. Certains pixels d'un sprite sont marqués comme transparents, et à ces endroits, c'est l'arrière-plan qui doit s'afficher. Cela permet d'afficher des personnages ou objets complexes alors que l'image du sprite est rectangulaire. Cette gestion basique de la transparence ne permet pas de gérer des effets trop complexe, où on mélange la couleur du sprite avec celle de l'arrière-plan.

Les VDC avaient des limitations en termes de sprites matériels supportés. Notamment, ils géraient un nombre maximal de sprites par image. Cette limitation par image était secondée par une limitation du nombre de sprites par ligne. Par exemple, la NES et la Master Sytem géraient 64 sprites par image, avec maximum 8 sprites par ligne. Cependant, ces limitations pouvaient être contournées par l'usage de raster interrupts, et précisément l'horizontal blank interrupt qui précise quand l'affichage d'une ligne précise est terminée. L'idée est de changer les sprites d'une image et de les repositionner, pendant le tracé de l'image, entre l'affichage de deux lignes. Les sprites qui ont déjà été affichés en haut de l'écran sont remplacés par des sprites à afficher plus bas. Les programmeurs utilisaient ce genre de ruses pour afficher plus de sprites à l'écran que ne peut en supporter la console.

Les cartes 2D les plus simples ne géraient que deux niveaux : soit l'arrière-plan, soit un sprite devant l'arrière-plan. Il n'est donc pas possible que deux sprites se superposent, partiellement ou totalement. Dans ce cas, l'image n'a que deux niveaux de profondeur. C'était le cas sur les consoles de seconde et troisième génération, comme la NES ou la Sega Saturn. Par la suite, une gestion des sprites superposés est apparue. Pour cela, il fallait stocker la profondeur de chaque sprite, pour savoir celui qui est superposé au-dessus de tous les autres. Cela demandait d'ajouter un registre pour chaque sprite, qui mémorisait la profondeur. Le circuit devait donc être modifié de manière à gérer la profondeur, en gérant la priorité des sprites.

Il faut noter que certaines consoles géraient des sprites situés sous l'arrière-plan. Quelques effets graphiques le requérait. Par exemple, imaginez un personnage qui passe derrière un arbre. L'arbre est dessiné dans l'arrière-plan, afin d'économiser des sprites, alors que le personage est composé de plusieurs sprites. Avec des sprites pouvant passer derrière l'arrière-plan, on peut simuler cet effet de personnage qui passe derrière l'arbre, donc sous l'arrière-plan.

La palette indicée des sprites

[modifier | modifier le wikicode]

Certaines consoles de jeux utilisaient à la fois des sprites et une palette indicée. En soi, rien d'incompatible, les deux techniques sont totalement orthogonales. Mais il y a des consoles qui utilisaient deux palettes séparées : une pour les sprites, l'autre pour l'arrière-plan. En général, les deux palettes ont une couleur commune : la couleur transparente. Elle est codée deux fois, une fois dans chaque palette. Mais les autres couleurs des palettes sont potentiellement différentes dans les deux palettes. L'usage de deux palettes indicées séparées est assez commun sur les consoles 8 bits.

L'utilité de ce système est de gérer deux fois plus de couleurs, tout en réduisant les besoins en mémoire. En théorie, on pourrait gérer deux fois plus de couleurs en utilisant une palette unique deux fois plus grande, ce qui serait plus flexible. Mais en utilisant une palette deux fois plus grande, on doit ajouter un bit en plus par pixel, pour encoder deux fois plus de couleurs. Avec deux palettes séparées pour les sprites et l'arrière-plan, pas besoin : le bit est implicite, il vaut 0 pour les sprites et 1 pour l'arrière-plan.

La Master System a deux palettes : une réservée à l'arrière-plan, l'autre servant à la fois pour les sprites et le décor. Les sprites ont à disposition 16 couleurs qu'ils peuvent configurer comme ils le souhaitent. La couleur de transparence n'est pas obligatoire. A l'inverse, la palette pour l'arrière-plan dispose de 15 couleurs configurables, et une couleur de transparence fixe. L'arrière-plan a donc à sa disposition 31 couleurs configurables : 16 provenant de la palette des sprites, 15 provenant de l'autre palette, la 32ème couleur fixe étant la couleur de transparence.

La NES utilise un système de palette indicé assez complexe pour réduire encore plus le nombre de bits utilisé pour encoder un sprite. Pour simplifier, elle dispose de 4 palettes pour les sprites. Chaque palette gère 4 couleurs chacune, dont l'une est la couleur transparente, ce qui fait 3 couleurs configurables. Chaque sprite précise quelle palette utiliser avec deux bits de configuration précisés dans le registre associé au sprite. L'avantage est que cela réduit grandement la quantité de mémoire utilisée pour stocker un sprite : deux bits par pixel du motif, deux pixels pour préciser la palette utilisée pour le sprite. Au total, cela fait maximum 13 couleurs différentes : 3 couleurs fois 4 palettes plus la couleur de transparence. Même principe pour la palette de l'arrière-plan, qui est elle aussi découpée en 4 sous-palettes avec chacune ayant sa propre copie de la couleur de transparence.

De rares consoles 8 bits disposent de plus de deux palettes. Par exemple, la PC-Engine a 32 palettes indicées de 16 couleurs chacune. La première palette est toujours consacrée à l'arrière-plan, la dernière est toujours réservée aux sprites, les 30 palettes restantes sont elles utilisables à volonté par le programmeur. L'avantage est que l'on peut utiliser une palette différente par sprite, au lieu d'avoir une palette indicée partagée par tous les sprites. Mine de rien, trouver un ensemble de couleur partagé par plus d'une dizaine de sprite est assez compliqué. Les graphismes sont donc plus colorés, sans que cela demande d'utiliser une palette trop large. Et quand on veut modifier la palette à la volée pour un seul sprite, cette implémentation avec des palettes séparées est bien plus simple.

Les consoles comme la NES n'avaient que 4 couleurs par sprite. Mais il y avait moyen de contourner ces limitations en superposant plusieurs sprites de couleurs différentes. Par exemple, on peut simuler un sprite de 8 couleurs avec deux sprites de 4 couleurs chacun. Quelques consoles avaient même des fonctionnalités matérielles pour faciliter cette superposition. Par exemple, la MSX 2 fait automatiquement un OU entre deux sprites superposés.

Le zoom et la rotation des sprites

[modifier | modifier le wikicode]

Il a existé des VDC qui pouvaient zoomer sur des sprites, voire les faire tourner. Le ZOOM des sprites permet de faire grossir ou de réduire la taille d'un sprite dynamiquement. L'effet de zoom pouvait servir pour générer des effets de profondeur. Par exemple, quand on veut simuler un personnage qui se rapproche de l'écran en marchant/courant, il suffit de prendre son sprite et de grossir le zoom progressivement pour donner l'illusion d'un personnage qui se rapproche. Idem pour n'importe quel sprite : plus le sprite est loin, plus on en réduit la taille. L'idée est de gérer la taille des sprites en fonction de la profondeur du sprite. L'effet donnait un rendu en pseudo-3D. On parle alors de Sprite Scaling.

Les implémentations les plus simples permettaient de faire multiplier les dimensions d'un sprite par 2, 4, 8 fois. En clair, la largeur et la longueur du sprite étaient multipliés par 2, 4, 8, etc. Il y avait possibilité de séparer zoom vertical et horizontal. Quelques VDC ne supportaient que le zoom horizontal, d'autre que en vertical, mais la plupart de ceux qui supportaient le zoom supportaient les deux.

ZOOM Entier sur un "sprite".

Les implémentations plus complexes supportaient un zoom plus fluide, avec tous les intermédiaires possibles entre *1 et *X. Pour cela, le sprite devait subir des opérations d'interpolation pour avoir un rendu agréable à l'écran. Pour comprendre ce que cela veut dire : imaginez que vous souhaitez multiplier la taille d'un sprite par X. Pour multiplier par 2, 3, 4 ou tout autre entier, cela demande juste de dupliquer chaque pixel. Mais pour les valeurs fractionnaires, les choses sont plus compliquées. Vous devrez appliquer un filtre pour faire le zoom, filtre qui prend les valeurs des pixels et les mélange pour calculer les pixels finaux. Nous reviendrons sur les filtres possibles dans le chapitre sur le filtrage de texture. Vous avez bien lu : le zoom des sprites n'est pas si différent du filtrage de texture, les algorithmes utilisés pour zoomer sur les sprites et filtrer des textures sont d'ailleurs très similaires, voire identiques.

L'effet miroir ou flipping

[modifier | modifier le wikicode]

Une autre possibilité est de faire tourner les sprites. La technique la plus simple est un effet de miroir, à savoir que le sprite est inversé horizontalement, il est tourné dans l'autre sens. Pour vous donner une idée de l'utilité de cette technique, imaginez que vous jouez à Mario : si vous allez à droite, Mrio sera tourné vers la droite. Mais si vous allez à gauche, le sprite sera tourné à gauche. Mais il s'agit d'un seul sprite, qui est tourné dans un sens ou l'autre avec un effet de miroir.

Il est aussi possible d'inverser le sprite verticalement, pour le retourner. Mais cette possibilité est plus rarement utilisée. La raison est que les rares jeux où cela pourrait être utile sont des jeux en vue de dessus, comme par exemple les Zelda, les Pokemon, et d'autres jeux dans le genre. Mais ils utilisent souvent un effet de perspective qui fait que la caméra n'est pas placée exactement à la verticale, ce qui fait qu'inverser un sprite verticalement ne respecte pas la perspective.

Toutes les consoles 8 bits supportaient l'effet de miroir directement en matériel, celui-ci étant très utile. Une des rares exception était la Master System de Sega. Sur cette dernière, le GPU était un VDC TMS9918 de Texas Instrument qui ne gérait pas l'effet miroir. Deux autres consoles utilisaient ce VDC : la colevision et la Sega SG-1000. Elles avaient le même problème. Cependant, il était possible de gérer l'effet miroir en utilisant le CPU.

L'avantage de la technique est qu'elle réduit le nombre de sprites nécessaires, ce qui entraine un gain en mémoire ROM. Les concepteurs peuvent ainsi faire rentrer un jeu sur une cartouche plus facilement, voire peuvent en profiter pour rajouter des sprites. Mais un défaut de cette méthode est que les personnages donnent l'impression d'être ambidextres ! Au passage, sur la Master System, les concepteurs de jeu n'avaient pas d'autre choix que d'utiliser des sprites séparés pour les personnages/ennemis vus de gauche et vu de droite. Et on peut le remarquer au fait que les ennemis/personnages ne sont pas ambidextres, les artistes ayant souvent pris en compte de détail.

Au passage, j'en profite pour vous renvoyer vers ce lien, qui détaille l'utilisation de l'effet de miroir dans plusieurs jeux :

La rotation des sprites

[modifier | modifier le wikicode]

La rotation des sprites regroupe plusieurs techniques diverses, qui vont de simples effets de miroir à des techniques de rotation complexes. L'effet miroir est techniquement une rotation de sprite particulière, mais ce n'est pas la seule. Les techniques plus évoluées permettent de faire tourner un sprite, pour faire comme si on voyait un objet de biais. Un sprite rectangulaire représente un objet vu soit de profil, soit de face. Mais si on veut donner l'illusion d'un objet faisant un angle différent, il faut modifier la forme du sprite. Le sprite rectangulaire est transformé en trapèze, qui est d'autant moins proche d'un rectangle que l'angle est important. Déformer un sprite rectangulaire en trapèze est un effet de rotation général. Le tout donne un effet de perspective.

Mode 7, test.
Mode 7.

L'implémentation de la perspective est assez simple. Le sprite est une image formée de plusieurs lignes de pixels. L'idée est que la taille des lignes est d'autant plus réduite que la ligne est censée être loin. Les portions proches du sprite seront de taille normale, les autres sont réduites. Mais la technique demande cependant de multiplier la taille de chaque ligne par un coefficient, qui n'est pas forcément entier. Là encore, des algorithmes permettent de lisser les images des sprites pour les mettre à l'échelle. L'algorithme peut faire à la fois la gestion de la perspective et le zoom des sprites, et utiliser des algorithmes proches de ceux du filtrage de textures. Une implémentation complexe est celle du mode 7 de la Super Nintendo.

L'implémentation matérielle est paradoxalement assez simple. Mais la comprendre demande de faire la distinction entre les pixels du framebuffer et les pixels du sprite. Le sprite est une image qui est composée de pixels. Pour faire la distinction avec les pixels du framebuffer, nous allons appeler les pixels du sprite des texels. Terme qui est normalement utilisé pour les textures, mais il y a un lien qui sera fait dans quelques chapitres.

Placer le sprite sur l'arrière-plan demande de prendre les coordonnées de chaque texel et de faire un calcul qui donne les coordonnées finales x,Y dans le framebuffer. Sans rotation et sans zoom, le calcul est simple : on additionne la position x,y du sprite et la coordonnée du texel dans le sprite. Pour faire tourner les sprites, il faut faire des calculs impliquant des matrices (des objets mathématiques en forme de tableaux de nombre), que je ne détaillerais pas du tout. Pour le dire très rapidement, il faut juste multiplier les coordonnées du texel par une matrice qui encode à la fois la rotation et le zoom, mais aussi le placement au bon endroit sur l'écran (la translation). La matrice est calculée par le processeur, le VDC ne fait que faire la multiplication matricielle.

L'accélération matérielle du défilement

[modifier | modifier le wikicode]
Exemple de scrolling.

Le défilement permet de faire défiler l'environnement sur l'écran, spécialement quand le joueur se déplace. Les jeux de plateforme rétro utilisaient énormément le défilement, le joueur se déplaçait généralement de gauche à droite, ce qui fait que l'on parle de défilement horizontal. Mais il y avait aussi le défilement vertical, utilisé dans d'autres situations. Peu utilisé dans les jeux de plateforme, le défilement vertical est absolument essentiel pour les Shoot 'em up !

Les cartes accélératrices intégraient souvent des techniques pour accélérer le défilement. La première optimisation est l'usage du blitter. En effet, défiler ne demande pas de régénérer toute l'image à afficher à partir de zéro. L'idéal est de déplacer l'image de quelques pixels vers la gauche, puis de dessiner ce qui manque. Pour cela, on peut utiliser le blitter pour déplacer l'image dans le framebuffer. L'optimisation est souvent très intéressante, mais elle est imparfaite et n'était pas suffisante sur les toutes premières consoles de jeu. Elle l'était sur les consoles plus récentes, ou disons plutôt : moins rétro. Les consoles moins rétros avaient des mémoires RAM plus rapides, ce qui rendait l'usage du blitter suffisante.

Mais certains VDC implémentaient une forme de défilement accéléré en matériel totalement différent. Les implémentations sont multiples, mais elles ajoutaient toutes des registres de défilement dans le VDC, qui permettaient de défiler facilement l'écran. Il faut noter que l'implémentation du défilement vertical est bien plus simple que pour le défilement horizontal. En effet, les images sont stockées dans le framebuffer ligne par ligne ! Et le défilement vertical demande de déplacer l'écran d'une ou plusieurs lignes. Les deux vont donc bien ensemble.

Le défilement vertical implémenté dans le CRTC

[modifier | modifier le wikicode]

Une technique utilise le fonctionnement d'un CRTC, couplé avec un framebuffer amélioré. Nous l’appellerons la technique du framebuffer étendu, terme de mon invention qui aide à comprendre en quoi consiste cette technique. Elle utilise cependant plus de mémoire vidéo qu'un framebuffer normal. Elle fonctionne très bien pour le défilement vertical, elle demande quelques adaptations pour le défilement horizontal.

Implémentation du défilement vertical accéléré en hardware via un pointeur de framebuffer

La méthode demande d'utiliser un framebuffer beaucoup plus grand que l'image à afficher. Par exemple, imaginez qu'une console ait une résolution de 640*480, avec des couleurs sur 16 bits. L'image à afficher prend donc 640*480*2 = 600 Kilooctets. Maintenant, imaginez que la mémoire vidéo pour l'arrière-plan fasse 2 mégaoctets. On peut y stocker l’image à rendre, et ce qu'il y a hors de l'écran. Si une scène est assez petite, l'arrière-plan tient entièrement dans la mémoire vidéo, changer l'adresse de base permet de défiler l'écran sur une distance assez longue, voire pour toute la scène. L'idée est de tout simplement dire au CRTC de demander à afficher l'image à partir de la ligne numéro X !

Avec cette technique, il faut faire la différence entre le framebuffer et le viewport. Le viewport est la portion du framebuffer qui mémorise l'image à afficher à l'écran. Le framebuffer, lui, mémorise plus que ce qu'il faut afficher à l'écran, il mémorise quelques lignes en plus, voire un niveau entier ! Le pointeur de framebuffer et la résolution indiquent la position du viewport dans le framebuffer, qui monte ou descend en fonction du sens de défilement.

Pour la comprendre, prenez le cas où on souhaite défiler d'un pixel, verticalement vers le bas. La première ligne de l'image disparait, une nouvelle apparait en bas de l'image. Cas simple, un peu irréaliste, mais qui permet de bien comprendre l'idée. Pour rappel, un CRTC incorpore deux compteurs de ligne et de colonne, ainsi qu'un registre pour l'adresse de départ de l'image dans le framebuffer. L'idée est que l'adresse de départ de l'image est augmentée de manière à pointer sur la ligne suivante, en l'augmentant de la taille d'une ligne. Il ne reste plus qu'à remplir la ligne suivante, pas besoin de faire la moindre copie en mémoire vidéo ! Et défiler vers le haut demande au contraire de retrancher la taille d'une ligne de l'adresse. On peut généraliser le tout pour du défilement vertical.

L'implémentation la plus complexe demande d'ajouter un registre de défilement vertical, dans lequel on place le numéro de ligne à partir de laquelle il faut dessiner l'image. L'image en mémoire vidéo a plus de lignes que l'écran peut en afficher, le CRTC parcourt autant de ligne que ce que demande la résolution, le registre de défilement vertical indique à partir de quelle ligne commencer l'affichage. Le calcul de l'adresse prend en compte le contenu de ce registre pour parcourir le framebuffer.

Le défilement vertical infini : le warp-around des adresses

[modifier | modifier le wikicode]

La technique précédente fonctionne bien, mais elle ne permet pas d'avoir du défilement infini, à savoir avec des maps dont la longueur n'est pas limitée par la mémoire vidéo. Mais on peut l'adapter pour obtenir du défilement vertical, en utilisant un comportement particulier du calcul des adresses. Le comportement en question est le warp-around.

Pour le comprendre, prenons l'exemple suivant. La mémoire vidéo peut contenir 1000 lignes, la résolution verticale est de 300 lignes. Si on démarre l'affichage à la 700ème ligne, tout va bien, on n'a pas besoin de warp-around. Mais maintenant, imaginons que le défilement vertical fasse démarrer l'affichage à partir de la 900ème ligne. L'affichage se fera normalement pour 100 lignes, mais la 101ème débordera hors du framebuffer, il n'y aura pas d'adresse associée. Le warp-around fait repartir les adresses à zéro lors d'un tel débordement. Ainsi, la 101ème adresse sera en fait l'adresse 0, la 102ème sera l'adresse 1, etc. En clair, si on commence à afficher l'image à la 900ème ligne, les 100 premières seront prises dans les 100 lignes à la fin du framebuffer, alors que les 200 suivantes seront les 200 lignes au début du framebuffer.

En faisant cela, on peut avoir un défilement vertical infini. Quand l'image affichée est démarrée assez loin, le début du framebuffer est libre, il contient des lignes qui ne seront sans doute pas affichées par la suite. Dans ce cas, on écrit dedans la suite du niveau, celle située après la ligne à la fin du framebuffer. Il suffit que le programmeur se charge de modifier le framebuffer de cette manière et on garantit que tout va bien se passer.

Avec cette technique, on peut avoir un défilement infini en utilisant seulement deux fois plus de mémoire vidéo que ce qui est nécessaire pour stocker une image à l'écran. Il est même possible de tricher pour utiliser moins de mémoire vidéo. Mais laissons cela de côté, tout cela est laissé à l’appréciation du programmeur.

Le défilement horizontal et vertical implémenté par le CRTC

[modifier | modifier le wikicode]

Pour le défilement horizontal, il faut procéder de la même manière, mais en trichant un peu. Là encore, il y a une différence entre viewport et framebuffer, et les deux n'ont pas la même résolution. Mais cette fois-ci, outre la résolution verticale qui est plus grande, la résolution horizontale l'est aussi. Par exemple, si je prends la console de jeux NES, elle a une résolution pour l'écran de 256 par 240, alors que l'arrière-plan a une résolution de 512 par 480.

L'implémentation utilise des compteurs de ligne et de colonne séparés. L'idée est d'avoir des lignes plus longues dans le framebuffer que ce qui est indiqué dans le registre de résolution. On peut alors mémoriser une ligne plus longue que ce qui est affiché à l'écran, avec des portions non-affichées à l'écran.

L'idée est là encore d'initialiser le compteur de colonne avec une valeur qui est incrémentée d'un pixel à chaque fois qu'on défile vers la droite, décrémentée quand on va vers la gauche. Le registre pour la résolution horizontale, qui vérifie si la fin de la ligne/colonne est atteinte, est lui aussi incrémenté. La même méthode peut être utilisée pour le défilement horizontal en faisant la même chose pour le compteur de ligne. L'implémentation demande d'ajouter un registre de défilement horizontal, en plus du registre de défilement vertical, le principe derrière est le même mais pour le numéro de colonne et non de ligne.

Implémentation du défilement horizontal avec in viewport

Notons que le comportement de warp-around peut aussi être implémenté pour les adresses et compteurs de colonne. Cela permet d'avoir du défilement horizontal infini.

La fonctionnalité était disponible sur les cartes EGA, les toutes premières cartes d'affichage datant d'avant même le standard VGA. Tout était en place sur ces cartes graphiques pour implémenter la technique : une mémoire vidéo assez grande, un framebuffer potentiellement plus grand que ce qui est affiché à l'écran, une adresse de départ qu'on peut incrémenter ligne par ligne, par incréments de 1 pixel. Le logiciel devait cependant utiliser cette faculté adéquatement pour implémenter le défilement.

Les raster effects : défilement partiel et sprites supplémentaires

[modifier | modifier le wikicode]

Les raster effects sont des effets graphiques qui sont implémentés en modifiant le VDC pendant l'affichage de l'image. Par exemple, il est possible de changer la couleur de l'arrière-plan à partir d'une certaine ligne, afin de séparer le ciel du sol. Un exemple classique est celui qui permet de contourner le nombre maximal de sprites à l'écran.

La limite de sprites par écran

[modifier | modifier le wikicode]

Un VDC normal ne peut gérer qu'un nombre maximal de sprites par image/écran. Mais grâce aux raster effects, il est possible de dépasser cette limite, en changeant les sprites à la volée, pendant que l'image s'affiche. Par exemple, les sprites déjà affichés sont recyclés et remplacés par d'autres sprites. Leur coordonnée passe d'une portion affichée de l'image (typiquement en haut de l'écran) à une portion pas encore affichée (typiquement plus bas).

En faisant cela, on peut afficher plus de sprites que ce que gére le VDC. Par contre, on ne peut pas dépasser la limite de sprite par scanline. Par exemple, si un VDC gère maximum 64 sprites par imaeg et 8 sprites par ligne, la première limite est facilement contournée via les raster effects, mais pas la seconde. Autant changer des sprites entre l'affichage de deux lignes est possible, autant le faire pendant l'affichage d'une ligne ne l'est pas. En effet, le VDC n'est souvent pas reconfigurable par le processeur pendant l'affichage d'une ligne. Il l'est entre l'affichage de deux lignes, pas pendant.

Le défilement partiel

[modifier | modifier le wikicode]

De nombreux effets graphiques demandent de ne faire défiler qu'une partie de l'écran, le reste de l'écran reste en place. Dans ce cas, nous parlerons de défilement partiel, le terme partiel indiquant qu'il ne touche qu'une portion de l'image. De tels effets sont généralement implémentés avec des raster effect, mais il faut que le matériel le supporte pleinement.

Pour donner un exemple, prenez les phases d’ascenseur dans les jeux de type beat'them up, où votre personnage est sur une plateforme qui monte/descend, les ennemis arrivant dessus par vagues successives. Dans ce cas, la plateforme est immobile et le reste de l'image défile verticalement vers le haut (ascenseur descend) ou vers le bas (ascenseur monte).

Un cas particulier de défilement partiel est celui utilisé pour le HUD : les compteurs de vie, score, et autres. Il arrive que le HUD soit dessiné avec des sprites, mais ce n'est pas la majorité des cas. En réalité, le HUD est placé dans l'arrière-plan directement. Et il ne doit pas être défilé, pour rester en place. par exemple, si le HUD est placé au sommet de l'écran, il doit y rester et ne pas subir de défilement vertical ou horizontal.

D'autres effets demandent de changer le défilement ligne par ligne : des effets de vagues, d'ondulation, ou autre. Par exemple, c'est ce qui permet de créer des virages sur les jeux de course ! Il n'y a qu'une seule image de route en ligne droite, qu'on décale à chaque ligne pour simuler un virage !

D'autres effets demandent un défilement partiel à la verticale. Il a été utilisé pour le HUD, les Boss de grande taille de certains jeux, des effets d'eau qui monte et bien d'autres encore. Voici un article qui explique le tout en détail :

L'implémentation des raster effects

[modifier | modifier le wikicode]

Les VDC étaient souvent conçus avec les raster effects en tête. Ils supportent souvent des fonctionnalités qui facilitent l'implémentation des raster effects : compteurs de ligne, interruptions horizontales, et autres que nous allons voir dans ce qui suit. En théorie, les raster effects peuvent se faire même si le VDC ne supporte pas ces fonctionnalités. Mais cela demande de synchroniser le processeur et le VDC au cycle près, ou presque. Les programmeurs de l'Atari 2600 faisaient ainsi, mais il faut avouer que ce n'était pas pratiques.

La première fonctionnalité est le compteur de ligne. Le nom est assez transparent : il s'agit d'un registre qui contient le numéro de la dernière ligne affichée. Par exemple, si le VDC a affiché la 20ème ligne et se prépare à afficher la 21ème, alors ce compteur contient le numéro 20. Le compteur de ligne permet au processeur/logiciel de savoir où il en est lors de l'affichage de l'écran. Il peut regarder régulièrement ce compteur et changer le défilement et/ou les sprites quand un certain numéro apparait dans ce compteur de ligne.

Le compteur de ligne en question existe déjà dans la plupart des VDC, vu qu'il s'agit de celui vu dans les chapitres précédents, qui sert pour l'adressage de la mémoire vidéo et quelques autres fonctionnalités. Mais il n'est pas forcément accesible par le processeur. Les VDC anciens ont un compteur de ligne interne, qui n'est accesible que par les circuits du VDC, mais le processeur n'y a pas accès. La fonctionnalité dont on parle ici rend ce registre visible par le processeur, un peu comme l'est le registre d'état.

Une seconde fonctionnalité, très liée au compteur de ligne, est l'horizontal blank interrupt, une cousine de l'interruption de vertical blank interrupt qui indique qu'une image complète a été affichée à l'écran. L'horizontal blank interrupt fait la même chose, mais au niveau des lignes et d'une manière configurable. L'idée est que l'on peut demander au VDC de déclencher une interruption quand il atteint la ligne numéro X, par exemple quand il a finit d'afficher la 20ème ligne, la 50ème, etc. Le programmeur peut précise à quelle ligne se déclenche l'interruption. Pour le dire autrement, cette interruption se déclenche quand le compteur de ligne atteint une valeur bien précise, configurable par le programmeur.

Prenons l'exemple où programmeur veut afficher un HUD de 30 pixels de hauteur au-dessus d'un arrière-plan qui défile horizontalement. L'image ne défile pas initialement et le jeu affiche d'abord le HUD. Mais le programmeur a configuré l'interruption pour se déclencher à la 30ème ligne. L'interruption réactive le défilement de l'arrière-plan et change l'affichage pour basculer du HUD vers l'arrière-plan.

Un problème est que le VDC doit être totalement reconfigurable lors de l'affichage de l'image. La quasi-totalité des consoles permettait cela, avec cependant quelques exceptions. Par exemple, la Master System permettait de changer les sprites à la volée, idem pour le défilement horizontal, mais ne permettait pas de changer le défilement vertical pendant l'affichage d'une image. Sur Master System, le registre de défilement vertical était pris en compte uniquement au début de l'affichage, le modifier en cours d'affichage n'avait pas le moindre effet sur l'affichage. On parle alors de Vlock. Il était possible de contourner le problème avec des solutions logicielles, avec un cout en performance et quelques limitations quant au résultat.

L'usage de plusieurs arrière-plans et d'avant-plans

[modifier | modifier le wikicode]

Une carte graphique 2D basique gére un arrière-plan et un avant-plan contenant plusieurs sprites, avec éventuellement un avant-plan pour le HUD ou une interface sur laquelle afficher les vies, et d'autres informations. En tout, cela fait trois plans aux usages bien précis. Mais il est possible d'ajouter plusieurs arrière-plans, voir plusieurs avant-plan. L'utilité n'est pas évidente, mais cela sert pour de nombreux effets graphiques. Voyons lesquels.

Les décors à l'avant-plan

[modifier | modifier le wikicode]

Il faut noter que les cartes graphiques peuvent aussi gérer des avant-plans, à savoir des décors qui passent devant les sprites. Une forme d'avant-plan très utile est celui pour le HUD, les compteurs de vie, mana, munitions et autres. Implémenter un HUD avec du défilement est compliqué, car le HUD ne doit pas défiler : il doit rester à la même place sur l'écran, alors que le reste de l'écran défile.

Il faut noter que les avant-plans ne sont pas forcément séparés des arrière-plans. La seule différence entre les deux est la profondeur ! Et précisément la profondeur comparée à la couche pour les sprites. Plus profond que les sprites : c'est un arrière-plan. Moins profond, c'est un avant-plan. Les cartes graphiques gèrent souvent des "couches" (layers) qui se superposent, avec une couche spécialisée pour les sprites, et d'autres couches qu'on peut configurer à volonté comme arrière- ou avant-plan.

Les avant-plan ont aussi utilisés dans quelques jeux pour un effet purement esthétique, pour mettre un décor ou des objets devant les sprites. De plus, ils sont occasionnellement nécessaires pour rendre certains effets graphiques, ou pour ajouter de la profondeur à une scène. Les avant-plans défilent à une vitesse différente des arrière-plans, sans quoi le résultat est absolument affreux. Aussi, l'usage d'avant-plans est très fortement corrélé à l'usage du défilement à parallaxe qu'on va voir dans ce qui suit.

Le défilement horizontal à parallaxe

[modifier | modifier le wikicode]
Défilement avec parallaxe.
Défilement avec parallaxe : illustration des plans.

Le défilement à parallaxe donne une illusion de profondeur dans l'environnement? Pour cela, il utilise plusieurs arrière-plans superposés qui défilent à des vitesses différentes. Le défilement à parallaxe demande que les éléments à l'arrière-plan bougent à des vitesses différentes, mais pas les sprites à l'avant-plan. Le défilement sans parallaxe fait bouger le framebuffer en bloc, ce qui fait bouger tout l'arrière-plan tout d'un coup.

Pour implémenter la technique, il est possible d'utiliser un défilement partiel basé sur des raster effects. L'idée est de changer le défilement horizontal ou vertical pendant l'affichage. En général, l'écran est découpé en bandes qui défilent à des vitesses différentes, pour donner des effets parallaxe, parfois des effets de rouleau (le sol forme un tore) ou d'autres effets graphiques.

Une autre solution utilise des arrière-plans partiellement transparents qui se superposent. Les différents arrière-plans ont chacun une profondeur qui dit quel arrière-plan passe devant les autres. Il s'agit de l'implémentation la plus simple, mais elle demande que la carte graphique gère la transparence. Ce n'est cependant pas la seule option et quelques consoles de jeu se débrouillaient pour mélanger plusieurs arrière-plans sans pour autant gérer directement la transparence. Mais nous verrons cela plus tard.

L'émulation de la transparence, des ombres et lumières

[modifier | modifier le wikicode]

L'usage de plusieurs arrière-plans marche bien si la carte graphique gère la transparence. C'est à dire que chaque pixel indique s'il est totalement opaque, totalement transparent, ou partiellement opaque. En faisant cela, déterminer quel pixel afficher est simple : on prend en compte la profondeur de chaque arrière-plan, et on détermine quel est le premier pixel visible. Par un exemple, si un pixel transparent est situé devant un pixel opaque, c'est ce dernier qui s'affiche. Pour les pixels partiellement transparents, il faut mélanger les couleurs des pixels en faisant une simple moyenne.

Mais cela implique que la carte graphique gère la transparence, que chaque pixel contienne une couleur alpha qui indique son niveau de transparence, couleur ajoutée aux couleurs RGB existantes. Des consoles comme la SNES utilisaient un système totalement différent. Elles utilisait des arrière-plans dont les pixels étaient codés en RGB, sans composante de transparence alpha. Mais elles arrivaient à combiner plusieurs arrière-plans entre eux sans problèmes.

Les arrière-plans étaient combinés entre eux, ce qui donnait l'image finale à afficher. La combinaison pouvait utiliser plusieurs opérations, qui étaient appliquées pixel par pixel. Les opérations de combinaison sont : l'addition, la soustraction, la moyenne additive, et une soustraction suivie d'une division par deux. Pour résumer, on peut additionner/soustraire des pixels à la même place dans les différents arrière-plans, puis éventuellement diviser par deux le résultat. Précisons que les additions/soustractions sont dites saturées : elles n'ont pas de débordements d'entiers.

Les différentes opérations permettaient surtout d'implémenter des effets d'éclairage, d'ombrage, de transparence, voire de masquage (n'afficher qu'une partie de l'écran). L'addition ne peut qu'augmenter la couleur du pixel final, en augmenter la luminosité, ce qui la rend idéale pour appliquer des effets de lumière. A l'inverse, la soustraction ne peut que réduire la couleur final d'un pixel, ce qui permet d'appliquer des ombres. La moyenne mélange deux pixels entre eux, qui sont à la même place à l'écran, mais n'ont pas la même profondeur, vu qu'ils sont dans des arrière-plans différents. En clair, elle applique des effets de transparences assez limités. Les pixels de deux arrière-plans sont mélangés avec une proportion fixe de 50-50. Ce qui permet d'appliquer des effets de transparence très limités.

Avec l'addition, il suffit de calculer les lumières à appliquer dans un arrière-plan, qui est additionné avec le reste de l'image. Pour appliquer des ombres, il faut faire la même chose mais avec la soustraction. Les ombres sont calculées dans un arrière-plan à part, qui est soustrait du résultat du reste. Pour être plus précis, l'arrière-plan contenait une image d'ombrage, qui indique quelles sont les parties de l'image dans l'ombre et l'intensité de l'ombre pour chaque pixel. Plus un pixel est dans l'ombre, plus sa couleur est proche du blanc (en RGB) : la palette de couleur est inversée. En soustrayant cette seconde image d'ombrage à l'image du framebuffer principal, on ombre les pixels.

Il faut noter que certaines consoles, comme la NES, permettaient en plus d'additionner une couleur fixe pour tous les pixels de l'écran, voire de la soustraire, voire de soustraire le pixel à cette couleur fixe. Cela permettait de biaiser les couleurs de base avec une couleur de base. Là encore, l'utilité était une question d'ombrage, d'éclairage, de transparence, ou autre. Cela permettait d'affiner les couleurs finales. La couleur de base était mémorisée dans un registre. Notons que le registre pouvait être changé entre l'affichage de deux lignes, via des raster interrupts, ce qui permettait d'appliquer des dégradés de lumière verticaux, d'appliquer un effet d'horizon, d'ajouter du brouillard de distance.

L'implémentation matérielle et le cas particulier de la SNES

[modifier | modifier le wikicode]

Utiliser plusieurs arrière-plans demande que le matériel soit adapté. Il faut typiquement ajouter de quoi gérer et mémoriser plusieurs arrière-plans et ajouter des circuits pour les combiner entre eux. Le hardware pour faire cette combinaison détermine quel pixel passe devant les autres, il peut gérer de la transparence, etc. Les sprites sont souvent gérés par un système de sprite matériel, ce qui fait que le défilement n'est pas tellement un problème pour eux.

Les arrière/avant-plans sont typiquement dans des mémoires séparées afin de faciliter la gestion du défilement . Avec plusieurs mémoires, la technique précédente (le framebuffer étendu et un viewport rectangulaire) peut s'appliquer à chaque arrière-plan. Et on peut les faire défiler à des vitesses différentes. Il est aussi possible d'utiliser une seule mémoire pour cela, qui mémorise plusieurs arrière-plans, c'est tout à fait possible.

La console de jeu SNES gérait 4 arrière-plans différents, mais elle n'utilisait pas plusieurs mémoires. Les 4 arrière-plans étaient combinés entre eux, ce qui donnait l'image finale à afficher. Il était possible d'activer ou de désactiver les arrière-plans si besoin. Il était par exemple possible de n'utiliser que 2 arrière-plans, ou 3 arrière-plans dont un soustrait de l'addition des deux autres, etc.

Elle disposait de plusieurs modes, appelés mode 1, mode 2, ..., mode 7. Le mode 7 est un mode de rendu avec un seul arrière-plan, mais où les techniques des sections précédentes sont utilisables. Les optimisations pour le défilement horizontal et vertical sont présentes, et le viewport peut même être déformé, tourné, etc. Il peut utiliser un second arrière-plan très particulier, dérivé du premier. Il utilise les mêmes tiles, avec cependant la possibilité de se retrouver devant les sprites ou les autres arrière-plans. Il était utilisé pour gérer des tunnels vus de dessus, ou pour quelques effets graphiques où des élèments de décor se retrouvaient à l'avant-plan.

Mode vidéo BG 1 BG 2 BG 3 BG 4
0 4 couleurs (2 bits) 4 couleurs (2 bits) 4 couleurs (2 bits) 4 couleurs (2 bits)
1 16 couleurs (4 bits) 16 couleurs (4 bits) 4 couleurs (2 bits)
2 16 couleurs (4 bits) 16 couleurs (4 bits)
3 256 couleurs (8 bits) 16 couleurs (4 bits)
4 256 couleurs (8 bits) 4 couleurs (2 bits)
5 16 couleurs (4 bits) 4 couleurs (2 bits)
6 16 couleurs (4 bits)
7 256 couleurs (8 bits) EXTBG

L'accélération matérielle du tracé de ligne et de figures géométriques

[modifier | modifier le wikicode]
Tracé d'une ligne sur un écran.

La dernière optimisation du rendu 2D est le tracé de lignes et de figures géométriques accéléré en matériel. Quelques VDC incorporent cette optimisation, dont le nom est assez clair sur ce qu'elle fait. Tracer une ligne, un segment, est l'opération la plus courante sur de tels VDC, le tracé de cercles est déjà plus rare. Tracer des polygones est entre les deux, : plus rare que le tracé de ligne pur, moins que le tracé de cercles.

Les circuits de tracé de ligne

[modifier | modifier le wikicode]

L'algorithme le plus utilisé par le matériel pour tracer des lignes est l'algorithme de Bresenham. Il est très simple et s'implémente très facilement dans un circuit électronique. Il fut dite que cet algorithme utilise seulement des additions, des soustractions et des décalages, opérations très simples à implémenter en hardware. Il est de plus un des tout premiers algorithme découvert dans le domaine du graphisme sur ordinateur. Il existe de nombreuses modifications de cet algorithmes, qui vont de mineures à assez profondes. Et certaines d'entre elles sont plus faciles à implémenter en hardware que d'autres.

Coordonnées du pixel de départ et d'arrivée.
Tracé d'une ligne sur un écran, pixel par pixel.

Le fonctionnement du circuit de tracé de ligne est le suivant. Premièrement, on précise le pixel de départ et le pixel d'arrivée en configurant des registres à l'intérieur du circuit, avec une paire de registre pour les coordonnées du pixel de départ, une autre parie pour les coordonnées du pixel d'arrivée. Le circuit dessine la ligne pixel par pixel, avec un pixel dessiné par cycle d'horloge.

Le tracé de figures géométriques

[modifier | modifier le wikicode]

Passons maintenant aux tracé de figures géométrique. Le tracé se fait là encore pixel par pixel, sur le principe. Généralement, le tracé est limité à des figures géométriques simples, que vous avez tous vu quand vous étiez au collège, en cours de maths. Tracer des carrés/rectangles/pentagones/trapèzes ou autres polygone est très simple : il suffit de tracer plusieurs lignes les unes à la suite des autres.

La vraie utilité est l'implémentation de courbes, comme des cercles, des ellipses, ou autres. L'algorithme de Bressenham peut être modifié pour implémenter des cercles, ce qui donne le Midpoint circle algorithm. D'autres extensions permettent de dessiner des ellipses, et même des courbes plus complexes comme des courbes de Bezier ou d'autres courbes assez complexes à expliquer.

Le tracé et le remplissage de figures géométriques par le blitter

[modifier | modifier le wikicode]

Outre le tracé des figures géométriques, il est aussi possible de gérer en hardware les opérations de remplissage. Cela veut dire dessiner l'intérieur d'une figure géométrique, comme remplir un carré ou un rectangle, avec une couleur uniforme. Par exemple : dessiner un carré en rouge, remplir un rectangle existant de bleu clair, etc. Le remplissage est souvent disponible pour certaines formes géométriques simples, comme des carrés ou rectangles, rarement plus. Pour le remplissage des triangles ou d'autres figures géométriques, le support matériel est encore plus rare, ne parlons même pas des cercles.

Le remplissage de rectangles est souvent réalisé par le blitter. Pour faire une comparaison, la méthode utilisée est globalement la même que celle utilisée pour lire le viewport dans le framebuffer, mais cette fois-ci réalisé par le blitter. Le viewport est remplacé par le rectangle à remplir, et la lecture du pixel à envoyer à l'écran est remplacée par l'écriture d'une couleur précisée dans un registre.

Pour cela, on précise la couleur, la coordonnée X,Y et la largeur et la hauteur du rectangle dans des registres dédiés. Le remplissage commence à la coordonnée X,Y. L'adresse mémoire est alors incrémentée jusqu'à ce que la largeur voulue soit atteinte. On incrémente alors le compteur de ligne pour passer à la suivante, le compteur de colonne est réinitialisé avec la coordonnée X de la première colonne. Le remplissage s’arrête une fois que la hauteur voulue est atteinte.

Un exemple est le blitter des anciennes consoles Amiga, qui gère nativement des blocs en forme de rectangles, et de trapèzes dessinés à l'horizontale. Ils sont remplis en fournissant plusieurs informations : la position de leur premier pixel, une largeur qui est un multiple de 16 bits, une hauteur mesurée en nombre de lignes, un décalage qui indique de combien de pixels sont décalées deux lignes consécutives. Le décalage est ajouté une fois le compteur de colonne réinitialisé à sa valeur précédente (au démarrage de la ligne précédente).