Unity Mesh基础系列(四)mesh变形(制作一个弹力球)

Wesley13
• 阅读 1282

目录

1 场景搭建

2 Mesh调节器

2.1 准备

2.2 顶点速度

3 Mesh 调节器的输入

3.1 输入检测

3.2 施加力

3.3 力偏移

4 最基础的变形

4.1 将力转换为速度

4.2 移动顶点

5 保持形状

5.1 弹簧

5.2 阻尼

6 处理变换

6.1 调整缩放

本章内容重点

向对象投射射线并绘制调试线。

将力转换为顶点速度。

用弹簧和阻尼保持形状。

补偿对象转换。

这篇教程的主要内容是介绍一下网格变形。把网格变成一个弹力球,然后戳它。

本教程是CatLikeCoding系列的一部分,原文地址见文章底部。“原创”标识意为原创翻译而非原创教程。

这篇教程是基于上一篇 立方体球 的。它复用了同一个网格,并在此基础上做增加更多的测试模型。本示例适用于Unity5.0.1及以上版本。

Unity Mesh基础系列(四)mesh变形(制作一个弹力球)

(施加了一些按压操作)

1 场景搭建

我们会从一个以单个立方体球体为中心的场景开始。你既可以从头开始,也可以复用上一章 立方体球 的场景,然后删除所有多余的东西。

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

Unity Mesh基础系列(四)mesh变形(制作一个弹力球)

Unity Mesh基础系列(四)mesh变形(制作一个弹力球)

(从一个规则的立方体球开始)

2 Mesh调节器

创建一个新的MeshDeformer脚本来处理变形。就像立方体球面组件一样,它需要一个mesh filter组件来处理。

Unity Mesh基础系列(四)mesh变形(制作一个弹力球)

Unity Mesh基础系列(四)mesh变形(制作一个弹力球)

(添加完组件之后的展示)

这里需要注意,和前面的章节不同,这里我们只需要mesh filter。这里我们不在乎它是如何得到网格数据的。而现在正在使用我们的程序生成的 立方体球,但其实它可以是任何网格。

2.1 准备

要进行任何的变形,我们都需要访问Mesh。一旦我们有了Mesh,就可以提取到原始的顶点位置。除此之外,还必须跟踪变形过程中的位移点。

Unity Mesh基础系列(四)mesh变形(制作一个弹力球)

在Start方法中对网格及其顶点进行赋值,并将原始顶点复制到移位点。

Unity Mesh基础系列(四)mesh变形(制作一个弹力球)

之所以要使用Start,是因为我们需要在Awake时生成过程网格,因为Awake总是在Start前被调用。这种方法依赖于其他组件在Awake时处理好他们自身的东西,Awake本身的先后顺序并不能保证,所以你其实可以在Unity的设置里自己调节脚本执行顺序,以强制执行第一和最后的脚本。

2.2 顶点速度

当网格变形是由顶点移动造成的。所以我们还必须存储每个顶点的速度。

Unity Mesh基础系列(四)mesh变形(制作一个弹力球)

这样我们就有了支持网格变形的基本成分了。

3 Mesh 调节器的输入

我们需要一些手段来控制mesh如何变形,这里就要用到用户输入,也就是交互。不管什么时候用户触碰了物体,都会给这个点施加一个力。

MeshDeformer 类会处理实际的变形逻辑,但是它并不关心输入。我们需要创建另外一个单独的类来处理用户输入。给这个组件配置一个可以自定义输入的力变量。

Unity Mesh基础系列(四)mesh变形(制作一个弹力球)

把这个组件附加到相机上,因为它代表了用户的视角关注点。绑在其他物体上也行,但不应该绑定到需要变形的网格对象上,因为场景中可能有很多网格对象。

Unity Mesh基础系列(四)mesh变形(制作一个弹力球)

(把 mesh deformer input组件绑定在摄像机上)

3.1 输入检测

当按住鼠标按钮时,我们就需要处理用户的输入。并根据后续的操作,可以得到点击或拖动操作。

Unity Mesh基础系列(四)mesh变形(制作一个弹力球)

拿到鼠标事件之后,还需要找出用户的指向位置。可以通过把镜头中的光线投射到场景中来实现这一点。通过抓取场景的主摄像机,并使用它将光标位置转换为光线。

Unity Mesh基础系列(四)mesh变形(制作一个弹力球)

使用物理引擎来投射射线并存储关于它碰撞信息。如果光线与某物发生接触,就可以从被击中的对象中检索 MeshDeformer 组件。

Unity Mesh基础系列(四)mesh变形(制作一个弹力球)

Physics.Raycast 是如何工作的?

这是一个静态的方法,用来把射线投影到3D的场景里。它有很多个变体方法,最简单的一个就是传递一个ray参数,然后返回是否击中了对象。

我们使用的版本有一个附加参数。它是RaycastHit类型的输出参数。里面包含被击中的信息和接触点的结构。

3.2 施加力

如果我们击中了一个物体,并且该某物有一个 MeshDeformer 组件的话,就可以使它变形了!因此,现在需要在接触点增加变形的力。

Unity Mesh基础系列(四)mesh变形(制作一个弹力球)

当然,这段代码调用了 MeshDeformer 组件有AddDeformingForce方法。所以需要在相应的组件里添加这个方法。

不过,我们暂时不做任何变形。而是画一条调试线从主摄像机到点,以便以可视化的力的情况。

Unity Mesh基础系列(四)mesh变形(制作一个弹力球)

Unity Mesh基础系列(四)mesh变形(制作一个弹力球)

3.3 力偏移

我们想要实现的效果是网格被用户按压和凹陷。

这就需要将接触点附近的所有顶点推进表面里。然而,变形的力并没有指定内在的方向。它会平等地应用于各个方向。这将会导致平面上的顶点被向周围推开,而不是向内推。

通过将力点从表面拉出,我们可以增加一个方向。一个轻微的偏移就可以保证顶点总是被推入表面。而接触点处的法线正好可以作为偏移方向。

Unity Mesh基础系列(四)mesh变形(制作一个弹力球)

(不同力的方向产生的偏移效果)

Unity Mesh基础系列(四)mesh变形(制作一个弹力球)

Unity Mesh基础系列(四)mesh变形(制作一个弹力球)

Unity Mesh基础系列(四)mesh变形(制作一个弹力球)

4 最基础的变形

现在可以开始做一些变形效果了。MeshDeformer .AddDeformingForce 必须要循环遍历所有当前移位的顶点,并将变形力分别应用到每个顶点才可以。

Unity Mesh基础系列(四)mesh变形(制作一个弹力球)

4.1 将力转换为速度

一个力被施加到网格的每个顶点之后,网格就会变形。当顶点被推入的时候,它们需要获得一个速度。随着时间的推移,顶点就会改变它们的位置。如果所有顶点都经历完全相同的力的话,整个物体就会移动而不改变其形状了。

想象一个爆炸。如果你在边上,就必死无疑。但如果你离的远些,可能会被气浪撞倒。而当你在很远的地方的时候就几乎受到影响。

也就是说,力是会随着距离的推移而减弱的。结合方位的差异,就会产生方向上的衰减,这也是造成物体变形的原理。

所以我们需要知道每个顶点变形力的方向和距离,而两者都可以从力点指向顶点位置的矢量导出。

Unity Mesh基础系列(四)mesh变形(制作一个弹力球)

可以用逆平方定律找到衰减力,只需将力除以距离平方即可。

地址:https://en.wikipedia.org/wiki/Inverse-square\_law

Unity Mesh基础系列(四)mesh变形(制作一个弹力球)

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

Unity Mesh基础系列(四)mesh变形(制作一个弹力球)

这可以确保当距离为零的时候,力处于全力状态。否则,力就会在距离1的地方达到最大的强度,并且越靠近点,它就会朝无穷远的方向飞去。

Unity Mesh基础系列(四)mesh变形(制作一个弹力球)

(红色是改良后的力衰减)

Unity Mesh基础系列(四)mesh变形(制作一个弹力球)

现在我们有了力,然后可以把它转换成速度。实际上,力首先转化为加速度。

Unity Mesh基础系列(四)mesh变形(制作一个弹力球)

然后加速度会改变速度。

Unity Mesh基础系列(四)mesh变形(制作一个弹力球)

为了计算简单,我们将忽略质量,就好像它是均匀分布的,每个顶点的都一样。所以最后的速度变化是:

Unity Mesh基础系列(四)mesh变形(制作一个弹力球)

Unity Mesh基础系列(四)mesh变形(制作一个弹力球)

在该点上,我们已经有了一个速度了,但还没有方向。这可以通过规范最开始使用的法线向量来得到。然后我们可以把结果加到顶点速度上。

Unity Mesh基础系列(四)mesh变形(制作一个弹力球)

4.2 移动顶点

顶点有速度之后,我们就可以移动它们了。添加一个更新方法来处理每个顶点的位置。然后,将位移顶点分配给网格,使其实际发生变化。因为网格的形状不再是恒定的,我们也必须重新计算它的法线。

Unity Mesh基础系列(四)mesh变形(制作一个弹力球)

更新顶点是调整其位置可以通过:

Unity Mesh基础系列(四)mesh变形(制作一个弹力球)

Unity Mesh基础系列(四)mesh变形(制作一个弹力球)

这些顶点会一直更新下去吗?

是的,每个update 所有的顶点都被移位,然后分配给网格,然后法线被重新计算。即使没有施加力,因为这个函数就是每帧执行的。但是如果用户没有让网格变形,这个方法可以被认为是浪费性能。因此,只有当网格处于不断变形的时候,再使用这个方法。

Unity Mesh基础系列(四)mesh变形(制作一个弹力球)

(累计的速度)

5 保持形状

一旦我们对顶点施加了一些力,他们就会开始移动,但他们并不会停下来。如果它们不停地移动的话,物体的原始形状就会消失。现在我们来让物体进行回弹以恢复到原来的形状。

真实的固态物体,在变形的过程中会被压缩和拉伸,但是它们自身能抵抗这种变形。一旦不受干扰,就可以恢复到原来的形状。

而我们并没有真正的体积,只是一个描述表面的顶点集合而已。所以我们不能用它来进行真实的物理模拟。但这并不是问题,我们真正需要的是看上去像就可以了。

5.1 弹簧

在前面,我们已经能跟踪到每个顶点的原始和变形位置。假设我们在每个顶点的两个版本之间附加一个弹簧。每当变形的顶点被移离原始顶点时,弹簧就会把它拉回来。变形顶点越远,弹簧的拉力就越大。

Unity Mesh基础系列(四)mesh变形(制作一个弹力球)

(偏移的顶点被拉回)

我们可以直接利用位移矢量作为速度调整,乘以一个可配置的弹簧力。简单,并且听上去也不错。

Unity Mesh基础系列(四)mesh变形(制作一个弹力球)

Unity Mesh基础系列(四)mesh变形(制作一个弹力球)

Unity Mesh基础系列(四)mesh变形(制作一个弹力球)

(变形后反弹)

5.2 阻尼

顶点现在抵抗变形,跳回原来的位置了。但是他们跳得太快了,而且不停地弹。这是因为弹簧力一直在拉它,而顶点却在自我校正,从而提高了它的速度。而且它只有在向后移动很远后才会减速。

这里可以通过不断地减缓顶点的速度来防止这种永恒的振荡。这种阻尼效应可以替代电阻、阻力、惯性等。是一个简单的因素,它会随着时间的推移而降低速度。

Unity Mesh基础系列(四)mesh变形(制作一个弹力球)

阻尼越高,物体的弹性就越小,表现的速度也就越慢。

Unity Mesh基础系列(四)mesh变形(制作一个弹力球)

Unity Mesh基础系列(四)mesh变形(制作一个弹力球)

Unity Mesh基础系列(四)mesh变形(制作一个弹力球)

(恢复到它先前的形状)

6 处理变换

网格变形的功能现在是完整的了。但是如果我们要对物体的transform进行变换的话,还需要一些些处理。现在所有的计算都是在局部空间进行的。移动或旋转我们的球体。你会发现变形力会被不正确地施加。

我们必须补偿物体的transformation。通过将变形力的位置从世界空间转换到局部空间来实现这一点。

Unity Mesh基础系列(四)mesh变形(制作一个弹力球)

Unity Mesh基础系列(四)mesh变形(制作一个弹力球)

(正确的位置,但是不同的缩放)

6.1 调整缩放

力现在被施加在正确的位置,但是其他的地方仍然是错误的。向上或向下均匀地缩放球体。你会注意到变形鳞片的数量是一样的。但这是不对的。小的和大的物体应该受到同样的物理的影响才对。

所以过程中就必须补偿对象的缩放。首先,我们需要知道它的统一缩放值。这可以通过检查一个transform的local scale轴来找到。而且每次更新都要这么做,这样我们就可以在某种程度上处理那些动态改变其规模的对象了。

Unity Mesh基础系列(四)mesh变形(制作一个弹力球)

如果不统一的缩放该怎么办?

你可以用一个3D向量代替一个单一的值。然后分别调整每个维度的补偿。但实际上,你不会想处理不均匀的尺度。

现在修正 AddForceToVertex ,方法是通过统一标度缩放点 pointToVertex 。这确保了我们使用正确的距离。

Unity Mesh基础系列(四)mesh变形(制作一个弹力球)

对Update顶点中的位移也做同样的操作。现在我们的速度是正确的。

Unity Mesh基础系列(四)mesh变形(制作一个弹力球)

对于一个没有缩放的物体,我们的速度现在是正确的。但由于我们的对象实际上是缩放的,我们也必须调整顶点的运动。这一次我们需要除以它,而不是乘。

Unity Mesh基础系列(四)mesh变形(制作一个弹力球)

Unity Mesh基础系列(四)mesh变形(制作一个弹力球)

(不同的缩放值,相同的物理表现)

现在所有工作都完成了。在任意位置、旋转和均匀比例上都能正常展示的变形网格。请记住,这是一个简单和相对廉价的视觉效果。这并不是一个软体物理模拟。物体的碰撞也不会改变,所以物理引擎还是不知道物体的形状的。

欢迎扫描二维码,查看更多精彩内容。点击 阅读原文 可以跳转原教程。

本文翻译自 Jasper Flick的系列教程

原文地址:

https://catlikecoding.com/unity/tutorials

Unity Mesh基础系列(四)mesh变形(制作一个弹力球)

本文分享自微信公众号 - 壹种念头(OneDay1Idea)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
4个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
10个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这