-
Notifications
You must be signed in to change notification settings - Fork 395
Asset
游戏和其它软件最大的不同是:除了代码,开发游戏需要创作大量的非文本多媒体资产(asset)。图片、模型、动画、等等。使用一个游戏引擎,开发者最关心的事务之一是怎样生产和管理这些资产。
注:这里使用 Asset 来代表这些数据,不过由于一些历史原因,我们在引擎的代码中有些地方将其称为资源 (resource) 。
Ant 并没有一个 All in One 的开发工具,可以用来生成一切引擎用到的资产。几乎没有游戏引擎会有这样的工具。创作者可能会用 PhotoShop 制作图片、用 Blender 制作模型…… 开发一个专有工具取代这些专业软件的功能没有意义。专业资产创作软件向游戏引擎导入数据,就必须定义一种双方都认可的数据格式。
在使用 Ant 引擎时,和其它游戏引擎不同,并不需要经过一个资产库导入过程。
资产创作人员把资产的通用格式的原始文件放在 Ant 的 Package 中,通常就是复制到本地文件系统下。根据资产类别,可能还需要补充一些信息,例如,贴图直接复制一个 png 文件是不能用的,还需要补充一个 .texture 文件。
补充信息可以手工编写一个格式为 DataList 的文本,也可以用引擎自带的编辑器生成。
这些数据均可提交到版本管理系统,建议把代码和多媒体资产分为子模块做版本管理。因为 VFS 支持不同的本地文件映射形式,所以资产文件放在本地文件系统的哪个位置并不重要。
在程序第一次运行时,会触发资产编译模块的转换流程,将这些原始数据转换为引擎认可的形式,并将结果储存在本地缓存中。
贴图 (texture) 通常是以图片的形式存在。有些场合也会用到一些非 2D 图片,例如 cubemap 等。这些图片数据最终需要根据平台硬件不同转换为不同的数据格式,大多数情况下,我们会采用 ASTC 压缩格式,有时也保留无损信息。但最终的数据只存在于 VFS 的缓存中,使用者无需关心。我们使用了于 bgfx 配套的 bimg 库处理图片的数据源,常见的 png jpg 等数据格式都支持的不错。
引擎无法直接使用这些通用格式,因为还缺少一些信息。我们需要额外构造一个 .texture 文件。可以手写,也由可以工具生成。这是一个 DataList 格式的文本文件。下面是一个名为 solar-panel.texture 的贴图文件示例:
colorspace: sRGB # 一般使用 sRGB ,少数情况使用 linear ,未来还会支持 HDR
compress: # 发布时如何压缩图片,根据不同平台有不同配置
android: ASTC6x6 # ASTC4x4 到 ASTC12x12 压缩比上升,同时质量有更大的损失
ios: ASTC6x6
windows: BC3 # BC3 为 R5G6B5A8 ,还可以选择动态调节每通道 bit 数的 BC7 。对于双通道可用 BC5 ,单通道使用 BC4 ……
# format : RGBA8 # 不填 compress 则需要定义格式
noresize: true # 不要改变原始图片尺寸
normalmap: false # 并非法线图
path: ../../../images/icons/item/solar-panel.png # 图片原始文件的位置
sampler: # 采样参数
MAG: LINEAR # 采样方式 LINEAR POINT ANISOTROPIC
MIN: LINEAR
U: WRAP # CLAMP MIRROR BORDER WRAP
V: WRAP
type: texture # 贴图类型,通常为 texture
#maxsize : 1024 # 可用来设置发布时的尺寸
#mipmap : true # 发布时生成 mipmap
它描述了一张贴图,其数据内容存在于另一个 png 文件中。其余的贴图元信息引擎需要了解,但在 png 文件中没有。
引用的这个 png 文件有一个 VFS 路径:../../../images/icons/item/solar-panel.png
,这里涉及一些引擎细节,一般的使用者不需要太关心,但这里还是展开一下:
对于处于 C/S 模式下的 vfs 来说,运行时的程序和提供本地文件映射的 fileserver 分处两个不同的进程;如果是本地模式,同一个进程内也有两个不同的 vfs 实例。
对于引擎使用这张贴图的部分,它关联的 vfs 实例上,该 .texture 文件其实是一个目录,这个目录下有两个文件:
main.bin
source.ant
其中,main.bin 是一个二进制文件,它根据不同平台有所不同,在 windows 下,是一个 BC3 压缩的压缩数据;在 ios 下是 ASTC6x6 格式的压缩数据,等等。而 source.ant 则是转换后的元信息,还是 datalist 格式,但内容有所不同,方便引擎直接使用:
flag: "+luwvw-lSg"
info:
bitsPerPixel: 8
cubeMap: false
depth: 1
format: BC3
height: 128
numLayers: 1
numMips: 1
storageSize: 16384
width: 128
也就是说,在游戏运行的时候,从它绑定的 VFS 上是看不到原来的 solar-panel.texture 文件的;取而代之的是,有 solar-panel.texture 这个目录,引擎可以打开 solar-panel.texture/main.bin
和 solar-panel.texture/source.ant
。
而在另一侧(fileserver 进程中,或是本地模式下的资产编译模块中),有另外一个 vfs 实例。它的映射规则让它可以看到 solar-panel.texture 这个文件。这个资产编译模块,通过调用外部工具,可以将 solar-panel.texture 以及它说依赖的 solar-panel.png 图片文件编译成上面所述的那个目录。这个目录存放在本地缓存中。编译只发生在第一次加载这个 .texture 文件,如果这个文件没有发生过变动,就不会多次编译。编译结果会被软连接到另一端的 vfs 的 solar-panel.texture 目录。
我们使用 .gltf/.glb 做模型文件的交换格式。这种格式主流的建模软件都支持。它不光可以表示模型还可以描述动画贴图等资产类型,这里暂且介绍模型部分。
模型文件的管理方式和贴图类似,但由于一些历史原因稍有不同(未来可能会统一)。模型文件并没有类似 .texture 的专门元信息描述文件。而直接使用 .glb 作为后缀。.glb 文件就是模型文件,大多数情况下,我们可以直接用其它创作软件生成的 .glb 文件使用。它的信息是完整的,所以不需要额外的元信息描述。
但一些情况下,我们需要对 .glb 文件做一些调整。这个调整工作是通过引擎自带的图形化编辑器完成的:例如删掉 .glb 文件中一些不要的数据,增加一些额外信息,等等。 gltf 格式本身支持这种修改( .glb 的元信息是基于 json 的,有足够的动态性),但我们不希望直接修改第三方创作软件生成的文件,因为那样,通过我们的编辑器二次加工后,就很难再送回原始创作软件再次修改而不丢失信息。
所以,我们增加了一个叫做 .patch 的文件,它是由引擎编辑器生成的(并非手写),也是 datalist 格式。在 /pkg/ant.test.simple/resource/miner-1.glb.patch
就是这样一个示例:
---
file: animations/animation.ozz
op: replace
path: /animations
value:
work: $path ./miner.bin
它的工作原理比上面的材质编译略复杂一些,这里大略写一下细节供感兴趣的同学了解:
gltf 格式其实包罗万象,它不仅仅用来表示模型,还可以描述骨骼、动画、贴图等等几乎一切 3d 渲染需要的东西。一个 gltf 文件(glb 是它的二进制形式) 包含了非常多的信息。而对于 Ant 引擎的角度看,我们希望不同类别的资产数据位于 vfs 上的多个文件中,一切皆是文件,而不是数据包。gltf 却像一个数据包。
所以,glb 在编译时,会被资产编译工具展开成一个目录,把里面的数据分别提取出来,存在不同的文件中。在游戏运行时,就像上面贴图的例子中,一张图片变成了两个文件那样,一个 glb 源文件会变成更多的文件,存放在 .glb 的同名目录下。
而 .patch 指的是对这个目录的加工。所以在 .glb 编译完成后,.patch 会触发进一步的编译过程。这里的 patch 文件的功能是修正前一步 glb 编译结果目录中的那个 animations/animation.ozz
文件。这个文件也是一个 datalist 格式的文本,而所有 datalist 都是树结构的,此处的 path: /animations
指定的是这个 datalist 文件对应的树结构中的一个节点。接下来的 value 字段中的 $path ./miner.bin
会先用源文件所在路径和后面的 ./miner.bin
合并计算出绝对路径,然后把这个节点的内容替换 (replace) 为 work: /pkg/ant.test.simple/resource/miner.bin
。
最终,资产编译模块,将 miner-1.glb 和 miner-1.glb.patch 两个文件编译成了一个目录:
animations
animations/animation.ozz
animations/Armature.skinbin
animations/ArmatureAction.bin
animations/skeleton.bin
animations/work.ozz
debris.prefab
hitch.prefab
images
images/miner_color.png
images/miner_color.texture
images/miner_color.texture/main.bin
images/miner_color.texture/source.ant
images/miner_light.png
images/miner_light.texture
images/miner_light.texture/main.bin
images/miner_light.texture/source.ant
images/miner_Metallic-miner_Roughness.png
images/miner_Metallic-miner_Roughness.texture
images/miner_Metallic-miner_Roughness.texture/main.bin
images/miner_Metallic-miner_Roughness.texture/source.ant
images/miner_normal.png
images/miner_normal.texture
images/miner_normal.texture/main.bin
images/miner_normal.texture/source.ant
materials
materials/Material.002.material
materials/Material.002.material/attribute.ant
materials/Material.002.material/di.bin
materials/Material.002.material/fs.bin
materials/Material.002.material/source.ant
materials/Material.002.material/varying.def.sc
materials/Material.002.material/varying.di.def.sc
materials/Material.002.material/vs.bin
materials/Material.002_PTpt.material
materials/Material.002_PTpt.material/attribute.ant
materials/Material.002_PTpt.material/di.bin
materials/Material.002_PTpt.material/fs.bin
materials/Material.002_PTpt.material/source.ant
materials/Material.002_PTpt.material/varying.def.sc
materials/Material.002_PTpt.material/varying.di.def.sc
materials/Material.002_PTpt.material/vs.bin
materials.names
mesh.prefab
meshes
meshes/BezierCurve.001_P1.ibbin
meshes/BezierCurve.001_P1.meshbin
meshes/BezierCurve.001_P1.vb2bin
meshes/BezierCurve.001_P1.vbbin
meshes/Cylinder.001_P1.ibbin
meshes/Cylinder.001_P1.meshbin
meshes/Cylinder.001_P1.vb2bin
meshes/Cylinder.001_P1.vbbin
meshes/Cylinder.005_P1.ibbin
meshes/Cylinder.005_P1.meshbin
meshes/Cylinder.005_P1.vb2bin
meshes/Cylinder.005_P1.vbbin
meshes/Cylinder_P1.ibbin
meshes/Cylinder_P1.meshbin
meshes/Cylinder_P1.vb2bin
meshes/Cylinder_P1.vbbin
meshes/Plane_P1.ibbin
meshes/Plane_P1.meshbin
meshes/Plane_P1.vb2bin
meshes/Plane_P1.vbbin
translucent.prefab
work.prefab
其中的 animations/animation.ozz
被 .patch 做了稍许修改。
Ant 的骨骼动画模块使用的是 Ozz-animation 。
我们有两种方式生成动画资产,一种是使用专门的工具如 blender 以 gltf 格式导出动画文件;另一种是用我们的编辑器直接编辑动画。用引擎编辑器创作的动画后缀名为 .anim ,主要用来描述一些有规则的刚体动画。这里是一个文件示例,它由引擎的编辑器创作:
---
duration: 3
name: work
sample_ratio: 30
target_anims:
---
clips:
---
amplitude_pos: 8
amplitude_rot: 0
direction: 2
random_amplitude: false
range: {2, 79}
repeat_count: 1
rot_axis: 2
tween: 6
type: 2
inherit: {false, false, false}
target_name: Bone.002
---
clips:
---
amplitude_pos: 0
amplitude_rot: -120
direction: 2
random_amplitude: false
range: {12, 67}
repeat_count: 8
rot_axis: 2
tween: 1
type: 1
inherit: {false, false, false}
target_name: Bone.003
---
clips:
---
amplitude_pos: 0
amplitude_rot: 27
direction: 2
random_amplitude: false
range: {2, 79}
repeat_count: 1
rot_axis: 3
tween: 6
type: 2
inherit: {false, false, false}
target_name: Bone.004
---
clips:
---
amplitude_pos: 0
amplitude_rot: 180
direction: 2
random_amplitude: false
range: {1, 78}
repeat_count: 1
rot_axis: 2
tween: 4
type: 2
inherit: {false, false, false}
target_name: Bone.001
type: ske
它最终会经由 ozz-animation 库的代码编译为它的私有格式;用 blender 等第三方创作工具生成的包含动画的 gltf 文件也一样,同样会被编译为相同格式的数据。
在游戏运行时,引擎从 vfs 中可见的是处理后的数据文件,可以直接被 ozz-animation 模块使用。
Ant 曾经实现过一套特效模块,但因为工具开发的工作量问题,转向了开源的 Effekseer 。所以,引擎编辑器不再能创作特效,而使用 Effekseer 的编辑器。
Effekseer 工具生成的是 .efk 文件,Ant 引擎不需要二次加工。
Ant 继承了 fmod 。未来可能集成更多其它类似模块作为 fmod 的替代品。
和特效一样,fmod 有自带的创作工具,它生成的资产是 .bank 文件,Ant 引擎不需要二次加工。
Ant 的材质文件放在 .material 文件中,/pkg/ant.test.simple/resource/skybox.material
是一个示例:
fx:
vs: /pkg/ant.resources/shaders/sky/skybox/vs_skybox.sc
fs: /pkg/ant.resources/shaders/sky/skybox/fs_skybox.sc
setting:
lighting: off
no_predepth: true
properties:
s_skybox:
stage: 0
texture: /pkg/ant.resources.test/sky/colorcube2x2.texture
u_skybox_param: {1.0, 0.0, 0.0, 0.0} #intensity, NOTUSE, NOTUSE, NOTUSE
state:
ALPHA_REF: 0
CULL: CW
DEPTH_TEST: GEQUAL
MSAA: true
WRITE_MASK: RGBA
材质文件也需要经过编译,变成一个目录。其中,它引用了 .sc 着色器脚本。其编译流程的原理,可以参看前面贴图的编译部分。因为这里涉及着色器 (shader) 脚本,所以资产编译模块需要调用 bgfx 的 shaderc 完成编译。
材质文件更接近代码,是手工编写的。但有些 gltf 文件中也包含一些别的工具生成好的材质,编辑器可以通过 patch 机制替换为 Ant 引擎支持的材质,并引用其中的部分参数。
Patch 是 Asset 机制的一部分。但多用于修改材质。例如,当你需要以半透明方式渲染一个已经制作好的 prefab ,就需要定义一个半透明材质:在 state 中设置 BLEND: ALPHA
。因为引擎暂时不提供运行时修改材质的 state 的方法,所以必须离线生成。一般,我们可以复制一份 prefab ,并添加一项。
例如,有一个 floor.glb 文件,系统默认生成了 /mesh.prefab 如下:
---
data:
scene: {}
policy:
ant.scene|scene_object
---
data:
material: $path materials/Material.material
mesh: $path meshes/Cube.002_P1.meshbin
scene: {}
visible: true
visible_masks: main_view|selectable|cast_shadow
mount:
/scene/parent: 1
policy:
ant.render|render
tag:
Cube.037
我们可以看到,材质 material 并没有直接写在这个文件中,而是引用了内部的 materials/Material.material
。而它也是一个目录,真正的数据在 materials/Material.material/source.ant
。
这个文件的后半段(我们关心的 state 部分)是这样的:
state:
ALPHA_REF: 0
CULL: CCW
DEPTH_TEST: GREATER
MSAA: true
WRITE_MASK: RGBAZ
我们需要先复制一个 mesh.prefab ,叫做 trans.prefab 。同时复制 materials/Material.material
生成一个 materials/Material_trans.material` 。然后在
materials/Material_trans.material的 state 字段添加一项
BLEND: ALPHA。另外,需要添加
render_layer: translucent` 。
这一系列工作可以通过增加一个 floor.glb.patch
,剩下的工作让系统自动完成。这个 .patch 可以写成:
---
file: mesh.prefab
op: copyfile
path: trans.prefab
---
file: materials/Material.material
op: copyfile
path: materials/Material_trans.material
---
file: trans.prefab
op: replace
path: /2/data/material
value: materials/Material_trans.material
---
file: trans.prefab
op: add
path: /2/data/render_layer
value: translucent
---
file: materials/Material_trans.material
op: add
path: /state/BLEND
value: ALPHA
以上是介绍原理。这个过程比较麻烦,推荐使用编辑器的对应功能,或是写一个自动化脚本为所有需要的 glb 文件自动添加。