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/255 | 11,51 | 123 |
rendu($ff0000) = rendu($ffffff) × (255,0,0)/255 | 3,90 | 247 |
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 :
| Valeur | Pixels | Rapport à l'entrée (128) |
|---|---|---|
| 145 | 182 | ×1,133 |
| 128 | 32 | ×1,000 |
| 118 | 141 | ×0,922 |
| 112 | 77 | ×0,875 |
| 105 | 28 | ×0,820 |
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.

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

$ffffff
$808080
$ff0000
$3366cc3. 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ée | slot 0 | slot 2 | slot 3 | slot 4 |
|---|---|---|---|---|
$ff0000 | 1,000 | 0,929 | 0,878 | 0,824 |
$00ff00 | 1,000 | 0,929 | 0,878 | 0,824 |
$0000ff | 1,000 | 0,898 | 0,824 | 0,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…).
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 :
| x | y | Valeur à 128 | Rapport |
|---|---|---|---|
| 63 | 0 | 105 | ×0,820 |
| 63 | 1 | 112 | ×0,875 |
| 63 | 2 | 118 | ×0,922 |
| 63 | 3 | 128 | ×1,000 |
| 63 | 4 | 145 | ×1,133 |
| 63 | 5 | 154 | ×1,203 |
| 63 | 6 | 163 | ×1,273 |
| 63 | 7 | 172 | ×1,344 |
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 :
| Slot | Ratio ajusté | Équivalent sur base 255 | Écart max |
|---|---|---|---|
| +4 | 1,34785 | 344 | 1 |
| +3 | 1,27732 | 326 | 1 |
| +2 | 1,20693 | 308 | 1 |
| +1 | 1,13688 | 290 | 1 |
| 0 | 1,00000 | 255 | 0 |
| −1 | 0,92938 | 237 | 0 |
| −2 | 0,87412 | 223 | 1 |
| −3 | 0,82352 | 210 | 0 |
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.
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)