在本教程中,我们将学习如何使用 Blender 的 Rigid Body 模拟,用物体填充一个容器。
本教程的视频版本目前尚未提供该语言版本。英文版视频可通过以下链接观看:
视频文字稿
你好,大家好。
在本教程中,我们将学习如何使用 Blender 的 Rigid Body 模拟,用物体填充一个容器。
我使用的是 Blender 3.3 来制作这个教程。为了展示一个实际示例,我会使用我自己的一个资产,名为 “Pharma capsule pills and bottle”。不过,这并不是必需的,即使没有这个特定的模型,你也可以学习本教程的内容。
这里最重要的是理解这项技术。它可以用于以随机但可信的方式放置大量物体,不仅可以用在容器内部,也可以用在表面上。原理是相同的,你可以将它应用到药丸、糖果、金币或任何其他物体上。
我使用的这个 3D 模型实际上由多个彼此关联的对象组成。
我们不需要瓶盖,所以可以像我这样把它隐藏或删除。

我们现在有三个对象:容器、标签,以及一个代表所有药丸的对象,这些药丸目前已经合并成了一个单一对象。我们可以通过暂时隐藏容器和标签来更清楚地查看它们。

要创建模拟,我们需要先分离出一颗药丸,并为它添加一个 Rigid Body 物理组件。然后复制这个作为原型的药丸,并开始下落模拟。
选中 Pills 对象后,我切换到 Edit mode。接着,将鼠标光标放在某一颗药丸的一个顶点上,按下 L,也就是 “Linked”,这样就可以选中与光标附近元素相连接的所有几何体。

然后,我使用 CTRL+I 反转选择,并通过按 X,选择 Vertices,删除所有其他药丸。

回到 Object mode 后,我注意到 “Pills” 对象的 Origin 并没有位于唯一保留下来的那颗药丸的中心位置。我立刻通过右键点击,选择 “Set Origin”,然后选择 “Origin to Geometry” 来修正它。

我还会确认缩放变换已经被应用。检查方法很简单,只要看 Transform 面板中的 Scale XYZ 数值是否全部为 1。
如果不是 1,就需要按 CTRL+A,然后选择 “Apply Scale” 来应用缩放。
接下来,我再次显示容器,因为我想确认药丸的位置是否能够通过容器的开口。我打算从上方把药丸丢进容器里。
实际上,在当前这个位置,药丸是无法掉进容器的,所以我手动移动它,使其在顶视图中位于容器开口的内部。

连接药丸和容器的虚线表示药丸是容器的子对象。这在复制药丸并进行物理模拟时可能会产生问题,因此我需要在保持药丸位置和其他变换不变的情况下解除这两个对象之间的父子关系。为此,我选中药丸,按 ALT+P,然后在 “Clear Parent” 菜单中选择 “Clear and keep transformation”。

现在,药丸已经成为一个独立的对象。
在创建复制之前,我需要先为这颗药丸添加 Rigid Body 模拟组件,这样之后生成的所有副本都会自动具备相同的物理属性。

要添加 Rigid Body,我只需像刚才展示的那样,在 Physics Properties 面板中点击 “Rigid Body”。
新创建的 Rigid Body 默认类型是 Active,这正是我们需要的,因为该对象必须对重力作出反应。
为了在这个阶段观察物理模拟效果,我们在 Timeline 中将动画帧设置为 1,然后点击 Play。可以看到药丸下落并穿过容器,因为此时容器还没有参与场景的物理模拟。
为了让容器接住药丸,我们需要像对药丸那样为容器添加一个 Rigid Body 组件,但这一次要立刻将其类型改为 Passive,因为容器不应该掉落,只需要与其他对象发生物理交互。
在完成这些更改后,为了重新开始物理模拟,我们需要回到 Timeline 中动画的第一帧,然后再次点击 Play 按钮。
我们会立刻注意到,药丸下落并且开始与容器发生“交互”,但结果并不是我们期望的那样。它卡在了容器的开口处,悬浮在半空中,就好像开口是封闭的一样。

这是因为 Blender 在 Rigid Body 面板中用于计算容器碰撞的形状仍然是默认值 “Convex Hull”。这是一个封闭的盒状体,用于简化物理模拟引擎的计算。

为了解决这个问题,我将 Shape 参数的值从 “Convex Hull” 改为 “Mesh”。这样,Blender 就会根据容器的实际形状来近似计算碰撞体。
正如你可以想象的那样,这种模式对性能的消耗比较大,应该谨慎使用。在这个例子中,场景并不复杂,所以问题不大,但在其他情况下,你可能需要评估是否改用 Rigid Body 的 Shape 菜单中提供的其他选项。
至于药丸对象,例如你也可以选择 Capsule 形状,它对这种物体的近似效果非常好。不过在这里,我仍然保留默认选项,也就是 Convex Hull。
现在我们回到 Timeline 的第一帧,点击 Play,观察结果。这一次,药丸会落入容器内部,在底部反弹几次之后停下来。

请注意,当药丸接触到容器底部时,模拟开始变慢。这是因为 Blender 正在计算碰撞。
当我们添加更多药丸时,模拟会明显变得更慢,因为需要执行的计算数量会大幅增加。
现在我们再次回到模拟的第一帧。
我们的目标是生成一组堆叠在一起的药丸,然后让它们一起掉入容器中,由 Blender 自动计算它们与容器壁以及药丸彼此之间的碰撞,从而得到最终的位置。
实际上,生成多个药丸副本非常简单。我们只需要给药丸添加一个 Array modifier,将 X 和 Y 轴的 Relative Offset 设置为 0,而将 Z 轴的 Relative Offset 设置为 2。
我并不清楚这个容器里到底能装下多少颗药丸,因为我在视频开始时并没有去数,所以我先尝试使用 40 颗药丸,在 Array modifier 的 Count 参数中输入这个数值。

现在我们还不能运行模拟,因为这个 modifier 还没有被应用。此时所有的药丸仍然是实例,而不是独立的对象。

因此,我在该 modifier 的面板中点击 Apply 来应用它。
不过,即使这样,所有药丸仍然属于同一个对象。它们只是同一对象中的几何体,我们需要把它们变成相互独立的对象。
为此,我们只需要进入 Edit Mode,选中所有几何体,按下 P,然后在弹出的 Separate 菜单中选择 “By loose parts”。

按照这种方式,正如工具提示所说明的那样,我们将为每一个 “island” 创建一个对象,也就是为每一块独立的几何体创建一个对象。
我们回到 Object mode,可以注意到所有新的药丸实际上都是独立的对象,这一点在 Outliner 中也可以清楚地看到。
这些对象都已经像第一颗药丸一样正确地设置了 Rigid Body 组件,第一颗药丸起到了所有其他药丸的“原型”作用。

不过,我们也可以发现,每一个副本的 Origin 仍然位于第一颗药丸的 Origin 位置。
这个问题很容易解决。我们先选中其中一个副本,使其成为活动对象,然后再选中所有其他副本。我这里是使用 B 键配合鼠标来完成选择。接着,在选区上右键点击,在屏幕中出现的 “Object Context” 菜单中选择 “Set Origin”,然后选择 “Origin to Geometry”。

现在,所有药丸都是独立的对象,Origin 位置也都设置正确,并且它们都正确地配置了 Rigid Body 组件。
接下来,我们可以回到 Timeline 的第一帧,点击 Play 按钮来开始模拟。
在这种情况下,模拟将需要几分钟的时间才能完成。

实际上,对于这个容器来说,40 颗药丸可能还是太少了。
不管怎样,我们可以在动画播放的不同阶段暂停播放,来观察药丸的排列情况。
让我们跳转到动画的最后一帧,并暂时隐藏容器和标签。可以看到,药丸处于一个合理的静止状态,没有明显的空隙或相互穿插,因此这些动画帧已经足以得到一个不错的结果。

动画本身很有意思,但教程还没有完全结束。如果我们想导出这次物理模拟的结果,也就是将动画最后一帧中药丸在容器内的排列状态作为最终结果保存下来,我们就需要把这次物理模拟“固定”下来。
请注意,不能直接在 Physics 面板中移除药丸的 Rigid Body 组件,也不能把药丸彼此合并,或者与容器合并。这些操作都会导致我们失去物理模拟结果,从而不得不重新开始。
正确的做法是,将时间轴停留在我们希望作为最终状态的那一帧,比如最后一帧。然后选中一颗药丸,这样选区中至少会有一个活动对象,接着再选中所有药丸。为了完成这个选择,我们可以使用 B 键配合鼠标。

在选中了所有药丸,并且其中有一个是活动对象的情况下,我们需要在 Blender 的搜索框中查找 “Apply Transformation”。我这里是通过空格键呼出搜索框。然后,在 Rigid Body 分类下选择对应的选项,因为我们要应用的是 Rigid Body 的变换。

这一步还没有结束。我们需要再次打开搜索框,开始输入 “Remove Rigid Body”,因为现在我们希望从所有选中的药丸中移除 Rigid Body 组件。
在菜单中,我们应该选择 Object,Rigid Body,Remove 这一操作。
现在,模拟结果已经被固定下来。无论在 Timeline 的不同帧之间切换,我们都可以看到药丸不再移动,并且它们也不再具有 Rigid Body 物理组件。

此时,这个对象就可以被导出,用于其他场景中。
这些药丸都是独立的网格对象,并且每一个的 Origin 都位于其几何体的中心位置。你可以根据自己的需求决定是保持这种状态,还是将它们分组、建立父子关系,甚至合并为一个对象。
本教程到此结束。我们下次再见。