Tekniska Detaljer
Utbildnings- och historisk dokumentation om hur systemet som driver denna webbplats fungerar
Counter-Strike 2 och Counter Strike Global Offensive använder en enda bastextur (som Case Hardened) för att generera hundratals unikt utseende skin-varianter genom ett system av randomiserade transformationer. Detta uppnås via en deterministisk, frö-baserad mekanism — en nyckel till att förstå och reproducera Blue Gem-mönster utanför spelet.
Varför kallas det ett Paint "Seed"?
För att generera texturvariation på ett konsekvent men randomiserat sätt använder Valve en pseudo-slumpmässig enhetlig generator inbyggd i Source Engine. Eftersom datorer är deterministiska maskiner kräver de ett initialt värde — ett frö — för att producera slumpmässighet. I CS2 tilldelas varje skin ett paint seed från 1 till 999. Detta frö initialiserar slumptalsgeneratorn, som sedan producerar en specifik uppsättning värden som används för att tillämpa transformationer på texturen.
Eftersom denna algoritm är deterministisk och enhetlig över alla plattformar kommer ett givet paint seed alltid att ge samma transformation — vilket säkerställer att skinnet ser identiskt ut på varje system.
TL;DR: Paint seed initialiserar Valves pseudo-slumpmässiga generator för att definiera hur bastexturen positioneras, roteras och skalas för mönster-, slitage- och smutslagren.
Hur beräknas texturtransformationer?
För att förstå hur CS2 randomiserar skin-utseenden hjälper det att förstå UV-mappning: varje vapenmodell har en UV-layout som definierar hur en 2D-textur wrappas runt 3D-meshen. Valve tillämpar texturen på denna UV-karta och använder sedan paint seed för att randomisera dess translation, rotation och skala.
Detta påverkar inte bara huvudmönstret (som Case Hardened färgfläckar), utan också slitage- och smutsöverläggen — texturer som lägger till repor, åldrande och smuts.
Om en skins stil inte använder slumpmässig förskjutning (t.ex. möjligen enfärgade skins som Asiimov), kommer den inte att påverkas av detta system.
UV-mappningskoncept
Håll muspekaren över kubens ytor eller UV-kartan för att se hur 3D-ytor mappas till 2D-texturkoordinater
3D Model
UV Map (Unfolded)
Steg-för-steg: Paint Seed → Transformation
1. Bestäm basskala
Varje vapen och målningsstil har en specifik skala, vanligtvis definierad i Valves items_game.txt. Basskalan beräknas från vapenlängden och UV-skalan, beroende på målningsstilen:
if (paint_style == 3 || paint_style == 6) {
scale = weapon_length * 0.027777778;
} else {
scale = uv_scale;
}Till exempel har Karambit:
- WeaponLength = 9.813000
- UVScale = 0.438000
- → vilket resulterar i en basskala på 0.438
2. Generera slumpmässiga värden
När basskalan är känd används paint seed för att generera 11 pseudo-slumpmässiga float-värden, som definierar hur varje texturlager tillämpas:
| Texturlager | Parametrar |
|---|---|
| Mönster | skala, translateX, translateY, rotation |
| Slitage | skala × multiplikator, translateX, translateY, rotation |
| Smuts | skala × multiplikator, translateX, translateY, rotation |
Dessa floats genereras i fast ordning och intervall:
Mönster
- translateX → 0.0 – 1.0
- translateY → 0.0 – 1.0
- rotate → 0.0 – 360.0
Slitage
- scaleMult → 1.6 – 1.8
- translateX → 0.0 – 1.0
- translateY → 0.0 – 1.0
- rotate → 0.0 – 360.0
Smuts
- scaleMult → 1.6 – 1.8
- translateX → 0.0 – 1.0
- translateY → 0.0 – 1.0
- rotate → 0.0 – 360.0
Dessa värden tillämpas oberoende på varje lager med följande logik:
Transformationsordning: Skala → Translate → Rotation(Upprepas för mönster, slitage och smuts)Exempel: Karambit | Case Hardened — Paint Seed 633
Med ovanstående logik ger paint seed 633 på en Karambit:
- 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
Slitage- och smutslagren får sina egna värden, också härledda från fröet men med olika multiplikatorer och vinklar.
Återstående mysterium: Rotationscentrum
Under lång tid kvarstod en fråga: var exakt är rotationspunkten? Medan <180° vinklar verkade rotera runt UV:ns övre vänstra hörn, betedde sig högre vinklar inkonsekvent när man försökte simulera resultatet utanför spelet.
Från Reddit till pattern.wiki
Det tidigaste försöket att bakåtkonstruera detta system kom från det berömda Reddit-inlägget av Step7750 tillbaka 2016. Det inlägget lade ut grundidén om hur frö-baserade transformationer fungerar — och i åratal fungerade det som de facto-referens för communityn.
Till slut byggde sajter som Broskins och csfloat.com fungerande förhandsgranskningssystem — men den faktiska logiken som Valve använde förblev odokumenterad för allmänheten.
Det ändrades i början av 2024, när pattern.wiki publicerade en fullständig nedbrytning av transformationsprocessen — inklusive specifika värden, matematik och visuella exempel. Det var ett stort steg framåt.
Vad vi klargjorde
Medan pattern.wikis implementation tydligt fungerar i praktiken stämde inte deras beskrivning av transformationsordningen och rotationspunkten helt överens med hur Source beter sig under huven. Genom testning och debugging klargjorde vi några subtila (men viktiga) punkter:
- Alla transformationer tillämpas runt origo (0, 0) — inte efter translation
- Den effektiva matrisordningen är: T₂ × R × S × T₁
Mall Transform Application (Slutgiltig modell)
För att sampla rätt område av texturen med ett paint seed tillämpar vi en sekvens av affina transformationer — var och en modifierar UV-koordinaterna innan bastexturen samplas.
Steg-för-steg transformationskedja
1. Centrerad translation
Förskjut mönstret med paint seedets offsets, men centrera transformationen runt UV-origo:
translate(offsetX - 0.5, offsetY - 0.5)2. Skala (runt origo)
Enhetligt skala mönstret med basskalan — runt origo (0, 0):
scale(scaleX, scaleY)3. Rotation (runt origo)
Rotera mönstret moturs från origo:
rotate(rotation)4. Extra offset (slutlig justering)
Efter rotation och skala, tillämpa en slutlig offset som kompenserar för den hörnbaserade rotationen:
invScale = 0.5 / scaleangle = -rotationDeg * (π / 180)extraX = invScale * cos(angle) - invScale * sin(angle)extraY = (extraX * sin(angle)) + (invScale * cos(angle))translate(extraX, extraY)Slutlig matrissekvens
I affina termer skapar detta en fullständig transformationsmatris:
A = T₂ × R × S × T₁Eller på ren svenska:
- Centrera UV-offset
- Tillämpa skala
- Rotera runt origo
- Korrigera med extra offset
De slutliga transformerade UV:erna wrappas sedan med modulo:
uv_final = (A × uv_coords) % 1.0Så genereras float-styrt slitage
Högre float-värden får en skin att se mer sliten ut. Vi återskapar detta på exakt samma sätt som Counter-Strike gör – genom att kompositera tre texturer ovanpå Case Hardened-mönstret i linjärt ljus, styrt av föremålets float-värde. Vid float 0 (Factory New) ser du det rena mönstret; vid float 1 (Battle-Scarred) är metallen dämpad, mörkare och kornig.
Basmönster
Det färgrampade Case Hardened-mönstret (blått / guld / lila), placerat av samma seed-transform som beskrivs ovan.
Slitagekarta
En gråskalekarta som avgör vilka delar av bladet som slits först – höga kanter och exponerade ytor före skyddade.
Grunge-karta
En finkornig smuts- och prickkarta som ger realistisk ytuppbrytning och smuts när floaten stiger.
Float-värdet styr hela effekten genom en enda parameter. Att dra det från 0 till 1 avmättar, mörknar och smutsar gradvis ner mönstret samtidigt som det förblir synligt – Case Hardened åldras genom att skifta färg och samla smuts, det skrapas inte ner till bar metall.
worn = compose(base, wear, grunge, AO, float) // float 0 = Factory New … 1 = Battle-Scarred
Samma float-styrda slitage-motor, live — sveper denna Karambit #387 från Fabriksny till Stridsärrad.
Kornigheten ('Maserung') kommer från vapnets geometri
Varje vapen har en inbakad kavitets- / ambient occlusion-karta. Slitage och smuts samlas i springorna och längs kanterna, vilket skapar den karakteristiska spräckliga kornighet du ser på ett battle-scarred blad. Varje kniv använder sin egen AO-karta, så slitagemönstret är unikt för det vapnets form.
- Placera Case Hardened-mönstret, slitagekartan och grunge-kartan med de mönsterspecifika seed-transformerna.
- Använd float-värdet för att avgöra hur kraftigt varje pixel slits, viktat efter vapnets kavitets- / AO-karta.
- Avmätta och mörkna de slitna områdena mot en smutston, och lägg till fin smuts från grunge-kartan.
- Kompositera allt i linjärt ljus och visa det live på 3D-modellen medan du drar i reglaget.
Detta är exakt samma matematik från Counter-Strikes egen finish-shader, återskapad och reproducerad – så det slitna utseendet du förhandsgranskar matchar hur föremålet faktiskt ser ut i spelet.
För att bättre förstå detta tillvägagångssätt kan du leka med vår texturtransformationsstudio som tillämpar exakt dessa transformationer live i din egen webbläsare.