Szczegóły Techniczne
Dokumentacja edukacyjna i historyczna dotycząca działania systemu obsługującego tę stronę
Transformacje Wzorów
Counter-Strike 2 i Counter Strike Global Offensive używają pojedynczej tekstury bazowej (jak Case Hardened) do generowania setek unikalnie wyglądających wariantów skinów poprzez system losowych transformacji. Osiąga się to za pomocą deterministycznego mechanizmu opartego na ziarnie — klucz do zrozumienia i odtworzenia wzorów Blue Gem poza grą.
Dlaczego nazywa się to Paint "Seed"?
Aby generować różnorodność tekstur w spójny, ale losowy sposób, Valve wykorzystuje pseudolosowy generator równomierny wbudowany w silnik Source. Ponieważ komputery są maszynami deterministycznymi, wymagają wartości początkowej — ziarna — do produkcji losowości. W CS2 każdemu skinowi przypisywane jest paint seed od 1 do 999. To ziarno inicjalizuje generator liczb losowych, który następnie produkuje określony zestaw wartości używanych do zastosowania transformacji do tekstury.
Ponieważ ten algorytm jest deterministyczny i jednolity na wszystkich platformach, dane paint seed zawsze da tę samą transformację — zapewniając, że skin wygląda identycznie na każdym systemie.
TL;DR: Paint seed inicjalizuje pseudolosowy generator Valve, aby określić, jak tekstura bazowa jest pozycjonowana, obracana i skalowana dla warstw wzoru, zużycia i brudu.
Jak są obliczane transformacje tekstur?
Aby zrozumieć, jak CS2 losuje wygląd skinów, pomocne jest zrozumienie mapowania UV: każdy model broni ma układ UV, który definiuje, jak tekstura 2D owija się wokół siatki 3D. Valve nakłada teksturę na tę mapę UV, a następnie używa paint seed do losowania jej translacji, rotacji i skali.
Wpływa to nie tylko na główny wzór (jak kolorowe plamy Case Hardened), ale także na nakładki zużycia i brudu — tekstury dodające zarysowania, starzenie i brud.
Jeśli styl skina nie używa losowego przemieszczenia (np. prawdopodobnie jednokolorowe skiny jak Asiimov), nie będzie podlegał temu systemowi.
UV Mapping Concept
Hover over the cube faces or UV map to see how 3D surfaces map to 2D texture coordinates
3D Model
UV Map (Unfolded)
Krok po kroku: Paint Seed → Transformacja
1. Określenie skali bazowej
Każda broń i styl malowania ma określoną skalę, zwykle zdefiniowaną w pliku items_game.txt Valve. Skala bazowa jest obliczana z długości broni i skali UV, w zależności od stylu malowania:
if (paint_style == 3 || paint_style == 6) {
scale = weapon_length * 0.027777778;
} else {
scale = uv_scale;
}Na przykład, Karambit ma:
- WeaponLength = 9.813000
- UVScale = 0.438000
- → co daje skalę bazową 0.438
2. Generowanie wartości losowych
Po określeniu skali bazowej, paint seed jest używany do generowania 11 pseudolosowych wartości float, które definiują, jak każda warstwa tekstury jest stosowana:
| Warstwa tekstury | Parametry |
|---|---|
| Wzór | skala, translacjaX, translacjaY, rotacja |
| Zużycie | skala × mnożnik, translacjaX, translacjaY, rotacja |
| Brud | skala × mnożnik, translacjaX, translacjaY, rotacja |
Te wartości float są generowane w stałej kolejności i zakresach:
Wzór
- translateX → 0.0 – 1.0
- translateY → 0.0 – 1.0
- rotate → 0.0 – 360.0
Zużycie
- scaleMult → 1.6 – 1.8
- translateX → 0.0 – 1.0
- translateY → 0.0 – 1.0
- rotate → 0.0 – 360.0
Brud
- scaleMult → 1.6 – 1.8
- translateX → 0.0 – 1.0
- translateY → 0.0 – 1.0
- rotate → 0.0 – 360.0
These values are applied independently to each layer using the following logic:
Transform Order: Scale → Translate → Rotate(Repeated for pattern, wear, and grunge)Przykład: Karambit | Case Hardened — Paint Seed 633
Używając powyższej logiki, paint seed 633 na Karambit daje:
- 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
Warstwy zużycia i brudu otrzymują własne wartości, również pochodzące z ziarna, ale z różnymi mnożnikami i kątami.
Remaining Mystery: Rotation Center
Przez długi czas pozostawało jedno pytanie: gdzie dokładnie znajduje się punkt obrotu? Podczas gdy kąty <180° wydawały się obracać wokół lewego górnego rogu UV, wyższe kąty zachowywały się niespójnie przy próbie symulacji wyniku poza grą.
From Reddit to pattern.wiki
Najwcześniejsza próba inżynierii wstecznej tego systemu pochodziła ze słynnego posta na Reddit od Step7750 w 2016 roku. Ten post przedstawił podstawową ideę działania transformacji opartych na ziarnie — i przez lata służył jako de facto odniesienie dla społeczności.
Ostatecznie strony takie jak Broskins i csfloat.com zbudowały działające systemy podglądu — ale faktyczna logika używana przez Valve pozostawała nieudokumentowana publicznie.
To zmieniło się na początku 2024 roku, kiedy pattern.wiki opublikował pełną analizę procesu transformacji — w tym konkretne wartości, matematykę i przykłady wizualne. To był znaczący krok naprzód.
What We Clarified
While pattern.wiki's implementation clearly works in practice, their description of the transform order and rotation pivot didn't fully align with how Source behaves under the hood. Through testing and debugging, we clarified a few subtle (but important) points:
- All transforms are applied around the origin (0, 0) — not after translation
- The effective matrix order is: T₂ × R × S × T₁
Template Transform Application (Finalized Model)
To sample the correct area of the texture using a paint seed, we apply a sequence of affine transformations — each one modifying the UV coordinates before sampling the base texture.
Step-by-Step Transform Chain
1. Wyśrodkowana translacja
Przesunięcie wzoru przy użyciu przesunięć paint seed, ale wyśrodkowanie transformacji wokół początku UV:
translate(offsetX - 0.5, offsetY - 0.5)2. Skala (wokół początku)
Jednolite skalowanie wzoru przy użyciu skali bazowej — wokół początku (0, 0):
scale(scaleX, scaleY)3. Rotacja (wokół początku)
Obracanie wzoru przeciwnie do ruchu wskazówek zegara od początku:
rotate(rotation)4. Dodatkowe przesunięcie (ostateczna korekta)
Po rotacji i skali, zastosuj ostateczne przesunięcie, które kompensuje rotację opartą na narożniku:
invScale = 0.5 / scaleangle = -rotationDeg * (π / 180)extraX = invScale * cos(angle) - invScale * sin(angle)extraY = (extraX * sin(angle)) + (invScale * cos(angle))translate(extraX, extraY)Ostateczna sekwencja macierzy
W kategoriach afinicznych tworzy to pełną macierz transformacji:
A = T₂ × R × S × T₁Lub prostym językiem:
- Wyśrodkuj przesunięcie UV
- Zastosuj skalę
- Obróć wokół początku
- Skoryguj dodatkowym przesunięciem
Ostateczne przekształcone UV są następnie zawijane przy użyciu modulo:
uv_final = (A × uv_coords) % 1.0Aby lepiej zrozumieć to podejście, możesz pobawić się naszym studiem transformacji tekstur, które stosuje dokładnie te transformacje na żywo w twojej przeglądarce.
