Détails Techniques
Documentation éducative et historique sur le fonctionnement du système qui alimente ce site
Counter-Strike 2 et Counter Strike Global Offensive utilisent une seule texture de base (comme Case Hardened) pour générer des centaines de variantes de skins d'apparence unique grâce à un système de transformations aléatoires. Ceci est réalisé via un mécanisme déterministe basé sur une graine — une clé pour comprendre et reproduire les motifs Blue Gem en dehors du jeu.
Pourquoi l'appelle-t-on Paint "Seed" ?
Pour générer une variété de textures de manière cohérente mais aléatoire, Valve utilise un générateur uniforme pseudo-aléatoire intégré au moteur Source. Comme les ordinateurs sont des machines déterministes, ils nécessitent une valeur initiale — une graine — pour produire du hasard. Dans CS2, chaque skin se voit attribuer un paint seed allant de 1 à 999. Cette graine initialise le générateur de nombres aléatoires, qui produit ensuite un ensemble spécifique de valeurs utilisées pour appliquer des transformations à la texture.
Parce que cet algorithme est déterministe et uniforme sur toutes les plateformes, un paint seed donné produira toujours la même transformation — garantissant que le skin a l'air identique sur chaque système.
TL;DR : Le paint seed initialise le générateur pseudo-aléatoire de Valve pour définir comment la texture de base est positionnée, tournée et mise à l'échelle pour les couches de motif, d'usure et de saleté.
Comment sont calculées les transformations de textures ?
Pour comprendre comment CS2 randomise l'apparence des skins, il est utile de comprendre le mapping UV : chaque modèle d'arme a une disposition UV qui définit comment une texture 2D s'enroule autour du maillage 3D. Valve applique la texture à cette carte UV, puis utilise le paint seed pour randomiser sa translation, rotation et échelle.
Cela affecte non seulement le motif principal (comme les taches de couleur Case Hardened), mais aussi les superpositions d'usure et de saleté — des textures qui ajoutent des rayures, du vieillissement et de la crasse.
Si le style d'un skin n'utilise pas de déplacement aléatoire (par exemple, possiblement des skins de couleur unie comme Asiimov), il ne sera pas affecté par ce système.
Concept de mappage UV
Survolez les faces du cube ou la carte UV pour voir comment les surfaces 3D se mappent sur des coordonnées de texture 2D
3D Model
UV Map (Unfolded)
Étape par étape : Paint Seed → Transformation
1. Déterminer l'échelle de base
Chaque arme et style de peinture a une échelle spécifique, généralement définie dans le fichier items_game.txt de Valve. L'échelle de base est calculée à partir de la longueur de l'arme et de l'échelle UV, selon le style de peinture :
if (paint_style == 3 || paint_style == 6) {
scale = weapon_length * 0.027777778;
} else {
scale = uv_scale;
}Par exemple, le Karambit a :
- WeaponLength = 9.813000
- UVScale = 0.438000
- → résultant en une échelle de base de 0.438
2. Générer des valeurs aléatoires
Une fois l'échelle de base connue, le paint seed est utilisé pour générer 11 valeurs float pseudo-aléatoires, qui définissent comment chaque couche de texture est appliquée :
| Couche de texture | Paramètres |
|---|---|
| Motif | échelle, translationX, translationY, rotation |
| Usure | échelle × multiplicateur, translationX, translationY, rotation |
| Saleté | échelle × multiplicateur, translationX, translationY, rotation |
Ces floats sont générés dans un ordre et des plages fixes :
Motif
- translateX → 0.0 – 1.0
- translateY → 0.0 – 1.0
- rotate → 0.0 – 360.0
Usure
- scaleMult → 1.6 – 1.8
- translateX → 0.0 – 1.0
- translateY → 0.0 – 1.0
- rotate → 0.0 – 360.0
Saleté
- scaleMult → 1.6 – 1.8
- translateX → 0.0 – 1.0
- translateY → 0.0 – 1.0
- rotate → 0.0 – 360.0
Ces valeurs sont appliquées indépendamment à chaque couche selon la logique suivante :
Ordre de transformation : Échelle → Translation → Rotation(Répété pour motif, usure et saleté)Exemple : Karambit | Case Hardened — Paint Seed 633
En utilisant la logique ci-dessus, le paint seed 633 sur un Karambit donne :
- Base Scale: 0.438
- Translate X: 0.471 → shifts the pattern ~47% left
- Translate Y: 0.624 → shifts the pattern ~62% up
- Rotation: 96.7° counterclockwise
Les couches d'usure et de saleté obtiennent leurs propres valeurs, également dérivées de la graine mais avec des multiplicateurs et angles différents.
Mystère restant : Centre de rotation
Pendant longtemps, une question est restée : où exactement se trouve le pivot de rotation ? Alors que les angles <180° semblaient tourner autour du coin supérieur gauche de l'UV, les angles plus élevés se comportaient de manière incohérente lors des tentatives de simulation du résultat en dehors du jeu.
De Reddit à pattern.wiki
La première tentative de rétro-ingénierie de ce système est venue du célèbre post Reddit de Step7750 en 2016. Ce post a exposé l'idée de base du fonctionnement des transformations basées sur la graine — et pendant des années, il a servi de référence de facto pour la communauté.
Finalement, des sites comme Broskins et csfloat.com ont construit des systèmes de prévisualisation fonctionnels — mais la logique réelle utilisée par Valve restait non documentée pour le public.
Cela a changé début 2024, quand pattern.wiki a publié une analyse complète du processus de transformation — incluant des valeurs spécifiques, des mathématiques et des exemples visuels. C'était un bond en avant majeur.
Ce que nous avons clarifié
Bien que l'implémentation de pattern.wiki fonctionne clairement en pratique, leur description de l'ordre de transformation et du pivot de rotation ne correspondait pas entièrement au comportement de Source sous le capot. À travers des tests et du débogage, nous avons clarifié quelques points subtils (mais importants) :
- Toutes les transformations sont appliquées autour de l'origine (0, 0) — pas après la translation
- L'ordre effectif de la matrice est : T₂ × R × S × T₁
Application de transformation de modèle (modèle finalisé)
Pour échantillonner la bonne zone de la texture en utilisant un paint seed, nous appliquons une séquence de transformations affines — chacune modifiant les coordonnées UV avant d'échantillonner la texture de base.
Chaîne de transformation étape par étape
1. Translation centrée
Décaler le motif en utilisant les décalages du paint seed, mais centrer la transformation autour de l'origine UV :
translate(offsetX - 0.5, offsetY - 0.5)2. Échelle (autour de l'origine)
Mettre à l'échelle uniformément le motif en utilisant l'échelle de base — autour de l'origine (0, 0) :
scale(scaleX, scaleY)3. Rotation (autour de l'origine)
Faire tourner le motif dans le sens antihoraire depuis l'origine :
rotate(rotation)4. Décalage supplémentaire (ajustement final)
Après rotation et échelle, appliquer un décalage final qui compense la rotation basée sur le coin :
invScale = 0.5 / scaleangle = -rotationDeg * (π / 180)extraX = invScale * cos(angle) - invScale * sin(angle)extraY = (extraX * sin(angle)) + (invScale * cos(angle))translate(extraX, extraY)Séquence de matrice finale
En termes affines, cela crée une matrice de transformation complète :
A = T₂ × R × S × T₁Ou en langage simple :
- Centrer le décalage UV
- Appliquer l'échelle
- Tourner autour de l'origine
- Corriger avec un décalage supplémentaire
Les UV finaux transformés sont ensuite enveloppés en utilisant modulo :
uv_final = (A × uv_coords) % 1.0Comment l'usure pilotée par le float est générée
Plus la valeur de float est élevée, plus un skin paraît usé. Nous reproduisons cela exactement comme le fait Counter-Strike — en composant trois textures par-dessus le motif Trempé en lumière linéaire, le tout piloté par la valeur de float de l'objet. À float 0 (Neuf de l'usine), vous voyez le motif net ; à float 1 (Endommagé au combat), le métal est atténué, assombri et granuleux.
Motif de base
Le motif Trempé avec sa rampe de couleurs (bleu / or / violet), placé par la même transformation de seed décrite ci-dessus.
Carte d'usure
Une carte en niveaux de gris qui détermine quelles parties de la lame s'usent en premier — les arêtes hautes et les zones exposées avant celles qui sont protégées.
Carte de saleté
Une carte fine de saleté et de taches qui ajoute une dégradation et une crasse de surface réalistes à mesure que le float augmente.
La valeur de float pilote tout l'effet via un seul paramètre. En la faisant glisser de 0 à 1, le motif se désature, s'assombrit et se salit progressivement tout en restant visible — le Trempé vieillit en changeant de couleur et en accumulant de la crasse, il ne se raye pas jusqu'au métal nu.
worn = compose(base, wear, grunge, AO, float) // float 0 = Factory New … 1 = Battle-Scarred
Le même moteur d’usure piloté par le float, en direct — ce Karambit #387 de Neuve à Marquée par les combats.
Le grain ('Maserung') provient de la géométrie de l'arme
Chaque arme possède une carte de cavité / occlusion ambiante précalculée. L'usure et la crasse se concentrent dans les creux et le long des arêtes, ce qui produit le grain marbré caractéristique que l'on voit sur une lame endommagée au combat. Chaque couteau utilise sa propre carte d'AO, donc le motif d'usure est unique à la forme de cette arme.
- Placer le motif Trempé, la carte d'usure et la carte de saleté à l'aide des transformations de seed propres à chaque motif.
- Utiliser la valeur de float pour déterminer l'intensité de l'usure de chaque pixel, pondérée par la carte de cavité / AO de l'arme.
- Désaturer et assombrir les zones usées vers une teinte de crasse, et ajouter de la saleté fine depuis la carte de saleté.
- Composer le tout en lumière linéaire et l'afficher en direct sur le modèle 3D pendant que vous déplacez le curseur.
Il s'agit du calcul exact issu du propre shader de finition de Counter-Strike, récupéré et reproduit — ainsi le rendu usé que vous prévisualisez correspond à l'apparence réelle de l'objet en jeu.
Pour mieux comprendre cette approche, vous pouvez jouer avec notre studio de transformation de textures qui applique exactement ces transformations en direct dans votre propre navigateur.