screenshot001.png

Substance套件作为当下游戏美术资产制作中,贴图制作这一环节的主流工具是相当成功的。其优点

  • 其一是方便美术在3D视角下绘制贴图,
  • 其二是可以利用程序纹理加快贴图制作效率。

本文探讨的是Headless Substance,即无人工无GUI参与的Substance贴图生成流程。当然这在大多数情况下并无必要,主流的生产管线中给美术做好程序纹理和SP智能材质模板就行了,然而这种方式仍然是人力密集的,并不方便scale up。本文希望探讨的技术可能对于以下应用场景有所帮助

  • 自动化批量处理贴图,比如转通道,转mask
  • 给模型加贴图
    • 比如高模(美术制作或扫描)自动生成可用的游戏资产
    • 程序化资产直接生成可用的游戏资产
    • 传统管线中部分特殊mask的自动化生成
    • 甚至UGC的资产生成?

当然也需要向读者指出的是,以上每个情景都可能有很多种技术方案,笔者这里探讨的仅仅是其中一种

读者肯定想到的是substance automation toolkit,本文也基本上是采用这种方法,笔者做的更多的是一个pipeline集成的工作以及范例资产的创建。

一番调研以后(参见后文案例研究篇),发现其实有蛮多方案了,比如在houdini里调用SAT或者直接执行sbsar,有给sat写了个界面的,官方甚至还有sat的dockerfile范例

然而笔者不太满意,笔者希望substance可以作为一个微服务运行在集群上,客户端通过网络协议与之交互。

技术选型上,SAT运行在docker上是官方证实的,所以我们就用docker配环境好了。网络协议上,考虑到SAT处理贴图的时间可能比较久,持续几分钟甚至更长,我们尝试使用websocket协议来发送请求和接收服务器的返回结果。最后,SAT怎么来?

其实订阅一下就行,在substance官网上订阅一个pro/indie版本,就能看到SAT的下载链接。好在SAT本身是不需要license审查的。

1. 技术实现

1.1 Dockerfile

首先笔者假定读者有一定Docker的基础知识,这里就不介绍了。

为了让SAT可以运行,我们得装一些依赖包,比如libglu之类的图形库

官方的dockerfile里已经写好了 https://github.com/AllegorithmicSAS/sat_batchtools_gpu_free/blob/master/Dockerfile

笔者写的有点类似

1
2
3
4
5
6
7
8
9
10
RUN apt-get install -y --fix-missing
    sudo
    python3-pip
    libglu1-mesa
    libxrandr2
    libsm6
    xvfb
    libfontconfig
    libxkbcommon-x11-0
ENV DISPLAY=:1

这其中有一个奥妙是装了xvfb。要不然的话,sbsbaker需要去找framebuffer但是找不到,而xvfb提供了一个虚拟的framebuffer

参考https://forum.qt.io/topic/120349/run-qt-gui-application-inside-docker/13

因此docker启动时必须带一个命令,让xvfb创建一个虚拟屏幕

1
Xvfb :1 -screen 0 1024x768x16

1.2 SAT

参考官方文档https://docs.substance3d.com/sat

其主要功能主要有两个

  • sbsbaker,烘焙贴图,类似substance designer/painter里的贴图烘焙功能
  • sbsrender,用sbsar节点图处理贴图

其他的感觉用处不大。

使用上有两种,一种是CLI命令行模式,可以将参数传入命令行执行。另一种是官方自带的Pysbs包,通过python调用。不过阅读源码发现其实也就是起了一个subprocess执行命令行调用。

具体使用方法这里就不细说了,直接参考官方文档就行。

值得注意的一点是,sbsbaker除了支持单个通道烘焙的函数调用(比如sbsbaker_position_from_mesh)外,还有一个sbsbaker_run的函数,可以一次烘焙很多贴图。它接受一个json配置文件。而这个json配置文件是可以从substance designer中导出的。笔者也用的是这个方式,运行时拼装一个json配置文件,然后运行sbsbaker_run

1.3 Server

这个服务有几个特点

  • 客户端给出请求以后,服务器是可以多次推送回状态和消息的
  • 上传和下载的数据量可能比较大,几十上百兆
  • 连接比较长,服务器处理可能持续几分钟

这时相比于Restful HTTP,使用websocket可能是更好的选择,借用一张Azure的图比较它们的区别,

websocket相比于HTTP

  • 连接可以比较久
  • 双向互相传输数据
  • 每次请求overhead比较少

比较适合这个场景,所以我们选择websocket作为通信协议。

WebSocket support in Azure Application Gateway | Microsoft Docs

667783a9eaa1986602aded1f509150cd.png

题外话是通信协议的另一种选择是gRPC,不过依赖有点多而我们希望甚至DCC里也能调用,所以先算了。

这样带来的一个直接好处是,可以更好支持泛型,比如服务器可以先接受一个manifest再接受其他数据,task运行完以后,给客户端推送结果也可以是泛型的

1
2
3
4
5
6
7
8
await self.receive_manifest()

for item in self.manifest:
    item_data = await self.receive_data(item)
    if 'entry' in item_data:
        params['entries'].append(item_data['entry'])
    else:
        params['values'].append(item_data['value'])

写好以后就可以打包进docker,我们写一个docker-compose文件以后,build就变得很简单,直接

1
docker-compose build sat

就可以了,

然后通过以下命令启动这个微服务

1
docker-compose up sat

具体代码参考笔者Github代码库

https://github.com/maajor/docker-sat

1.4 Client

客户端需要一个websocket库,笔者这里使用的是websocket-client这个库,它还算简单,而且支持到python2.7

和服务器一样,由于接受参数比较自由,客户端方法的签名甚至可以使用**kwargs

1
2
3
4
5
6
7
8
9
10
11
def render(self, sbsar_path, **inputs):
    manifest = {
        "sbsar": {'path': sbsar_path}
    }
    for input_name in inputs:
        val = inputs[input_name]
        if os.path.exists(val):
            data = {"path":val}
        else:
            data = {"value":val}
        manifest[input_name] = data

这样sbsrender我们可以用一个函数来支持。

不过仍然有一点问题,在传输大文件时候仍然需要切块。否则传输的payload会爆。但用websocket的好处是切块也很方便

1
2
3
4
5
6
7
8
9
10
11
12
filesize = os.path.getsize(item_path)
part_count = math.ceil(filesize/self.BUFFER_SIZE)
websocket.send(str(part_count))
with open(item_path, "rb") as f:
    if filesize < self.BUFFER_SIZE:
        ws.send_binary(f.read())
    else:
        while True:
            bytes_read = f.read(self.BUFFER_SIZE)
            if not bytes_read:
                break
            ws.send_binary(bytes_read)

具体代码参考笔者Github  https://github.com/maajor/docker-sat/blob/main/client/satclient.py

2. 实验

在Substance中做自动处理贴图的主要方式就是用position,normal,ao,curvature,id之类的图,来生成与创建mask,以及混合贴图。下面两个案例即是如此

这里尝试了一个voronoi车的建模,高模和低模是houdini中生成,贴图使用SAT自动处理。

不局限于程序化生成的模型,任何已有高模(手工雕刻,扫描),都可以用类似的方法处理使之成为游戏内资产。

screenshot000.png

建模的部分不细说,主要参考Siggraph Asia2018上, Akira Saito的讲座Procedural Hard Surface Design

f00e156aff29c8e556a2c16f300e3d1d.png

具体步骤就跟Saito桑讲的一样,没有太多原理上复杂的东西。

steps.png

而后在贴图部分,需要烘焙AO, ID, Normal三张图,在SD中做处理,主要操作就是用ID混合一些材质,导出一个sbsar的文件

5d9bc2548e126872858e1fbb08ec73c8.png

在叠加上烘焙出来的法线,AO

screenshot001.png

screenshot002.png

用来生成贴图的只是一小段代码。这里高模低模是Houdini存储出来的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import os, shutil
from satclient import SATClient

def main():
    client = SATClient('127.0.0.1:1028')

    for i in range(1, 9):
        low = os.path.join(str(i), "low_bake.fbx")
        high = os.path.join(str(i), "high.fbx")
        sbsar = "proc_car.sbsar"

        bake_outputs = client.bake(high, low, channels=['normal', 'ao', 'id'], size=1024)

        render_outputs = client.render(sbsar, input_ID=bake_outputs['id'], input_AO=bake_outputs['ao'], input_Normal=bake_outputs['normal'])
        for output in render_outputs:
            basename = os.path.basename(render_outputs[output])
            shutil.move(render_outputs[output], os.path.join(str(i), basename))

if __name__ == '__main__':
    main()

连接上本地docker中运行的SAT服务,上传模型和sbsar让它烘焙就行了

最后就是这一套车子

screenshot000.png

3. 总结

本文探讨了一种使用Substance处理贴图的流程。在流程层面,substance是微服务化的。生产中可以怎么方便怎么来,比如集成到Houdini PDG,直接从引擎中调用等等。微服务化的主要好处是方便部署和扩容,可以上云。在制作层面,主要是利用ID/几何信息,使用程序化纹理生成mask的方式制作贴图,可以用在任意已经有高模的资产的情况下。一个案例是给houdini程序化模型上材质。

本文没有讨论模型资产的自动化流程,但案例中低模UV都是houdini自动生成的。实现上完全可以让houdini也无头化成为一个资产处理的微服务。

本文仅代表个人研究,自己编写的服务器和SAT使用过程中有一些bug,仅供探讨,在生产环境中使用出现问题笔者概不负责:)

附录:案例研究

sat_batchtools_gpu_free

官方的一个dockerfile,可以让SAT运行在linux docker image中,对本文相当有启发

不过readme中如果没有sat使用经验的话容易搞不懂它到底干了啥。

AllegorithmicSAS/sat_batchtools_gpu_free: A Docker alternative to use batchtools without GPUs. (github.com)

笔者总结一下,它

  1. 让sat可以运行在没有gpu的虚拟机上,这就为sat上云创造了可行性
  2. 有一个CLI入口可以执行各种sat的功能

sat-scon
这个很有意思做了一个资产自动构建的项目,用sat生成缩略图,可以理解成一个buildmachine
AllegorithmicSAS/sat-scons: A sample showing how using the Substance Automation Toolkit together with the scons build system to do incremental content builds. (github.com)

substance designer batch tool
读者如果看substance designer的安装目录下面有一个sbscooker,其实跟SAT的sbs cooker是一样的。只不过缺少sbsbaker
Substance Designer: Batch Tools | jason brackman’s (wordpress.com)

substance uber baker
Substance Uber Baker (orbolt.com)
其实就是在houdini里调用sat的一个接口节点

substance in houdini
sidefx官方gamedevelopmenttool的工具,在COP中可以调用sbsar文档来处理贴图
好像不依赖substance还不错,只不过有时候会崩溃
江流大佬做的是在houdini里面调用SAT的api
https://zhuanlan.zhihu.com/p/107424364

HQueue + TOPs +Substance Automation Tool kitでテクスチャをベイクする! – Born Digital サポート
用houdini的农场来调用SAT,蛮像影视工作室的方案,渲染农场可不是谁都有的

Substance Automation Toolkitを利用してHDRP向けLOD0テクスチャをLOD1、LOD2に転写するツールを作成した | 測度ゼロの抹茶チョコ (matcha-choco010.net)
基本是在讲怎么用SAT的API

Substance Automation Toolkitによる簡単自動化 - もんしょの巣穴ブログ Ver2.0 (hatenablog.com)

af84508553c056c4c996ff5633efc29b.png

下载

AllegorithmicSAS/sat_batchtools_gpu_free: A Docker alternative to use batchtools without GPUs. (github.com)

AllegorithmicSAS/sat-scons: A sample showing how using the Substance Automation Toolkit together with the scons build system to do incremental content builds. (github.com)

Substance Designer: Batch Tools | jason brackman’s (wordpress.com)

houdini sbs baker
Substance Uber Baker (orbolt.com)

HQueue + TOPs +Substance Automation Tool kitでテクスチャをベイクする! – Born Digital サポート

Substance Automation Toolkitを利用してHDRP向けLOD0テクスチャをLOD1、LOD2に転写するツールを作成した | 測度ゼロの抹茶チョコ (matcha-choco010.net)

Substance Automation Toolkitによる簡単自動化 - もんしょの巣穴ブログ Ver2.0 (hatenablog.com)
procedural pipeline framework? python pipeline nodes