2 points par GN⁺ 2024-03-01 | 1 commentaires | Partager sur WhatsApp
  • Une LUT (table de correspondance) consiste à consulter des valeurs précalculées comme dans un tableau ; elle est utilisée dans de nombreux contextes, de la colorisation d’images thermiques en noir et blanc aux variations d’objets de jeu et à l’étalonnage des couleurs vidéo
  • L’exemple WebGL montre un flux où les frames vidéo sont envoyées au GPU sous forme de texture 2D, puis où le tinting, une LUT 1D et une LUT 3D sont appliqués dans le Fragment Shader qui détermine la couleur de chaque pixel
  • Une LUT 1D utilise la valeur de luminosité [0.0 - 1.0] comme coordonnée dans un tableau de couleurs pour convertir une entrée noir et blanc en RGB ; le filtrage bilinéaire du GPU interpole naturellement les valeurs intermédiaires, même avec une petite LUT
  • Comme dans le cas de Left 4 Dead de Valve, le tinting et les LUT servent à créer des variations de voitures, de peau de zombies et de couleurs de vêtements avec une mémoire texture limitée
  • Les LUT ajoutent un coût de lecture de texture supplémentaire, mais constituent un outil pratique pour stocker à l’avance des calculs coûteux comme la correction gamma, ou pour intégrer des workflows externes d’étalonnage des couleurs dans du rendu graphique temps réel

Le rôle des LUT en programmation graphique

  • Une table de correspondance (LUT) consiste à précalculer des valeurs, à les stocker dans des lignes ou un tableau, puis à les consulter au besoin
  • En programmation graphique, cette structure simple permet plusieurs effets visuels
    • Convertir une vidéo noir et blanc en couleur
    • Appliquer une correction et une stylisation des couleurs
    • Créer efficacement des variations d’objets dans les jeux vidéo
  • L’exemple utilise WebGL, mais la même technique s’applique aussi à DirectX, OpenGL, Vulkan, Unity et à la visualisation de données scientifiques
  • Les vidéos de démonstration sont des captures réalisées avec un Panasonic GH6 et une caméra thermique TESTO 890 ; le traitement reste dans l’appareil et s’effectue localement sur le GPU

Pipeline WebGL et tinting

  • La sortie de la caméra thermique est une vidéo en noir et blanc, et chaque frame est transmise au GPU via WebGL sous forme de texture 2D
  • Le Fragment Shader est le code qui détermine la couleur finale de chaque fragment en sortie
    • Dans l’exemple, texture2D(video, tex).rgb lit la couleur de la vidéo à la position courante
    • Dans WebGL 1.0, la sortie finale est écrite en RGBA dans gl_FragColor
  • Le tinting consiste à multiplier la couleur de la texture par une couleur donnée pour lui appliquer une teinte
    • Dans l’exemple, videoColor * vec3(1.0, 0.5, 0.0) multiplie par de l’orange
    • Le blanc devient orange, tandis que le noir reste noir puisqu’il est multiplié par 0
  • Dans ce contexte, le coût de la multiplication du tinting peut être difficile à mesurer
    • Le coût de récupération d’une texture depuis la mémoire est bien supérieur à celui d’une multiplication
    • Comme plusieurs threads s’exécutent en parallèle, la multiplication d’un autre pixel peut être effectuée pendant qu’un pixel attend la lecture de sa texture
    • Cela explique l’écart produit par le tinting, mais ne signifie pas que toute l’optimisation des performances est terminée

L’utilisation du tinting par Valve

  • Left 4 Dead, de Valve Software, utilise le tinting pour créer des variations de voitures
  • Prévoir une texture distincte pour chaque type et chaque couleur de voiture peut dépasser le budget de mémoire texture
  • Valve utilise une texture de masque supplémentaire pour teinter différemment, instance par instance, les zones peintes des voitures
    • Une même texture de couleur est partagée entre 3 modèles de voitures
    • Avec le coût de deux textures, il est possible de créer 4 modèles de voitures et un nombre pratiquement illimité de variations de couleurs
  • Dans le Source Engine, on peut teinter intrinsèquement presque tout, pas seulement les voitures

Transformer des valeurs noir et blanc en couleurs avec une LUT 1D

  • Une LUT 1D est un tableau de nombres ; pour une image RGB, c’est un tableau unidimensionnel de couleurs
  • La valeur de luminosité d’une vidéo noir et blanc sert de coordonnée sur l’axe X de la LUT pour récupérer une couleur
    • Les valeurs de luminosité de la vidéo sont dans l’intervalle [0.0 - 1.0]
    • Le noir 0.0 est mappé sur la couleur de gauche de la LUT, et le blanc 1.0 sur la couleur de droite
    • Les valeurs intermédiaires sont mappées sur la couleur située à la position correspondante
  • WebGL 1.0 ne dispose pas de textures 1D, donc on utilise une texture 2D de 1 px de haut
    • Le code d’exemple est de la forme texture2D(lut, vec2(videoColor, 0.5))
    • L’entrée est une valeur 1D et la sortie est du RGB, soit une structure « 1D vector in, 3D vector out »
  • Le filtrage bilinéaire du GPU interpole automatiquement les valeurs intermédiaires lors de la lecture de texture
    • Une vidéo noir et blanc 8 bits compte 256 niveaux de luminosité
    • Même si une LUT 1D ne fait que 32 pixels de large, les accès entre pixels sont interpolés linéairement
    • Avec seulement 8 pixels d’information, on peut produire un résultat proche d’un dégradé coloré de 256 pixels de large

Choix des colormaps et uniformité perceptuelle

  • L’exemple inclut une liste de colormaps prises en charge par matplotlib et exportées en LUT 1D
  • Si les données n’ont pas de structure particulière, il est préférable d’utiliser une colormap perceptuellement uniforme ou de définir les couleurs sur cette base
    • La famille viridis en est un exemple représentatif
  • Une colormap perceptuellement uniforme conserve les zones froides sombres et les zones chaudes claires, même en sortie noir et blanc
  • Les colormaps comme jet, qui changent principalement la couleur (hue) en ignorant la luminosité perçue, ne garantissent pas cette propriété
  • Le fait que les personnes présentant des anomalies de la vision des couleurs puissent interpréter correctement les données est un critère important dans le choix d’une colormap

Performances des LUT 1D et approximation polynomiale

  • Le coût d’une petite LUT 1D peut généralement être faible, mais il y a davantage de points à considérer que pour le tinting
  • La principale préoccupation de performance est la lecture de texture dépendante
    • Le résultat de la première lecture de texture détermine les coordonnées de la seconde lecture de texture
    • En programmation graphique, ce motif peut supprimer une partie des chemins optimisables
  • Les GPU disposent d’un cache de textures, et une petite LUT peut y tenir, ce qui peut rendre le coût de lecture de la LUT très faible
  • Pour juger finement les performances, il faut des outils d’analyse propres à chaque plateforme
    • Nvidia NSight fournit une analyse des performances par étape de shader, mais ne prend pas en charge OpenGL
  • Implémenter une colormap sous forme d’approximation polynomiale permet d’éviter de manipuler une texture de LUT
    • L’approximation viridis de Matt Zucker sur ShaderToy est écrite sous forme de Horner’s method
    • Elle calcule la couleur avec des opérations multiply-add de la forme c0+t*(c1+t*(c2+t*(c3+t*(c4+t*(c5+t*c6)))))
    • Sans analyse propre à chaque plateforme, il est difficile de savoir si c’est réellement plus rapide qu’une LUT

Variations de zombies dans Left 4 Dead 2

  • Left 4 Dead 2 utilise des LUT pour les variations de couleur de peau et de vêtements lorsque le simple tinting ne suffit pas
  • Le tinting simple peut ne pas fournir une diversité suffisante de luminance dans les couleurs
  • Pour la peau et les vêtements, quelques LUT sont sélectionnées aléatoirement afin de créer diverses variations de couleurs
    • Les artistes choisissent les color ramps appropriées à inclure dans le jeu final
    • Une seule texture peut produire à la fois des variantes de costumes clairs et de costumes sombres
  • La présentation complète associée est disponible dans Shading a Bigger Better Sequel de la GDC
  • « Exclusive Masking » consiste à placer deux plages de texture dans un même canal
    • Texture 1 utilise la plage 0-128
    • Texture 2 utilise la plage 128-256
    • Le coût est une baisse de la précision des couleurs

Stocker à l’avance des calculs dans une LUT 1D

  • En programmation graphique, une LUT 1D peut aussi servir de cache pour des calculs coûteux
  • La correction gamma en est un exemple représentatif
    • Sans approximation, elle nécessite la fonction pow()
    • Sur les anciens GPU en particulier, pow() peut être une instruction coûteuse
    • Utiliser la courbe sRGB standard par morceaux ajoute aussi des chemins de branchement
  • Précalculer la conversion gamma dans une LUT 1D permet d’éviter le calcul pour chaque pixel
    • Dans ce cas, la structure est « 1D vector in, 1D vector out »
    • Une LUT 1D peut sortir jusqu’à 4 canaux de couleur
  • Redshift est un exemple logiciel qui teinte le moniteur en orange la nuit pour réduire la fatigue oculaire
    • Il modifie la Gamma Ramp des canaux Red, Green et Blue du moniteur
    • La conversion Kelvin Warmth → RGB et le calcul gamma supplémentaire sont précalculés dans 3 LUT 1D
    • L’implémentation correspondante se trouve dans le code source de Redshift
  • Avec cette approche, le remapping se fait dans le moniteur plutôt que sur la carte graphique, donc il n’y a pas d’impact sur les performances ; toutefois, la prise en charge des interfaces matérielles est aujourd’hui souvent instable ou cassée
    • La pile graphique du Raspberry Pi a déjà perdu cette prise en charge dans une mise à jour récente
    • Microsoft déconseille l’utilisation de SetDeviceGammaRamp

LUT 3D : remapper tout l’espace RGB

  • Une LUT 3D représente tout l’espace RGB sous forme d’un cube et remappe toutes les couleurs possibles
  • Elle transforme un vecteur RGB d’entrée à trois dimensions en un vecteur RGB de sortie à trois dimensions, soit une structure « 3D vector in, 3D vector out »
  • Appliquer une LUT 1D à chacun des canaux Red, Green et Blue permet de modifier l’équilibre des couleurs
  • Une LUT 3D est nécessaire pour les transformations qui dépendent des combinaisons RGB d’entrée
    • Changement de saturation
    • Changement de couleur (hue)
    • Ajustement de couleurs spécifiques
    • Séparation des couleurs
  • Les LUT 3D sont généralement sous forme de cube ; dans les jeux, elles sont stockées sous forme de strip ou de square, et dans les outils de montage vidéo sous forme de fichiers .cube « Iridas/Adobe »

Implémenter une LUT 3D dans WebGL 1.0

  • La vidéo de l’exemple est une séquence avec le profil couleur Panasonic V-Log, filmée avec un Panasonic GH6
  • V-Log est un profil logarithmique qui préserve une plus grande plage dynamique et définit clairement le gamut et le gamma
  • WebGL 1.0 ne prend pas en charge les textures 3D ; la lecture d’une texture 3D est donc implémentée avec deux lectures de texture 2D et une interpolation
  • L’ancien code d’exemple contenait une erreur dans le calcul de l’axe Z du cube, qui décalait les couleurs vers le bleu ; elle a été corrigée en 2019
  • Pour rester compatible avec WebGL 1.0, OpenGLES 2 et OpenGL 2.1 sans OES_texture_3D, il faut utiliser la version la plus récente du code d’échantillonnage de texture 3D

Intégrer un workflow d’étalonnage des couleurs avec une LUT 3D

  • Les corrections appliquées à une LUT 3D s’appliquent telles quelles à la vidéo ou à la scène graphique qui passe par cette LUT
  • Dans l’exemple, la vidéo V-Log et la LUT sont importées et traitées dans DaVinci Resolve
    • La V-Log to V-709 3D-LUT de Panasonic est appliquée
    • Le contraste et le white point sont corrigés pour que le blanc devienne full-bright
    • La LUT corrigée est ensuite réexportée
  • La vidéo réelle reste inchangée ; l’étalonnage des couleurs est appliqué en temps réel dans WebGL via la LUT
  • La LUT téléversée doit conserver la même taille
    • La contrainte de l’exemple est 1024 px × 32 px
    • Il faut conserver une forme de cube de 32³ px

L’usage des LUT 3D dans l’industrie du jeu

  • Le fait de styliser les couleurs d’un jeu dans un outil externe via une LUT 3D est un workflow standard bien connu dans l’industrie du jeu vidéo
  • La procédure habituelle est la suivante
    • Prendre une capture d’écran de la scène à corriger
    • Ouvrir la capture d’écran et une LUT 3D initialisée ensemble dans un outil comme Photoshop
    • Appliquer le même étalonnage des couleurs à la capture d’écran et à la LUT
    • Découper la partie LUT 3D et l’exporter
  • Left 4 Dead utilise aussi cette méthode
  • Les outils d’étalonnage des couleurs de Photoshop peuvent être utilisés librement, mais les filtres qui modifient les relations entre plusieurs pixels ne doivent pas l’être
    • Les filtres de type convolution comme blur, sharpen et emboss peuvent flouter les couleurs remappées et produire des résultats inattendus

Cuire une correction colorimétrique complexe dans une seule LUT

  • Les outils d’étalonnage des couleurs comme DaVinci Resolve peuvent créer des transformations de couleur et des color grades complexes, puis les exporter en LUT 3D
  • L’exemple applique une LUT Kodak 2383 visant un rendu de tirage film
    • Conversion d’une vidéo en gamma V-Log vers Cineon Film Log
    • Conversion de l’espace colorimétrique vers Rec.709
    • Application d’une émulation de film et reconversion du gamma vers Rec.709
    • Ajustement final du white point
  • Sans implémenter directement les transformations détaillées complexes, on peut les cuire dans une seule LUT et les importer dans une application graphique
  • La vidéo d’exemple est dans un format 8 bits très compressé pour la compatibilité de l’article, mais ce travail se fait généralement sur du footage 10 bits
  • Les tailles de LUT utilisées sont plus petites qu’on pourrait le croire
    • La V-Log to V-709 3D-LUT officielle de Panasonic fait 33³ px
    • Les LUT de monitoring intégrées aux caméras Panasonic utilisent 17³ px
    • Même les caméras cinéma Panasonic dans la tranche de prix à cinq chiffres en dollars utilisent 17³ px

Exemples de LUT hors du graphisme

  • Les LUT sont également utilisées en dehors de la programmation graphique
  • Le portage de Tomb Raider sur Game Boy Advance a aussi exploité des LUT
  • Le port du moteur open source OpenLara traite de nombreux éléments en logiciel, car la Game Boy Advance ne dispose pas de fonctions 3D
  • L’un de ces éléments est une LUT qui implémente la division entière
  • Cette LUT est placée au début de l’espace d’adressage ROM afin d’éviter une instruction load

1 commentaires

 
GN⁺ 2024-03-01
Commentaires Hacker News
  • Sans table de lookup, j’ai l’impression qu’il aurait été impossible d’implémenter cet effet : https://twitter.com/zeta0134/status/1756988843851383181
    L’idée clé, c’est d’utiliser deux tables de lookup. La grande table stocke les informations d’éclairage circulaire dans un rayon configurable autour du joueur, avec une table entière pour chaque rayon. La deuxième table contient un ordre pseudo-aléatoire des lignes de fond. À chaque appel de la routine d’éclairage de la torche, il n’y a le temps de mettre à jour que 1/20 de l’écran, mais en randomisant légèrement l’ordre, les bords paraissent plus doux et le balayage raster qui serait autrement visible est masqué. Cet ordre sert de grab bag pour éviter que certaines lignes soient privées de mise à jour, donc on utilise une table parce que le calcul en temps réel serait trop lent

    • Ça a l’air sacrément bien, et les graphismes sont bien meilleurs que dans n’importe quel jeu NES dont je me souvienne, au point que ça ressemble plutôt à du niveau SNES. Ça semble tourner dans un émulateur, et je me demande si cet émulateur bride les performances au niveau de la NES
    • Ça a l’air d’un jeu vraiment génial. Je me demande s’il a été inspiré par Crypt of the Necrodancer. C’est l’un de mes jeux préférés. Je me suis déjà demandé jusqu’où la NES ou d’autres vieilles consoles auraient pu aller si ce genre de mécaniques avait été inventé à l’époque, et ça semble montrer que c’était possible
    • Le rafraîchissement à 1/20 fonctionne bien parce qu’il crée l’illusion que les tuiles se révèlent naturellement quand la lampe bouge. C’est cool de voir qu’on peut s’en sortir avec ce genre d’effet
    • Il est toujours fascinant de voir à quel point les jeux sur une même console deviennent de plus en plus beaux à mesure que les gens apprennent à mieux coder dessus. Par exemple Mario 1 et 3, ou les deux Zelda sur N64
    • Le mot-clé de recherche pour le passage « garder un ordre aléatoire de type grab bag pour éviter que des lignes soient privées de mise à jour » est probablement quasirandom
  • Le premier effet de table de lookup qui m’a vraiment impressionné, c’est son usage pour créer un tunnel texturé
    On dispose d’une table de lookup qui donne, pour chaque pixel à l’écran, l’angle et la distance par rapport au centre de l’écran, puis on s’en sert pour choisir quel texel placer à chaque position de pixel. On a l’impression de se déplacer dans un tunnel en géométrie 3D, mais le coût est extrêmement faible, au point que c’est possible même sur pico : https://www.lexaloffle.com/bbs/?pid=63818
    Au départ, je pensais que Stardust utilisait cet effet, mais en vérifiant à l’instant, je vois qu’en réalité il ne faisait que jouer une animation en 6 frames répétée en arrière-plan : https://codetapper.com/amiga/sprite-tricks/stardust/

    • Si on considère la palette de couleurs comme une table de lookup, alors le palette cycling était lui aussi très courant, et ça semble assez lié
    • Une utilisation sophistiquée du même principe, c’est le parallax occlusion mapping (POM). Ça peut même gérer l’auto-ombrage
      https://web.engr.oregonstate.edu/~mjb/cs557/Projects/Papers/Parallax_Occlusion_Mapping.pdf
    • Si vous voulez voir comment ça marche, il y a un petit bouton Code ▽ sous la démo
    • Regarder les symétries dans certains cas peut aider à trouver une solution élégante
  • Il existe une vidéo sur la manière dont Wind Waker utilise plusieurs LUT pour créer son apparence distinctive. BoTW et ToTK emploient la même technique
    https://www.youtube.com/watch?v=mnxs6CR6Zrk

  • Je suis l’auteur de l’article. Je peux répondre aux questions

    • Les exemples d’usage des LUT sont excellents, et c’est agréable de voir un article qui explique ce genre de sujet proprement. J’ai aussi apprécié l’usage de WebGL et le fait de pouvoir téléverser ses propres données
      En revanche, la façon de présenter le color grading via LUT m’a un peu surpris. Dans l’article, on a l’impression que c’est une solution de niche ingénieuse utilisée par L4D2, alors qu’en réalité c’est un standard de l’industrie depuis longtemps, et je l’ai vu dans tous les jeux sur lesquels j’ai travaillé. Des AAA comme NFS (2015) jusqu’à de gros jeux indés comme Lost in Random
    • Très bon article, et j’aimerais compléter un peu la dernière section. La Game Boy Advance prenait en charge la division entière dans une certaine mesure, mais ce n’était pas accéléré matériellement [1]. Le modèle à base de LUT était probablement motivé par des raisons de performance, et ce jugement semble tout à fait raisonnable
      [1] https://www.copetti.org/writings/consoles/game-boy-advance/#operating-system
    • À cause d’une faute de frappe, j’ai été perdu un bon moment en croyant qu’il existait une autre bibliothèque de plotting
      « Here is every single colormap that matlibplot supports,... »
    • Il y a bien trop de GIF animés, c’est agaçant et distrayant. On peut aussi se passer des mèmes boomer pas drôles façon années 2010. Avec tous ces GIF criards qui surgissent sans arrêt, c’est pénible de lire l’article
  • Même dans le domaine un peu ennuyeux des processus métier, c’est étonnamment utile
    Il y a souvent des cas où une base de code remplie de conditions peut être simplifiée en une table de lookup propre. On n’y pense pas toujours parce que même quelque chose qui paraît simple peut donner une table de lookup de 50 000 lignes. Ça peut sembler énorme pour l’utilisateur final, mais l’ordinateur, lui, s’en moque assez largement. En plus, les tables de lookup sont répétitives, donc généralement faciles à maintenir, et même quand ce n’est pas le cas, elles gardent de la valeur. L’utilisateur final peut même les configurer sans modifier le code. De manière générale, c’est un concept particulièrement utile pour les nombreux scénarios qui reviennent sans cesse quand on code de la logique métier

    • Et c’est comme ça qu’on finit par obtenir un DSL
    • Une table de lookup en double buffering, carrément !
  • Les jeux rétro utilisaient énormément de tables. À l’époque, la mémoire était très rapide et les processeurs lents, donc il était logique de déplacer autant de calculs que possible dans des tables. Plus on était malin, plus on pouvait créer des jeux spectaculaires

    • Ce n’était pas toujours le cas, et cela dépend sans doute de la plateforme que l’on met derrière rétro. Kaze Emanuar sur YouTube fait beaucoup de développement N64, et à peu près la moitié de ses discussions sur l’optimisation portent sur l’impact du bus mémoire sur toutes sortes d’optimisations. Dans Mario 64, la table de recherche existante pour la fonction sinus a été remplacée par une approximation, parce qu’elle était plus rapide et suffisamment précise. En fait, c’étaient deux approximations pour deux objectifs différents.
      J’aime bien cette chaîne. Il a remanié tout le code de Mario 64 pour que son mod tourne plus vite et atteigne un 60 FPS stable [0]
      [0] https://www.youtube.com/watch?v=t_rzYnXEQlE
    • Je me souviens de l’époque où l’on limitait la précision des rotations à des unités de 360/256 degrés pour les stocker dans un seul octet, puis utiliser cette valeur comme index dans une table de recherche trigonométrique :)
  • Si vous travaillez beaucoup avec les LUT, je développe une app Mac qui gère la science des couleurs avancée et les opérations de transformation : https://videovillage.com/lattice

  • Je m’étais un peu intéressé à la scène homebrew NES, et j’y avais vu dans un jeu appelé Bobl des effets physiques étonnants, bien au-delà de ce que le système pouvait calculer. En réalité, c’était une table de recherche, et cela m’a fait comprendre qu’un outil simple pouvait donner l’impression d’un processus très complexe
    https://morphcatgames.itch.io/bobl

    • Je ne nie pas que l’effet soit impressionnant, mais si tu parlais des ondulations de surface ou des ondes capillaires, je me demande à quel point il aurait vraiment été difficile pour la NES de calculer en temps réel un automate cellulaire unidimensionnel
  • A Podcast Of Unnecessary Detail a publié un épisode sur le portage de Doom sur SNES et la manière dont les LUT étaient utilisées pour la trigonométrie. Le contexte était que la SNES n’avait pas de processeur graphique
    https://festivalofthespokennerd.com/podcast/series-3-episode-4/
    https://github.com/RandalLinden/DOOM-FX

    • Dire que « la SNES n’avait pas de processeur graphique » est complètement faux. La NES aussi en avait un. En plus du CPU de la SNES, un clone du Motorola 65c816, le PPU (picture processing unit) était une puce personnalisée qui fournissait plusieurs agencements pour afficher des arrière-plans à base de tuiles et des sprites pendant que le téléviseur CRT dessinait physiquement chaque scanline
      Il permettait aussi la rotation et la mise à l’échelle des arrière-plans. C’est le Mode 7 auquel Mario Kart fait penser, et il gérait également la transparence matérielle visible dans de nombreux jeux. De plus, pour Doom et Starfox, la cartouche contenait une puce Super FX chargée des calculs 3D. Donc, pour ces titres précis, on manipulait en pratique deux processeurs graphiques
      Il existe une excellente série, douloureusement détaillée, sur le matériel de la SNES : Retro Game Mechanics Explained
      https://www.youtube.com/watch?v=57ibhDU2SAI&list=PLHQ0utQyFw5KCcj1ljIhExH_lvGwfn6GV&index=1
  • Les usages des LUT qui me viennent à l’esprit dans ce que j’ai écrit incluent la diffusion atmosphérique, la coloration de sprites, les lunettes de visée à vision nocturne, les viseurs FLIR, les effets de « flux vidéo » en noir et blanc, les effets de glitch, l’ombrage de heightmaps, les coefficients alpha par point pour les panaches d’échappement de vaisseaux spatiaux, les heatmaps de temps de survol de la souris des visiteurs d’un site web, les effets cristallins et, enfin, la colorisation en post-traitement dans un espace colorimétrique primitif
    Une LUT est une visualisation d’un tableau de valeurs déjà connues, et c’est étonnamment utile

    • La « coloration de sprites » ? Les jeunes d’aujourd’hui ont donc complètement oublié les sprites à palette ! D’où crois-tu que vient l’expression « palette swap » ? ;)
      http://www.effectgames.com/demos/canvascycle/
      À l’origine, la Xbox gérait mal ça et Photoshop ne le prenait pas en charge, donc la technique a perdu en popularité. Mais le matériel moderne est tout à fait capable de gérer les palette swaps réalisés dans AESprite.