返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >Unity3D实现模型随机切割
  • 393
分享到

Unity3D实现模型随机切割

2024-04-02 19:04:59 393人浏览 独家记忆
摘要

本文实例为大家分享了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();
} 

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

--结束END--

本文标题: Unity3D实现模型随机切割

本文链接: https://lsjlt.com/news/121795.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • Unity3D实现模型随机切割
    本文实例为大家分享了Unity3D实现模型随机切割的具体代码,供大家参考,具体内容如下 模型切割的效果图如下: 我们都知道,模型是由一个个小三角形面组成的,因此我们不妨将问题简化...
    99+
    2024-04-02
  • Unity3D怎么实现模型随机切割
    这篇文章主要介绍Unity3D怎么实现模型随机切割,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!模型切割的效果图如下:我们都知道,模型是由一个个小三角形面组成的,因此我们不妨将问题简化,先实现个小目标,完成单个三角形...
    99+
    2023-06-14
  • golang实现文件切割
    随着存储设备容量的不断增加,越来越多的数据需要存储。在处理大型文件时,常常会遇到内存不足的问题,此时文件切割就成为了一种有效的解决方案。今天我们就来探讨一下如何使用golang实现文件切割。文件切割指的是将一个大文件按照一定的大小切割成若干...
    99+
    2023-05-14
  • Python随机森林模型实例详解
    本篇文章给大家带来了关于Python的相关知识,其中主要整理了随机森林模型的相关问题,包括了集成模型简介、随机森林模型基本原理、使用sklearn实现随机森林模型等等内容,下面一起来看一下,希望对大家有帮助。【相关推荐:Python3视频教...
    99+
    2022-07-01
    python
  • Python+random模块实现随机抽样
    目录1. 设置随机数种子 seed()2. random() 与 randint()3. sample()方法 无放回抽样4. choice() 与 choices() 有放回抽样5...
    99+
    2024-04-02
  • Nginx怎么实现日志切割
    这篇文章主要介绍“Nginx怎么实现日志切割”,在日常操作中,相信很多人在Nginx怎么实现日志切割问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Nginx怎么实现日志切割”...
    99+
    2024-04-02
  • Unity实现切割图集工具
    本文实例为大家分享了Unity实现切割图集工具的具体代码,供大家参考,具体内容如下 操作步骤 先将脚本拖入Editor 1.选中要切割的图片,texture type 选为defau...
    99+
    2024-04-02
  • js实现图片切割功能
    本文实例为大家分享了js实现图片切割的具体代码,供大家参考,具体内容如下 代码: <!DOCTYPE html> <html lang="en"> &l...
    99+
    2024-04-02
  • java代码实现空间切割
    目录问题代码效果演示测试数据结果总结问题 给定一个大的立方体和一批小的立方体,对于每个立方体,都知道如图的信息(知道x,y,z坐标和长、宽、高),且任意小立方体都被包含在大立方体内、...
    99+
    2024-04-02
  • Java实现图片切割功能
    本文实例为大家分享了Java实现图片切割功能的具体代码,供大家参考,具体内容如下 工具类 package com.xudaolong.Utils; import javax.ima...
    99+
    2024-04-02
  • 随机森林模型及案例(Python)
    目录 1 集成模型简介 1.1 Bagging算法简介 1.2 Boosting算法简介 2 随机森林模型基本原理 3 使用sklearn实现随机森林模型 4 案例:股票涨跌预测模型 4.1 股票衍生变量生成 4.1.1 获取股票基本数据 ...
    99+
    2023-09-01
    学习
  • Python随机森林模型是什么
    这篇“Python随机森林模型是什么”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Python随机森林模型是什么”文章吧。1...
    99+
    2023-07-02
  • 【图像分割】Meta分割一切(SAM)模型环境配置和使用教程
    注意:python>=3.8, pytorch>=1.7,torchvision>=0.8 Feel free to ask any question. 遇到问题欢迎评论区讨论. 官方教程: https://github.com/faceb...
    99+
    2023-08-31
    人工智能 目标检测 python 计算机视觉
  • python切割图片的实现示例
    用opencv处理一下pillow也可以,但是试过有时候会把图片自动旋转180°,cv没有这个问题 import os from cv2 import cv2 def s...
    99+
    2024-04-02
  • Linux下如何实现文件切割
    这篇文章主要介绍了Linux下如何实现文件切割,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。linux下文件分割可以通过split命令来实现,可以指定按行数分割和安大小分割两...
    99+
    2023-06-28
  • C++怎么实现字符串切割
    本篇内容介绍了“C++怎么实现字符串切割”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!字符串切割的两种方法字符串切割的使用频率还是挺高的,s...
    99+
    2023-07-02
  • Python Opencv实现图片切割处理
    本文实例为大家分享了Python Opencv实现图片的切割处理,供大家参考,具体内容如下 Opencv对图片的切割: 方法一: import os from PIL import ...
    99+
    2024-04-02
  • 【peft】huggingface大模型加载多个LoRA并随时切换
    加载多个LoRA并随时切换 参考Multi Adapter support 要求 peft>=0.3.0 用法说明 在加载第一个适配器时,可以通过 PeftModel.from_pretrain...
    99+
    2023-09-02
    python peft lora llm
  • python如何实现列表切割赋值
    这篇文章主要介绍了python如何实现列表切割赋值,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。列表切割赋值>>>&nbs...
    99+
    2024-04-02
  • logrotate实现日志切割方式(转储)
    目录logrotate日志切割(转储)背景日志切割配置文件/etc/logrotate.conf设置定时任务logrotate日志切割报错 文件不再同一个用户组下总结logrotate日志切割(转储) 背景 系统每天凌晨...
    99+
    2023-05-12
    logrotate日志切割 logrotate转储 logrotate实现日志切割
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作