概述

3d模型的任意切割一直是游戏开发里的一个很大的问题,模型切割的关键点就只有生成横切面的新顶点以及切口纹理的缝合,理论上解决了这两点,就近乎可以做到以假乱真的程度了。本篇文章就这两点进行描述

详细

代码下载:http://www.demodashi.com/demo/11343.html

一、准备工作

解压缩后得到ClipDemo.unitypackage文件,将此文件导入unity5.45中,双击main场景,运行即可。运行后可以看到一个球体,对着球体拖动鼠标做切割动作,可以看到Hierarchy面板生成多个new Model,即为切割生成的模型,可以在Scene中拖动这些物体,可以看到是切割后的物体。压缩包内容如下:

二、程序实现

如上图所示,当切割模型时,对于切面上的三角面,无非是如图中3种情况(正好切在三角形的某个顶点上几乎不可能,不过也可以考虑在内,这里就不画出来了),所以每个三角形正好被切到的时候,其自身内部应该生成新的顶点(图中-1,-2点)。生成处新的顶点之后,我们需要将原来的一个三角形重新分割为如图绿色的数字标志的三个三角形,也就是原来一个三角形被分为三个三角形。代码如下:

MeshFilter mf = this.gameObject.GetComponent<MeshFilter>();//顶点数组转顶点容器List<Vector3> verticeList = new List<Vector3>();int verticeCount = mf.mesh.vertices.Length;for (int verticeIndex = 0; verticeIndex < verticeCount; ++verticeIndex){verticeList.Add(mf.mesh.vertices[verticeIndex]);}//三角形数组转三角形容器List<int> triangleList = new List<int>();int triangleCount = mf.mesh.triangles.Length;for (int triangleIndex = 0; triangleIndex < triangleCount; ++triangleIndex){triangleList.Add(mf.mesh.triangles[triangleIndex]);}//uv坐标数组转uv坐标容器List<Vector2> uvList = new List<Vector2>();int uvCount = mf.mesh.uv.Length;for (int uvIndex = 0; uvIndex < uvCount; ++uvIndex){uvList.Add(mf.mesh.uv[uvIndex]);}//顶点颜色数组转顶点颜色容器List<Vector3> normalList = new List<Vector3>();int normalCount = mf.mesh.normals.Length;for (int normalIndex = 0; normalIndex < normalCount; ++normalIndex){normalList.Add(mf.mesh.normals[normalIndex]);}//检查每个三角面,是否存在两个顶点连接正好在直线上for (int triangleIndex = 0; triangleIndex < triangleList.Count;){int trianglePoint0 = triangleList[triangleIndex];int trianglePoint1 = triangleList[triangleIndex + 1];int trianglePoint2 = triangleList[triangleIndex + 2];Vector3 point0 = verticeList[trianglePoint0];Vector3 point1 = verticeList[trianglePoint1];Vector3 point2 = verticeList[trianglePoint2];float planeY = 0.3f;//0-1,1-2相连线段被切割if ((point0.y - planeY)* (point1.y - planeY) < 0 && (point1.y - planeY) * (point2.y - planeY) < 0){//截断0-1之间的顶点float k01 = (point1.y - point0.y) / (planeY - point0.y);float newPointX01 = (point1.x - point0.x) / k01 + point0.x;float newPointZ01 = (point1.z - point0.z) / k01 + point0.z;Vector3 newPoint0_1 = new Vector3(newPointX01, planeY, newPointZ01);verticeList.Add(newPoint0_1);//uvif(uvList.Count > 0){Vector2 uv0 = uvList[trianglePoint0];Vector2 uv1 = uvList[trianglePoint1];float newUV_x = (uv1.x - uv0.x) / k01 + uv0.x;float newUV_y = (uv1.y - uv0.y) / k01 + uv0.y;uvList.Add(new Vector2(newUV_x, newUV_y));}//法向量Vector3 normalX0 = normalList[trianglePoint0];Vector3 normalX1 = normalList[trianglePoint1];Vector3 normalX2 = normalList[trianglePoint2];float newNoramlX01 = (normalX1.x - normalX0.x) / k01 + normalX0.x;float newNoramlY01 = (normalX1.y - normalX0.y) / k01 + normalX0.y;float newNoramlZ01 = (normalX1.z - normalX0.z) / k01 + normalX0.z;normalList.Add(new Vector3(newNoramlX01, newNoramlY01, newNoramlZ01));//截断1-2之间的顶点float k12 = (point2.y - point1.y) / (planeY - point1.y);float newPointX12 = (point2.x - point1.x) / k12 + point1.x;float newPointZ12 = (point2.z - point1.z) / k12 + point1.z;Vector3 newPoint1_2 = new Vector3(newPointX12, planeY, newPointZ12);verticeList.Add(newPoint1_2);if (uvList.Count > 0){Vector2 uv1 = uvList[trianglePoint1];Vector2 uv2 = uvList[trianglePoint2];float newUV_x = (uv2.x - uv1.x) / k12 + uv1.x;float newUV_y = (uv2.y - uv1.y) / k12 + uv1.y;uvList.Add(new Vector2(newUV_x, newUV_y));}//法向量float newNoramlX12 = (normalX2.x - normalX1.x) / k12 + normalX1.x;float newNoramlY12 = (normalX2.y - normalX1.y) / k12 + normalX1.y;float newNoramlZ12 = (normalX2.z - normalX1.z) / k12 + normalX1.z;normalList.Add(new Vector3(newNoramlX12, newNoramlY12, newNoramlZ12));int newVerticeCount = verticeList.Count;//插入顶点索引,以此构建新三角形triangleList.Insert(triangleIndex + 1, newVerticeCount - 2);triangleList.Insert(triangleIndex + 2, newVerticeCount - 1);triangleList.Insert(triangleIndex + 3, newVerticeCount - 1);triangleList.Insert(triangleIndex + 4, newVerticeCount - 2);triangleList.Insert(triangleIndex + 6, trianglePoint0);triangleList.Insert(triangleIndex + 7, newVerticeCount - 1);}//1-2,2-0相连线段被切割else if ((point1.y - planeY) * (point2.y - planeY) < 0 && (point2.y - planeY) * (point0.y - planeY) < 0){//截断1-2之间的顶点float k12 = (point2.y - point1.y) / (planeY - point1.y);float newPointX12 = (point2.x - point1.x) / k12 + point1.x;float newPointZ12 = (point2.z - point1.z) / k12 + point1.z;Vector3 newPoint1_2 = new Vector3(newPointX12, planeY, newPointZ12);verticeList.Add(newPoint1_2);if (uvList.Count > 0){Vector2 uv1 = uvList[trianglePoint1];Vector2 uv2 = uvList[trianglePoint2];float newUV_x = (uv2.x - uv1.x) / k12 + uv1.x;float newUV_y = (uv2.y - uv1.y) / k12 + uv1.y;uvList.Add(new Vector2(newUV_x, newUV_y));}//法向量Vector3 normalX0 = normalList[trianglePoint0];Vector3 normalX1 = normalList[trianglePoint1];Vector3 normalX2 = normalList[trianglePoint2];float newNoramlX12 = (normalX2.x - normalX1.x) / k12 + normalX1.x;float newNoramlY12 = (normalX2.y - normalX1.y) / k12 + normalX1.y;float newNoramlZ12 = (normalX2.z - normalX1.z) / k12 + normalX1.z;normalList.Add(new Vector3(newNoramlX12, newNoramlY12, newNoramlZ12));//截断0-2之间的顶点float k02 = (point2.y - point0.y) / (planeY - point0.y);float newPointX02 = (point2.x - point0.x) / k02 + point0.x;float newPointZ02 = (point2.z - point0.z) / k02 + point0.z;Vector3 newPoint0_2 = new Vector3(newPointX02, planeY, newPointZ02);verticeList.Add(newPoint0_2);//uvif (uvList.Count > 0){Vector2 uv0 = uvList[trianglePoint0];Vector2 uv2 = uvList[trianglePoint2];float newUV_x = (uv2.x - uv0.x) / k02 + uv0.x;float newUV_y = (uv2.y - uv0.y) / k02 + uv0.y;uvList.Add(new Vector2(newUV_x, newUV_y));}//法向量float newNoramlX02 = (normalX1.x - normalX0.x) / k02 + normalX0.x;float newNoramlY02 = (normalX1.y - normalX0.y) / k02 + normalX0.y;float newNoramlZ02 = (normalX1.z - normalX0.z) / k02 + normalX0.z;normalList.Add(new Vector3(newNoramlX02, newNoramlY02, newNoramlZ02));int newVerticeCount = verticeList.Count;//插入顶点索引,以此构建新三角形//{0}//{1}triangleList.Insert(triangleIndex + 2, newVerticeCount - 2);triangleList.Insert(triangleIndex + 3, newVerticeCount - 1);triangleList.Insert(triangleIndex + 4, newVerticeCount - 2);//{2}triangleList.Insert(triangleIndex + 6, newVerticeCount - 1);triangleList.Insert(triangleIndex + 7, trianglePoint0);triangleList.Insert(triangleIndex + 8, newVerticeCount - 2);}//0-1,2-0相连线段被切割else if((point0.y - planeY) * (point1.y - planeY) < 0 && (point2.y - planeY) * (point0.y - planeY) < 0){//截断0-1之间的顶点float k01 = (point1.y - point0.y) / (planeY - point0.y);float newPointX01 = (point1.x - point0.x) / k01 + point0.x;float newPointZ01 = (point1.z - point0.z) / k01 + point0.z;Vector3 newPoint0_1 = new Vector3(newPointX01, planeY, newPointZ01);verticeList.Add(newPoint0_1);//uvif (uvList.Count > 0){Vector2 uv0 = uvList[trianglePoint0];Vector2 uv1 = uvList[trianglePoint1];float newUV_x = (uv1.x - uv0.x) / k01 + uv0.x;float newUV_y = (uv1.y - uv0.y) / k01 + uv0.y;uvList.Add(new Vector2(newUV_x, newUV_y));}//法向量Vector3 normalX0 = normalList[trianglePoint0];Vector3 normalX1 = normalList[trianglePoint1];Vector3 normalX2 = normalList[trianglePoint2];float newNoramlX01 = (normalX1.x - normalX0.x) / k01 + normalX0.x;float newNoramlY01 = (normalX1.y - normalX0.y) / k01 + normalX0.y;float newNoramlZ01 = (normalX1.z - normalX0.z) / k01 + normalX0.z;normalList.Add(new Vector3(newNoramlX01, newNoramlY01, newNoramlZ01));//截断0-2之间的顶点float k02 = (point2.y - point0.y) / (planeY - point0.y);float newPointX02 = (point2.x - point0.x) / k02 + point0.x;float newPointZ02 = (point2.z - point0.z) / k02 + point0.z;Vector3 newPoint0_2 = new Vector3(newPointX02, planeY, newPointZ02);verticeList.Add(newPoint0_2);//uvif (uvList.Count > 0){Vector2 uv0 = uvList[trianglePoint0];Vector2 uv2 = uvList[trianglePoint2];float newUV_x = (uv2.x - uv0.x) / k02 + uv0.x;float newUV_y = (uv2.y - uv0.y) / k02 + uv0.y;uvList.Add(new Vector2(newUV_x, newUV_y));}//法向量float newNoramlX02 = (normalX1.x - normalX0.x) / k02 + normalX0.x;float newNoramlY02 = (normalX1.y - normalX0.y) / k02 + normalX0.y;float newNoramlZ02 = (normalX1.z - normalX0.z) / k02 + normalX0.z;normalList.Add(new Vector3(newNoramlX02, newNoramlY02, newNoramlZ02));int newVerticeCount = verticeList.Count;//插入顶点索引,以此构建新三角形//{0}triangleList.Insert(triangleIndex + 1, newVerticeCount - 2);triangleList.Insert(triangleIndex + 2, newVerticeCount - 1);triangleList.Insert(triangleIndex + 3, newVerticeCount - 2);//{1}//{2}triangleList.Insert(triangleIndex + 6, trianglePoint2);triangleList.Insert(triangleIndex + 7, newVerticeCount - 1);triangleList.Insert(triangleIndex + 8, newVerticeCount - 2);}//只有0-1被切else if((point0.y - planeY) * (point1.y - planeY) < 0){Debug.Log("只有01被切");}//只有1-2被切else if ((point1.y - planeY) * (point2.y - planeY) < 0){Debug.Log("只有12被切");}//只有2-0被切else if ((point2.y - planeY) * (point0.y - planeY) < 0){Debug.Log("只有02被切");}triangleIndex += 3;}//筛选出切割面两侧的顶点索引List<int> triangles1 = new List<int>();List<int> triangles2 = new List<int>();for (int triangleIndex = 0; triangleIndex < triangleList.Count; triangleIndex += 3){int trianglePoint0 = triangleList[triangleIndex];int trianglePoint1 = triangleList[triangleIndex + 1];int trianglePoint2 = triangleList[triangleIndex + 2];Vector3 point0 = verticeList[trianglePoint0];Vector3 point1 = verticeList[trianglePoint1];Vector3 point2 = verticeList[trianglePoint2];//切割面float planeY = 0.3f;if(point0.y > planeY || point1.y > planeY || point2.y > planeY){triangles1.Add(trianglePoint0);triangles1.Add(trianglePoint1);triangles1.Add(trianglePoint2);}else{triangles2.Add(trianglePoint0);triangles2.Add(trianglePoint1);triangles2.Add(trianglePoint2);}}//缝合切口//for (int verticeIndex = verticeCount; verticeIndex < verticeList.Count - 2; ++verticeIndex)//{//    triangles1.Add(verticeIndex + 2);//    triangles1.Add(verticeIndex);//    triangles1.Add(verticeCount);//    triangles2.Add(verticeCount);//    triangles2.Add(verticeIndex);//    triangles2.Add(verticeIndex + 2);//}mf.mesh.vertices = verticeList.ToArray();mf.mesh.triangles = triangles1.ToArray();if (uvList.Count > 0){mf.mesh.uv = uvList.ToArray();}mf.mesh.normals = normalList.ToArray();//分割模型GameObject newModel = new GameObject("New Model");MeshFilter meshFilter = newModel.AddComponent<MeshFilter>();meshFilter.mesh.vertices = mf.mesh.vertices;meshFilter.mesh.triangles = triangles2.ToArray();meshFilter.mesh.uv = mf.mesh.uv;meshFilter.mesh.normals = mf.mesh.normals;Renderer newRenderer = newModel.AddComponent<MeshRenderer>();newRenderer.material = this.gameObject.GetComponent<MeshRenderer>().material;

切出来的模型新生成的顶点是无序的,但是我们可以连接任意两个无序顶点定为参考向量,然后其他任意顶点与参考向量中的起点连接形成新的向量,求得这两个向量之间的夹角,利用这个夹角大小来排序,如图所示:

顶点的排序算法如下:

//重新排序新生成的顶点,按照角度List<SortAngle> SortAngleList = new List<SortAngle>();for (int verticeIndex = verticeCount + 1; verticeIndex < verticeList.Count; verticeIndex++){//计算角度,以0-1为参照Vector3 vec1to0 = verticeList[verticeCount + 1] - verticeList[verticeCount];Vector3 indexTo0 = verticeList[verticeIndex] - verticeList[verticeCount];float moIndexto0 = indexTo0.magnitude;float mo1to0 = vec1to0.magnitude;float dotRes = Vector3.Dot(indexTo0, vec1to0);if (moIndexto0 == 0.0f){continue;}float angle = Mathf.Acos(dotRes / (mo1to0 * moIndexto0)); //Vector3.Angle(indexTo0.normalized, vec1to0.normalized);bool isExis = false;for (int i = 0; i < SortAngleList.Count; ++i){//同样角度,距离近的被剔除if (Mathf.Abs(SortAngleList[i].Angle * 180.0f / Mathf.PI - angle * 180.0f / Mathf.PI) < 0.1f){float dis1 = Vector3.Distance(verticeList[SortAngleList[i].Index], verticeList[verticeCount]);float dis2 = Vector3.Distance(verticeList[verticeIndex], verticeList[verticeCount]);if (dis2 >= dis1){SortAngleList[i].Index = verticeIndex;}isExis = true;break;}}if (!isExis){//Debug.Log(angle);SortAngle sortAngle = new SortAngle();sortAngle.Index = verticeIndex;sortAngle.Angle = angle;SortAngleList.Add(sortAngle);}}SortAngleList.Sort();

三、运行效果

四、项目截图

代码下载:http://www.demodashi.com/demo/11343.html

注:本文著作权归作者,由demo大师发表,拒绝转载,转载需要作者授权

unity模型任意无限切割插件相关推荐

  1. Unity UGUI Image/RawImage 无限切割 挂上脚本就能用

    被切割物体任意旋转.自带刀光,在Canvas下空物体添加该脚本并选择图片即可. 无限切割, 物体切割后自带刚体可碰撞 UniWebViewUnityPackage4.2.0_UniWebView44. ...

  2. sketchup生成面域插件_SU对象切割插件,传说中的SU模型切割神器!

    小吧又来给大家安利神仙插件了,今天分享的是SketchUp中切割神器:对象切割 你看他像不像你倔强的稀疏头发呢? 插件功能介绍 对象切割 - SUAPP编号298 - 对象切割插件功能可以通过两点切割 ...

  3. Unity快速入门之四 - Unity模型动画相关

    最近要给公司的小伙伴做Unity入门,针对几个常用的知识进行快速入门介绍. Unity快速入门之一 3D基础概念.Camera.Canvas RenderMode的几种方式对比_翕翕堂 Unity快速 ...

  4. Unity 简单易用的插件汇总

    Unity 简单易用的插件汇总 2dToolKit,是一款2D开发组件,它具有很强的灵活性,可以让开发者在Unity中进行2D开发 PlayerMaker,是一个可视化脚本工具,开发者可以使用它很快的 ...

  5. Unity 5.x的专用插件推荐

    本帖集合了Unity 5.x的专用插件,如果是使用 4.6.x 的伙伴们切勿下载安装. Sprite Color FX 2.0 - 像素特效 http://www.narkii.com/club/th ...

  6. UNITY 模型与动画优化选项

    UNITY 模型与动画优化选项 1,RIG: Optimze Game Objects,[默认是没勾选的] 效果:将骨骼层级从模型中移除,放到动画控制器中,这样性能提高明显.实测中发现原来瞬间加载5个 ...

  7. unity模型制作(终章):柔体布料绘制

    在前六章模型绘制的基础上,加一些几何算法,很容易制作出不同样式的模型,例如下图中的几种模型:直梯.曲梯.各种屋顶等等. 不过最终章主要想讲一下柔体的绘制:所谓柔体,其实就是物体在受力的作用时,会产生形 ...

  8. Unity模型制作导出规范

    Unity模型制作导出规范 一.模型制作流程 素材采集-模型制作(高低模)-展UV-贴图制作-场景整合-层级整理.命名.(展lightmapUV)-(动画烘焙)-场景调整导出 二.模型制作规范 1. ...

  9. Unity 模型导入材质丢失解决方案

    Unity 模型导入材质丢失解决方案 1.模型导入材质丢失解决方案 步骤如下: 1.打开材质球 2.,反射率和法线贴图分别赋值,即下面的两个物体对号入座 3.渲染成功 2.3d人物模型材质丢失 步骤如 ...

最新文章

  1. Jenkins的环境部署
  2. 2014腾讯WE大会:开启未来的五大科技发展趋势
  3. linux 权限 mask,Linux mask有效权限详解
  4. 序列化的作用_Java 序列化的高级认识
  5. mysql日志管理_关于MySQL的日志管理(binlog)
  6. 开机发现超级管理员账户不见了
  7. 什么情况下不应该使用深度学习?
  8. C#区分中英文统计字符串的长度
  9. James Gosling畅言Java技术未来十年发展
  10. html控制标签,html中文本控制类标签基础知识
  11. 克莱姆森大学计算机排名,2020年克莱姆森大学排名TFE Times美国最佳计算机科学硕士专业排名第102...
  12. 植物大战僵尸修改存档文件-C1认证
  13. Python字符串底层原理
  14. ORA-01426数字溢出问题
  15. [开发]resin+spring+struts配搭在线上常见的三个问题
  16. 基于微信小程序房屋出租民宿预定app设计
  17. bios设置 hp z800_《惠普工作站设置BIOS从U盘装系统》
  18. 黑群晖(DSM7)使用docker挂载zerotier one实现内网穿透
  19. const T、const T*、T *const、const T、const T* 的区别
  20. Java Algorithm 简单算法

热门文章

  1. android9的手机,可防手机上瘾?安卓9.0首批升级的机型都在这里
  2. mysql 字段操作_Mysql:数据库操作、数据表操作、字段操作整理
  3. STM32通用和复用功能IO
  4. 数据结构与算法分析(十)——母牛的故事
  5. 卡尔曼滤波 -- 从推导到应用(二)
  6. mysql多个外键删除设置_Mysql在删除集上使用多个外键创建表
  7. vue 使用了浏览器的刷新之后报错_Electron-vue运行之后出现了文件浏览器
  8. python的Dict和set类型
  9. 力扣报错“AddressSanitizer: heap-buffer-overflow on address...”的解决办法
  10. 召回率和精确率(recall and precision)