IsItABlueGem LogoIsItABlueGem
Войти

Технические Подробности

Образовательная и историческая документация о том, как работает система, поддерживающая этот сайт

Counter-Strike 2 и Counter Strike Global Offensive используют одну базовую текстуру (например, Case Hardened) для генерации сотен уникально выглядящих вариантов скинов через систему случайных трансформаций. Это достигается с помощью детерминированного механизма на основе сида — ключа к пониманию и воспроизведению паттернов синих самоцветов вне игры.

Почему это называется "Сид" раскраски?

Чтобы генерировать разнообразие текстур последовательным, но случайным образом, Valve использует псевдослучайный равномерный генератор, встроенный в движок Source. Поскольку компьютеры — детерминированные машины, им требуется начальное значение — сид — для создания случайности. В CS2 каждому скину назначается сид раскраски от 1 до 999. Этот сид инициализирует генератор случайных чисел, который затем производит определенный набор значений, используемых для применения трансформаций к текстуре.

Поскольку этот алгоритм детерминирован и единообразен на всех платформах, данный сид раскраски всегда будет давать одну и ту же трансформацию — гарантируя, что скин выглядит одинаково на каждой системе.

Кратко: Сид раскраски инициализирует псевдослучайный генератор Valve для определения того, как базовая текстура позиционируется, поворачивается и масштабируется для слоев паттерна, износа и грязи.

Как вычисляются трансформации текстур?

Чтобы понять, как CS2 рандомизирует внешний вид скинов, полезно понимать UV-маппинг: каждая модель оружия имеет UV-развертку, которая определяет, как 2D-текстура оборачивается вокруг 3D-меша. Valve применяет текстуру к этой UV-карте, а затем использует сид раскраски для рандомизации ее перемещения, поворота и масштаба.

Это влияет не только на основной паттерн (например, цветовые пятна Case Hardened), но и на наложения износа и грязи — текстуры, которые добавляют царапины, старение и грязь.

Если стиль скина не использует случайное смещение (например, возможно, одноцветные скины вроде Asiimov), он не будет затронут этой системой.

Концепция UV-развёртки

Наведите на грани куба или UV-карту, чтобы увидеть, как 3D-поверхности отображаются на 2D-координаты текстуры

3D Model

1
2
3
4
5
6

UV Map (Unfolded)

5
4
1
3
6
2

Пошагово: Сид раскраски → Трансформация

1. Определение базового масштаба

Каждое оружие и стиль раскраски имеют определенный масштаб, обычно определенный в файле items_game.txt от Valve. Базовый масштаб вычисляется из длины оружия и UV-масштаба, в зависимости от стиля раскраски:

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

Например, у Керамбита:

  • WeaponLength = 9.813000
  • UVScale = 0.438000
  • → результирующий базовый масштаб 0.438

2. Генерация случайных значений

Когда базовый масштаб известен, сид раскраски используется для генерации 11 псевдослучайных значений с плавающей точкой, которые определяют, как применяется каждый слой текстуры:

Слой текстурыПараметры
Паттернмасштаб, смещениеX, смещениеY, поворот
Износмасштаб × множитель, смещениеX, смещениеY, поворот
Грязьмасштаб × множитель, смещениеX, смещениеY, поворот

Эти числа генерируются в фиксированном порядке и диапазонах:

Паттерн
  • 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

Эти значения применяются независимо к каждому слою по следующей логике:

Порядок трансформации: Масштаб → Смещение → Поворот
(Повторяется для паттерна, износа и грязи)

Пример: Керамбит | Case Hardened — Сид раскраски 633

Используя вышеописанную логику, сид раскраски 633 на Керамбите дает:

  • 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₁

Применение трансформации шаблона (Финализированная модель)

Чтобы взять правильную область текстуры, используя сид раскраски, мы применяем последовательность аффинных преобразований — каждое изменяет UV-координаты перед взятием базовой текстуры.

Пошаговая цепь трансформации

1. Центрированное смещение

Сдвиг паттерна с использованием смещений сида раскраски, но центрирование трансформации вокруг 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 (Factory New) вы видите чистый паттерн; при float 1 (Battle-Scarred) металл становится тусклым, потемневшим и зернистым.

Базовый паттерн

Паттерн Case Hardened с цветовой растяжкой (синий / золотой / фиолетовый), размещённый тем же seed-преобразованием, что описано выше.

Карта износа

Чёрно-белая карта, которая определяет, какие части лезвия изнашиваются первыми — выступающие грани и открытые участки раньше защищённых.

Карта загрязнения

Карта мелкой грязи и крапин, которая добавляет реалистичные разрывы поверхности и потёртости по мере роста float.

Значение float управляет всем эффектом через один параметр. Перетаскивая его от 0 к 1, вы постепенно обесцвечиваете, затемняете и загрязняете паттерн, сохраняя его видимым — Case Hardened стареет за счёт смещения цвета и появления грязи, а не стирается до голого металла.

worn = compose(base, wear, grunge, AO, float) // float 0 = Factory New … 1 = Battle-Scarred
Karambit Case Hardened паттерн 387, blue gem первого тира
Прямо с завода · флоат 0.000

Тот же движок износа на основе флоата, вживую — этот Karambit #387 от Прямо с завода до Закалённого в боях.

Зернистость ('Maserung') исходит из геометрии оружия

У каждого оружия есть запечённая карта впадин / ambient occlusion. Износ и грязь концентрируются в углублениях и вдоль граней, что и создаёт характерную пёструю зернистость, которую вы видите на потрёпанном лезвии. Каждый нож использует собственную AO-карту, поэтому паттерн износа уникален для формы конкретного оружия.

  1. Разместить паттерн Case Hardened, карту износа и карту загрязнения с помощью seed-преобразований, индивидуальных для каждого паттерна.
  2. Использовать значение float, чтобы определить, насколько сильно изнашивается каждый пиксель, с учётом веса по карте впадин / AO оружия.
  3. Обесцветить и затемнить изношенные участки в сторону грязного тона и добавить мелкую грязь из карты загрязнения.
  4. Скомпоновать всё в линейном свете и отобразить вживую на 3D-модели по мере перетаскивания ползунка.

Это точная математика из собственного шейдера финишей Counter-Strike, восстановленная и воспроизведённая — поэтому изношенный вид, который вы видите в предпросмотре, совпадает с тем, как предмет на самом деле выглядит в игре.

Чтобы лучше понять этот подход, вы можете поиграть с нашей студией трансформации текстур, которая применяет именно эти трансформации вживую в вашем браузере.