Unity3D实现模型随机切割

本文实例为大家分享了Unity3D实现模型随机切割的具体代码,供大家参考,具体内容如下

模型切割的效果图如下:

我们都知道,模型是由一个个小三角形面组成的,因此我们不妨将问题简化,先实现个小目标,完成单个三角形的切割,甚至继续细分成求一条线段与某个平面的交点。

三角形与切割平面的位置关系主要有以下三种:

1. 三角形与切割平面有两个交点,一个交点在顶点上,一个交点在边上。这时,原有的三角形将被分成两个三角形,分别为013、042。

2. 三角形与切割平面有两个交点,两个交点都在边上。这时,原有的三角形将被分成三个三角形,分别为:034、561、126。

3. 其它(无交点、三角形完全在切割平面上、一条边在切割平面上)

那么,我们如何求线段与平面的交点呢?

即已知平面ABC,线段P0P1,求交点P。

故:

N为平面ABC法向量,可得:N= AB X AC;

P在P0P1上,可得:P = P0 + t * L;   L = P1 - P0;

又因P在平面ABC上,可得: N * PA = 0;

代入得:

=> N * (A - P0 + t * L) = 0;

=> N * (A - P0)   + t * N * L = 0;

=> t =  (P0 - A) * N / (N * L);

=> t =  (P0 - A) * (AB X AC) / (N * (P1 - P0));

最终求得P坐标,因为P0P1是线段而非直线,所以我们需要再做个判断,P是否在线段P0P1中间,用向量点乘可轻易实现。

具体代码如下,其中abc为切割平面上的三个顶点(确保必定构成一个平面):

public static GameObject[] Split(GameObject obj, Vector3 a, Vector3 b, Vector3 c)
 {
  if(obj == null)
  {
  return null;
  }
  MeshFilter filter = obj.GetComponent<MeshFilter>();
  if(filter == null)
  {
  return null;
  }

  //切割面位置调整为相对于模型的本地坐标
  a = a - obj.transform.position;
  b = b - obj.transform.position;
  c = c - obj.transform.position;

  List<Vector3> vertices = new List<Vector3>(filter.mesh.vertices);
  List<int> triangles = new List<int>(filter.mesh.triangles);
  List<Vector2> uvs = new List<Vector2>(filter.mesh.uv);

  for (int i = 0; i < filter.mesh.triangles.Length; i = i + 3)
  {
  //取三角形;
  Vector3[] p = new Vector3[3];
  for (int m = 0; m < 3; m++)
  {
   p[m] = filter.mesh.vertices[filter.mesh.triangles[i + m]];
  }

  //0 1 2
  //1 2 0 ==> 切割每条边,判断是否有交点,如有交点,在交点处生成两个新的顶点:L/R
  //2 0 1
  //凡是顶点与平面相交的,一律以新顶点替换
  //判断以交点为其中一个顶点的三角形在面的哪一面
  //指定:交点到其它顶点形成的向量与平面法向量方向一致,则使用L,否则使用R
  //无交点
  //其中一个顶点在平面上
  //其中的一条边在平面上
  //整个三角形都在平面上

  List<Point> cross = new List<Point>();

  for (int m = 0; m < 3; m++)
  {
   //求线段与面的交点-无交点返回null
   Point tpoint = MathfUtils.LineCrossPlane(p[m], p[(m + 1) % 3], a, b, c);

   //排除线段两个端点与平面相交的情况;
   if (MathfUtils.PointAtPlane(p[m], a, b, c) || MathfUtils.PointAtPlane(p[(m + 1) % 3], a, b, c))
   {
   cross.Add(null);
   continue;
   }

   cross.Add(tpoint);
  }

  int tcount = cross.FindAll(t => t != null).Count;

  if (tcount == 0)
  {
   //完全没交点;
   continue;
  }

  if(tcount == 1)
  {
   //只与一条边有交点;
   //012 tidx = 0 交点x在 0-1上,则有三角形 02x 12x
   //012 tidx = 1 交点x在 1-2上,则有三角形 01x 02x
   //012 tidx = 2 交点x在 2-3上,则有三角形 01x 12x
   int tidx = cross.FindIndex(t => t != null);
   if(tidx < 0)
   {
   continue;
   }
   vertices.Add(cross[tidx].GetVector3());
   vertices.Add(cross[tidx].GetVector3());
   Vector2 tuv = (uvs[triangles[i + tidx]] + uvs[triangles[i + (tidx + 1) % 3]]) * 0.5f;
   uvs.Add(tuv);
   uvs.Add(tuv);

   //计算法线,保证新三角形与原来的三角形法线保持一致;
   Vector3 nor0 = Vector3.Cross((p[1] - p[0]).normalized, (p[2] - p[0]).normalized);

   //改一个
   triangles[i + 0] = filter.mesh.triangles[i + tidx];
   triangles[i + 1] = filter.mesh.triangles[i + (tidx + 2) % 3];
   triangles[i + 2] = vertices.Count - 2;
   Vector3 nor1 = Vector3.Cross((vertices[triangles[i + 1]] - vertices[triangles[i + 0]]).normalized,
   (vertices[triangles[i + 2]] - vertices[triangles[i + 0]]).normalized);
   if(Vector3.Dot(nor0, nor1) < 0)
   {
   //使用法线方向判断三角形顶点顺序是否与原来一致
   int tpidx = triangles[i + 1];
   triangles[i + 1] = triangles[i + 2];
   triangles[i + 2] = tpidx;
   }

   //新增一个
   triangles.Add(filter.mesh.triangles[i + (tidx + 1) % 3]);
   triangles.Add(filter.mesh.triangles[i + (tidx + 2) % 3]);
   triangles.Add(vertices.Count - 1);
   Vector3 nor2 = Vector3.Cross((vertices[triangles[triangles.Count - 2]] - vertices[triangles[triangles.Count - 3]]).normalized,
   (vertices[triangles[triangles.Count - 1]] - vertices[triangles[triangles.Count - 3]]).normalized);
   if (Vector3.Dot(nor0, nor2) < 0)
   {
   int tpidx = triangles[triangles.Count - 1];
   triangles[triangles.Count - 1] = triangles[triangles.Count - 2];
   triangles[triangles.Count - 2] = tpidx;
   }

  }

  if(tcount == 2)
  {
   //与两条边有交点;
   //012 tidx = 0 交点xy不在 0-1上,则有三角形 xy2 xy1 01y
   //012 tidx = 1 交点xy不在 1-2上,则有三角形 xy0 xy2 12y
   //012 tidx = 2 交点xy不在 2-3上,则有三角形 xy1 xy0 01y
   // x-y-tidx+2 是独立三角形,使用一组顶点
   int tidx = cross.FindIndex(t => t == null);
   if (tidx < 0)
   {
   continue;
   }

   //计算法线,保证新三角形与原来的三角形法线保持一致;
   Vector3 nor0 = Vector3.Cross((p[1] - p[0]).normalized, (p[2] - p[0]).normalized);

   //x
   vertices.Add(cross[(tidx + 1) % 3].GetVector3());
   vertices.Add(cross[(tidx + 1) % 3].GetVector3());
   Vector2 tuvx = (uvs[triangles[i + (tidx + 1) % 3]] + uvs[triangles[i + (tidx + 2) % 3]]) * 0.5f;
   uvs.Add(tuvx);
   uvs.Add(tuvx);
   //y
   vertices.Add(cross[(tidx + 2) % 3].GetVector3());
   vertices.Add(cross[(tidx + 2) % 3].GetVector3());
   Vector2 tuvy = (uvs[triangles[i + tidx]] + uvs[triangles[i + (tidx + 2) % 3]]) * 0.5f;
   uvs.Add(tuvy);
   uvs.Add(tuvy);

   //改一个
   triangles[i + 0] = filter.mesh.triangles[i + (tidx + 2) % 3];
   triangles[i + 1] = vertices.Count - 4;
   triangles[i + 2] = vertices.Count - 2;
   Vector3 nor1 = Vector3.Cross((vertices[triangles[i + 1]] - vertices[triangles[i + 0]]).normalized,
   (vertices[triangles[i + 2]] - vertices[triangles[i + 0]]).normalized);
   if (Vector3.Dot(nor0, nor1) < 0)
   {
   int tpidx = triangles[i + 1];
   triangles[i + 1] = triangles[i + 2];
   triangles[i + 2] = tpidx;
   }

   //新增一个
   triangles.Add(filter.mesh.triangles[i + (tidx + 1) % 3]);
   triangles.Add(vertices.Count - 3);
   triangles.Add(vertices.Count - 1);
   Vector3 nor2 = Vector3.Cross((vertices[triangles[triangles.Count - 2]] - vertices[triangles[triangles.Count - 3]]).normalized,
   (vertices[triangles[triangles.Count - 1]] - vertices[triangles[triangles.Count - 3]]).normalized);
   if (Vector3.Dot(nor0, nor2) < 0)
   {
   int tpidx = triangles[triangles.Count - 1];
   triangles[triangles.Count - 1] = triangles[triangles.Count - 2];
   triangles[triangles.Count - 2] = tpidx;
   }

   //新增一个
   triangles.Add(filter.mesh.triangles[i + tidx % 3]);
   triangles.Add(filter.mesh.triangles[i + (tidx + 1) % 3]);
   triangles.Add(vertices.Count - 1);
   Vector3 nor3 = Vector3.Cross((vertices[triangles[triangles.Count - 2]] - vertices[triangles[triangles.Count - 3]]).normalized,
   (vertices[triangles[triangles.Count - 1]] - vertices[triangles[triangles.Count - 3]]).normalized);
   if (Vector3.Dot(nor0, nor3) < 0)
   {
   int tpidx = triangles[triangles.Count - 1];
   triangles[triangles.Count - 1] = triangles[triangles.Count - 2];
   triangles[triangles.Count - 2] = tpidx;
   }
  }
  }

  //根据顶点索引数组确定mesh被分成了几份
  //经实验:不可行;因为同一个位置的点在不同的面中是不同的点,无法判断这两个三角形是否是连接起来的
  //故只能按方向将模型分成两个
  List<List<int>> ntriangles = new List<List<int>>();
  List<List<int>> temps = new List<List<int>>();
  List<List<Vector3>> nvertices = new List<List<Vector3>>();
  List<List<Vector2>> nuvs = new List<List<Vector2>>();

  //切割面的法向量;
  Vector3 pnormal = Vector3.Cross((c - a).normalized, (b - a).normalized);
  ntriangles.Add(new List<int>());
  ntriangles.Add(new List<int>());
  temps.Add(new List<int>());
  temps.Add(new List<int>());
  nuvs.Add(new List<Vector2>());
  nuvs.Add(new List<Vector2>());
  nvertices.Add(new List<Vector3>());
  nvertices.Add(new List<Vector3>());
  for (int i = 0; i < triangles.Count; i = i + 3)
  {
  //判断新的三角形在面的哪一侧;
  float t = 0;
  for(int j = 0; j < 3; j++)
  {
   Vector3 dir = (vertices[triangles[i + j]] - a).normalized;
   float tt = Vector3.Dot(dir, pnormal);
   t = Mathf.Abs(tt) > Mathf.Abs(t) ? tt : t;
  }

  int tidx = t >= 0 ? 0 : 1;

  for (int j = 0; j < 3; j++)
  {
   int idx = temps[tidx].IndexOf(triangles[i + j]);
   if (idx < 0)
   {
   ntriangles[tidx].Add(nvertices[tidx].Count);
   nvertices[tidx].Add(vertices[triangles[i + j]]);
   temps[tidx].Add(triangles[i + j]);
   nuvs[tidx].Add(uvs[triangles[i + j]]);
   continue;
   }

   ntriangles[tidx].Add(idx);
  }
  }

  if(nvertices[0].Count == 0 || nvertices[1].Count == 0)
  {
  //没有切割到物体
  return null;
  }

  //生成新的模型;
  List<GameObject> items = new List<GameObject>();
  MeshRenderer render = obj.GetComponent<MeshRenderer>();
  for (int i = 0; i < ntriangles.Count; i++)
  {
  GameObject tobj = new GameObject(i.ToString());
  tobj.transform.position = obj.transform.position;
  items.Add(tobj);
  MeshFilter fi = tobj.AddComponent<MeshFilter>();
  MeshRenderer mr = tobj.AddComponent<MeshRenderer>();

  if(render != null)
  {
   mr.material = render.material;
  }

  Mesh mesh = new Mesh();
  mesh.vertices = nvertices[i].ToArray();
  mesh.triangles = ntriangles[i].ToArray();
  mesh.uv = nuvs[i].ToArray();

  mesh.RecalculateNormals();
  mesh.RecalculateTangents();
  mesh.RecalculateBounds();

  fi.mesh = mesh;
  }

  return items.ToArray();
} 

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Unity使用EzySlice实现模型多边形顺序切割

    Unity使用EzySlice实现模型切割,供大家参考,具体内容如下 老规矩,直接上代码: 注意:脚本搭载和需要的材质球以及切割数组填充 EzySlice 多边形顺序切割 using System.Collections; using System.Collections.Generic; using UnityEngine; using EzySlice; public class SplitterModel_ZH : MonoBehaviour { //切割预制体材质 public Mate

  • Unity切割图集转换为多张图片

    本文实例为大家分享了Unity切割图集转换为多张图片的具体代码,供大家参考,具体内容如下 这是网上看来的一个工具,用于Unity中将图集切割为多张的格式后将这些sprite改为一张张图片,就是切割速度太慢,图集中的图片较多的时候还会丢失一部分图片,有时间本人会进一步改善再修改这篇博客. 1.首先选中要切割的图集,texture type 选为default,并勾选Advanced下的read/Write Enabled. 2.texture type改为sprite(2D and UI),Spr

  • Unity3D实现模型随机切割

    本文实例为大家分享了Unity3D实现模型随机切割的具体代码,供大家参考,具体内容如下 模型切割的效果图如下: 我们都知道,模型是由一个个小三角形面组成的,因此我们不妨将问题简化,先实现个小目标,完成单个三角形的切割,甚至继续细分成求一条线段与某个平面的交点. 三角形与切割平面的位置关系主要有以下三种: 1. 三角形与切割平面有两个交点,一个交点在顶点上,一个交点在边上.这时,原有的三角形将被分成两个三角形,分别为013.042. 2. 三角形与切割平面有两个交点,两个交点都在边上.这时,原有的

  • Unity3D实现模型淡入淡出效果

    开发中我们不仅需要UI界面淡入淡出,有时候还需要模型淡入淡出.我们在面板上修改color的a值时发现并没有效果.那是因为我们设置的RenderingMode是Opaque.官方标准shader中的Opaque pass段是不能显示半透明效果的,所以我们需要设置RenderingMode为Fade或者Transparent.然后在修改color的a值,达到淡入淡出的效果.效果如下: 通常我们淡入一个模型只会传入这个模型的GameObject,所以我们自写一个类来处理这个模型淡入的一些事件.代码如下

  • 解决unity3d导入模型贴图材质丢失的问题

    今天导入了一个模型,但是模型贴图丢失了,而且Inspector面板中处于不能编辑的状态 虽然可以通过重新创建材质来替换,但是这样会生成一个新的prefab,觉得不太好 做出如下更改,将对应材质填入即可 补充:Unity光照贴图丢失的坑 最近项目出版本的时候出现了部分建筑光照贴图不正常的情况,而且仅仅是部分模型,这部分模型都是有多个场景会用到的,所以都是单独出来打包的. 然后以为是模型上面的光照信息丢失了,通过测试,并没有丢失,可以通过获取模型上的MeshFilter打印出来,部分丢失的话,也与光

  • js按条件生成随机json:randomjson实现方法

    前端开发中,在做前后端分离的时候,经常需要手写json数据,有3个问题特别揪心: 1,数据是写死的,不能按一定的条件随机生成长度不一,内容不一的数据 2,写数组的时候,如果有很多条,需要一条一条地写,费时费力 3,mock图片特别困难 randomjson用来根据模型随机生成json,mock json数据的时候特别有用 github地址: https://github.com/finance-sh/randomjson 如何使用 # randomjson 根据条件生成json对应的随机json

  • python机器学习之随机森林(七)

    机器学习之随机森林,供大家参考,具体内容如下 1.Bootstraping(自助法) 名字来自成语"pull up by your own bootstraps",意思是依靠你自己的资源,称为自助法,它是一种有放回的抽样方法,它是非参数统计中一种重要的估计统计量方差进而进行区间估计的统计方法.其核心思想和基本步骤如下: (1) 采用重抽样技术从原始样本中抽取一定数量(自己给定)的样本,此过程允许重复抽样. (2) 根据抽出的样本计算给定的统计量T. (3) 重复上述N次(一般大于100

  • 一文搞懂Python Sklearn库使用

    目录 1.LabelEncoder 2.OneHotEncoder 3.sklearn.model_selection.train_test_split随机划分训练集和测试集 4.pipeline 5 perdict 直接返回预测值 6 sklearn.metrics中的评估方法 7 GridSearchCV 8 StandardScaler 9 PolynomialFeatures 4.10+款机器学习算法对比 4.1 生成数据 4.2 八款主流机器学习模型 4.3 树模型 - 随机森林 4.

  • Unity3D网格功能生成球体网格模型

    本文实例为大家分享了Unity3D网格功能生成球体网格模型的具体代码,供大家参考,具体内容如下 前面已经讲过怎样使用mesh生成一个自己的网格,那么本文将会讲述怎样将这个网格变换成自己想要的形状,比如一个球体. 我们需要知道一个从平面坐标到球体坐标的映射公式.假设平面坐标是(x,y),球体坐标是(x0,y0,z0),则 球体坐标(x0,y0,z0)可以通过以下代码得到,(x,y) 对应vertices[i].x和vertices[i].y. v.x = r * Mathf.Cos(vertice

  • Python实现随机森林RF模型超参数的优化详解

    目录 1 代码分段讲解 1.1 数据与模型准备 1.2 超参数范围给定 1.3 超参数随机匹配择优 1.4 超参数遍历匹配择优 1.5 模型运行与精度评定 2 完整代码 本文介绍基于Python的随机森林(Random Forest,RF)回归代码,以及模型超参数(包括决策树个数与最大深度.最小分离样本数.最小叶子节点样本数.最大分离特征数等)自动优化的代码. 本文是在上一篇文章Python实现随机森林RF并对比自变量的重要性的基础上完成的,因此本次仅对随机森林模型超参数自动择优部分的代码加以详

  • python实现随机森林random forest的原理及方法

    引言 想通过随机森林来获取数据的主要特征 1.理论 随机森林是一个高度灵活的机器学习方法,拥有广泛的应用前景,从市场营销到医疗保健保险. 既可以用来做市场营销模拟的建模,统计客户来源,保留和流失.也可用来预测疾病的风险和病患者的易感性. 根据个体学习器的生成方式,目前的集成学习方法大致可分为两大类,即个体学习器之间存在强依赖关系,必须串行生成的序列化方法,以及个体学习器间不存在强依赖关系,可同时生成的并行化方法: 前者的代表是Boosting,后者的代表是Bagging和"随机森林"(

随机推荐