技术洞察
关于本站系统工作原理的教育和历史文档
图案变换
《反恐精英2》和《反恐精英:全球攻势》使用单一基础纹理(如硬化皮肤)通过随机变换系统生成数百种独特外观的皮肤变体。这是通过确定性的种子机制实现的——这是理解和在游戏外重现蓝宝石图案的关键。
为什么叫涂装"种子"?
为了以一致但随机的方式生成纹理多样性,Valve使用了内置于Source引擎的伪随机均匀生成器。由于计算机是确定性机器,它们需要一个初始值——种子——来产生随机性。在CS2中,每个皮肤都被分配了一个1到999之间的涂装种子。这个种子初始化随机数生成器,然后产生一组特定的值,用于对纹理应用变换。
因为这个算法是确定性的,并且在所有平台上都是统一的,给定的涂装种子总是会产生相同的变换——确保皮肤在每个系统上看起来都一样。
简而言之:涂装种子初始化Valve的伪随机生成器,以定义基础纹理如何为图案、磨损和污垢层进行定位、旋转和缩放。
纹理变换是如何计算的?
要理解CS2如何随机化皮肤外观,有助于理解UV映射:每个武器模型都有一个UV布局 ,定义了2D纹理如何包裹到3D网格上。Valve将纹理应用到这个UV贴图上,然后使用涂装种子来随机化其平移、旋转和缩放。
这不仅影响主要图案(如硬化皮肤的颜色斑块),还影响磨损和污垢叠加层——添加划痕、老化和污垢的纹理。
如果皮肤的样式不使用随机位移(例如,可能像Asiimov这样的纯色皮肤),它就不会受到这个系统的影响。
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)
分步说明:涂装种子 → 变换
1. 确定基础缩放
每种武器和涂装样式都有特定的缩放比例,通常在Valve的items_game.txt中定义。基础缩放是根据武器长度和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
These values are applied independently to each layer using the following logic:
Transform Order: Scale → Translate → Rotate(Repeated for pattern, wear, and grunge)示例:爪子刀 | 硬化皮肤 — 涂装种子 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
磨损和污垢层获得它们自己的值,也是从种子派生的,但使用不同的倍数和角度。
Remaining Mystery: Rotation Center
很长一段时间以来,有一个问题仍然存在:旋转轴心到底在哪里?虽然<180°的角度似乎围绕UV的左上角旋转,但在尝试在游戏外模拟结果时,更高的角度表现不一致。
From Reddit to pattern.wiki
逆向工程这个系统的最早尝试来自Step7750在2016年发布的著名Reddit帖子。那篇帖子阐述了基于种子的变换如何工作的基本思想——多年来,它一直是社区的事实参考。
最终,像Broskins和csfloat.com这样的网站建立了可工作的预览系统——但Valve使用的实际逻辑仍然没有公开记录。
这在2024年初发生了变化,当时pattern.wiki发布了变换过程的完整分解——包括具体值、数学和视觉示例。这是一个重大飞跃。
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. 中心平移
使用涂装种子的偏移量移动图案,但将变换围绕UV原点居中:
translate(offsetX - 0.5, offsetY - 0.5)2. 缩放(围绕原点)
使用基础缩放均匀缩放图案——围绕原点(0, 0):
scale(scaleX, scaleY)3. 旋转(围绕原点)
从原点逆时针旋转图案:
rotate(rotation)4. 额外偏移(最终调整)
在旋转和缩放之后,应用最终偏移来补偿基于角落的旋转:
invScale = 0.5 / scaleangle = -rotationDeg * (π / 180)extraX = invScale * cos(angle) - invScale * sin(angle)extraY = (extraX * sin(angle)) + (invScale * cos(angle))translate(extraX, extraY)最终矩阵序列
用仿射术语来说,这创建了一个完整的变换矩阵:
A = T₂ × R × S × T₁或者用通俗的话说:
- 居中UV偏移
- 应用缩放
- 围绕原点旋转
- 用额外偏移校正
然后使用模运算包裹最终变换的UV:
uv_final = (A × uv_coords) % 1.0为了更好地理解这种方法,您可以使用我们的纹理变换工作室,在您自己的浏览器中实时应用这些变换。
