本篇博客给读者介绍关于如何实现模型的变形,在项目开发中经常会涉及到模型的变形操作,比如如下效果图:
第一部分准备工作
首先在Unity中建立一个场景,在场景中放置一个球体,这个球体可以使用Max工具建立,在球体上放几张Materials,效果如下所示:
下面开始具体实现,创建一个新的MeshDeformer脚本来处理变形, 就像立方体球体组件一样,它需要一个网格过滤器来处理。
using UnityEngine;
[RequireComponent(typeof(MeshFilter))]
public class MeshDeformer : MonoBehaviour {
}
将新组件脚本添加到球体上
接下来要读取网格数据,要进行任何变形,我们需要访问网格。 一旦我们有了网格,我们可以提取原始的顶点位置, 我们还必须在变形期间跟踪位移的顶点。
Mesh deformingMesh;
Vector3[] originalVertices, displacedVertices;
实现方式,在Start方法中获取网格及其顶点,并将原始顶点复制到移动的顶点。
void Start () {
deformingMesh = GetComponent<MeshFilter>().mesh;
originalVertices = deformingMesh.vertices;
displacedVertices = new Vector3[originalVertices.Length];
for (int i = 0; i < originalVertices.Length; i++) {
displacedVertices[i] = originalVertices[i];
}
}
我们使用的是Start,因此也可以在Awake中生成过程网格,这首先被调用, 这种方法依赖于其他组件在Awake中的执行顺序, 您还可以调整脚本执行顺序,以强制执行谁是第一个和最后一个。
另外,顶点随着网格变形而移动, 所以我们也要存储每个顶点的速度。
Vector3[] vertexVelocities;
void Start () {
…
vertexVelocities = new Vector3[originalVertices.Length];
}
现在我们有了支持网格变形的基本成分,代码下载地址:链接:http://pan.baidu.com/s/1bP4qi6 密码:h1h5
第二部分网格变形输入
我们需要一些方法来控制网格如何变形, 我们将使用用户输入,因此是交互式的, 每当用户触摸我们的对象时,我们将在这一点施加力量。
另外,MeshDeformer组件负责实际的变形,但不关心输入,我们应该创建一个单独的组件来处理用户输入, 给它一个可配置的输入力。
using UnityEngine;
public class MeshDeformerInput : MonoBehaviour {
public float force = 10f;
}
在这里要注意,将这个组件附加到相机是最有意义的, 我们不应该将其附加到变形网格物体,因为场景中可能有多个。
具体操作时,当按住默认鼠标按钮时,我们将处理用户的输入, 所以每当有点击或拖动时,只要用户按住。
void Update () {
if (Input.GetMouseButton(0)) {
HandleInput();
}
}
现在我们必须弄清楚用户指向的位置,我们通过将相机的光线投射到场景来做到这一点, 我们将抓住场景的摄像头,
并使用它将光标位置转换为光线。
void HandleInput () {
Ray inputRay = Camera.main.ScreenPointToRay(Input.mousePosition);
}
我们使用物理引擎投射射线并存储关于它所击中的信息, 如果射线与某物相撞,我们可以从被击中的对象中检索出MeshDeformer组件。
Ray inputRay = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(inputRay, out hit)) {
MeshDeformer deformer = hit.collider.GetComponent<MeshDeformer>();
}
如果我们射线击中了一些东西,那东西有一个MeshDeformer组件,那么我们可以改变一些东西! 所以继续在接触点增加变形力。
MeshDeformer deformer = hit.collider.GetComponent<MeshDeformer>();
if (deformer) {
Vector3 point = hit.point;
deformer.AddDeformingForce(point, force);
}
当然,假设我们的MeshDeformer组件具有AddDeformingForce方法。 所以添加这个方法。 不过,我们还没有做任何变形。 首先,只需从主摄像头画一个调试线就可以看出光线。
public void AddDeformingForce (Vector3 point, float force) {
Debug.DrawLine(Camera.main.transform.position, point);
}
当网状物体被用户戳戳和凹陷。 这要求接触点附近的顶点被推入表面。 然而,变形力没有固有的方向。 它将在各个方向均匀地应用。 这将导致平坦表面上的顶点被推开,而不是向内推。
我们可以通过将力点拉离表面来增加方向,稍微偏移已经确保顶点总是被推入表面, 接触点的法线可以用作偏移方向。
public float forceOffset = 0.1f;
void HandleInput () {
Ray inputRay = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(inputRay, out hit)) {
MeshDeformer deformer = hit.collider.GetComponent<MeshDeformer>();
if (deformer) {
Vector3 point = hit.point;
point += hit.normal * forceOffset;
deformer.AddDeformingForce(point, force);
}
}
}
第三部分基本变形
接下来,MeshDeformer.AddDeformingForce必须循环遍历所有当前位移的顶点,并将变形力单独应用于每个顶点。
public void AddDeformingForce (Vector3 point, float force) {
for (int i = 0; i < displacedVertices.Length; i++) {
AddForceToVertex(i, point, force);
}
}
void AddForceToVertex (int i, Vector3 point, float force) {
}
网格因为向每个顶点施加力而变形,当顶点被推动时,它们获得速度, 随着时间的推移,顶点都改变了它们的位置。 如果所有顶点都会遇到完全相同的力,整个物体将会移动而不改变其形状, 但他们没有。
我们需要知道每个顶点的变形力的方向和距离, 两者都可以从从力点指向顶点位置的向量导出。
void AddForceToVertex (int i, Vector3 point, float force) {
Vector3 pointToVertex = displacedVertices[i] - point;
}
现在可以使用反平方律找到衰减力, 将原始力除以距离平方:
Fv=