英文原文 | 《Unreal Engine 4 Tutorial: Dynamic Mesh Painting in Unreal Engine 4》
中文翻译 | 《Unreal Engine 4 渲染目标教程 之 动态网格绘制》 (上)
中文翻译 | 《Unreal Engine 4 渲染目标教程 之 动态网格绘制》 (下)作者 | Tommy Tran Sep 7 2018 | 翻译 开发游戏的老王
阅读时长 | 15分钟 内容难度|中级
在本教程中,您将学会如何在任意类型的网格上绘制颜色。
本教程渲染目标系列教程四部曲之一:
网格绘制指的是游戏运行时玩家在物体上绘制的能力。比较经典的例子:《超级马力阳光》中的粘性物质,《传送门》中的凝胶剂,《喷射战士》中的染料。网格绘制可用于游戏性元素也可纯粹用于视觉效果。无论如何,它都给游戏设计师和艺术家提供了大量的可能性。
尽管上述经典案例的效果相似,你依然可以使用网格绘制实现喷溅物体、渲染角色的伤口甚至允许玩家为他们的角色画脸谱!
本教程内容 重点 是在骨骼网格(skeletal mesh)上绘制。我们会介绍:
- 为一个网格展 UV
- 使用射线追踪的撞击位置为网格进行 球形遮罩
- 使用 scene capture 将展过 UV 的网格和 球形遮罩 渲染到渲染目标
- 使用遮罩将纹理和角色材质进行混合
本教程并非顶点绘制教程。顶点绘制依赖于网格的分辨率并且无法在运行时改变。而本教程中的方法 不依赖 于网格分辨率并且可以在运行时执行。
开始吧
译者注: 本教程提供了范例工程,如果需要可以到原文网站免费注册并下载。
在 MeshPainterStarter 文件夹中打开 MeshPainter.uproject ,点击 Play 的话,你会看到一个小人儿,这就是我们要绘制对象。
和前面 雪地足迹 教程和 可交互草地 教程一样,本教程也需要 scene capture ,为了节省时间,笔者已经创建好了 scene capture 蓝图。如果想了解 scene capture 设置,请参阅前面的教程。
首先,咱们看一下如何在网格上绘制。
网格绘制
大多数情况下,我们使用的网格都已经展好 UV 了,这时我们只需要使用 渲染目标 创建遮罩并将其应用到网格上就可以了。然而,直接在 渲染目标 上绘制遮罩会使 UV 壳(UV shell)出现一些不连续的问题。
以下为一个立方体 UV 和一个 球形遮罩 的例子:
当我们把遮罩应用到立方体上:
如你所见,一个 2D 球形遮罩 无法包裹那些拐角。如果要生成正确的遮罩,球形遮罩 需要在网格的世界坐标系中采样。然而,我们绘制渲染目标时如何才能访问到世界坐标呢?
在油管上一个名为 Ryan Bruck 的 UP 主做了一个关于使用 渲染目标 实现 角色伤害效果 的视频(本教程中的方法就基于 Ryan Bruck 的方法)。在他的视频中,他成功地生成了 3D 球体遮罩,并把它应用到了 渲染目标 上。之所以能够实现,是因为 Ryan Bruck 创建了一个 渲染目标 来存储 网格的世界坐标 ,这样他就可以用 球形遮罩 来采样了。下面,咱们深入聊一聊这事儿。
Ryan 的方法
Ryan 的方法一共有 4 个步骤。
第 1 步
就是对网格 “展 UV” 。例如,下图就是角色的 UV :
下面是角色在虚幻中展完 UV 的样子:
我们可以在给网格展 UV 的时候使用点简单的 世界位置偏移量 计算(后面会解释这个)。
第 2 步
展完 UV,接着就是要把网格的 世界位置 编码到 渲染目标 中。我们可以将 展开材质(unwrap material) 的颜色设为 绝对世界位置(Absolute World Position) ,然后使用 Scene Capture 来捕获展开。
注意:之所以 渲染目标 的颜色会改变,因为角色是带动画的,因此 世界位置 也是持续改变的。
第 3 步
就是创建 球形遮罩 。现在我们就可以访问网格的 世界位置 ,并使用 球形遮罩 对其采样。我们可以将 球形遮罩 直接渲染到 第二个 渲染目标上。
第 4 步
就是在角色的材质中使用遮罩来 混合 颜色、纹理以及材质。以下是第 3、4 步的可视化表示:
接下来我们来看一下笔者提出的方法。
笔者(Tommy)的方法
Ryan 的方法是可行的,但它的实现方法略繁琐:
- 需要两次对渲染目标的绘制。第一次用于捕获展开的网格,第二次用于累加 球形遮罩 。
- 一个渲染目标存储世界位置。
- 一个渲染目标累加球形遮罩,而且我们需要为每一个要绘制的角色配一个独立的渲染目标。
本教程的方法舍弃了 第二次绘制 和用于 存储世界位置 的 渲染目标 。其实现 原理 是将展开和 球形遮罩 图组合存储到一个 展开材质 中(unwrap material)。然后在 叠加合成模式(additive composite mode)中捕获展开并将球形遮罩累加上去。
需要 注意 的是,两种方法都是在模型的 UV 没有重叠 的时候效果最好。因为重叠的 UV 意味着一些像素会共用 UV 空间,并且也会共用遮罩。例如,我们让角色的双手的 UV 重叠在一起,如果一只手被遮罩了,那么另一只手也会被遮罩。
大致了解了这个方法,我们来创建展开材质。
创建展开材质(Unwrap Material)
在 Materials 文件夹中创建新的材质并命名为 M_Unwrap ,然后打开它。
接下来,修改如下设置:
- Shading Model:
Unlit,确保 scene capture 不捕获任何光照信息。 - Two Sided:
Enabled,有些时候展开的 UV 面会反向,所以开启 Two Sided 以后,就能够确保即使有面反转了也能被看到。 - Usage -> Used with Skeletal Mesh:
Enabled,编译用于骨骼网格材质的必要着色器。
接下来,对网格展 UV。创建如下节点。笔者在 MPC_Global 资源中已经创建了 CaptureSize 和 UnwrapLocation 变量。
上述代码将为网格以指定的位置和大小来展 UV 。注意,如果你的网格 UV 在另外的通道,你需要修改 TextureCoordinate 节点的 Coordinate Index。比如,UV 在通道 1 ,则将Coordinate Index 设为 1 。
接下来,创建球形遮罩。我们需要 2 个参数:碰撞位置(hit location)和 球的半径 (sphere radius):
上述代码将为球形遮罩范围内的像素返回 白色 ,为其范围之外的像素返回 黑色 。先不用设置参数的值,我们会在后续的蓝图中做这件事。
一定 要将 Absolute World Position node 节点设为 Absolute World Position (Excluding Material Shader Offsets) 。因为像素的世界位置会因展 UV 而发生改变。排除材质的偏移量会给我们展 UV 之前的原始世界位置。
以上就是创建展开材质的全部工作。点击 Apply 然后关闭材质。接下来我们将材质应用到角色上,并将其展 UV 。
为角色展 UV
本教程中 scene capture 蓝图将负责展 UV 和捕捉。首先,我们需要一个展开材质的动态实例。在 Blueprints 文件夹中打开 BP_Capture 。然后为 Event BeginPlay 事件添加如下高亮节点。确保将 Parent 设为 M_Unwrap 。
接下来,我们需要一个函数实施展 UV 和捕获。创建一个名为 PaintActor 的函数。然后为其添加如下参数输入:
ActorToPaint: 类型是 Actor,被展 UV 和捕捉的角色。HitLocation: 类型是 Vector,球形遮罩的中心。BrushRadius: 类型是 Float,球形遮罩的半径(世界单位)。
尽管该方法适用于任何 actor ,但是我们还是要检查一下输入的 actor 是否为 Character 。简单起见,我们把角色的骨骼网格存储到一个变量中,因为我们要访问它很多次:
接下来,实现展 UV 和球形遮罩。在节点链的最后添加如下高亮节点:
解释以下上述代码:
- 先保存网格的原始材质(后续我们会将它重新应用到网格上),然后应用展开材质。
- 第二行会把碰撞位置和笔刷半径传给展开材质。
在测试展 UV 之前,我们需要从玩家角度做一次射线检测以获取碰撞位置。
获取碰撞位置
点击 Compile 并回到主编辑器,打开 BP_Player ,找到 Shoot 函数并添加如下高亮节点。本教程中,将 Brush Radius 设为 10 。
点击 Compile 并关闭 BP_Player 。点击 Play 然后在角色上单击左键,就可以展 UV 并进行遮罩了。
你可能很奇怪,为什么遮罩总在动,因为角色的身体相对于球形遮罩,总是进进出出的。但这不算是问题,因为我们仅会在碰撞的瞬间捕获展开。
现在我们已经展开了网格,接下来该捕获它了。
捕获展开
首先,在被展开网格前面添加一个 unlit 的黑色平面以挡住UV壳边缘上的接缝。打开 BP_Capture 添加一个 Plane 并命名为 BackgroundPlane 。为节省时间,笔者已经创建好了黑色材质。将材质设为 M_Background 。
本教程中,展开和捕捉大小为 500×500 个单位,这也是背景平面的最小尺寸。将背景平面的 Scale 设为 (5.0, 5.0, 1.0) 。
因为平面的位置和展开的位置是相同的,所以最好将平面的位置下移一些,以防止它们在 Z 轴上冲突(z-fighting),将 BackgroundPlane 的 Location 设为 (0.0, 0.0, -1.0)
接下来,执行捕捉。回到 PaintActor 函数,并添加如下高亮节点:
上述代码会捕捉展开的网格。然后重新应用网格的原始材质。
我们需要确保 scene capture 向 渲染目标 “添加” 内容,而非覆盖掉先前的内容。因此,选择 SceneCapture 组件,然后将 Scene Capture -> Composite Mode 设置为Additive。
点击 Compile 并关闭蓝图。接下来,我们要在角色材质中使用 渲染目标。
使用遮罩
在 Characters -> Mannequin -> Materials 中打开 M_Mannequin 。然后添加如下高亮节点,并确保
Texture Sample 设为 RT_Capture 。
实现的结果就是,遮罩为白色的地方显示为红;遮罩为黑色的地方显示为橘黄。如果你愿意,还可以使用这个遮罩把纹理或材质层混合起来。
点击 Apply 并关闭材质。点击 Play 然后用左键在角色身上点击,就开始绘制了。