文档章节

3d模型任意切割的算法(一)

 梁potato
发布于 2017/08/27 09:37
字数 1738
阅读 5
收藏 0
点赞 0
评论 0

其实本篇文章最开始我是在csdn上发表的,到这里再重发一遍吧算是。3d模型的任意切割一直是游戏开发里的一个很大的问题,主要的问题是在切面的纹理上,然而如果是单纯的切面片则并不存在切面纹理的问题,关于切面纹理的解决方案在下一篇文章中探讨,总之本文权当研究吧。先看看我切出来的第一个版本的效果(未缝合切口)

下面先看看切出平整切痕的原理吧,本文需要图形学的基础扎实。

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

所以我在代码中做的,就是将正好被切割的三角形重新划分为三个三角形。代码如下


/*
 * @authors: liangjian
 * @desc:	
*/
using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class ClipMesh : MonoBehaviour {

	// Use this for initialization
	void Start () {
        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);
                //uv
                if(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);
                //uv
                if (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);
                //uv
                if (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);
                //uv
                if (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);
            }
        }


        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;
    }
}

 

 

© 著作权归作者所有

共有 人打赏支持
粉丝 0
博文 1
码字总数 1738
作品 0
南宁
【Unity3D】3D模型的使用——FBX的使用与Animation设置

FBX的使用与Animation设置是一个很有必要的技能,对于美工MM送过来的3D模型你应该懂得如何将其设置到游戏场景之中使用,不然真的羞死人的,这都不会。毕竟游戏里面许许多多的主角的动作都是一...

yongh701 ⋅ 2017/06/10 ⋅ 0

3D打印机的切片引擎--Cura-3D

Cura 是一款开源3D打印机的切片引擎。 切片程序的主要过程如下: i. 导入3D模型(STL,OBJ等等)。 ii. 分析并修复3D模型(源码里面貌似木有这一步…)。 iii. 将3D模型切割成2D层。 iv. 用上一步...

匿名 ⋅ 2016/04/12 ⋅ 0

求教 有没有什么基于WEB的3d引擎

现有一个3d模型,可以是3dmax导出的任意格式的模型包, 浏览器通过某种技术加载这个3d模型包并显示,同时支持点击, 浏览器页面左侧可以是树状菜单显示点击的节点,右侧显示3d模型,3d模型也...

xhu_cf ⋅ 2014/06/01 ⋅ 2

家具开源项目--OpenDesk

在OpenDesk这个项目之前,可能并没有人多少想过家具也能变成开源项目。作为一个免费、开源的家具图纸源,OpenDesk 不仅可以让用户自己下载图样制作,还能让拥有数控机床的制作厂协助制作部件...

红薯 ⋅ 2013/08/21 ⋅ 4

机器人的「语料」,如何获取?

本文来自作者 李烨 在 GitChat 上分享「应用聚类模型获得聊天机器人语料」,「阅读原文」查看交流实录 「文末高能」 编辑 | 嘉仔 0. 聊天机器人系列第三部 之前笔者开过两个关于聊天机器人开...

gitchat ⋅ 2017/11/28 ⋅ 0

聚类分析简单介绍(附R对应函数介绍)

聚类分析是一种机器学习领域最常用的分类方法,它在在客户分类,文本分类,基因识别,空间数据处理,卫星图片处理,医疗图像自动检测等领域有着广泛应用。聚类就是将相同,相似的对象划分到同...

wzgl__wh ⋅ 02/04 ⋅ 0

HT for Web 3D游戏设计设计--汉诺塔(Towers of Hanoi)

在这里我们将构造一个基于HT for Web的HTML5+JavaScript来实现汉诺塔游戏。 汉诺塔的游戏规则及递归算法分析请参考http://en.wikipedia.org/wiki/TowerofHanoi。 知道了汉诺塔的规则和算法,...

xhload3d ⋅ 2015/01/12 ⋅ 0

验证码识别技术二——北京市

大约8个月前,曾经写过一篇关于验证码识别的文章,很久没再更新了,今天再来介绍一下北京市的验证码识别技术。 1、验证码样例 北京市验证码有两种类型,第一种类型是四个字母或数字混合类,如...

东方神剑 ⋅ 2016/11/27 ⋅ 0

深度 | 2017CV技术报告:从3D物体重建到人体姿态估计

  选自TheMTank   机器之心编译      The M Tank 编辑了一份报告《A Year in Computer Vision》,记录了 2016 至 2017 年计算机视觉领域的研究成果,对开发者和研究人员来说是不可多...

机器之心 ⋅ 2017/12/10 ⋅ 0

【AI全球大战医生】Hinton:5-10年内深度学习取代放射科医生

2017年4月,Hinton在接受《纽约客》采访时说:“作为放射科医生你就像卡通里的歪心狼一样,你已经冲出悬崖很远,但还没有往下看,实际上你的脚下空空如也。”商用的深度学习乳腺癌检测系统已...

技术小能手 ⋅ 01/03 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

JavaScript零基础入门——(八)JavaScript的数组

JavaScript零基础入门——(八)JavaScript的数组 欢迎大家回到我们的JavaScript零基础入门,上一节课我们讲了有关JavaScript正则表达式的相关知识点,便于大家更好的对字符串进行处理。这一...

JandenMa ⋅ 50分钟前 ⋅ 0

sbt网络问题解决方案

转自:http://dblab.xmu.edu.cn/blog/maven-network-problem/ cd ~/.sbt/launchers/0.13.9unzip -q ./sbt-launch.jar 修改 vi sbt/sbt.boot.properties 增加一个oschina库地址: [reposit......

狐狸老侠 ⋅ 今天 ⋅ 0

大数据,必须掌握的10项顶级安全技术

我们看到越来越多的数据泄漏事故、勒索软件和其他类型的网络攻击,这使得安全成为一个热门话题。 去年,企业IT面临的威胁仍然处于非常高的水平,每天都会看到媒体报道大量数据泄漏事故和攻击...

p柯西 ⋅ 今天 ⋅ 0

Linux下安装配置Hadoop2.7.6

前提 安装jdk 下载 wget http://mirrors.hust.edu.cn/apache/hadoop/common/hadoop-2.7.6/hadoop-2.7.6.tar.gz 解压 配置 vim /etc/profile # 配置java环境变量 export JAVA_HOME=/opt/jdk1......

晨猫 ⋅ 今天 ⋅ 0

crontab工具介绍

crontab crontab 是一个用于设置周期性被执行的任务工具。 周期性执行的任务列表称为Cron Table crontab(选项)(参数) -e:编辑该用户的计时器设置; -l:列出该用户的计时器设置; -r:删除该...

Linux学习笔记 ⋅ 今天 ⋅ 0

深入Java多线程——Java内存模型深入(2)

5. final域的内存语义 5.1 final域的重排序规则 1.对于final域,编译器和处理器要遵守两个重排序规则: (1)在构造函数内对一个final域的写入,与随后把这个被构造对象的引用赋值给一个引用...

江左煤郎 ⋅ 今天 ⋅ 0

面试-正向代理和反向代理

面试-正向代理和反向代理 Nginx 是一个高性能的反向代理服务器,但同时也支持正向代理方式的配置。

秋日芒草 ⋅ 今天 ⋅ 0

Spring 依赖注入(DI)

1、Setter方法注入: 通过设置方法注入依赖。这种方法既简单又常用。 类中定义set()方法: public class HelloWorldOutput{ HelloWorld helloWorld; public void setHelloWorld...

霍淇滨 ⋅ 昨天 ⋅ 0

马氏距离与欧氏距离

马氏距离 马氏距离也可以定义为两个服从同一分布并且其协方差矩阵为Σ的随机变量之间的差异程度。 如果协方差矩阵为单位矩阵,那么马氏距离就简化为欧氏距离,如果协方差矩阵为对角阵,则其也...

漫步当下 ⋅ 昨天 ⋅ 0

聊聊spring cloud的RequestRateLimiterGatewayFilter

序 本文主要研究一下spring cloud的RequestRateLimiterGatewayFilter GatewayAutoConfiguration @Configuration@ConditionalOnProperty(name = "spring.cloud.gateway.enabled", matchIfMi......

go4it ⋅ 昨天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部