在此虚幻引擎 4 教程中,您将学习如何通过使用场景捕捉和粒子创建矢量场来创建交互式草地
直到现在,游戏中的草通常由地面上的纹理表示,而不是渲染单个草片。
但是随着硬件能力的增强,我们渲染草地的能力也会增强。
您可以在《地平线:零之黎明》和《塞尔达传说:荒野之息》等游戏中看到这方面的绝佳示例。
在这些游戏中,玩家可以穿过草地,但更重要的是,草地会对玩家做出反应。
幸运的是,创建一个系统来做到这一点并不难。事实上,您今天将学习如何做!在本教程中,您将学习如何:
-
使用场景捕捉和粒子系统创建矢量场
-
使用矢量场将草从玩家那里弯曲
注意:本教程假定您已经了解使用虚幻引擎的基础知识。如果您是虚幻引擎新手,请点击虚幻引擎初学者教程
注意:本教程是关于在虚幻引擎中使用渲染目标的 4 部分教程系列的一部分:
入门
解压缩它并导航到InteractiveGrassStarter并打开InteractiveGrass.uproject。
您将看到一小片草地,它将作为本教程的主题。
我还创建了一个小部件来显示场景捕获的渲染目标。
在我们开始之前,请确保您已阅读我们关于创建交互雪地的教程,因为我将跳过该教程中的一些信息。
请注意,本教程还将使用捕获和投影。
为了节省时间,我也已经设置了一个类似于创建交互雪地中的捕捉蓝图。
在我们深入之前,让我们看看创建交互式草地的不同方法
。最常见的方法是将玩家的位置发送到草地材质,然后使用球体遮罩将玩家一定半径范围内的草地弯曲掉。
虽然这是一种不错的方法,但如果您想添加更多影响草的 actor,它的扩展性就不好。
对于您添加的每个actor,您需要向材质添加另一个位置参数和球体遮罩。
一种扩展性更好的方法是使用矢量场。
什么是矢量场?
矢量场只是一种纹理,其中每个像素代表一个方向。
如果您以前使用过流程图,它们是同一回事。
但不是移动 UV,而是使用World Position Offset pin 移动顶点。
与球体遮罩方法不同,材料只需要对矢量场进行一次采样即可获得弯曲方向。
让我们看看如何将方向存储到纹理中。看看这个网格:
假设红点是您要移动的对象。
如果你把它移到右下角,什么矢量代表这个移动?如果您回答(1, 1),那么您是正确的!
您可能知道,您还可以将矢量表示为颜色,这就是将它们存储到纹理中的方式。
让我们将这个向量插入到 Unreal 的颜色选择器中,看看它返回什么颜色。
如您所见,(1, 1)的方向返回黄色。
这意味着如果你想将草向正 XY 轴弯曲,你只需要在纹理中使用这种颜色。
现在让我们看看所有矢量的颜色。
右下象限看起来很不错,因为它在两个轴上都有渐变。
这意味着您可以将该象限中的任何矢量存储为一种颜色,因为每个矢量都有唯一的颜色。
但是其他三个象限是有问题的。
它们只有一个轴上有渐变或根本没有渐变。
这意味着多个向量将共享一种颜色。
例如,无法区分向量(-1, 1)和(0, 1)。
这三个象限的每个矢量没有唯一颜色的原因是因为您只能使用 0 到 1 之间的值表示颜色。
但是,三个象限使用超出该范围的负值。
解决方案是重新映射向量,使它们适合 0 到 1 的范围。
您可以通过将向量乘以0.5然后加上0.5来实现。这是一个可视化的样子:
现在,每个矢量都有独特的颜色。
当你需要使用它进行计算时,只需要将它重新映射回-1到1的范围即可。
以下是几种颜色以及它们在重新映射后代表的方向:
-
(0, 0):负 X 和 Y
-
(0.5, 0.5):没有移动
-
(0, 1):负 X 和正 Y
-
(1, 0):正 X 和负 Y
接下来,让我们看看如何在 Unreal 中创建矢量场。
创建矢量场
与雪道不同,您不会捕捉物体的形状。
相反,您将使用“画笔”在渲染目标上绘画。
这些将只是自定义矢量场的图像。
我将这些称为方向刷。
您可以使用粒子来代替使用蓝图绘制到渲染目标。
粒子将显示方向刷并从玩家发射。要创建矢量场,您只需使用场景捕获并仅捕获粒子。
这种方法的优点是创建轨迹非常容易。它还允许您轻松控制轨迹持续时间和大小等属性。
粒子还会产生半持久轨迹,因为它们在离开和重新进入捕获区域后仍然存在。
以下是您可以使用的方向笔刷的几个示例及其在草地上的效果。
请注意,在下面的示例中,粒子是不可见的。
首先,让我们创建将显示方向刷的材质。
创建方向材料
有两种创建方向刷的方法:
-
数学上:这是您定义材料内的方向和形状的地方。这样做的好处是不需要外部软件,简单的形状也很容易。
-
转换法线贴图:您可以在此处创建所需方向和形状的法线贴图。要转换为可用的矢量场,您只需删除蓝色通道。这种方法的优点是您可以轻松创建复杂的形状。下面是一个很难用数学方法复制的画笔示例。
对于本教程,您将以数学方式创建一个。
导航到Materials文件夹并打开M_Direction。
请注意,此材质的着色模型是Unlit。
这很重要,因为这将允许场景捕获捕获粒子而不会影响它们。
为简单起见,您将创建一种材质,使草从粒子中心移开。
为此,请创建以下内容:
现在您需要进行重新映射。为此,添加突出显示的节点并连接所有内容,如下所示:
接下来,让我们给它一个圆形。为此,添加突出显示的节点:
RadialGradientExponential控制圆的大小和硬度。
将它与粒子颜色相乘将允许您从粒子系统控制粒子的不透明度。
我将在下一节中对此进行更详细的介绍。
这是画笔的样子:
单击“应用”,然后关闭材料。现在您已经创建了材质,是时候创建尾迹粒子系统了。
创建轨迹粒子系统
导航到ParticleSystems文件夹并打开PS_GrassTrail。
为了节省时间,我已经创建了您需要的所有模块。
-
Spawn:Spawn值会影响您的踪迹的平滑程度。如果您的足迹看起来“波涛汹涌”,您应该提高Spawn值。对于本教程,我们将其保留为默认值 20。
-
Lifetime:草地恢复到默认状态前路径的持续时间
-
Initial Size:初始大小或者轨迹的大小
-
Color Over Life:由于您在材质中使用了粒子颜色,因此您可以在此处控制不透明度。您还可以调整 alpha 曲线来控制轨迹如何淡化。例如,您可以进行线性淡入淡出、缓入和/或缓出。对于本教程,我们将其保留为默认值,即线性淡入淡出。
-
Lock Axis:用于确保粒子面向场景捕获
-
Initial Rotation: 用于确保粒子朝向正确的轴(稍后会详细介绍)
首先,您需要设置材质。
选择Required模块,然后将Material设置为M_Direction。
当您在这里时,还将Sort Mode设置为PSORTMODE Age Newest First。
这种排序模式将确保较新的粒子将渲染在较旧的粒子之上。
如果不这样做,较旧的粒子可能会影响草,而不是较新的粒子。
接下来是路径的持续时间。
选择Lifetime模块并将Constant设置为5。
这将导致轨迹在五秒钟内消失。
接下来是轨迹大小。选择Initial Size模块并将Constant设置为(150, 150, 0)。
这将使每个粒子覆盖 150×150 的区域。
现在您需要确保粒子面向场景捕获。
由于本教程中的场景捕捉是从上方捕捉的,因此粒子需要面向正 Z 轴。
为此,请选择Lock Axis模块并将Lock Axis Flags设置为Z。
最后,您需要设置粒子的旋转。
目前,画笔中的颜色与它们所代表的方向不一致。
这是因为默认情况下,粒子系统将应用 90 度旋转。
要解决此问题,请选择Initial Rotation模块并将Constant设置为-0.25。
这会将粒子逆时针旋转 90 度。
这就是粒子系统所需的全部,让我们关闭它。
接下来,您需要将粒子系统附加到您想要创建轨迹的任何位置。
在这种情况下,您会将其附加到玩家角色。
附加粒子系统
导航到Characters\Mannequin并打开BP_Mannequin。
然后,创建一个Partice System组件并将其命名为GrassParticles。
接下来,您需要设置粒子系统。
转到 Details 面板并将Particles\Template设置为PS_GrassTrail。
因为玩家能在游戏中看到踪迹会很奇怪,最好将它对玩家隐藏起来。
为此,启用Rendering\Owner No See。
由于粒子系统附加到玩家(所有者),玩家不会看到它,但它仍然对其他一切可见。
单击编译,然后按播放。请注意粒子不出现在玩家相机中,但仍出现在渲染目标中。
现在,场景捕捉设置为捕捉一切。
显然这不太好,因为粒子是唯一应该影响草的东西。
在下一节中,您将学习如何只捕获粒子。
捕捉粒子
如果你现在捕捉粒子,你会在没有粒子的区域得到不需要的弯曲。
这是因为渲染目标的背景颜色为黑色。发生弯曲是因为黑色表示向负 XY 轴移动(重新映射后)。
为确保空白区域没有移动,您需要确保渲染目标的背景颜色为(0.5, 0.5, 0)。
一个简单的方法是创建一个巨大的飞机并将其连接到玩家身上。
首先,让我们为背景创建材质。
返回内容浏览器并打开Materials\M_Background。
然后,将常数(0.5, 0.5, 0)连接到Emissive Color。
注意:就像粒子材质一样,您计划捕捉的任何材质都需要未点亮。
单击“应用”,然后关闭材质。
返回BP_Mannequin,然后创建一个新的Plane组件。
将其命名为背景。
接下来,设置以下属性:
-
位置:(0、0、-5000)。这会将它放置得足够低,这样它就不会遮挡任何粒子。
-
规模:(100、100、1)。这会将其缩放到足够大以覆盖捕获区域。
-
材质: M_Background
就像粒子一样,如果玩家能看到它们下方有一个巨大的黄色平面,那就太奇怪了。
要隐藏它,请启用Rendering\Owner No See。
现在背景已经全部设置好,是时候捕捉粒子了。
您可以通过将粒子系统添加到场景捕获的仅显示列表来执行此操作。
这是场景捕获将独占捕获的组件列表。
使用仅显示列表
在您可以添加到仅显示列表之前,您需要一种方法来获取所有影响草的actor。
一种方法是使用标签。标签只是您可以分配给参与者和组件的字符串。
然后,您可以使用Get All Actors With Tag节点来获取具有特定标签的所有actor。
由于 player actor 应该影响草地,因此需要一个标签。
要添加标签,请先单击Class Defaults按钮。
然后,在Actor\Tags下创建一个新标签并将其命名为GrassAffector。
由于 show-only 列表只接受components,您还需要为影响草地的组件添加标签。
选择GrassParticles组件,然后在标签部分下添加一个新标签。
也将其命名为GrassAffector(您不必使用标签)。对背景组件执行相同的操作。
现在您需要将所有影响草地的组件添加到捕获的仅显示列表中。
单击Compile,然后关闭BP_Mannequin。
然后,打开Blueprints\BP_Capture。转到Event BeginPlay并添加突出显示的节点。
确保您也设置了指示的引脚。
这将遍历所有带有GrassAffector标签的角色。
然后它将检查演员是否有任何具有相同标签的组件并将它们添加到仅显示列表中。
接下来,您需要告诉场景捕获使用仅显示列表。
选择SceneCapture组件,然后转到Scene Capture部分。
将原始渲染模式设置为使用 ShowOnly 列表。
单击编译,然后关闭蓝图。如果您按Play,您会看到渲染目标现在只捕获粒子和背景平面。
接下来是您一直在等待的部分。是时候让草做交互了!
交互草
首先,您需要将渲染目标投影到草地上。
转到Materials文件夹并打开M_Grass。然后,创建下面的节点。
确保将纹理设置为RT_Capture。
由于您已将颜色重新映射到 0 到 1 范围,因此在使用它之前需要将其重新映射回 -1 到 1。
为此,添加突出显示的节点:
现在您有了弯曲方向,您需要一些方法将草向该方向旋转。
幸运的是,已经有一个名为RotateAboutAxis的节点。继续创建一个。
让我们从NormalizedRotationAxis引脚开始。
顾名思义,这是顶点将围绕其旋转的轴。
要计算这一点,您只需将弯曲方向与(0, 0, -1)进行叉积。
为此,添加突出显示的节点:
接下来是RotationAngle,它是顶点应该围绕枢轴旋转多少。
默认情况下,它需要一个介于 0 和 1 之间的值,其中 0 是 0 度,1 和 360 度。
要获得旋转角度,您可以使用弯曲方向的长度乘以最大旋转。
乘以0.2的最大旋转意味着最大旋转角度为72度。
计算PivotPoint有点棘手,因为一个草网格包含多个草叶。
这意味着您不能使用对象位置节点之类的东西,因为它会为所有草叶返回相同的点。
理想情况下,您将使用 3D 包在 UV 通道内存储枢轴点。
但是对于本教程,我们将只近似一个枢轴点。
这样做的方法是简单地从顶部向下移动一定的偏移量。
为此,添加突出显示的节点:
在本教程中,草大约有80个单位高,这就是我将PivotOffset设置为该值的原因。
接下来,您将需要执行两个掩码。
第一个面具将确保草叶的根部不会移动。
第二个掩码将确保捕捉区域外的草不受矢量场的影响。
遮罩
在本教程中,我设置了草的顶点颜色,使底部顶点为黑色,顶部顶点为白色。
要屏蔽掉根,只需将RotateAboutAxis的结果与Vertex Color节点相乘即可。
要遮盖捕获区域外的草地,请将先前的结果与突出显示的节点相乘:
单击“应用”,然后关闭材质。按下播放键开始四处奔跑,在草地上开辟小径!
写在最后
虽然本教程中的方法非常适用于像草这样的简单事物,但在需要更多动态的对象上使用它时可能会失败。缓解这种情况的一种方法是使用植物的物理版本。
点击查看这篇关于交互式树叶的帖子了解更多信息。