前言

  Unreal 内置有顶点色绘制的功能,可以很方便的在引擎里面绘制颜色。
  但是这个功能有个小缺点就是依赖模型的顶点色,如果模型的面数很少的情况下,顶点色绘制就非常不方便了。
  那么能否在很少面数的情况下一样去驱动绘制呢?
  经过我的研究是完全 可以 实现的,而使用 Python 则更可以更进一步将流程自动化,艺术家只要关注绘制即可。

  其实这个顶点色操作和 Unreal 自带的地形实现的贴图混合效果差不多,好处就是可以用任意的自定义模型。
  然后通过一个材质绘制 alpha ,然后最后将绘制效果 bake 成一张贴图。
  这样就可以实现任意模型贴图混合并且最后输出一张混合到一起完整贴图。

细分模型

  通过上面的描述可以知道,第一步的绘制操作需要在引擎添加面数才能实现细微的控制。
  在引擎里面提高模型面数有两种方案。

Editable Mesh

  使用 Editable Mesh 需要开启插件 Editable Mesh 插件。

开启插件

  这个方案有个好处就是功能是基于蓝图实现的,因此可以完全基于 Python 实现调用。
  但是网上查了一轮,居然发现这个蓝图怎么用一点文档都没有。(开源就是那么任性)
  我经过一轮搜索好不容易搜到一个相关的问题,但是却没有任何人提供相关回答 链接
  最新的回答就是我自己了……

  当时这个蓝图操作也是看着 Python 里面的 API 搭建出来的。
  各种函数都暴露到了 Python ,比起官方的 蓝图 API 还要清晰。
  所以简单摸索搭配一下就成了。

  后来搜索模型细分相关的信息的时候,还是在论坛很深的地方找到了别人关于 Editable mesh 的研究成果 链接,其实也和我搭建的蓝图一样。
  新版本需要多加一个 commit 蓝图节点才能在视窗上看到实现的效果。

蓝图构建

  上面的蓝图只是我 Extrude 操作的演示, Editable Mesh 提供了 set_subdivision_count 和 tessellate_polygons 的方法。
  上面两个都可以实现增加细分的效果。
  设置 subdivision 和 Maya 的3键效果一样的,会将模型全部平滑。
  如果正方形面片,平滑了之后就变成了 圆形面片 了。
  另外这里需要注意,细分之后的结果是直接存储到 asset 里面的。
  如果直接拖基础方块到场景的话,引擎官方的方块 asset 会被细分,如果不甚保存了结果。
  那么恭喜你,你的基础方块永远都是细分过的高面数方块了(:з」∠)

  因为直接设置细分有平滑的问题,所以用 tessellate_polygons 加面的方案比较好,因为这里只想保持模型外轮廓不变的情况下加面数。


  不过最后折腾了一圈之后,并没有采用 Editable Mesh 的方案。
  因为在我们项目的魔改引擎里测试很容易导致崩溃,非常不稳定。
  貌似官方版本的引擎会好好很多。
  鉴于这些问题,即便 Python 可以调用这些蓝图,也不得不放弃了这个方案。

Mesh Tool Remesh

  在引擎里面除了刚才提到的没什么人用的 Editable Mesh 之外。
  还有 Mesh Tool 这个官方力推的建模工具箱,里面提供了 Remesh 功能,同样可以实现细分效果。
  Remesh 也完美保证外轮廓不变,和 Houdini 的 Remesh 效果差不多,会重新拓扑成不规则的三角面。

  使用 Mesh Tool 需要开启插件。

开启插件

  开启之后可以在 Mode 下面找到 Mesh Tool 工具集。
  选择 Deform 然后点击 Remesh 就可以细分模型了。

  当然这里需要注意 Editable Mesh 同样的问题,这个修改会直接应用到当前 asset 里面。
  然而这个细分的资源只是用来临时通过顶点色绘制贴图的,因此我们这里复制一个新的模型并手动细分处理。
  通过 Mesh Tool 也可以让艺术家自己调节细分的程度,比起 Editable Mesh 的一步到位,没有多少可以调节的参数要好很多。

材质 Bake

  正如上面提到,这里绘制是需要一个 贴图 混合的材质。
  这个材质可以简单通过 lerp 节点实现。
  当然也可以搭建更为复杂的效果,比如说通过高度图实现更细微的贴图混合。

  那么绘制完成之后,要怎么输出绘制的贴图呢?
  首先需要将材质 bake 成一个新的材质。
  这样可以将材质混合的贴图类似 PS 的图层合并一样 合并成一张贴图。

材质bake

  Bake 输出完成之后,会输出一个特殊的材质以及配到一张合并好的贴图。这个 材质 和 贴图 都在 Transient 临时目录里面
  如果是 Python 驱动的话,甚至连临时的文件都没有了,是一个动态 Texture2d 贴图。
  这个操作就类似你用 蓝图节点 import File as Texture2D 获取到 Texture2D 。

import File as Texture2D

  这个 Texture2D 完全没有 Asset 路径,也无法直接存储到 Unreal 的 Content Browser 里面。
  最后我的处理方法是将这张贴图导出通过 export to disk 方法将图片导出到本地。
  然后再通过 import asset tasks 将图片重新导入到 Content Browser 里面。

  当然除了这种曲线救国的导出引擎在导入的方法之外,也可以用 RenderTarget 的方式将图片存储到 Content Browser。
  只是 Render Target 的处理要麻烦很多,需要通过 Python 搭建材质然后调用 Draw Material to Render Target 节点实现材质转纹理
  所以最后还不如做一次导出导入的操作流程。

  完成贴图导出导入的流程基本就大功告成,后续就是清理掉之前生成的高模即可。

  这里的操作和我们这边的材质属性直接挂钩,就不直接 post 程序代码,下面放出一些过程代码给大家参考。
  由于这里需要输出的是一张黑白的单通道纹理,所以 bake 的时候实际上是生成了一个 顶点色 连接的材质
  然后将顶点色的贴图 bake 出来,导出 TGA 贴图然后再导入会引擎。 (TGA 贴图在下个章节解释)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# NOTE 生成临时材质
material = unreal.Material()
unlit = unreal.MaterialShadingModel.MSM_UNLIT
# NOTE 设置为 unlit 模式
material.set_editor_property("shading_model", unlit)

# NOTE 生成顶点色节点 位置为 0,0
vertex_color = unreal.MaterialEditingLibrary.create_material_expression(
material,unreal.MaterialExpressionVertexColor)

# NOTE 将顶点色节点的 R 通道和 emissive 属性相连
emissive = unreal.MaterialProperty.MP_EMISSIVE_COLOR
unreal.MaterialEditingLibrary.connect_material_property(vertex_color, "R", emissive)
# NOTE 编译材质
unreal.MaterialEditingLibrary.recompile_material(material)

# NOTE 将临时材质赋予到选择模型的 StatiMeshComponent 上
static_comp.set_editor_property("override_materials",[material])

# NOTE 设置 Bake 属性
option = unreal.MaterialOptions()
option.set_editor_property("use_mesh_data", True)

properties = unreal.PropertyEntry()
MP_EMISSIVE_COLOR = unreal.MaterialProperty.MP_EMISSIVE_COLOR
properties.set_editor_property("property_", MP_EMISSIVE_COLOR)
option.set_editor_property("properties", [properties])

# NOTE 获取 Gui 上的输入的贴图大小
x = self.Tex_Size_X_SP.value()
y = self.Tex_Size_Y_SP.value()
size = unreal.IntPoint(x, y)
option.set_editor_property("texture_size", size)

# NOTE 通过 Editor Tests 插件提供的蓝图 bake 出材质和贴图
unreal.EditorTestsUtilityLibrary.bake_materials_for_component(
static_comp, option, unreal.MaterialMergeOptions())

# NOTE 获取Bake出来的 材质 | 从材质上获取生成的目标贴图
material = static_comp.get_editor_property("override_materials")[0]
texture = material.get_texture_parameter_value("EmissiveColorTexture")


# NOTE 导出 TGA 图片 | 方便 顶点色 导入
asset_tool = unreal.AssetToolsHelpers.get_asset_tools()
# NOTE 输出到临时路径上
texture_path = os.path.join(tempfile.gettempdir(),"Unreal_temp",name+"_BakeTexture.tga")
task = unreal.AssetExportTask()
task.set_editor_property('automated', True)
task.set_editor_property('filename', texture_path)
task.set_editor_property('object', texture)
task.set_editor_property('prompt', False)
task.set_editor_property('exporter', unreal.TextureExporterTGA())

check = unreal.Exporter.run_asset_export_task(task)
if not check:
alert(u"tga 图片生成失败")
return

# NOTE 等待贴图生成
time.sleep(1)

# NOTE https://forums.unrealengine.com/development-discussion/python-scripting/1608720-python-scripting-for-importing-custom-assets
# NOTE 导入贴图
task = unreal.AssetImportTask()
task.set_editor_property('automated', True)
task.set_editor_property('destination_name', name + '_BakeTexture')
task.set_editor_property('destination_path', path)
task.set_editor_property('filename', texture_path)
task.set_editor_property('replace_existing', True)
task.set_editor_property('save', True)
asset_tool.import_asset_tasks([task])

bake_path = path + "/" + name + '_BakeTexture'
texture = unreal.EditorAssetLibrary.load_asset(bake_path)

顶点色绘制还原

  本来是觉得这个纹理处理流程已经完美了,没想到在美术使用之后遇到了一个非常致命的问题。
  那就是贴图画得不好想要重新画该怎么办?

  好在虚幻的顶点色绘制提供了重新导入顶点色纹理的方法。
  只不过模型在处理过程中已经将细分删除了,需要重新复制一个模型,然后细分,再然后导入之前导出的 黑白 纹理为顶点色即可。
  由于引擎只支持导入 TGA 纹理,所以上面导出的时候也将格式改为 TGA。

总结

  这次贴图处理流程有些许模糊不清,主要原因是 Material 节点的连接上不是我做的。
  我负责写工具将流程自动化,并且对接这个特殊 Meterial 上的一些参数。
  以后有时间也得再好好研究一下 Unreal 的 Material 节点连接。