IsItABlueGem LogoIsItABlueGem
התחברות

פרטים טכניים

תיעוד חינוכי והיסטורי על אופן הפעולה של המערכת המפעילה את האתר הזה

Counter-Strike 2 ו-Counter Strike Global Offensive משתמשים בטקסטורת בסיס אחת (כמו Case Hardened) ליצירת מאות וריאנטים ייחודיים של סקינים באמצעות מערכת של טרנספורמציות אקראיות. זה מושג באמצעות מנגנון דטרמיניסטי מבוסס זרע — מפתח להבנה ושחזור דפוסי Blue Gem מחוץ למשחק.

למה זה נקרא Paint "Seed"?

כדי ליצור מגוון טקסטורות בצורה עקבית אך אקראית, Valve משתמשת במחולל פסאודו-אקראי אחיד המובנה במנוע Source. מכיוון שמחשבים הם מכונות דטרמיניסטיות, הם דורשים ערך התחלתי — זרע — כדי לייצר אקראיות. ב-CS2, כל סקין מקבל paint seed בין 1 ל-999. זרע זה מאתחל את מחולל המספרים האקראיים, שמייצר סט ספציפי של ערכים המשמשים להחלת טרנספורמציות על הטקסטורה.

מכיוון שהאלגוריתם הזה דטרמיניסטי ואחיד בכל הפלטפורמות, paint seed נתון תמיד יניב את אותה טרנספורמציה — מה שמבטיח שהסקין נראה זהה בכל מערכת.

TL;DR: ה-paint seed מאתחל את המחולל הפסאודו-אקראי של Valve כדי להגדיר איך טקסטורת הבסיס ממוקמת, מסובבת ומשונה בקנה מידה עבור שכבות הדפוס, הבלאי והלכלוך.

איך מחושבות טרנספורמציות טקסטורות?

כדי להבין איך CS2 יוצר אקראיות במראה הסקינים, עוזר להבין מיפוי UV: לכל דגם נשק יש פריסת UV שמגדירה איך טקסטורה דו-ממדית נכרכת סביב הרשת התלת-ממדית. Valve מחילה את הטקסטורה על מפת UV זו ואז משתמשת ב-paint seed כדי לבצע אקראיות בתרגום, סיבוב וקנה מידה שלה.

זה משפיע לא רק על הדפוס הראשי (כמו כתמי הצבע של Case Hardened), אלא גם על שכבות הבלאי והלכלוך — טקסטורות שמוסיפות שריטות, הזדקנות ולכלוך.

אם הסגנון של סקין לא משתמש בהזזה אקראית (למשל, אולי סקינים בצבע אחיד כמו Asiimov), הוא לא יושפע מהמערכת הזו.

מושג מיפוי UV

רחפו מעל פאות הקובייה או מפת ה־UV כדי לראות כיצד משטחי תלת-ממד ממופים לקואורדינטות מרקם דו-ממדיות

3D Model

1
2
3
4
5
6

UV Map (Unfolded)

5
4
1
3
6
2

שלב אחר שלב: Paint Seed → טרנספורמציה

1. קביעת קנה המידה הבסיסי

לכל נשק וסגנון צביעה יש קנה מידה ספציפי, בדרך כלל מוגדר בקובץ items_game.txt של Valve. קנה המידה הבסיסי מחושב מאורך הנשק וקנה מידת UV, בהתאם לסגנון הצביעה:

if (paint_style == 3 || paint_style == 6) {
    scale = weapon_length * 0.027777778;
} else {
    scale = uv_scale;
}

לדוגמה, ל-Karambit יש:

  • WeaponLength = 9.813000
  • UVScale = 0.438000
  • → מה שמביא לקנה מידה בסיסי של 0.438

2. יצירת ערכים אקראיים

ברגע שקנה המידה הבסיסי ידוע, ה-paint seed משמש ליצירת 11 ערכי float פסאודו-אקראיים, שמגדירים איך כל שכבת טקסטורה מוחלת:

שכבת טקסטורהפרמטרים
דפוסקנה מידה, הזזהX, הזזהY, סיבוב
בלאיקנה מידה × מכפיל, הזזהX, הזזהY, סיבוב
לכלוךקנה מידה × מכפיל, הזזהX, הזזהY, סיבוב

ערכי ה-float האלה נוצרים בסדר וטווחים קבועים:

דפוס
  • translateX → 0.0 – 1.0
  • translateY → 0.0 – 1.0
  • rotate → 0.0 – 360.0
בלאי
  • scaleMult → 1.6 – 1.8
  • translateX → 0.0 – 1.0
  • translateY → 0.0 – 1.0
  • rotate → 0.0 – 360.0
לכלוך
  • scaleMult → 1.6 – 1.8
  • translateX → 0.0 – 1.0
  • translateY → 0.0 – 1.0
  • rotate → 0.0 – 360.0

ערכים אלה מיושמים באופן עצמאי על כל שכבה לפי הלוגיקה הבאה:

סדר טרנספורמציה: קנה מידה → הזזה → סיבוב
(חוזר עבור דפוס, בלאי ולכלוך)

דוגמה: Karambit | Case Hardened — Paint Seed 633

בשימוש בלוגיקה למעלה, paint seed 633 על 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

שכבות הבלאי והלכלוך מקבלות ערכים משלהן, גם הם נגזרים מהזרע אבל עם מכפילים וזוויות שונות.

המסתורין שנותר: מרכז הסיבוב

במשך זמן רב, נותרה שאלה אחת: איפה בדיוק נקודת הסיבוב? בעוד שזוויות <180° נראו מסתובבות סביב הפינה השמאלית העליונה של ה-UV, זוויות גבוהות יותר התנהגו בצורה לא עקבית כשניסו לדמות את התוצאה מחוץ למשחק.

מ-Reddit ל-pattern.wiki

הניסיון המוקדם ביותר להנדס לאחור את המערכת הזו הגיע מהפוסט המפורסם ב-Reddit של Step7750 ב-2016. הפוסט הזה הציג את הרעיון הבסיסי של איך טרנספורמציות מבוססות זרע עובדות — ובמשך שנים, הוא שימש כהפניה בפועל עבור הקהילה.

בסופו של דבר, אתרים כמו Broskins ו-csfloat.com בנו מערכות תצוגה מקדימה עובדות — אבל הלוגיקה האמיתית שבה השתמשה Valve נשארה לא מתועדת לציבור.

זה השתנה בתחילת 2024, כאשר pattern.wiki פרסם ניתוח מלא של תהליך הטרנספורמציה — כולל ערכים ספציפיים, מתמטיקה ודוגמאות חזותיות. זו הייתה קפיצה משמעותית קדימה.

מה הבהרנו

בעוד שהיישום של pattern.wiki עובד בבירור בפועל, התיאור שלהם של סדר הטרנספורמציה ונקודת הסיבוב לא התאים לחלוטין להתנהגות של Source מתחת למכסה המנוע. באמצעות בדיקות וניפוי באגים, הבהרנו כמה נקודות עדינות (אך חשובות):

  • כל הטרנספורמציות מוחלות סביב הראשית (0, 0) — לא אחרי הזזה
  • סדר המטריצה האפקטיבי הוא: T₂ × R × S × T₁

יישום טרנספורמציית תבנית (מודל סופי)

כדי לדגום את האזור הנכון של הטקסטורה באמצעות paint seed, אנו מחילים רצף של טרנספורמציות אפיניות — כל אחת משנה את קואורדינטות ה-UV לפני דגימת טקסטורת הבסיס.

שרשרת טרנספורמציות שלב אחר שלב

1. הזזה ממורכזת

הזז את הדפוס באמצעות ההיסטים של ה-paint seed, אבל מרכז את הטרנספורמציה סביב ראשית ה-UV:

translate(offsetX - 0.5, offsetY - 0.5)
2. קנה מידה (סביב הראשית)

שנה קנה מידה אחיד של הדפוס באמצעות קנה המידה הבסיסי — סביב הראשית (0, 0):

scale(scaleX, scaleY)
3. סיבוב (סביב הראשית)

סובב את הדפוס נגד כיוון השעון מהראשית:

rotate(rotation)
4. היסט נוסף (התאמה סופית)

אחרי סיבוב וקנה מידה, החל היסט סופי שמפצה על הסיבוב המבוסס על פינה:

invScale = 0.5 / scale
angle = -rotationDeg * (π / 180)
extraX = invScale * cos(angle) - invScale * sin(angle)
extraY = (extraX * sin(angle)) + (invScale * cos(angle))
translate(extraX, extraY)

רצף מטריצה סופי

במונחים אפיניים, זה יוצר מטריצת טרנספורמציה מלאה:

A = T₂ × R × S × T₁

או בעברית פשוטה:

  1. מרכז היסט UV
  2. החל קנה מידה
  3. סובב סביב הראשית
  4. תקן עם היסט נוסף

ה-UV המותמרים הסופיים נעטפים לאחר מכן באמצעות מודולו:

uv_final = (A × uv_coords) % 1.0

כיצד נוצר הבלאי המונע על ידי ה-Float

ערכי Float גבוהים יותר גורמים לסקין להיראות בלוי יותר. אנו משחזרים זאת בדיוק כמו ש-Counter-Strike עושה — על ידי הרכבת שלוש טקסטורות מעל דפוס ה-Case Hardened באור לינארי, מונעת על ידי ערך ה-Float של הפריט. ב-Float 0 (חדש מהמפעל) רואים את הדפוס הנקי; ב-Float 1 (צלקות קרב) המתכת עמומה, מוכהה וגרגירית.

דפוס בסיס

דפוס ה-Case Hardened הצבוע ברצף צבעים (כחול / זהב / סגול), ממוקם על ידי אותה טרנספורמציית זרע שתוארה למעלה.

מפת בלאי

מפת גווני אפור שמחליטה אילו חלקים של הלהב נשחקים ראשונים — קצוות גבוהים ואזורים חשופים לפני המוגנים.

מפת לכלוך

מפת לכלוך וכתמים דקה שמוסיפה שבירת משטח ולכלוך ריאליסטיים ככל שה-Float עולה.

ערך ה-Float מניע את כל האפקט דרך פרמטר יחיד. גרירתו מ-0 ל-1 מבטלת רוויה בהדרגה, מכהה ומלכלכת את הדפוס תוך שמירה על נראותו — Case Hardened מזדקן על ידי שינוי צבע וצבירת לכלוך, הוא אינו נשרט עד למתכת חשופה.

worn = compose(base, wear, grunge, AO, float) // float 0 = Factory New … 1 = Battle-Scarred
קרמביט Case Hardened דפוס 387, ה־blue gem ברמה 1
חדש מהמפעל · float 0.000

אותו מנוע בלאי מבוסס float, בשידור חי — סוחף את הקרמביט ‎#387 מחדש מהמפעל ועד צלקות קרב.

הגרגיריות ('Maserung') נובעת מהגאומטריה של הנשק

לכל נשק יש מפת חללים / Ambient Occlusion אפויה. הבלאי והלכלוך מתרכזים בחריצים ולאורך הקצוות, וזה מה שיוצר את הגרגיריות המנומרת האופיינית שרואים על להב עם צלקות קרב. כל סכין משתמש במפת ה-AO שלו, כך שדפוס הבלאי ייחודי לצורה של אותו נשק.

  1. מקם את דפוס ה-Case Hardened, את מפת הבלאי ואת מפת הלכלוך באמצעות טרנספורמציות הזרע הספציפיות לכל דפוס.
  2. השתמש בערך ה-Float כדי להחליט עד כמה כל פיקסל נשחק, משוקלל לפי מפת החללים / AO של הנשק.
  3. בטל רוויה והכהה את האזורים הבלויים לעבר גוון לכלוך, והוסף לכלוך דק ממפת הלכלוך.
  4. הרכב את הכול באור לינארי והצג זאת בזמן אמת על המודל התלת-ממדי בזמן שאתה גורר את המחוון.

זוהי המתמטיקה המדויקת מתוך שיידר הגימור של Counter-Strike עצמו, ששוחזרה ושוכפלה — כך שהמראה הבלוי שאתה רואה בתצוגה המקדימה תואם לאופן שבו הפריט באמת מופיע במשחק.

כדי להבין טוב יותר את הגישה הזו, אתה יכול לשחק עם סטודיו טרנספורמציית הטקסטורות שלנו שמחיל בדיוק את הטרנספורמציות האלה בזמן אמת בדפדפן שלך.