
打造 Sanctum 的环境艺术
Sanctum 是一张《反恐精英2》(CS2)的竞技地图,场景设定在葱郁丛林中一座正在修缮的古庙周围。它将真实的历史建筑与新颖的创意构思相结合,兼顾出色的视觉表现与玩法体验。
在关卡设计师、环境美术与数字美术团队历时数月的协作后,这张地图于 2026 年 1 月 22 日正式加入 CS2 官方竞技地图池。本文将以环境美术的开发过程、我自制的工具,以及关键的艺术决策为主线展开。
先简单介绍一下我自己,我是 Twist:一名环境美术师(Environment Artist),使用过 Unreal、Unity 等游戏引擎,也用 Vray 和 Octane 做过静帧与过场动画渲染。我还开发了 Hammer5Tools——一套用于加速 CS2 地图制作流程的工具集,它是在 Sanctum 项目过程中为了解决实际痛点而逐渐成长起来的。
我在项目中的贡献
项目最初由 Ruban 作为负责人兼关卡设计师发起。我后来加入,接手视觉表现并主导制作,在调整布局以保证画面流畅上做了大量工作。我的主要职责包括:确立并构建完整的美术愿景、制作关键资产与材质、产出大部分最终内容、把控贴图/光照与整体一致性、协助打磨布局与玩法,以及为工作流开发 Hammer5Tools。
创意方向与概念
我加入项目时,主题已经确定,但几何结构尚未最终敲定,因此我们仍可以对场景设定做调整。CS2 创意工坊里已经存在不少类似的丛林神庙地图,这让我们这张图显得缺乏新意,同时又有风险、成本也高。更换主题意味着要重建整套布局,所以我们保留了原有的创意与选址。

丛林神庙类地图在《CS2》中相当常见,但我想要的并非照搬他人,而是做出独特的诠释。主要灵感来自柬埔寨的吴哥窟——世界上最著名的寺庙之一。我们无法原样复制它,毕竟这是一处神圣的佛教遗址,需要保持尊重以避免争议。

因此我对地图做了风格化处理。它在不追求照片级写实的前提下,抓住了吴哥窟的气韵与氛围。我改造并重新诠释了建筑语言,避开宗教造像与纹样,调整塔楼造型以降低相似度,整体以风格化表达优先于对史实的精确复刻。

关卡设计
虽然最初的关卡布局与基本形态是既定的,但我在与视觉相关的关卡设计上做了大量改动。一切大约始于 2024 年 8 月。

第一版更像是一份草稿。看起来所有造型似乎都差不多,其实并非如此。我和几位好友(syncope、g3om 等)花大量时间对这张地图进行了试玩打磨。

主要原因在于建筑与玩法的打磨。核心目标是要塑造一座真实可信、仿佛能在现实中存在的寺庙,同时确保不破坏玩法、几何结构与其他元素。建筑上的最大挑战,是为房屋与建筑生成对称的几何结构。
除了玩法与建筑,我还努力在地图上营造出漂亮的视觉点子。例如,这棵树就是受吴哥窟中真实生长的树木启发而来。树模型由 rainless 塑形、由我完成收尾。

在关卡设计过程中,我萌生了一个想法:做一个能辅助试玩、分析玩家在地图上的移动与行为的程序。

说实话,它在开发时并没有我想的那么好用。不过 CS2 关卡设计社区里有一些人觉得这个工具有帮助,能回馈 CS2 地图制作社区,我也很高兴!
到 2024 年底,我终于在玩法与视觉两个维度上都完成了让自己满意的几何结构。它距离最终成品还很远,但在后续开发中已经不需要再做大的玩法改动了。

在这张截图中,你或许能看到覆盖塔楼的、独特的藤蔓程序化生成效果,它出自 Rainless 之手。你还能看到一些道具与材质已经就位——这是因为我想先确立整体的观感,再深入关卡的细节刻画。在这个阶段,视觉美术方向也一并确立了。
视觉改动与美术方向在过程中不断演进,但至少玩法是确定下来了。我根据最初的布局几何调整视觉,做出了一个已经可玩、但视觉仍是草稿状态的地图。
视觉
视觉工作分多个阶段进行,试玩与想法紧密交织,很难说清哪一处改动最大。大致流程是:在地图上摆放粗略元素 → 试玩 → 清理 → 再试玩 → 修问题,有时干脆推倒某一处重来。
一旦对创意与构图满意,我就进入内容制作阶段。我首先完成地图的一个角落,作为全图的质量参考——这更偏向技术而非艺术层面。这通常是弄清场景需要哪些内容的最佳方式。自定义内容对我而言很有难度,但地图 80% 都用到了它。剩下 20% 来自第三方资产,比如 Dekogon 或《反恐精英》标准素材库。后期在时间压力下被迫引入第三方资产,但我把它们整合进来、修复技术问题,并重新命名部分资产以呈现独特外观。

当大部分自定义内容就位、且不再有灰盒(graybox)或开发期材质后,我们进入打磨阶段。这是最短却最有效的阶段:产品已经完整,只差最后的润色。

这个阶段的特别之处在于壁画与粒子。我非常高兴能与才华横溢的数字艺术家 Gurin 合作。他绘制了契合地图主题的美妙画作。这些画都讲述了与《反恐精英》相关的小故事,比如安放炸弹、双人 Wingman 模式、竞技对战,以及火山。
数字美术
所有壁画均出自我们的数字艺术家 GURIN 之手。

目标是抓住吴哥窟的神韵,而非直接复制。最初,我们想重现吴哥窟寺庙中的壁画,并将其改编到 CS2 的语境中。

不过,我们在开发早期就放弃了这种做法,因为我不想冒与真实地点过于相似而引发问题的风险。

即便一些很小的元素,也在制作后期被修改或做了审查处理。

这是海量的工作,我由衷感谢 GURIN——他在整个项目开发周期中持续投入到这些壁画的创作中。

所有壁画完成后,我用 Substance 3D Designer 对它们做处理,并补充了一些细节。

除了壁画,他还绘制了一些经典地图 Overpass 中的涂鸦作为彩蛋,但针对本图主题做了一些改编。

粒子
粒子与火山:在《CS2》地图制作社区里,此前没人做过这种效果,而且它做起来相当复杂。爆炸是最容易的部分,随手就能做,没什么难度。但熔岩流……我试过多种方法,比如着色器动画、网格动画、形变(morphing),但没有一种能兼顾像样的观感与性能。粒子成了唯一出路,而我想说,它并不像看上去那么简单。
起初,我以为有办法计算粒子与火山网格的碰撞。这在一处小细节上确实完美奏效:但它无法在 3D 天空盒上工作。
给没接触过 Source 2 引擎的朋友做个说明:
3D 天空盒是另一张始终运行在你主地图之上的地图,它可以包含山体等几何结构。这让我们可以营造出不错的雾效,并分摊光照贴图的分辨率。当然,背后也有技术原因:地图尺寸上限。在 Source 2 里你无法做出大型开放世界地图,必须塞进限制范围内。正因如此,在 Source 2 中我们会把 3D 天空盒与普通天空盒结合使用。
可行的方案有:把火山从天空盒搬到主地图,但这样的话我会失去施加在该网格上的雾效,而且还有性能上的考量。
现在我知道可以用 vsnap 文件来存储专用的粒子位置,但即便到现在,我也还在为创建和使用它们犯难,也不确定在本例中是否可行。
最终,我找到了解决方案!它依托于一套骨骼动画与粒子系统相结合的“奇怪”机制。熔岩流粒子用自身上一次的位置作为终点,起点则是生成点;随后随时间推移,它不断修改、改变颜色与形状。

我借助 Blender 及其出色的曲线动画工具,制作了熔岩流的线条,并为每条线生成了骨骼。有些熔岩流在喷发初期就开始,有些则稍晚,从而形成了熔岩的分流。

游戏内的实际效果
Source 2 有一项很棒的图形特性:体积光(volumetric light)。但在 CS2 的分支中,这项特性被禁用了。我找到了用自定义粒子来伪造这一效果的办法。

内容制作
这个项目的特别之处在于,地图上几乎一切物体都必须拥有独立的贴图集,才能获得良好的着色表现。我与 rainless 一起制作了所有必要的生产资产,并在过程中持续补全、新增。

资产
对于建筑构件、岩石与砌块,我使用 ZBrush 进行雕刻。我先在 Source 2 世界编辑器里确定正确的形状与尺寸,再导出到 Blender;借助 BlenderToZBrush 插件,把它们载入 ZBrush 雕刻,再通过 GoZ 导回。之后我对高模做了适度减面——既然用 100 万面的模型就能得到几乎一样的效果,就没必要用 1500 万面的模型。

为了制作低模,我用了更高的减面比例,然后展 UV。这很棘手,因为这类物体存在着色问题——没有补偿、又想保持合理面数的话,根本做不出干净的法线贴图。所以这是技术正确性与性能之间的平衡。
贴图方面,我先在 Substance 3D Designer 里做了几个自定义基础材质,再在 Substance 3D Painter 中搭建智能材质(smart material),以便快速套用到新资产上。

我对结果很满意。色彩浓郁,细节与材质质感都不错。它们略带风格化,但加入的噪点与精细细节平衡了这一点,让整体观感保持写实。
瓷砖材质
对于瓷砖网格与材质,尽管我熟悉 Substance 3D Designer、也知道如何在其中搭建这种材质,但我并不喜欢那种做法,因为结果看起来质感偏低。很多时候,手工雕刻网格再上材质,比在 Substance 3D Designer 里搭整套流程更快。
话虽如此,Designer 也有它的优势,主要是极高的多样性与灵活性——只是这个项目我并不需要那么多变化。雕刻网格的另一好处是,我能在材质之上叠加真实的几何体:从墙面或地面凸出的砖块为场景增添了纵深与细节,边缘烘焙良好、着色稳定,整体来看这种做法在这里更合适。
植被
在这个项目里我几乎没碰 SpeedTree,但确实用了一点,而且我觉得挺有意思。我喜欢它能让人手工打理树木,并动态指定顶点色。

智能道具(Smart Props)
能用到对我而言全新的工具——智能道具(smart props),让我很兴奋。地图上几乎每个资产都有自己的智能道具,无论简单与否。
如果你熟悉虚幻引擎(Unreal Engine),对智能道具最准确的描述,就是“专注于内容创建的受限蓝图”。这些工具能随机化物体摆放、简化模块化套件的使用,等等!


我很喜欢这个 Source 2 特性,但苦于没有现成工具,于是自己动手做了。我选了 Qt,因为它最强大的 GUI 框架(Source 2 地图编辑器正是用它构建的);而 Python 是因为我此前没有正经的编程经验。
我把这个项目命名为 Hammer5Tools:“Hammer” 代表 Source 2 地图编辑器,“5Tools” 代表其中包含的几种实用工具:Loading Editor(载入界面编辑器)、Soundevent Editor(声音事件编辑器)、Smartprop Editor(智能道具编辑器)、AssetGroupMaker(资产组生成器),以及 HotkeyEditor(快捷键编辑器)。
整个项目的初衷,就是填补 Source 2 缺失工具的空白。我确信 Valve 的员工有这类工具,引擎文件里甚至还有一些对应图标,但出于某些原因,他们并未向社区公开。
Source 2 的内容管线分为两个文件夹:content 与 game。与虚幻引擎先把资产转成自有格式再编译进游戏不同,Source 2 的做法是:源文件必须放在 content 文件夹里,再由 Source 2 把它编译成自有格式。
要区分文件类型,有几种格式约定:
- Vmdl – Valve Model(Valve 模型)的缩写。
- Vmat – Valve Material(Valve 材质)的缩写。
- Vsmart – Valve Smart Prop(Valve 智能道具)的缩写。
- Vtex – Valve Texture(Valve 贴图)的缩写。
基本上,这些都是最常用的格式,而且它们都有非二进制格式(可以用记事本打开)。所以编辑它们并不难,但要做一个程序来编辑它们就更复杂了。
我判断,做一个完整的 3D 视口对我而言属于过度设计——甚至毫无意义。
最先完成的是 Loading Editor(载入界面编辑器),它能在地图上创建截图,并添加载入画面、图标与说明。这是最简单的工具,但也逼着我学了一遍 Python 与 Qt 的基础。
短暂搁置后,我开始做 Soundevent Editor(声音事件编辑器)。soundevent(声音事件)描述声音文件该如何播放、在哪里播放、音量多大。
但随后我意识到,自己的编程能力还不足以驾驭 KV3 格式。正如前面所说,这是我第一次写程序,为一套内部格式写自定义解析器着实不易。
与此同时,Sanctum 的开发仍在推进,我和 rainless 开始制作内容。我们经常需要大量模型变体,而把它们一个个导入引擎很慢……AssetGroupMaker 正是在这时诞生的——为了让相似资产的导入更快,我做了这个工具。
举例来说,你有 200 个带不同变体的模型,但除了少数几项,它们的设置都完全一致。你创建一个文件作为所有其他文件的参考,程序会读取这个参考文件并实时更新那 199 个文件。如果你有少数不同的模型,可以把它们加入忽略列表;你也可以按扩展名过滤输入文件。

所有配置都存储在一个名为 %folder_name%.hbat 的文件中。
目录示例如下:

这个工具非常有用,因为在《CS2》里你无法像 Unity 或虚幻引擎那样同时修改多个资产的属性——而我们的资产数量相当庞大!实际上,它几乎能处理所有非二进制格式的文件。
坦白说,那时我对《CS2》与 Source 2 的声音并不太熟。但我找到了 kristiker(https://github.com/kristiker)已经写好的 Python 版 KV3 库。找到这个库后我立刻着手做这个工具,但它的 UI 设计有点怪,代码层面也写得很糟,所以“重写编辑器”的念头一直都在。
在 X 上发帖后,才华横溢的声音设计师 bman 加入了我的 Discord,我请他帮忙构思 Soundevent Editor 的新方案:一个面向声音工作者的界面该是什么样。
讨论到最后,我们草拟了新编辑器的设计概念。我还提到,如果能用一个参数控制声音随距离衰减的音量,就太酷了。当时我完全不知道该怎么逆向工程出这个函数并写进程序里。但 bman 说他有位朋友能帮忙。
Andrew900460(https://github.com/Andrew900460)是一位聪明的程序员,他逆向出了“距离音量映射曲线”(Distant Volume Mapping Curve)的计算算法,并协助把它实现进了项目。
“……我用 Ghidra 检视 cs2.exe,找到了解析音量曲线数据的函数。我不太确定,但我想我也用 Cheat Engine 弄清了文本文件转换后实际输入数据是如何存储的。”
“但如果我确实用了 Cheat Engine,那也只用在启动 MOD 工具时拉起的 CS2 实例上;正常启动游戏时从未用过。”
找到这个函数并不容易,而他找到之后,还得把反编译出的 C 代码改写得更易读(C++),再翻译成 Python。

刚完成 Soundevent Editor 的第一版,我就开始做我最想要的 Smartprop Editor(智能道具编辑器)。有了第一版 Soundevent Editor 的经验,我很快设计出了程序的基础框架——尽管它比 Soundevent Editor 复杂得多。
智能道具文件的结构很简单:由 Element(元素)、Modifier(修改器)、Selection Criteria(选择条件)等块构成。元素可以有子元素,也可以作为其他元素的子元素。Modifier 与 Selection Criteria 是用来描述元素功能的附加参数。有些元素不能有子元素,比如 smartprop 或 model。

每个参数都可以设置变量或表达式。

看起来或许很难,但我坚持认为它其实很好理解。于是我开始为所有属性创建逻辑与控件。在内容制作过程中,我也不断修改、为编辑器加入新功能。最后是 Hotkey Editor(快捷键编辑器),用于编辑 Source 2 世界编辑器里的快捷键。

很高兴看到自己的地图进入了《CS2》官方地图池。为了达成这一切,我付出了大量心血与时间。也要感谢 Ruban,是他给了我机会,在他的地图上展示我的环境美术功力。


评论留言