Retour au sommaire

  AFFICHAGE  

 

 

    ANIMATION SOMMAIRE IMPLEMENTATION SOUS VB 6    

 

Comme vous le savez sûrement, les sprites sont les images mobiles dans un jeu vidéo. Afin que leur aspect corresponde à leurs attitudes, il faut être capable de modifier en temps réel l'image qui les représente au cour de leur animation. Ce chapitre décrit en l'occurrence diverses techniques de manipulation de pixels nous permettant d'effectuer la plupart des opérations nécessaires à l'affichage d'un sprite.

 

Je fais abstraction pour l'instant de l'animation image par image car cette technique n'a pas été implémentée dans =FOOT=. Peut être en parlerais-je dans une prochaine mise à jour de ce site.

 

 

  1) Affichage d'images avec masque: Premiére méthode:  

Les images informatiques ont naturellement un aspect rectangulaire. Cela est tout à fait suffisant dans la plupart des cas mais il arrive parfois qu'il soit nécessaire d'afficher des formes un peu plus élaborées, comme c'est le cas notamment pour les sprites.

Pour cela, nous devons passer par l'utilisation d'un masque qui, comme son nom l'indique, permet de n'afficher que certains pixels de l'image à laquelle il est associé. Ainsi, il est possible grâce à lui d'incruster une image aux contours quelconques par dessus une autre image.  

 

+ + =
sprite   masque du sprite en noir et blanc   image de fond   sprite incrusté sur l'image de fond

 

Le moyen le plus simple de réaliser cela est de tester pour chaque pixel, la valeur du masque qui lui est associé. Si cette valeur est non-nulle; on affiche le pixel du sprite, sinon on affiche rien.

 

Techniquement, un masque est souvent une image noir et blanc, entrelacée ou non avec l'image couleur. (cf. Implémentation sous Visual Basic).

 

Parfois, il n'est pas nécessaire d'associer explicitement un masque à l'image. Il suffit juste de choisir une couleur n'intervenant pas dans la représentation du sprite, et de la considérer comme une couleur de transparence, à l'instar des images GIF.

 

L'avantage de cette méthode est sa grande simplicité de mise en oeuvre, mais son principal défaut est qu'elle ne fait pas dans la nuance: Un pixel est affiché ou pas du tout.

Si l'on veut procéder à une incrustation plus fine, avec des zone donnant l'impression d'être translucides, il faut un peu plus de travail...

 

 

  2) Affichage d'images avec masque: Seconde méthode:  

L'incrustation du sprite peut être de bien meilleur qualité si l'on considère les valeurs intermédiaires qui composent son masque. Ainsi, au lieu de se limiter à choisir s'il faut afficher un pixel ou pas, on peut  mélanger sa couleur avec celle du pixel qu'il recouvre en fonction de l'intensité du masque.

 

+ + =
sprite   masque du sprite en niveaux de gris   image de fond   sprite incrusté sur l'image de fond

(on peut remarquer que les parties grises du masque font s'afficher le sprite en transparence

La formule pour réaliser cela est la suivante:

couleur_finale_= ((intensité_masque* (couleur_source - couleur_destination)) / intensité_masque max) + couleur_destination

 

Cette méthode est quasiment la même lorsqu'il s'agit de faire apparaître progressivement une image par dessus une autre (fondu enchaîné). Il suffit pour cela d'appliquer la même intensité de masque à tous les pixels de l'image, et de la faire varier de 0 à intensité_masque_max.

 

 

  3) Affichage d'ombres portées: Première méthode:  

Comme le laisse présager l'exemple précédent; le masque peut servir à projeter une ombre en dessous du sprite. Cependant, il est préférable de ne pas intégrer directement cette dernière dans le masque afin de ne pas figer son décalage et son orientation.

Néanmoins, le masque est très utile pour nous renseigner sur la silhouette à afficher. La technique est très similaire à celle employée pour incruster un sprite. Simplement, au lieu d'afficher les pixels du sprite lorsque la valeur du masque est non-nulle, on diminue l'intensité de la couleur du pixel de l'image de fond.

 

Première étape: Affichage de l'ombre du sprite:

/_coeff. + =
masque décalé du sprite en noir et blanc diminution de l'intensité du masque   image de fond   l'image de fond est assombrie partout où les valeurs du masque sont non-nulles.

 

Deuxième étape: Affichage du sprite par dessus son ombre:

+ + =
sprite   masque du sprite en noir et blanc   image de fond + ombre   sprite et son ombre incrustés sur l'image de fond

 

Cependant, cette méthode présente les mêmes inconvénients que ceux évoqués précédemment, à savoir que l'intensité de l'ombre ne présente aucune nuance. Cette technique n'est donc pas adaptée pour la projection d'ombres des objets translucides.

 

 

  4) Affichage d'ombres portées: Seconde méthode:  

Afin de pendre en compte toutes les subtilités de l'image du sprite, nous allons encore une fois considérer l'ensemble des valeurs intermédiaires de son masque.

Pour cela, nous allons soustraire à chaque pixel de l'image de fond, pour lequel la valeur du masque du sprite est non-nulle, cette même valeur de masque diminuée d'un certain coefficient.

couleur_finale = (couleur_destination - (intensité_masque / coefficient_d'atténuation)

 

On voit bien ici que si la quantité (intensité_masque / coefficient_d'atténuation) est plus grande que couleur_destination, alors couleur_finale sera inférieure à 0, ce qu'il faut naturellement éviter. On prendra donc soin de ramener toutes les valeurs négatives à 0:

si (couleur_finale < 0) alors couleur_finale = 0

 

Exemple avec l'ombre d'une bulle:

- ( /_coeff._) =
image de fond     masque atténué    
= ...
  l'image de fond est assombrie en fonction des valeurs du masque.   résultat final

 

 

 

  5) Rotation d'images: Première méthode:  

Nous avons vu dans le chapitre consacré à l'animation que les sprites peuvent être orientés. Afin de les représenter correctement, il faut donc être capable de faire pivoter une image sur 360°.

Pour cela, nous pouvons transformer la position de chaque pixel de l'image avec la formule que voici:

x ' = (x * cos A) - (y * sin A)

y ' = (x * sin A) + (y * cos A)

La démarche naturelle consisterait à faire tourner chaque pixel de l'image source vers l'image de destination, mais on se heurterait alors d'emblée au problème d'imprécision des entiers.

En effet, la position de chaque pixel est définie par deux entiers: X et Y. Hors les résultats du calcul d'une rotation, du fait des cosinus et des sinus, sont des réels. Avec les erreurs d'arrondi, on risque alors d'écrire plusieurs pixels au même endroit ou pire encore, de ne pas en écrire du tout. La conséquence sera  une image laissant apparaître des trous au cours de sa rotation.

Pour remédier à ce petit désagrément Il faut procéder à l'envers:

On va parcourir chaque pixel de l'image de destination en allant chercher ―après leur avoir appliqué la rotation voulue― leur correspondance dans l'image source. Ainsi, seuls les pixels strictement utiles seront affichés, et il ne le seront qu'une seule fois par passe.

 

lecture des pixels de l'image source après rotation.

 

écriture de chaque pixel de l'image de destination

 

Comme cela est évoqué dans la partie Implémentation sous VB 6, l'origine des images Windows se situe dans leur coin bas / gauche. Si l'on fait tourner l'image avec la formule de rotation susmentionnée, celle-ci va pivoter autour de ce coin.

Pour la faire tourner sur son centre il faut donc avant tout la centrer sur son origine, la faire pivoter, et enfin la replacer au centre, ce qui correspond en réalité à un changement de repère, le temps de la rotation.

 

Bien que l'image ne souffre  plus d'aucun trou avec la technique d'écriture des pixels de destination à partir de l' image source (voir plus haut), elle n'en présente pas moins de nombreuses aberrations selon son angle de rotation. La raison est toujours la même: Les erreurs d'arrondi pour obtenir la position des pixels.

image source image destination

(on voit que les pixels composant l'image ne sont pas idéalement positionnés)

A cela nous ne pouvons pas grand chose puisque les coordonnées des pixels sont forcément des valeurs entières. Mais avec un peu d'astuce on peut cependant donner l'impression à l'observateur que les pixels sont parfaitement placés...

 

 

  6) Rotation d'images: Seconde méthode:  

 

La méthode décrite ici est davantage une technique de filtrage que de rotation. Elle peut être appliquée à toute forme de dessin (agrandissement d'image, dessin de lignes, de courbes, etc...) du moment que l'on connaît la position idéale des pixels que l'on veut afficher.

 

Le principe est le suivant:

Un pixel est défini par deux caractéristiques:

  1. Sa position

  2. Sa couleur

Comme on l'a vu précédemment, l'emplacement d'un pixel à l'écran est fatalement une quantité entière. Pour s'en convaincre, il suffit de s'imaginer le moniteur affichant nos images comme une grille colorée, dont les cases seraient les pixels.

Comme un pixel ne peut être affiché sur une valeur intermédiaire de la grille (entre deux  ou quatre cases), il faut donner l'illusion que cela est possible en jouant sur son second paramètre: la couleur.

 

position idéale du pixel dans l'image source position réelle du pixel dans l'image source couleur brute du pixel de destination

 

On voit dans cette exemple que la position idéale n'est pas toujours la position trouvée. Par conséquent la couleur du pixel de destination peut être assez éloignée de ce qu'elle devrait être, d'où les aberrations constatées dans l'image après rotation.

Il faut donc prendre en compte toutes les couleurs présentes dans la zone du pixel idéal, en les pondérant par les surfaces recouvertes.

Ainsi, d'après la figure précédente, on devrait avoir pour le pixel de destination: 13% de jaune + 26% de  vert + 21% de  bleu et 40% de  rouge

 

= 0.13_*_jaune_+_0.26_*_vert_+_ 0.21_*_bleu_+_0.4_*_rouge =

position idéale du pixel dans l'image source   traduction littérale   couleur corrigée du pixel de destination

 

On constate que la couleur corrigée du pixel de destination est assez différente de sa version brute. Voici le résultat sur un échantillon d'image après rotation:

 

image source image pivotée brute image pivotée filtrée

 

L'effet est encore plus flagrant dans =FOOT= lorsque l'on fait pivoter l'attaquant à l'aide de la souris et que l'on appuie successivement sur Q pour modifier le degré de qualité d'affichage.

 

 

 

  7) Déformation d'images  

La déformation d'image est en réalité un terme générique qui regroupe l'ensemble des techniques que l'on peut appliquer aux pixels d'une image afin d'en modifier son aspect. En ce sens, la rotation telle qu'elle a été décrite précédemment peut être considérée comme une déformation.

Afin d'illustrer ce principe, je vais tenter d'expliquer comment le ballon est dessiné dans =FOOT=. En effet, même s'il ressemble à une boule roulante (en 3 dimensions donc), ce n'est en réalité qu'un effet d'optique: Il s'agit  simplement d'une image carrée qui a été déformée pour lui donner l'aspect d'une sphère.

image source image "sphérisée"

 

L'image "sphérisée" de cet exemple à été obtenue avec un logiciel de retouche d'image. Néanmoins, cela nous donne un bon aperçu de ce que l'on veut obtenir en programmant l'effet nous même.

Que peut on remarquer en l'observant?

Première chose: Tous les pixels sont contenus à l'intérieur d'un cercle. Ensuite on peut voir que les pixels des coins de l'image initiale, sont tous rassemblés aux 4 points cardinaux de l'image transformée.

contraction des pixels aux quatres points cardinaux de l'image

 

Ainsi ―en ne considérant pour l'instant que les coordonnées horizontales des pixels― il faut parvenir à réduire chaque ligne de pixels de l'image source, à la même longueur que la corde du cercle de l'image de destination. Cette explication peut vous sembler légèrement confuse mais elle vous paraîtra plus claire en regardant le schéma ci-dessous:

 

Le but est de trouver le rapport entre les lignes L12 et l12, L32 et l32, etc...

Cela nous donnera alors les facteurs de réduction à appliquer sur les coordonnées horizontales des pixels. Il faudra ensuite faire de même pour les coordonnées verticales, afin de donnée son aspect sphérique à l'image.

 

Les longueurs L12, L32, etc... sont faciles à deviner puisqu'elles sont identiques et qu'elles correspondent de surcroît à la largeur de l'image originale. La question se pose davantage pour les longueurs  l12, l32, etc... Voyons comment les trouver:

 

R = rayon du cercle = demi-largeur de l'image

h = distance du centre O de l'image à la ligne de pixels traitée l

l = longueur du segment à trouver

 

On remarque sur le schéma la présence d'un triangle rectangle pour lequel nous connaissons la quantité R et h. Il ne nous reste plus qu'à calculer la longueur l  grâce au théorème de Pythagore:

 

Pour tout triangle ABC rectangle en A: BC² = AB² + AC²

soit: "Le carré de l'hypoténuse est égal à la somme des carrés des cotés de l'angle droit"

 

Ce qui nous donne avec notre exemple: R² = (l  / 2)² + h²  soit :

   l   = 2 * √(R² - h²)

 

Vous trouverez l'algorithme complet dans la procédure PreparerBallon() du module Preparation.bas

 

Comme la forme du ballon ne varie jamais, l'ensemble des nouvelles coordonnées (identifiées par un numéro d'index dans l'image) peuvent être stockées une fois pour toute dans un tableaux que l'on appellera par exemple: matrice_deformation[ ].

Ainsi, lorsqu'il faudra afficher l'image du ballon pixel par pixel, au lieu d'écrire image_destination[ index ] = image_source[ index ] ce qui ne nous affichera rien d'autre que l'image originale, il faudra écrire:

image_destination[ index ] = image_source[ matrice_deformation[ index ] ]

 

La matrice de déformation peut être considérée comme un filtre déformant, qui indique où chercher les pixels dans l'image source pour les afficher dans l'image de destination, à l'instar d'une lentille qui dévierait les rayons lumineux d'un sujet observé. De sorte qu'il est inutile de se préoccuper de ce que représente l'image source. Seule sa taille est importante.

Ainsi, pour donner l'impression que le ballon roule sur le sol, la texture originale est décalée dans le sens inverse du déplacement du ballon. Bien que l'image source soit modifiée, la matrice de déformation n'a pas besoin d'être recalculée.

 

Pour finir, voici l'ensemble des opérations nécessaires au dessin du ballon:

+ = =>
texture du ballon dont l'aspect peut varier en permanence   ajout d'un ombrage par transparence pour renforcer l'effet de volume   texture ombrée  
=> = ...
  application de la matrice de déformation   résultat de la déformation   image finale du ballon sur le terrain avec incrustation par masque et ombre portée

 

Vous pourrez trouver l'ensemble des algorithmes qui ont été présentés dans ce chapitre dans les modules Affichage.bas et Preparation.bas du code source.

 

 

 

    ANIMATION SOMMAIRE IMPLEMENTATION SOUS VB 6