Comment les graphiques NES ont-ils été organisés?

image

Lancée en 1983, la console de salon Nintendo Entertainment System (NES) était une machine bon marché mais puissante qui a connu un succès phénoménal. À l'aide de l'unité de traitement d'image (PPU), le système pourrait créer des graphiques assez impressionnants pour l'époque, ce qui semble encore assez bon dans le bon contexte. L'aspect le plus important était l'efficacité de la mémoire - lors de la création de graphiques, nous devions gérer avec le moins d'octets possible. Cependant, NES a fourni aux développeurs des fonctionnalités puissantes et faciles à utiliser qui lui ont permis de se démarquer des anciennes consoles de salon. Après avoir compris les principes de création de graphiques NES, vous pouvez sentir la perfection technique du système et réaliser à quel point il est plus facile pour les développeurs de jeux modernes de travailler.

Les graphiques d'arrière-plan NES ont été assemblés à partir de quatre composants distincts, dont la combinaison a formé l'image que nous voyons à l'écran. Chaque composante était responsable d'un aspect distinct; couleur, disposition, graphiques en pixels bruts, etc. Un tel système peut sembler inutilement compliqué et encombrant, mais il a finalement utilisé la mémoire beaucoup plus efficacement et a permis de créer des effets simples dans une petite quantité de code. Si vous voulez comprendre les graphiques NES, ces quatre composants seront des informations clés.

Cet article suppose que vous êtes familier avec les mathématiques informatiques, et en particulier avec le fait que 8 bits = 1 octet et 8 bits peuvent représenter 256 valeurs. Une compréhension du fonctionnement de la notation hexadécimale est également nécessaire. Mais même sans cette connaissance technique, l'article peut sembler intéressant.

Brève revue



Ci-dessus est une image de la première scène de Castlevania (1986): la porte menant au château, où le jeu aura lieu. Cette image mesure 256 × 240 pixels et utilise 10 couleurs différentes. Pour décrire cette image en mémoire, nous devons tirer parti de la palette de couleurs limitée et économiser de l'espace en ne stockant qu'une quantité minimale d'informations. L'une des approches naïves consiste à utiliser une palette indexée dans laquelle chaque pixel a un volume de 4 bits, c'est-à-dire que 2 pixels sont placés dans un octet. Cela nécessitera 256 * 240/2 = 30720 octets, mais comme nous le verrons bientôt, NES peut faire face à cette tâche beaucoup plus efficacement.

Les principaux concepts du thème graphique NES sont les tuiles et les blocs [1]. Une tuile est une zone de 8 × 8 pixels, et un bloc est une zone de 16 × 16 pixels, et chacun d'eux est lié à une grille avec la même taille de cellule. Après avoir ajouté ces grilles, nous pouvons voir la structure des graphiques. Voici l'entrée du château avec une grille à double grossissement.


Dans cette grille, les blocs sont affichés en vert clair et les tuiles en vert foncé. Les règles le long des axes ont des valeurs hexadécimales qui peuvent être ajoutées pour trouver une position; par exemple, le cœur dans la barre d'état est à 15 $ + 60 $ = 75 $, ce qui en décimal est 117. Chaque écran contient 16 × 15 blocs (240) et 32 ​​× 30 tuiles (960). Voyons maintenant comment cette image est décrite et commençons par les graphiques en pixels bruts.

CHR


La structure CHR décrit des graphiques de pixels «bruts» sans sa couleur et sa position, et est définie en mosaïques. La page de mémoire entière contient 256 tuiles CHR, et chaque tuile a une profondeur de 2 bits. Voici les graphiques du cœur:


Et voici comment cela est décrit dans CHR [2]:

pixel-heart-chr

Une telle description prend 2 bits par pixel, c'est-à-dire qu'avec une taille de 8 × 8, il s'avère que 8 * 8 * 2 = 128 bits = 16 octets. Ensuite, la page entière prend 16 * 256 = 4096 octets. Voici tous les CHR utilisés dans l'image de Castlevania.


Rappelons que le remplissage d'une image nécessite 960 tuiles, mais CHR n'en autorise que 256. Cela signifie que la plupart des tuiles sont répétées, en moyenne, 3,75 fois, mais le plus souvent seulement un petit nombre d'entre elles sont utilisées (par exemple, fond vide, tuiles monochromes). ou répétition de motifs). L'image de Castlevania utilise de nombreuses tuiles vides, ainsi que du bleu uni. Pour voir comment les tuiles sont attribuées, nous utilisons des tables de noms.

NAMETABLE


La table de noms attribue un fichier CHR à chaque position sur l'écran, et il y en a 960 au total. Chaque position est spécifiée dans un octet, c'est-à-dire que la table de noms entière prend jusqu'à 960 octets. Les tuiles sont attribuées dans l'ordre de gauche à droite, de haut en bas et correspondent à la position calculée trouvée en ajoutant les valeurs des règles indiquées ci-dessus. Autrement dit, la position dans le coin supérieur gauche est de 0 $, à droite de 1 $ et en dessous de 20 $.

Les valeurs de la table de noms dépendent de l'ordre dans lequel le CHR est rempli. Voici l'une des options [3]:


Dans ce cas, le cœur (à la position 75 $) a une valeur de 13 $.

Ensuite, pour ajouter de la couleur, nous devons sélectionner une palette.

Palette


NES possède une palette système de 64 couleurs [4], et nous en sélectionnons les palettes qui seront utilisées dans le rendu. Chaque palette contient 3 couleurs uniques plus la couleur de fond globale. L'image a un maximum de 4 palettes, qui occupent au total 16 octets. Voici les palettes de l'image de Castlevania:

castlevania-pal

Les palettes ne peuvent pas être utilisées arbitrairement. Une seule palette est appliquée par bloc. C'est à cause de ce besoin de séparer chaque zone 16 × 16 selon la palette de couleurs du jeu pour que NES ait un tel aspect «bloc». Les graphiques exécutés de manière magistrale, par exemple à partir de l'écran de démarrage Castlevania, peuvent être évités en mélangeant les couleurs sur les bords des blocs, ce qui masque la présence d'une grille.

La sélection d'une palette pour chaque bloc est effectuée en utilisant les derniers composants - attributs.

Attributs


Les attributs occupent 2 bits par bloc. Ils déterminent laquelle des 4 palettes utiliser. Cette image montre quelles palettes définies par les attributs utilisent différents blocs [5]:


Comme vous pouvez le voir, les palettes sont divisées en sections, mais cela est délicat en raison de l'utilisation des mêmes couleurs dans différentes zones. Le rouge au milieu de la porte se confond avec les murs qui l'entourent et un fond noir brouille la ligne entre le château
et portes.

Avec 2 bits par bloc ou 4 blocs par octet, les attributs d'image n'occupent que 240/4 = 60 octets, mais en raison de la façon dont ils sont codés, 4 autres octets sont perdus, c'est-à-dire que 64 octets au total sont obtenus. Cela signifie que l'image entière, y compris CHR, table de noms, palettes et attributs, prend 4096 + 960 + 16 + 64 = 5136 octets - bien mieux que le 30720 mentionné ci-dessus.

MAKECHR


La création de ces quatre composants pour les graphiques NES est plus difficile que l'utilisation des API bitmap standard, mais les outils viennent à la rescousse. Les développeurs NES avaient probablement une sorte de chaîne d'outils, mais quoi que ce soit, l'histoire ne l'a pas enregistrée. Aujourd'hui, les développeurs écrivent généralement des programmes pour convertir des graphiques au format NES souhaité.

Toutes les images de ce post ont été créées à l'aide de makechr , un outil réécrit utilisé par Star Versus . Il s'agit d'un outil en ligne de commande conçu pour les versions automatisées et destiné à la vitesse, la qualité des messages d'erreur, la portabilité et l'intelligibilité. Il crée également des visualisations intéressantes comme celles utilisées dans le post.

Les références


Surtout des connaissances sur la programmation pour NES, et en particulier sur la création de graphiques, j'ai obtenu des sources suivantes:


Remarques


[1] Terminologie - dans certains documents, les blocs sont appelés «méta-tuiles», ce qui me semble personnellement moins utile.

[2] Codage CHR - 2 bits par pixel ne sont pas stockés côte à côte. L'image complète est d'abord enregistrée uniquement avec les bits faibles, puis à nouveau enregistrée uniquement avec les bits élevés.

Autrement dit, le cœur sera stocké comme ceci:

pixel-coeur-baspixel-coeur-haut

Chaque ligne est un octet. Autrement dit, 01100110 est de 66 $, 01111111 est de 7 $. Au total, les octets du cœur ressemblent à ceci:

$ 66 $ 7f $ ff $ ff $ ff $ 7e $ 3c $ 18 $ 66 $ 5f $ bf $ bf $ ff $ 7e $ 3c $ 18 $

[3] Table de noms - dans ce tableau en jeu, la table des noms est utilisée différemment. En règle générale, les lettres de l'alphabet sont conservées en mémoire dans le quartier, y compris Castlevania.

[4] Palette système - NES n'utilise pas de palette RVB et les couleurs réelles qu'elle rend dépendent du téléviseur particulier. Les émulateurs utilisent généralement des palettes RVB complètement différentes. Les couleurs de cet article correspondent à la palette énoncée dans makechr.

[5] Encodage des attributs - Les attributs sont stockés dans un ordre étrange. Ils ne vont pas de gauche à droite, de haut en bas - la zone de bloc 2 × 2 est codée avec un octet, sous la forme de la lettre Z. C'est pourquoi 4 octets sont gaspillés; la ligne de fond est un plein 8 octets.

pal-block-group

Par exemple, un bloc de 308 $ est stocké avec 30 $, 348 $ et 34 $. Leurs valeurs de palette sont 1, 2, 3 et 3 et sont stockées dans l'ordre de la position la plus basse à la position la plus élevée, ou 11 :: 11 :: 10 :: 01 = 11111001. Par conséquent, la valeur en octets de ces attributs est $ f9.

2e partie


Dans la première partie, nous avons parlé des composants des graphiques d'arrière-plan NES - CHR, table de noms, palettes et attributs. Mais ce n'est que la moitié de l'histoire.

Pour commencer, il existe en fait deux tables de noms [6]. Chacun d'eux a ses propres attributs pour définir la couleur, mais ils ont le même CHR. L'équipement des cartouches détermine leur position: soit côte à côte, soit l'une au-dessus de l'autre. Voici des exemples de deux types d'emplacements différents: Lode Runner (1984) et Bubble Bobble (1988).


bulle-bobble-scrolling

Défilement


Pour profiter de la présence de deux tables de noms, PPU prend en charge la possibilité de faire défiler les pixels à la fois le long des axes X et Y. Il est contrôlé par un registre avec affichage en mémoire à 2005 $: l'écriture de seulement deux octets à cette adresse déplace tout l'écran au nombre de pixels souhaité [7] . Au moment de la sortie de NES, c'était le principal avantage par rapport aux autres consoles de salon, dans lesquelles pour le défilement, il fallait souvent réécrire toute la mémoire vidéo. Un tel système facile à utiliser a conduit à l'émergence d'un grand nombre de plateformes et de tireurs, et est devenu la principale raison d'un tel succès du système.

Pour un jeu simple, dont le champ ne fait que deux écrans de large, par exemple, Load Runner, il suffisait juste de remplir les deux tableaux de noms et de modifier le défilement en conséquence. Mais dans la plupart des jeux à défilement, les niveaux avaient une largeur arbitraire. Pour les implémenter, le jeu doit mettre à jour la partie hors écran des tables de noms avant qu'elles n'apparaissent à l'écran. La valeur de défilement est bouclée, mais comme la table des noms est constamment mise à jour, cela crée l'illusion d'une taille infinie.


Sprites


En plus de faire défiler les tableaux de noms, NES avait également un aspect complètement différent des graphiques: les sprites. Contrairement aux tables de noms qui doivent être alignées dans des grilles, les sprites peuvent être positionnés arbitrairement, de sorte qu'ils peuvent être utilisés pour afficher les personnages des joueurs, les obstacles, les projectiles et tous les objets avec des mouvements complexes. Par exemple, dans la scène ci-dessus de Mega Man (1987) pour afficher le personnage d'un joueur. les points et les bandes d'énergie sont des sprites utilisés, ce qui leur permet de sortir de la grille des tables de noms lors du défilement de l'écran.

Les sprites ont leur propre page CHR [8] et un ensemble de 4 palettes. De plus, ils occupent une page de mémoire de 256 octets. qui répertorie la position et l'apparence de chaque image-objet (il s'avère que la mémoire vidéo NES est deux fois et demie plus grande que celle mentionnée dans la première partie de l'article). Le format de ces enregistrements est assez inhabituel - ils contiennent d'abord une position en Y, puis un numéro de tuile, puis un attribut, puis une position en X [9]. Étant donné que chaque enregistrement prend 4 octets, il existe une restriction stricte: à l'écran, il ne peut pas y avoir plus de 256/4 = 64 sprites à la fois.

Les octets Y et X spécifient le pixel supérieur gauche du sprite dessiné. Par conséquent, sur le côté droit de l'écran, le sprite peut être rogné, mais sur le côté gauche, il laisse un espace vide. L'octet de la tuile est similaire à la valeur dans la table des noms, seulement pour ces tuiles les sprites utilisent leur propre CHR. Un octet d'attribut est un paquet de bits qui effectue trois tâches: deux bits sont alloués à la palette, deux bits sont utilisés pour refléter le sprite horizontalement ou verticalement, et un bit détermine s'il faut rendre le sprite sous les tables de noms [10].

Limitations


Les systèmes modernes permettent de travailler avec des sprites de n'importe quelle taille arbitraire, mais sur NES, le sprite dû aux limitations CHR devait avoir une taille de 8 × 8 [11]. Les objets plus grands sont constitués de plusieurs sprites, et le programme doit s'assurer que toutes les parties individuelles sont rendues côte à côte. Par exemple, une taille de personnage Megaman peut atteindre 10 sprites, ce qui vous permet également d'utiliser plus de couleurs, en particulier pour ses yeux blancs et son teint.


La principale limitation associée à l'utilisation de sprites est qu'il ne doit pas y avoir plus de 8 sprites par ligne raster. Si plus de 8 sprites apparaissent sur une ligne horizontale de l'écran, ceux qui sont apparus plus tard ne seront tout simplement pas rendus. C'est la raison du scintillement dans les jeux avec beaucoup de sprites; le programme échange les adresses des sprites en mémoire afin que chacun d'eux soit rendu au moins occasionnellement.

mégaman-scintillement

Enfin, le défilement n'affecte pas les sprites: la position du sprite sur l'écran est déterminée par ses valeurs Y et X, quelle que soit la position du défilement. Parfois, c'est un plus, par exemple, lorsque le niveau se déplace par rapport au joueur ou que l'interface reste dans une position fixe. Cependant, dans d'autres cas, c'est un inconvénient - vous devez déplacer l'objet en mouvement, puis changer sa position en fonction de la quantité de changement dans le défilement.

Remarques


[6] En théorie, il y a en fait quatre tables de noms, mais elles sont reflétées de telle manière que seulement 2 d'entre elles contiennent des graphiques uniques. Lorsqu'ils sont placés côte à côte, cela s'appelle la mise en miroir verticale, et lorsque les tables de noms sont situées l'une au-dessus de l'autre, la mise en miroir horizontale.

[7] Il existe également un registre qui sélectionne la table de noms avec laquelle commencer le rendu, c'est-à-dire que le défilement est en fait une valeur de 10 bits, ou 9 bits, compte tenu de la mise en miroir.

[8] Ce n'est pas toujours le cas. PPU peut être configuré pour utiliser la même page CHR pour les tables de noms que pour les sprites.

[9] Cette commande a peut-être été utilisée car elle correspond aux données que le PPU doit traiter pour un rendu efficace.

[10] Ce bit est utilisé pour divers effets, par exemple, pour déplacer Mario sous les blocs blancs dans Super Mario Bros 3, ou pour rendre le brouillard sur les sprites dans Castlevania 3.

[11] PPU a également une option pour activer les sprites 8 × 16, qui est utilisé dans des jeux comme Contra, où il y a de grands personnages. Cependant, toutes les autres restrictions s'appliquent.

3e partie


Dans les parties précédentes, nous avons parlé des données CHR, des arrière-plans basés sur des tables de noms, des sprites et du défilement. Et c'est pratiquement tout ce qu'une simple cartouche NES peut faire sans matériel supplémentaire. Mais pour aller plus loin, nous devons expliquer en détail le fonctionnement du rendu.

Rendu



Raster rendu avec une pause pour vblank

Comme d'autres anciens ordinateurs, NES a été conçu pour fonctionner avec les téléviseurs CRT. Ils dessinent des lignes de balayage sur l'écran, une à la fois, de gauche à droite, de haut en bas, à l'aide d'un canon à électrons qui se déplace physiquement jusqu'au point de l'écran où ces lignes sont tracées. Après avoir atteint le coin inférieur, une période de temps appelée «blanc vertical» (ou vblank) s'installe: le canon à électrons retourne dans le coin supérieur gauche pour préparer le dessin de l'image suivante. A l'intérieur de NES, le PPU (Picture Processing Unit) effectue automatiquement le rendu raster, dans chaque image, et le code travaillant dans le CPU fait toutes les tâches que le jeu doit effectuer. Vblank permet au programme de remplacer les données dans la mémoire PPU, car sinon ces données seront utilisées pour le rendu. Le plus souvent, des modifications sont apportées à la table des noms et aux palettes PPU pendant cette petite fenêtre.

Cependant, certains changements dans l'état du PPU peuvent être effectués pendant le rendu d'écran. Ils sont appelés «effets raster». L'action la plus courante effectuée pendant le rendu d'écran consiste à définir la position de défilement. Grâce à cela, une partie de l'image reste statique (par exemple, l'interface de jeu), et tout le reste continue de défiler. Pour obtenir cet effet, il est nécessaire de sélectionner précisément l'heure de modification de la valeur de défilement afin qu'elle se produise sur la ligne raster souhaitée. Il existe de nombreuses méthodes pour implémenter une telle synchronisation entre le code du jeu et PPU.

Écran partagé



Le niveau défile et l'interface en haut de l'écran reste immobile

Premièrement, PPU a un matériel intégré qui traite les sprites dans la position de mémoire zéro d'une manière spéciale. Lors du rendu de cette image-objet, si l'un de ses pixels chevauche la partie visible de l'arrière-plan, un bit est défini sous le nom "indicateur de l'image-objet". Le code du jeu peut d'abord placer ce sprite là où le fractionnement d'écran doit se produire, puis attendre en boucle, en vérifiant la valeur du drapeau sprite0. Par conséquent, lorsque la boucle est terminée, le jeu saura avec certitude quelle ligne raster est actuellement en cours de rendu. Cette technique est utilisée pour implémenter le partage d'écran simple dans de nombreux jeux NES, notamment Ninja Gaiden (1989), illustré ci-dessus [12]

ninja-hud

Sprite0 est situé à Y $ 26, X $ a0. Lorsque sa rangée inférieure de pixels est rendue, le drapeau sprite0 est défini

Dans certains jeux, le drapeau sprite0 est combiné avec une autre technique - boucle temporisée de manière prévisible («un cycle avec un timing prévisible»): le programme attend jusqu'à ce que plusieurs lignes supplémentaires soient rendues pour diviser l'écran en plusieurs parties. , Ninja Gaiden , , . , , , sprite0 , timed loops.

ninjas-in-field

castle-view

, , . ( , (memory mapping)), [13], . , . NES, , .

train-level

Ninja Gaiden 2, , , . , ; . , .


, . , , [14]. ( ), CHR, , . , . . Ninja Gaiden , , CHR.

goofall-bg

,

goofall-nt

,


CHR. ,

who-are-they-bottom-bank

CHR.

, ( ) . , , . , . , [15]. , CHR.

metal-storm-bg

Metal Storm (1991 )

metal-storm-nt



CHR — , . , ; . , CHR , , . , , , .


vice-fire

Vice: Project Doom (1991 ) , . , .

sword-master

Sword Master (1990 ) , .

Remerciements


, FCEUX. , sprite0 NesDev:



[12] , Ninja Gaiden . 8×16 sprites — , PPU, . sprite0 , sprite1 . z- , , .

[13] . . PPU , , . (IRQ), , , .

[14] , , . , , - 4 8 .

[15] CHR : , . , , 1 , .

Source: https://habr.com/ru/post/fr470614/


All Articles