网格合并之后物体的位置改变了_UnityMesh编程(4)网格变形

Mesh 翻译文章汇总

AmyBoy:Unity Mesh Basics(Unity Mesh基础)系列翻译汇总​zhuanlan.zhihu.com
90375a2f300e1045b3a441b2a5348a95.png

原作者:Jasper Flick

由于水平有限,可能翻译的会有错误,请大家在评论区指出,我会及时更新改正。

原文链接

Mesh Deformation, a Unity C# Tutorial​catlikecoding.com
cb555bd1e6f7a9e20d2afe28ae9b8037.png

本教程的目标

  • 将光线投射到对象上并绘制调试线
  • 将力转换为顶点的速度
  • 通过弹性和阻尼保持形状
  • 调整对象的转换

本教程是关于网格变形的介绍。我们将把一个网格变成有弹性的物体,并实现戳动的效果。

本教程是上篇教程的延续,我们将使用立方体球体作为我们的测试模型。

本教程使用的unity版本为Unity2018.4.1。

01f6ea928191e2adb9243952db53b68d.png
球体受压力作用

1 场景设定

我们从一个中心有一个立方体球体的场景开始。你可以从零开始,也可以继续立方体球体的场景,删除所有不需要的东西。

为了获得一个平滑的变形,球体应该包含相当数量的顶点。设置球体的网格大小为20,半径为1。

9e555f597e7ca84910631292cda3df61.png

2 网格变形器

创建一个新的MeshDeformer脚本来处理变形。与立方体球体组件一样,它也需要一个MeshFilter组件。

using 

将新组件添加到立方体球体。

a18868a6ee52e206967ad10f5f38a3f7.png

注意,我们只需要一个网格过滤器。我们不用关心它如何获得网格。现在我们使用的是代码生成的球体。

  • 2.1 准备

要执行任何变形,我们需要使用网格。一旦有了网格,就可以提取原始顶点位置。我们还必须跟踪在变形过程中移动的顶点。

Mesh 

在“Start”方法中获取网格及其顶点,然后将原始顶点复制到位移的顶点。

void 

我们这里使用Start来获取网格,在Awake方法中生成网格,这样可以保证在获取网格之前先生成网格。这种方法依赖于在其他组件的Awake方法中来处理这些内容,因此并不能保证先后顺序。你还可以调整脚本执行顺序,以执行谁先谁后的操作。这里我们并不做任何修改,因为我们脚本相对简单。

  • 2.2 顶点速度

顶点的位置随着网格的变形而改变。我们还需要存储每个顶点的速度。

Vector3

现在我们有了支持网格变形的基本元素。

3 网格变形器输入

我们需要一些方法来控制网格是如何变形的。我们将使用用户输入,所以它是交互式的。无论何时用户触摸我们的对象,我们都会在那一点施加一个力。

MeshDeformer组件负责实际的变形,但不关心输入方法。我们应该创建一个单独的组件来处理用户输入。给它一个可配置的输入力。

using 

将这个组件附加到相机上是最有意义的,因为它代表了用户的观点。我们不应该把它附加到变形的网格对象上,因为在场景中可能有多个这样的对象。

04e140d19a57fd0c05ef3b9d0d29b4fb.png
  • 3.1 检测输入

默认当用户按住鼠标按钮时,我们将处理用户的输入。因此,只要有用户单击或拖动即可。

void 

现在我们要找出用户指向哪里。我们通过将一束射线从摄像机投射到场景中来实现这一点。我们将抓取场景的主摄像机并使用它将点击位置转换为射线。

void 

我们使用物理引擎来发出射线并存储有关射线击中目标的信息。如果射线与什么东西相撞,我们可以从被击中的物体中检索出MeshDeformer组件。

Ray 
Rhysics.Raycast 是如何工作的?
它是一种将射线投射到三维场景中的静态方法。它有各种重载方法,最简单的方法是有一个ray参数并返回它是否击中了什么东西。
我们使用的方法有一个额外的参数。它是RaycastHit类型的输出参数。这是一个结构体,它包含有关被击中目标和接触点的信息。
  • 3.2 使用力

如果我们撞到一个物体,而这个物体有一个MeshDeformer组件,那么我们可以使这个物体变形!所以在接触点加一个变形力。

MeshDeformer 

当然,这假设我们的MeshDeformer组件有一个AddDeformingForce方法。添加这个方法。我们先不要变形。首先,画一条调试线从主摄像机到点,以可视化射线。

public 

c61fb3e2abbb2b6c7c8316a1c91a18ba.png
在哪里可以看到调试线?
它出现在场景视图中,所以你必须在游戏模式中保持游戏视图和场景视图可见。

3.3 力的偏移

我们想要得到的效果是球体表面被用户戳动后,发生凹陷。这要求接触的顶点被推入表面。然而,变形力并没有一个固定的方向,它将被平等地应用于各个方向。这将导致一个平面上的顶点被推开,而不是推向球体内部。

我们可以通过把接触点拉离表面来增加一个方向。一个微小的偏移量已经保证了顶点总是被推入表面。可以通过更改接触点处的法线来更改每个顶点的偏移方向。

c01b1bf3ccc841b29d560bba0bb452cd.png
用偏移量改变力的方向
public 

84d28e2df81cf15d8a46e5c42977009b.png

4 基本的变形

是时候做一些真正的位移了。MeshDeformer.AddDeformingForce 方法必须循环遍历所有当前移动的顶点,并将变形力分别应用于每个顶点。

public 
  • 4.1 将力转换为速度

网格发生变形是因为一个力被应用到每个顶点,当顶点被这个力推动时,它们将获得一个速度。随着时间的推移,所有的顶点的位置都发生了改变。如果所有的顶点都受到完全相同的力,那么整个物体就会在不改变形状的情况下移动。

想想爆炸,如果你在爆炸点正中心,那么你就死定了(因为你承受的力太大)。如果你在爆炸点的附近,你会被冲击力推倒(力比中心点较小)。如果你在很远的地方,那么你不会受影响。力随距离增大而减弱,再加上方向的不同,这种力的衰减导致了物体的变形。

所以我们需要知道每个顶点的变形力的方向和距离。两者都可以从力点指向顶点位置的向量中得到。

void 

现在可以用平方反比定律求出减弱的力。只要把原来的力除以距离的平方。

22e64cd74bc62dc59cc70ca2d3a1b32e.png

实际上,我除以1加上距离的平方

00907f3334278f8cf0aa50a93801f98e.png

这就保证了力在距离为0时达到最大强度。否则,力将在距离为1时达到最大强度,并且越接近该点,力就会变的无穷大。(如下图)

1830dd86720a69db3632de9e000c3983.png
Vector3 

现在我们有了力,我们可以把它转换成速度。

力首先转换为加速度

a394806b0a71908877edd0aabe0dc7fb.png

然后通过加速度得出速度

f2df4539052091dbdc997662687e9836.png

为了简单起见,我们将忽略质量,就好像每个顶点的质量都是1一样。最后我们得到速度的公式为:

7b1dd60e455d56f58712a0d9e245693c.png
Vector3 

在这一点我们有一个速度,但还没有方向。我们通过对开始时的向量进行归一化得到它。然后我们可以把结果加到顶点速度上。

Vector3 
  • 4.2 移动顶点

现在顶点有速度,我们可以移动它们。添加一个更新方法来处理每个顶点。然后,将置换顶点分配到网格上,这样网格就会发生变化。因为网格的形状不再是常数,我们也必须重新计算它的法线。

void 

更新一个顶点就是调整它的位置。

26dae111c77e80a9b82b6f6c707ffa45.png
void 
顶点是否一直更新?
是的,每次更新,所有的顶点都被置换,分配到网格,法线被重新计算。即使没有施加力。如果用户没有变形的网格,那么可以认为是浪费时间。所以只有在不断变形网格的时候才使用这个。

ac9b81226eaf502ea619e02183062cf9.png
变形的网格

5 保持变形效果

现在只要我们对顶点施加一个力,它们就会开始移动。并且会一直移动下去,导致物体的原始形状丢失。现在让对象回弹为原来的形状。

真实的物体是固体的,在变形时被压缩和拉伸。它们有抵抗这种变形能力,也有恢复原状能力。

我们没有实际的体积,只有一个顶点的集合来描述一个曲面。我们不能用它来进行真实的物理模拟。但这不是问题。我们真正需要的是在视觉上满足这种效果。

  • 5.1 弹性

我们跟踪每个顶点的原始位置和变形位置。假设我们在每个顶点的两个位置之间附加弹簧机制。每当变形的顶点从原来的顶点移开时,这个弹簧机制就会把它拉回来。变形顶点越远,弹簧机制的拉力就越大。

6a78ddc4adcfb5e12af2651fa1d96c1a.png
位移的顶点被拉回来

我们可以直接使用位移矢量作为速度调整,乘以一个可配置的弹簧力。这很简单,看起来也很不错。每次顶点更新时,我们都会这样做。

public 

1bdb927563918cd5788b39318afcaced.png
增加弹簧力
  • 5.2 阻尼

现在,我们的顶点可以抵抗变形并跳回到其原始位置。但是会一直来回振荡。发生这种情况是因为弹簧在顶点校正时不断拉动,从而提高了速度。它只有在移动一段距离后的时候才会减速,此时顶点又开重新赋值,弹簧再一次被相反的方向拉动,所以导致了这种现象。

我们可以通过不断降低顶点的速度来防止这种永恒的振荡。这种阻尼效应是阻力、惯性等的替代品。这是一个随着时间而降低速度的简单因素。

9fcc1959ebfa4d2deb841ed5418637de.png

阻尼越高,物体的弹性就越小,并且速度越慢。

public 

5b0e21566152d960d33b9ab2ef0144bd.png
增加阻尼

6 处理转换

我们的网格变形已经处理完毕,但当我们移动和旋转球体时,你会发现变形力的施加是不正确的。这是因为我们所有的计算都是在局部空间中进行的,而变形力是施加在世界空间下的。

我们必须调整物体之间的转换。我们将变形力的位置从世界空间转换为局部空间。

public 

1bbee8577902a46ad0de669eed5f1b70.png
不同的比例
  • 6.1 调整变形刻度

这个力现在作用在正确的地方,但是有些方面仍然是错误的。均匀地缩放球体,你会注意到变形的比例是相同的,这是不对的。大小物体应服从相同的物理规律。

我们必须根据对象的规模调整变形。首先,我们需要知道它的均匀尺度。我们可以通过检查变换的一个局部尺度轴来找到它。每次更新时都这样做,这样我们就可以在某种程度上处理动态改变其规模的对象。

float 
那不均匀的尺度呢?
你可以使用一个三维向量来代替单一的比例值。然后分别调整每个尺寸。但实际上,你并不想处理不均匀的尺度。

现在修改AddForceToVertex方法通过缩放顶点向量的统一比例。这确保我们使用正确的距离。

void 

然而,对于没有缩放的物体,速度是正确的。由于我们的对象实际上是缩放的,我们还必须调整顶点的移动。这次我们用除法代替乘法。

displacedVertices

bfa7ce9438b79201c95dcf9f5d2d8e68.png
不同的尺寸,相同的物理效果

这就是我们最终的结果。一种变形网格,可以在任何位置、旋转和均匀尺度下工作。请记住,这是一个相对简单的视觉效果。它不是一个软体物理模拟。物体的碰撞器不会改变,所以物理引擎不会察觉到物体的形状的改变。

本文git源码:

Amy6922/UnityMeshDemo​github.com
e6d07fd3a1330b54e174a1e2b7163968.png

Mesh系列文章已更新完毕。读者想进一步了解,有关Unity渲染方面知识。

请移步

HipHopBoy:Unity Scriptable Render Pipeline (Unity SRP 系列翻译汇总​zhuanlan.zhihu.com
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值