|
|||||||||||||||||||||||||||||||||||||||||||||||
|
Le choc de l'arrivée de la Super Nintendo fin 1991 pour les amateurs de micro-ordinateurs 16 bits, ce fut le Mode 7. Cette console était capable d'appliquer une rotation et un redimensionnement à un bitmap de grande taille dans la trame, et il était possible de trafiquer pour produire un effet de perspective. L'Amiga 500 était doublement handicapé pour parvenir à produire de tels effets : pas de circuit spécialisé pour cela, et une organisation des données affichées sous forme de plans de bits exigeant, pour y parvenir, de nombreuses opérations au processeur et/ou au Blitter. Toutefois, cela n'empêcha pas certains de produire des effets du genre, par exemple le zoom au démarrage de la démo World Of Commodore 92 de Sanity : Parmi tous ces effets de zoom avec ou sans perspective, la plupart se sont appuyés sur le zoom vertical matériel, découvert dès les débuts de l'Amiga, et certains sur le zoom horizontal matériel, qui l'a été bien plus tard. Comment procéder à l'un et l'autre de ces zooms avec le matériel ? En particulier, quel est ce fameux "$102 trick" régulièrement évoqué dans les forums de créateurs de démos, souvent succinctement, et parfois abusivement ? Et dans quelle mesure est-il possible de zoomer ainsi ? Tout cela et plus encore dans ce qui suit.
Le zoom vertical matériel, un usage banal des modulos Comme expliqué ici, le matériel affiche une image composée de plans de bits dont les adresses lui sont fournies via les couples de registres BPLxPTH/BPLxPTL. Une fois qu'il a lu et affiché les données d'une ligne, le matériel rajoute l'équivalent en octets à ces adresses - 40 octets pour un écran de 320 pixels de large -, puis il y rajoute un modulo. Ce modulo est stocké dans BPL1MOD pour les plans de bits impairs (1, 3, etc.), et BPL2MOD pour les plans de bits pairs (2, 4, etc.). Sachant que le matériel lit donc dans des registres que le Copper permet de modifier (au moins) à chaque ligne de l'écran, on conçoit facilement comment tirer profit de ce fonctionnement. Prenons l'exemple d'une image de 320x256 pixels en seize couleurs, donc quatre plans de bits. Il est possible de demander au Copper d'attendre le début d'une ligne et d'écrire dans les registres BPLxPTH/L pour modifier l'adresse de la ligne qui sera affichée. Cette adresse sera déterminée en fonction du zoom, pour sauter ou répéter des lignes, ou simplement passer à la ligne suivante. La liste Copper contient alors notamment un bloc suivant par ligne N, ici présenté en pseudo-code pour en faciliter la lecture :
Qui est certain de l'alignement des données en mémoire peut se dispenser d'écrire dans BPLxPTH sachant que sa valeur ne changera pas quel que soit le décalage rajouté pour progresser dans les plans de bits, mais peu importe : c'est le principe qu'on illustre ici. Il est aussi possible de simplement écrire dans BPL1MOD et BPL2MOD. Dans ce cas, c'est l'adresse de la ligne suivante, et non l'adresse de la ligne courante, qui sera impactée, le matériel utilisant les modulos en fin de ligne. La liste Copper contient alors notamment un bloc suivant par ligne N, ici encore présenté en pseudo-code pour en faciliter la lecture :
Utiliser BPLxMOD affecte tous les plans de bits impairs et/ou tous les plans de bits pairs, sans discrimination. Toutefois, ce n'est pas gênant, car le zoom à réaliser est généralement le seul effet à produire dans les plans de bits. Par ailleurs, utiliser ces registres est plus économique qu'utiliser BPLxPTH/L. En effet, il ne faut pas oublier que le facteur de zoom variant d'une trame à l'autre, la liste Copper devra être modifiée au processeur ou au Blitter pour mettre à jour les valeurs que le Copper écrit dans les registres utilisés. Or, le calcul est vite fait :
Dans le programme final zoom4.s, c'est la première solution qui a été adoptée par simplicité. Il faut noter que cela implique de modifier BPLxPTH/L pour parvenir à dissimuler une ou plusieurs des premières lignes. En effet, le modulo est une valeur que le matériel ajoute à l'adresse de la ligne qui vient d'être affichée. Or, par définition, la première ligne ne vient après aucune autre ligne. Toutefois, c'est le seul cas où ces registres sont modifiés pour zoomer. Par ailleurs, il faut noter que le faisceau d'électrons ne trace donc rien au-dessus ni en-dessous de l'image zoomée. Dans ces conditions, il est impossible d'afficher des bandeaux encadrant l'image zoomée. Pour ce faire, il faut opter pour la seconde solution. La figure suivante récapitule ce qui se passe dans le cas où les lignes 0, 1, 2 et 5 de l'image doivent être dissimulées :
A ce stade, il reste un point essentiel à régler : comment identifier les lignes à dissimuler à une étape du zoom donnée ? Cette question, qui se pose presque dans les mêmes termes pour le choix des colonnes à dissimuler, sera abordée plus loin. Le zoom horizontal matériel, un détournement surprenant du défilement L'histoire dira à qui revient le mérite d'avoir trouvé l'astuce (on évoque ici le génial Chaos/Sanity). Comme chacun sait, il est possible de demander au matériel de retarder l'affichage de l'image à l'écran d'un nombre de pixels allant de 0 à 15. La valeur de ce retard doit être spécifiée dans le registre BPLCON1, qui comporte quatre bits (PF1H3-0) pour le retard des plans de bits impairs (1, 3, etc.) et quatre bits (PF2H3-0) pour le retard des plans de bits impairs (2, 4, etc.). C'est le défilement matériel. Ainsi, une valeur de $005F dans BPLCON1 va retarder l'affichage des plans de bits pairs de cinq pixels et celui des plans de bits impairs de quinze pixels. A moins d'utiliser le double champ de jeu ou de chercher à manipuler distinctement les plans de bits pairs et impairs, les retards spécifiés pour ces plans de bits seront identiques. Il s'agira de produire un défilement horizontal en jouant sur BPLCON1 et sur les couples BPLxPTH/BPLxPTL - on ne s'étendra pas sur ce sujet trivial. Or le matériel lit les données qu'il va afficher par groupe de seize pixels. A chaque fois, il tient compte des décalages spécifiés dans BPLCON1 pour retarder plus ou moins l'affichage de ce groupe. Que se passerait-il si la valeur de ces décalages devait être réduite d'un groupe à un autre ? En particulier, le matériel n'afficherait-il pas plus tôt le second groupe, écrasant les derniers pixels du premier ? C'est exactement ce qui se passe. Et comme dissimuler des colonnes de pixels dans l'image produit une réduction de la largeur de cette dernière, il y a lieu de parler de zoom horizontal matériel : Encore faut-il régler une question pratique : quand et comment modifier la valeur des décalages dans BPLCON1 ? Comme cela vient d'être suggéré, il faut que l'écriture dans BPLCON1 soit synchronisée sur le cycle de lecture et d'affichage des groupes de pixels par le matériel. Chacun sait qu'il est toujours possible d'attendre une position du faisceau d'électron (le raster) à l'écran, et de modifier le contenu d'un registre matériel à cet instant. Cette manoeuvre peut être réalisée au processeur, mais s'il fallait réaliser le zoom matériel de cette manière, il serait impossible de rien faire d'autre durant une trame. Fort heureusement, le Copper est là, qui exécute sa liste Copper parallèlement. Pourquoi ne pas utiliser ses instructions WAIT et MOVE, détaillées ici, pour parvenir au résultat souhaité ? Le programme zoom1.s en donne une illustration. L'idée va être d'attendre la position à laquelle on souhaite dissimuler un certain nombre des derniers pixels d'un groupe donné. Mais où attendre ? A vrai dire, il y aurait deux solutions : utiliser un WAIT puis un MOVE pour modifier BPLCON1. Ou alors, comme dans le cas d'un effet plasma, enchaîner les MOVE en sachant qu'un MOVE prend huit pixels en basse résolution pour être exécuté, dont un MOVE pour modifier BPLCON1. Dans les faits, seule la seconde solution est praticable. Pourquoi ? Parce que c'est comme ça. Tous les codeurs qui utilisent le zoom horizontal matériel le font ainsi. Pourquoi, encore ? Parce qu'il faudrait vraiment, mais alors vraiment, se casser la tête pour parvenir à calculer précisément la position horizontale à spécifier dans le WAIT pour que le MOVE soit exécuté au bon moment - et encore, il n'est pas dit que ce serait possible. A ceux qui se lamenteraient de ce manque de rigueur, disons que pour aller au fond des choses, il faudrait savoir expliquer à tout instant le rapport entre la position horizontale du faisceau d'électron, telle qu'elle est formulée dans un WAIT du Copper, et la valeur horizontale telle qu'elle figure dans DDFSTRT. Or, si c'est certainement possible, cela reste à faire... Ainsi le Copper est programmé pour attendre le début de chaque ligne à la position horizontale $3D, empiriquement déterminée, et procéder à 40 MOVE dont certains vont modifier la valeur de BPLCON1 en réduisant le retard initial, eux aussi empiriquement déterminés. Il faut noter qu'une telle sollicitation du Copper contraint le nombre de plans de bits, donc le nombre de couleurs à l'écran. La raison en a déjà été présentée ici : au-delà de quatre plans de bits, le matériel vole des cycles au Copper, si bien que ce dernier perd la possibilité d'exécuter autant de MOVE par ligne : ![]() Pour afficher les plans de bits 5 et 6, le matériel vole des cycles au Copper
La liste Copper contient alors notamment un bloc suivant par ligne N, ici encore présenté en pseudo-code pour en faciliter la lecture :
Ajuster le zoom horizontal matériel, un vrai défi En modifiant ZOOM_MOVE dans le programme précédent, on indique l'indice du MOVE auquel on souhaite que le Copper écrive ZOOM_BPLCON1 dans BPLCON1, sachant que BPLCON1 est initialisé à $00FF. Il est alors possible d'observer le résultat à l'écran. Pour que ce dernier soit bien visible, l'effet est répété sur une bande de ZOOM_DY de hauteur, en l'occurrence 20 pixels, précédée et suivie d'une bande de même hauteur sans effet. Par ailleurs, un motif blanc sur fond rouge est dessiné dans le plan de bits : c'est une succession de groupes de seize pixels : dans le premier groupe, le dernier pixel est blanc ; dans le deuxième groupe, les deux derniers pixels sont blancs ; etc. Ce motif permet de réaliser un effet du genre : "si je me positionne au niveau du MOVE correspondant au groupe comportant quatre pixels blancs et que je réduis BPLCON1 de 4, est-ce que j'observe la disparition de ces quatre pixels ?" Ce qui peut être observé, c'est que très généralement, les résultats sont décevants. Par exemple, passer BPLCON1 à $0022 au 18e MOVE (c'est-à-dire ZOOM_MOVE valant 17) produit cela : ![]() Les aléas de la modification de BPLCON1 en cours de ligne ![]() La dissimulation réussie d'une colonne par modification de BPLCON1 en cours de ligne "Une valeur initiale de BPLCON1 donnée" ? Ce n'est pas $00FF ? Non, car il faut gérer une contrainte : lorsqu'une colonne est dissimulée, les colonnes qui suivent sont décalées à l'écran d'un pixel sur la gauche. A ce train, une image dont quinze colonnes sont dissimulées se trouve tassée de quinze pixels sur la gauche. Or, ce n'est pas ce qui est attendu lors d'un zoom réduisant la largeur d'une image de 320 à 0 pixels : comme évoqué en présentant le zoom vertical matériel, l'image doit rester centrée à l'écran. Pour y parvenir, il faut adopter le scénario suivant. Sur cette figure, chaque ligne représente une étape du zoom. A gauche, la valeur initiale de BPLCON1. A droite, sa valeur finale. Entre les deux, les vingt groupes de seize pixels composant une ligne d'une image de 320 pixels de large :
![]() Scénario du zoom horizontal matériel Adopter ce scénario a une conséquence capitale. Pour le comprendre, il faut désormais bien distinguer quatre choses :
Quand aucune colonne n'est dissimulée, il faut que le dessin soit centré à l'écran. Or BPLCON1 vaut alors $0077. Pour qu'un dessin décalé de sept pixels sur la gauche apparaisse centré dans l'écran de 320 pixels de large, il faut que la largeur de ce dessin ne dépasse pas 306 pixels. En effet, si sept pixels sont inutilisables à gauche, il faut que sept pixels le soient à droite : quatorze pixels en tout, à soustraire de 320, ce qui donne 306. C'est donc un dessin de 306 pixels de large, calé sur la gauche dans des plans de bits de 320 pixels de large, qui peut être au plus utilisé pour produire le zoom, la colonne de quatorze pixels de large sur la droite devant être en couleur 0 : ![]() Le dessin de 306 pixels de large dans un plan de bits de 320 pixels de large Cette liste est totalement spécifique :
Retour à notre liste. Sa mise en oeuvre dans le programme zoom2.s produit le résultat suivant, où un dessin de 306 pixels de large (lignes blanches sur fond gris) centré dans un plan de bits de 320 pixels de large (fond rouge) est réduit progressivement en dissimulant les derniers pixels de quinze groupes de seize pixels les uns après les autres. Pour que le zoom semble progressif s'il devait être animé, les dissimulations alternent entre la gauche et la droite du groupe de pixels du milieu. Le dessin est intact dans la moitié supérieure de l'écran ; il subit le zoom dans la moitié inférieure de l'écran, perdant un pixel toutes les huit lignes : ![]() Réduire progressivement la largeur d'une image de quinze pixels, tout en la centrant BPLCON1 permet d'introduire un retard initial de quinze pixels au plus au début de chaque ligne. Partant, c'est au plus quinze pixels qu'il est possible de dissimuler sur cette ligne, ce qui est clairement insuffisant pour produire un zoom où la largeur de l'image passerait de 320 à 0 pixels. Quand les possibilités du zoom horizontal matériel sont épuisées (BPLCON1 valant $00FF au début d'une ligne, et étant réduit progressivement jusqu'à $0000), nulle autre solution que de prendre le relai avec un zoom horizontal logiciel. Il s'agit alors de reproduire dans les plans de bits ce que le spectateur voit à l'écran avant de réinitialiser le zoom matériel (BPLCON1 valant $0077 au début d'une ligne, et n'étant pas réduit). Cela revient à dire qu'il faut supprimer véritablement les colonnes de pixels dissimulées dans les plans de bits, et recentrer horizontalement le dessin ainsi réduit dans ces derniers. Recentrer de combien de pixels ? Comme cela vient d'être rappelé, au moment où il faut prendre le relai du zoom matériel, la valeur de BPLCON1 est $00FF, ce qui correspond à un décalage de quinze pixels sur la droite. Par ailleurs, la valeur de BPLCON1 quand aucun pixel n'est dissimulé est de $0077, ce qui correspond à un décalage de sept pixels sur la droite. En conséquence, il faut produire un dessin qui, lorsque les plans de bits qui le contiennent seront ainsi décalés, présentera les caractéristiques suivantes :
Comment supprimer ? Il est possible d'utiliser le Blitter, qui s'entend à merveille pour décaler des mots sur la droite (en mode ascendant) comme sur la gauche (en mode descendant) après les avoir éventuellement masqués, ou le processeur, voire les deux. Une optimisation est possible - en fait, elle est même nécessaire. En effet, au fil du zoom logiciel, la largeur du dessin dans les plans de bits se réduit, jusqu'au point où le dessin n'empiète plus sur certains groupes de seize pixels qui se trouvent à sa gauche et à sa droite. Partant, il devient inutile de supprimer des colonnes dans ces groupes. De même, ces colonnes n'ont plus à être dissimulées. Ces optimisations sont obligatoires, car le spectateur ne comprendrait pas que le zoom semble faire une pause parce qu'une étape est consacrée à la dissimulation d'une colonne qui est vide. Il faut donc déterminer quelles colonnes doivent être dissimulées durant un cycle de zoom matériel, et supprimées par zoom logiciel au terme de ce cycle, en fonction de la largeur du dessin au début de ce cycle. C'est ce qui a été fait pour réaliser le programme zoom3.s à l'aide de la feuille Excel qui figure dans zoom.xlsx : ![]() Calcul des groupes à traiter à chaque cycle de zoom horizontal matériel Au final, le code de la suppression est assez complexe, et il serait trop long d'en présenter le détail ici. Qui veut en savoir plus peut se référer au source du programme indiqué. On y utilise le Blitter uniquement pour recopier en les décalant et en les masquant les colonnes de mots qui forment le dessin, et celles-là uniquement. Le choix des lignes et des colonnes à dissimuler : un dilemme d'informaticien Les techniques présentées pour dissimuler/supprimer des lignes et des colonnes fonctionnent bien, mais elles reposent sur des hypothèses dont il faut avoir conscience. Ces hypothèses portent sur la manière dont il convient d'identifier les lignes et les colonnes à dissimuler à une étape du zoom donnée. Elles ont notamment déterminé le scénario du zoom horizontal matériel présenté plus tôt. S'il faut dissimuler N lignes et colonnes à une étape du zoom donnée, le premier réflexe est de chercher à répartir également ces lignes sur la hauteur et ces colonnes sur la largeur du dessin. A bien y réfléchir, cela repose sur l'hypothèse que le zoom sera plus réaliste si la dissimulation est diffuse, car le spectateur devrait toujours disposer d'assez d'informations en tout point du dessin pour reconnaître sinon le dessin initial, du moins le dessin de l'étape précédente. Toutefois, ce n'est qu'une hypothèse. S'il s'agissait de zoomer un texte, il y a fort à parier que les caractères traités aussi arbitrairement deviendraient vite méconnaissables. En toute rigueur, la réduction d'un dessin, c'est le filtrage d'un message, lequel ne peut déboucher sur un résultat acceptable qu'à condition de tenir un minimum compte de la signification du message en question, quitte à reformuler ce dernier. Cette considération sémantique peut paraître stratosphérique dans le cas du zoom en temps réel d'un dessin sur Amiga, tant il est vrai que la puissance de calcul disponible interdira d'en tenir compte rigoureusement - si tant est qu'il soit seulement possible d'en tenir compte ! Toutefois, il est bon de l'évoquer pour échapper à une remise en question sans issue de la pertinence de l'hypothèse adoptée. Par là, on veut dire qu'en cherchant à diffuser les dissimulations, on cherche à faire au mieux pour emballer le spectateur avec les moyens du bord. Ce qui fonde l'hypothèse, ce ne sont pas de savants calculs ; c'est que le résultat qui en découle est, pour le spectateur, assez convaincant. Enfin, il faut tout de même questionner l'hypothèse du fait d'une contrainte technique qui limite sérieusement cette application. Cette contrainte porte sur le zoom horizontal matériel, dont on a vu qu'il n'est pas aussi souple que le zoom vertical matériel. En effet, il est impossible de choisir les colonnes à dissimuler aussi facilement que les lignes à traiter pareillement : c'est parmi les derniers pixels de groupes de seize pixels qu'il faut choisir. Par exemple, pour réduire un dessin de 320 pixels à 318 en dissimulant deux colonnes, il pourrait paraître cohérent de dissimuler les colonnes 106 et 214, l'espace entre les colonnes dissimulés étant alors régulier :
La distribution des colonnes de pixels qu'il est possible de dissimuler est contrainte. Qui veut réduire progressivement la largeur d'un dessin de 320 à 0 pixel en dissimulant une nouvelle colonne de pixels à chaque étape peut au mieux répartir les colonnes qu'il dissimule tous les seize pixels. Et encore, ce n'est pas tous les seize pixels du dessin, mais tous les seize pixels des plans de bits qui contiennent le dessin, comme on l'a vu. Enfin, quelle que soit l'hypothèse adoptée, il faut aussi tout de même la questionner sur un autre point. Continuons sur l'exemple du zoom horizontal. Indépendamment de la contrainte qui vient d'être évoquée, s'il faut enchaîner des dissimulations de colonnes, comment vaut-il mieux procéder :
Pour un esprit cartésien, cette histoire de zoom matériel, c'est un peu la déconvenue à chaque étape. L'identification des MOVE pour modifier BPLCON1 au bon moment sur une ligne ? A déterminer empiriquement. L'identification des colonnes et des lignes à dissimuler ? A déterminer empiriquement. Le choix de faire réapparaître ou non des colonnes et des lignes dissimulées ? A déterminer empiriquement. Mais c'est que l'esprit cartésien s'est trompé de domaine. Rappelons que le zoom matériel est employé dans une démo. Or, ainsi que l'a fort bien expliqué le formidable Navis/ASD, le principe de toute démo est de faire illusion : "We have to cheat. And you know, everybody does this. This is why demos are different to making games or making offline films." (Nous devons tricher. Et vous savez, tout le monde le fait. C'est pourquoi les démos sont différentes de la création de jeux ou de films hors ligne). C'est parole de Maître, car on ne saurait mieux définir le genre. Or, il est bon qu'on nous rappelle régulièrement à quoi nous sommes censés nous consacrer. C'est le meilleur moyen pour ne pas être victime d'une illusion, celle de la poursuite d'une perfection sans intérêt pour celui auquel nous destinons les fruits de notre labeur, à savoir le spectateur. Et dans une démo, il s'agit de faire du Hollywood pour attirer les foules, pas du cinéma français pour les tenir à distance... Si vous ne travaillez que pour vous-même, à moins d'être un génie - mais qui se pose cette question a déjà la réponse -, vous ne resterez pas dans l'Histoire : le génie subjugue ; le tiers doit convaincre. Sans donc nous faire d'illusion sur la catégorie dont nous relevons, concluons benoîtement notre exploration des délices du zoom matériel. Le programme zoom4.s rajoute le zoom vertical matériel à la combinaison des zooms horizontaux matériel et logiciel. Dans le cas du zoom vertical, le scénario adopté est exactement le même que celui retenu pour la dissimulation des colonnes, imposé par les limitations du zoom horizontal matériel. Un outil en HTML5, réalisé pour l'occasion, a permis de générer les indices des lignes à dissimuler tandis que le zoom progresse : ![]() Un outil réalisé en HTML pour tester un scénario de zoom vertical Il faut saluer la ténacité de ceux qui se sont lancés dans le zoom matériel pour en faire quelque chose, tant l'effet est pénible à maîtriser. Toutefois, il ne faut pas surestimer le gain qu'ils ont pu en tirer. Ce n'est pas parce qu'il y a zoom dans la trame bientôt en plein écran que ce dernier est matériel. En fait, quand le zoom horizontal est très réussi, il y a tout lieu de penser qu'il n'est pas matériel. Ainsi, la fameuse démo Elysium de Sanity s'ouvre par zoom irréprochable d'une image sur quatre plans de bits. Mais le désassemblage de la liste Copper ne donne à voir que des WAIT à chaque ligne qui débouchent sur des modifications de BPL1MOD et BPL2MOD. Autrement dit, le zoom horizontal matériel n'est pas utilisé ici, mais uniquement le zoom vertical matériel : ![]() Zoom irréprochable d'une image en quatre plans de bits dans la démo Elysium de Sanity ![]() Le rouleau de sélection dans la disquette musicale Jesterday de Sanity ![]() Le défilement à la Star Wars dans The Fall de The Deadliners & Lemon ![]() Le défilement à la Star Wars de Stardust, totalement parfait
|
||||||||||||||||||||||||||||||||||||||||||||||