Documentation → Étape 3

Étape 3 — Le modèle de colorisation

Comment une couleur hexadécimale devient un vêtement ombré. Vingt couleurs testées, une fausse piste, et une bande de 8 pixels cachée dans un coin.

La question

Un même layer doit pouvoir prendre n'importe quelle teinte tout en conservant son ombrage. Deux opérateurs classiques se présentent :

  • L'addition — écartée d'emblée : sur une base claire elle sature immédiatement (255 + n = 255) et détruit le modelé.
  • La multiplication (sortie = base × couleur / 255) — elle préserve exactement les rapports de luminance, et c'est l'opérateur qu'on attend.

Il a donc fallu la tester. Elle ne correspond pas.

1. La multiplication ne rend pas compte des mesures

Si le moteur multipliait, alors un rendu à $ffffff donnerait le gabarit brut, et tout autre rendu s'en déduirait. Test sur top/basic :

PrédictionÉcart moyenÉcart max
rendu($808080) = rendu($ffffff) × 128/25511,51123
rendu($ff0000) = rendu($ffffff) × (255,0,0)/2553,90247

Des écarts de cette ampleur ne sont pas des arrondis. Le modèle est faux — mais l'échec est instructif : les valeurs mesurées sont trop claires par rapport à la prédiction, ce qui suggère que le gabarit n'est pas « du blanc ombré ».

2. L'ombrage tient en niveaux discrets

En listant la palette exacte des pixels non transparents plutôt qu'en comparant des images entières, la structure saute aux yeux. Un t-shirt rendu à $808080 ne contient que cinq valeurs de gris couvrant une surface réelle :

ValeurPixelsRapport à l'entrée (128)
145182×1,133
12832×1,000
118141×0,922
11277×0,875
10528×0,820
Point d'appui

Le niveau à ×1,000 reproduit exactement la couleur demandée, sur toutes les couleurs testées sans exception. C'est l'ancrage qui permet de calibrer tout le reste.

Carte des slots du t-shirt basic
Le même t-shirt, chaque pixel coloré selon son niveau d'ombrage — du plus clair au plus sombre.

Le même layer, cinq teintes, ombrage préservé à l'identique :

$ffffff
$808080
$ff0000
$3366cc

3. L'anomalie du bleu, et sa résolution

Un balayage sur 20 couleurs a fait apparaître une incohérence apparemment fatale au modèle. À luminosité et saturation identiques, le rouge et le bleu ne donnaient pas les mêmes rapports :

Entréeslot 0slot 2slot 3slot 4
$ff00001,0000,9290,8780,824
$00ff001,0000,9290,8780,824
$0000ff1,0000,8980,8240,749

Rouge et vert s'accordent parfaitement ; le bleu diverge. Une dépendance à la teinte aurait invalidé l'hypothèse d'un simple facteur scalaire, et orienté vers un espace perceptuel (HSL, luminance pondérée…).

La vraie cause

Ce n'est pas la teinte : c'est l'écrêtage. Le slot le plus clair vaut ×1,348. Pour une entrée à 255, il vaudrait 344 — impossible sur 8 bits. Le moteur écrête, et l'écrêtage comprime le reste de la rampe vers le haut. Toutes les couleurs dont un canal atteint 255 subissent la même déformation ; le bleu paraissait différent seulement parce que je comparais des rampes comprimées à des rampes intactes.

En restant sous 190 pour chaque canal, les rapports redeviennent identiques quelle que soit la teinte. D'où la règle de capture : ne jamais mesurer à $ffffff. Le blanc est le pire choix possible — il écrase deux slots l'un sur l'autre.

4. La bande de calibration cachée

En classant les pixels par zone, 8 pixels se comportaient bizarrement : présents dans tous les layers, réagissant à la couleur secondaire même quand le cosmétique déclare uses_secondary_color: false, et occupant un slot chacun.

Leurs coordonnées :

xyValeur à 128Rapport
630105×0,820
631112×0,875
632118×0,922
633128×1,000
634145×1,133
635154×1,203
636163×1,273
637172×1,344
Interprétation

Une colonne unique, x = 63, y = 0 à 7 : le coin supérieur droit, précisément la région inutilisée du gabarit de skin Minecraft 64×64. Les 8 pixels y sont rangés du plus sombre au plus clair et énumèrent exactement les 8 niveaux d'ombrage du moteur. C'est une bande de calibration — une légende de la rampe, embarquée dans chaque skin produit.

C'est aussi la confirmation la plus directe de l'hypothèse « pixels marqueurs » de départ : le système encode bien de l'information de colorisation dans des pixels dédiés. Simplement, ces marqueurs ne servent pas à délimiter les zones — ils documentent la rampe.

5. Les 8 ratios, mesurés

Ajustement par moindres carrés sur 10 niveaux de gris en entrée (40 à 184), pour éviter aussi bien l'écrêtage haut que la perte de précision en bas :

SlotRatio ajustéÉquivalent sur base 255Écart max
+41,347853441
+31,277323261
+21,206933081
+11,136882901
01,000002550
−10,929382370
−20,874122231
−30,823522100

Un écart maximal de 1 sur 255, sur 10 points de mesure : le modèle multiplicatif à facteur scalaire est le bon, une fois l'écrêtage pris en compte.

Exprimée sur une base 255, la rampe présente une structure nette :

210   223   237   255   290   308   326   344
   +13   +14   +18   +35   +18   +18   +18

Côté clair, des pas rigoureusement constants de +18. Côté sombre, des pas resserrés (+14, +13) — un ombrage volontairement plus doux dans les ombres que dans les hautes lumières. Et un saut double (+35) juste au-dessus du niveau de base, qui laisse penser qu'un neuvième niveau existe dans l'outillage de l'auteur sans être utilisé dans les textures publiées.

6. La formule complète

Pour chaque pixel non transparent :

si le pixel est fixe        → recopier tel quel
sinon                       → couleur = (zone == primaire) ? C_prim : C_sec
                              sortie  = clamp(round(couleur × ratio[slot]), 0, 255)
l'alpha est toujours conservé sans modification

Le gabarit archivé pour chaque layer se réduit donc à trois informations par pixel : alpha, zone (primaire / secondaire / fixe) et index de slot parmi 8. Soit un octet par pixel là où le PNG rendu en consomme quatre — et surtout, une représentation qui redonne le layer générique que l'API n'expose pas.

Réserve honnête

L'écrêtage côté serveur n'est pas encore entièrement caractérisé. Quand un canal sature, la rampe se comprime plutôt que de simplement plafonner, ce qui laisse penser à une renormalisation. Tant que cette compression n'est pas modélisée, la reproduction locale restera exacte uniquement pour les couleurs dont tous les canaux sont inférieurs à environ 190. C'est le point à trancher lors de la validation de l'étape 6.

Bilan

Établi par la mesure

  • 8 niveaux d'ombrage discrets, universels
  • Facteur scalaire appliqué aux 3 canaux RVB
  • Le slot 0 restitue la couleur exacte
  • Bande de calibration en x=63, y=0..7
  • Alpha jamais modifié

Encore ouvert

  • Comportement exact de l'écrêtage au-delà de 190
  • Le neuvième niveau suggéré par le saut de +35
  • Existence de layers à plus de 2 zones (aucun trouvé à ce jour)