【Unity】运行时创建曲线(贝塞尔的运用)

[Unity]运行时创建线(贝塞尔的运用)

1. 实现的目标

在运行状态下创建一条可以使用贝塞尔方法实时编辑的网格曲线。

2. 原理介绍

2.1 曲线的创建

unity建立网格曲线可以参考Unity程序化网格体的实现方法。主要分为顶点,三角面,UV和法线。笔者有类似的文章unity 线绳管道纯代码创建方法_,详细的讲解了网格线的创建方法,这次的不同点在于法线的确立方法上。

2.2贝塞尔曲线点的确立

笔者有文章Unity 贝塞尔曲线的创建_描述了贝塞尔的创建方法。

3. 实现过程

3.1曲线的创建方法

线的组成原理
曲线由横截面圆和中心轴线组成。横截面的法线方向为前后两点向量差,如下图绿色线为中心轴线,A点为横截面所在点,横截面的法线方法为向量 A D ⃗ \vec{AD} AD 既向量 C B ⃗ \vec{CB} CB 的单位向量;终点和起点的法线方向为自身和前点或者后一点的向量。

代码源码

3.1.1 横截圆的创建
           #region 横切圆创建
           /// <summary>
           /// 得到管线横切圆
           /// </summary>
           /// <param name="Count">段数</param>
           /// <param name="R">半径</param>
           /// <returns></returns>
           Vector3[] CircularSection(int Count, float R)
           {
               Vector3[] vector3s = new Vector3[Count];
               float angle = 360 / Count;
               Vector3 vector3 = new Vector3(R, 0, 0);
               for (int i = 0; i < Count; i++)
               {
                   //根据角度得到圆的分布点
                   vector3s[i] = vector3.ToAngle(angle * i, Vector3.zero, Vector3.forward);
               }
               return vector3s;
           }
           #endregion
          

​ vector3旋转扩展方法

        /// <summary>
        /// 角度旋转
        /// </summary>
        /// <param name="vector3"></param>
        /// <param name="angle">旋转角度</param>
        /// <param name="center">旋转中心点</param>
        /// <param name="direction">旋转轴</param>
        /// <returns></returns>
        public static Vector3 ToAngle(this Vector3 vector3, float angle, Vector3 center, Vector3 direction)
        {
            Vector3 pos = center;
            Quaternion quaternion = Quaternion.AngleAxis(angle, direction);
            Matrix4x4 matrix = new Matrix4x4();
            matrix.SetTRS(pos, quaternion, Vector3.one);
            vector3 = matrix.MultiplyPoint3x4(vector3);
            return vector3;
        }
3.1.2 中心线的确立
           class LinePoint
             {
                 Vector3 location;
                 Vector3 direction;
     
                 public Vector3 Location { get => location; set => location = value; }
                 public Vector3 Direction { get => direction; set => direction = value; }
             }
             /// <summary>
             /// 中心线的确立
             /// </summary>
             /// <param name="createPoint">曲线点</param>
             /// <returns></returns>
             List<LinePoint> SetLinePoint(Vector3[] createPoint)
             {
                 List<LinePoint> pipePoints = new List<LinePoint>();
                 int length = createPoint.Length;
                 for (int i = 0; i < length; i++)
                 {
                     if (i == 0)
                     {
                         Vector3 tangent = (createPoint[i + 1] - createPoint[i]).normalized;//法线
                         AddPipePoints(createPoint[i], tangent, ref pipePoints);
                     }
                     else if (i == length - 1)
                     {
                         Vector3 tangent = (createPoint[i] - createPoint[i - 1]).normalized;//法线
                         AddPipePoints(createPoint[i], tangent, ref pipePoints);
                     }
                     else
                     {
                         Vector3 tangent = (createPoint[i+1] - createPoint[i - 1]).normalized;//法线
                         AddPipePoints(createPoint[i], tangent, ref pipePoints);
                     }
                 }
                 return pipePoints;
             }
     /// <summary>
             /// 增加中心轴线点
             /// </summary>
             /// <param name="location">位置</param>
             /// <param name="direction">法线</param>
             void AddPipePoints(Vector3 location, Vector3 direction,  ref List<LinePoint> pipePoints)
             {
                 LinePoint pipePoint = new LinePoint();
                 pipePoint.Location = location;
                 pipePoint.Direction = direction;
                 pipePoints.Add(pipePoint);
             }
3.1.3网格创建
        /// <summary>
        /// 立体网格创建
        /// </summary>
        /// <param name="createPoint">创建的点数据</param>
        /// <param name="circularCount">圆的段数</param>
        /// <param name="circularR">圆的半径</param>
        /// <returns></returns>
        public Mesh CreateLine3D(Vector3[] createPoint, int circularCount, float circularR)
        {
            //截面圆
            Vector3[] circul = CircularSection(circularCount, circularR);
            //中心线
            List<LinePoint> centreLine = SetLinePoint(createPoint);
            //网格点数据
            Vector3[] meshPoint = CreateMeshPoint(centreLine, circul);
            float uvX = Vector3.Distance(circul[0], circul[1]);
            //返回网格
            return CreatMesh(centreLine, meshPoint, circul.Length, uvX);
        }
/// <summary>
        /// 创建网格点数据
        /// </summary>
        /// <param name="linePoint"></param>
        /// <param name="circular"></param>
        /// <returns></returns>
        Vector3[] CreateMeshPoint(List<LinePoint> linePoint, Vector3[] circular)
        {
            int length = linePoint.Count;
            int circularCount = circular.Length;
            Vector3[] meshPoint = new Vector3[length * circularCount];
            for (int i = 0; i < length; i++)
            {
                for (int j = 0; j < circularCount; j++)
                {
                    meshPoint[(i * circularCount) + j] = circular[j].FromToMoveRotation(linePoint[i].Location, linePoint[i].Direction);
                }
            }
            return meshPoint;
        }
        /// <summary>
        /// 网格创建
        /// </summary>
        /// <param name="linePoints">线的轴心线组</param>
        /// <param name="meshPoint">网格点</param>
        /// <param name="count">段数</param>
        /// <param name="uvX">uv宽度</param>
        /// <returns></returns>
        Mesh CreatMesh(List<LinePoint> linePoints, Vector3[] meshPoint, int count, float uvX)
        {
            Mesh mesh = new Mesh();
            mesh.vertices = meshPoint;
            mesh.triangles = GetTriangles(linePoints.Count, count);
            mesh.uv = GetUV(linePoints, count, uvX);
            mesh.RecalculateNormals();
            mesh.RecalculateBounds();
            return mesh;
        }
   /// <param name="length">线段段数</param>
        /// <param name="count">横截面段数(也就是圆的段数)</param>
        /// <returns></returns>
        int[] GetTriangles(int length, int count)
        {
            int[] triangles = new int[(count * (length - 1)) * 6];
            int k = 0;
            if (count == 1)
            {
                for (int i = 0; i < length-1; i++)
                {
                    int a = i * 2;
                    triangles[k] = a;
                    triangles[k + 1] = a + 1;
                    triangles[k + 2] = a + 3;
                    triangles[k + 3] = a;
                    triangles[k + 4] = a + 3;
                    triangles[k + 5] = a + 2;
                    k += 6;
                }
            }
            else
            {
                for (int i = 0; i < length - 1; i++)
                {
                    
                    for (int j = 0; j < count; j++)
                    {
                        if (j == count - 1)
                        {
                           // Debug.Log("k=" + k);
                            triangles[k] = (i * count) + j;
                            triangles[k + 1] = (i * count) + 0;
                            triangles[k + 2] = ((i + 1) * count) + 0;
                            triangles[k + 3] = (i * count) + j;
                            triangles[k + 4] = ((i + 1) * count) + 0;
                            triangles[k + 5] = ((i + 1) * count) + j;
                        }
                        else
                        {
                            triangles[k] = (i * count) + j;
                            triangles[k + 1] = (i * count) + j + 1;
                            triangles[k + 2] = ((i + 1) * count) + j + 1;
                            triangles[k + 3] = (i * count) + j;
                            triangles[k + 4] = ((i + 1) * count) + j + 1;
                            triangles[k + 5] = ((i + 1) * count) + j;
                        }
                        k += 6;
                    }
                }
            }
            return triangles;
        }
  /// <summary>
        /// 创建uv
        /// </summary>
        /// <param name="linePoints"></param>
        /// <param name="count"></param>
        /// <param name="uvX"></param>
        /// <returns></returns>
        Vector2[] GetUV(List<LinePoint> linePoints,int count, float uvX)
        {
            int length = linePoints.Count;
            if (count == 1) { count = 2; }
            Vector2[] uvs = new Vector2[(count * length)];
            float lineDis = 0;
            int k = 0;
            for (int i = 0; i < length; i ++)
            {
                
                if (i != 0)
                {
                    lineDis += Vector3.Distance(linePoints[i].Location, linePoints[i - 1].Location);
                }
                for (int j = 0; j < count; j++)
                {
                    Vector2 vector2;
                    if (j % 2 != 0)
                    {
                        vector2 = new Vector2(uvX, lineDis);
                    }
                    else
                    {
                        vector2 = new Vector2(0, lineDis);
                    }
                    uvs[k] = vector2;
                    k += 1;
                }
            }
            return uvs;
        }
3.2贝塞尔曲线的建立方法

源码

 /// <summary>
    /// 获取绘制点
    /// </summary>
    /// <param name="controlPoints"></param>
    /// <param name="segmentsPerCurve"></param>
    /// <returns></returns>
    public List<Vector3> GetDrawingPoints(List<Vector3> controlPoints, int segmentsPerCurve)
    {
        List<Vector3> points = new List<Vector3>();
        // 下一段的起始点和上段终点是一个,所以是 i+=3
        for (int i = 0; i <= controlPoints.Count - 4; i += 3)
        {

            var p0 = controlPoints[i];
            var p1 = controlPoints[i + 1];
            var p2 = controlPoints[i + 2];
            var p3 = controlPoints[i + 3];
            float dis = Vector3.Distance(p0, p3);
            int count = Mathf.CeilToInt(segmentsPerCurve * dis);
            if (count < segmentsPerCurve)
            {
                count = segmentsPerCurve;
            }

            for (int j = 0; j <= count; j++)
            {
                var t = j / (float)count;
                points.Add(CalculateBezierPoint(t, p0, p1, p2, p3));
            }
        }
        return points;
    }
    // 三阶公式
    Vector3 CalculateBezierPoint(float t, Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3)
    {
        Vector3 result;

        Vector3 p0p1 = (1 - t) * p0 + t * p1;
        Vector3 p1p2 = (1 - t) * p1 + t * p2;
        Vector3 p2p3 = (1 - t) * p2 + t * p3;

        Vector3 p0p1p2 = (1 - t) * p0p1 + t * p1p2;
        Vector3 p1p2p3 = (1 - t) * p1p2 + t * p2p3;

        result = (1 - t) * p0p1p2 + t * p1p2p3;
        return result;
    }

3.3贝塞尔曲线应用

基于上述方法实现了贝塞尔创建,保存,读取,在编辑功能。
案例下载地址

创建曲线

保存曲线

保存方法有两种分别是,长期保存和暂时保存。长期保存是将保存数据写入本地文件,项目重启也可以读取;暂时保存是在项目运行期间保存数据,重启后丢失。demo使用暂时保存的方法

读取再编辑

读取曲线后可以继续编辑当前曲线

曲线浏览

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/258506.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

msal auzer 强制刷新获取令牌

背景&#xff1a;msal auzer token 过期时间微软默认事60至90分钟&#xff0c;普遍取中间值&#xff0c;现渗透测试部分&#xff08;Qtester&#xff09;要求30分token 过期。且不可使用msal的安全机制。 解决方案&#xff1a;‘ 后端&#xff0c;解析token 获取发证时间 ia…

【C++入门到精通】 原子性操作库(atomic) C++11 [ C++入门 ]

阅读导航 引言一、原子性操作库简介二、原子变量1. 原子类型2. 原子类型函数3. 使用示例 三、总结温馨提示 引言 当谈及并发编程时&#xff0c;确保数据的安全性和一致性是至关重要的。在C11中引入的原子性操作库&#xff08;atomic&#xff09;为我们提供了一种有效且可靠的方…

我的创作纪念日——成为创作者第1024天

机缘 一、前言 早上收到CSDN的推送信息&#xff0c;今天是我成为创作者的第1024天&#xff0c;回想起自己已经好久没有写博客了&#xff0c;突然间很有感触&#xff0c;想水一篇文章&#xff0c;跟小伙伴们分享一下我的经历。 二、自我介绍 我出生在广东潮汕地区的一个小城…

(1)(1.9) MSP (version 4.2)

文章目录 前言 1 协议概述 2 配置 3 参数说明 前言 ArduPilot 支持 MSP 协议&#xff0c;可通过任何串行端口进行遥测、OSD 和传感器。这样&#xff0c;ArduPilot 就能将遥测数据发送到 MSP 兼容设备&#xff08;如大疆护目镜&#xff09;&#xff0c;用于屏幕显示&#x…

【数据结构】八大排序之简单选择排序算法

&#x1f984;个人主页:修修修也 &#x1f38f;所属专栏:数据结构 ⚙️操作环境:Visual Studio 2022 目录 一.简单选择排序简介及思路 二.简单选择排序的代码实现 三.简单选择排序的优化 四.简单选择排序的时间复杂度分析 结语 一.简单选择排序简介及思路 简单选择排序算法…

11 v-bind指令

概述 v-bind指令可以说是Vue3中最常用的指令之一&#xff0c;使用v-bind&#xff0c;我们几乎能够给任何实现动态的绑定比值。 这里&#xff0c;我们主要演示以下&#xff0c;通过v-bind动态绑定CSS样式。 基本用法 我们创建src/components/Demo11.vue&#xff0c;在这个组…

【Git】Git基本操作

文章目录 Git 是什么Git 的优点Git 安装Linux UbuntuLinux CentOsWindows Git 基本操作1. 创建 Git 本地仓库2. 配置 Git3. Git工作区、暂存区和版本库4. 添加文件5. 查看 .git 文件6. 修改文件7. 版本回退 Git 是什么 Git是一个免费的、开源的分布式版本控制系统&#xff0c;…

Axure RP - 交互设计的强大引擎

目录 前言 1. 交互设计&#xff1a;连接用户与产品的纽带 2. 情景设计&#xff1a;预测用户行为的未来 3. 演示和共享&#xff1a;让设计活起来 我的其他博客 前言 在数字化时代&#xff0c;用户体验的重要性日益突显&#xff0c;而交互设计成为塑造产品与用户互动的关键。…

大创项目推荐 深度学习 机器视觉 人脸识别系统 - opencv python

文章目录 0 前言1 机器学习-人脸识别过程人脸检测人脸对其人脸特征向量化人脸识别 2 深度学习-人脸识别过程人脸检测人脸识别Metric Larning 3 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 深度学习 机器视觉 人脸识别系统 该项目…

抢先看!Salesforce Spring ‘24中的10个亮点功能!

Spring 24来临在即&#xff0c;Preview Orgs已上线。在Spring 24中&#xff0c;将会为管理员、开发人员和顾问带来更多新功能。在这片云计算的海洋里&#xff0c;一些亮点功能总能在Salesforce生态系统中引起强烈反响。本篇文章为学习者们盘点了Spring 24中的10个亮点功能&…

什么是年份酒?红酒也有年份酒?

提到年份酒&#xff0c;人们第一时间会想到白酒&#xff0c;市场上有5年&#xff0c;20年&#xff0c;30年不等的各种年份酒。云仓酒庄的品牌雷盛红酒LEESON分享那么&#xff0c;什么是年份酒&#xff0c;红酒有没有年份酒呢&#xff1f; 在红酒酒标上看到一些与年份有关的数字…

海外呼叫中心是什么?海外呼叫中心有哪些功能?海外呼叫中心有哪些应用场景?

海外呼叫中心是什么&#xff1f; 海外呼叫中心通过电话呼出、呼入、智能外呼等渠道&#xff0c;提供客户支持和进行营销、通知等服务。可以为全球范围内的客户提供24小时不间断的电话服务支持&#xff0c;解决客户的疑问和需求。这些呼叫中心通常是有能够覆盖到全球的语音线路…

光条中心线提取-Steger算法 [OpenCV]

在线结构光视觉传感器中&#xff0c;由线激光器发射出的线结构光&#xff0c;在本质上为一个连续且具有一定厚度的空间光平面&#xff0c;而在目标表面上所形成的具有一定宽度的光条特征&#xff0c;即为该光平面与目标表面相交而成的交线。在该空间光平面的厚度方向上&#xf…

MSSql将test数据库还原为另外一个名字test1的数据库

有时候咱们需要将sqlserver数据库还原成另一个数据库中&#xff0c;如下&#xff1a; 1.备份原数据库。右击原数据库选择如下 2.跳到如下页面&#xff0c;如果目标有记录&#xff0c;那么全部进行删除&#xff0c;然后再添加。在“文件名”文本框中输入有效的路径和文件名&…

信号处理基础之噪声与降噪(一) | 噪声分类及python代码实现

后续将给大家分享信号处理基础系列文章&#xff0c;本期是讲噪声相关知识&#xff0c;包括噪声的定义、分类及python代码实现。 1. 噪声的定义 噪声是信息信号在传输过程中所受到的各种各样干扰信号的总成&#xff0c;其直接影响信号的传输质量&#xff0c;甚至破坏正常的信号…

Flask重定向后无效果前端无跳转无反应问题

在网上搜了一下并没有什么好的解决方案&#xff0c;有的话也只是告诉你如何修改代码&#xff0c;并没有讲明白其中的原理以及导致问题的核心&#xff0c;因此特意去了解了一下HTTP的规范找到了答案 问题说明 问题出现的流程大致都是前端发送Ajax请求给后端&#xff0c;进行一些…

泛微e-cology XmlRpcServlet文件读取漏洞复现

漏洞介绍 泛微新一代移动办公平台e-cology不仅组织提供了一体化的协同工作平台,将组织事务逐渐实现全程电子化,改变传统纸质文件、实体签章的方式。泛微OA E-Cology 平台XmRpcServlet接口处存在任意文件读取漏洞&#xff0c;攻击者可通过该漏洞读取系统重要文件 (如数据库配置…

【TI毫米波雷达】上电时序、串口回环BUG及SOP模式不正常工作的解决方案(LP87524电源PMIC芯片的BUCK供电时序配置)

【TI毫米波雷达】雷达上电时序及SOP模式不正常工作的解决方案&#xff08;LP87524电源PMIC芯片的BUCK供电时序配置&#xff09; 文章目录 上电时序上电以后的雷达串口回环问题延迟上电时序LP87524电源PMIC芯片的BUCK供电时序LP87524电源PMIC芯片的BUCK默认供电输出附录&#x…

Java第十九章课堂总结

要开发高级应用程序&#xff0c;就必须掌握一定的图像处理技术。Java绘图是Java程序开发不可缺少的技术&#xff0c;使用这些技术可以为程序提供数据统计、图表分析等功能&#xff0c;还可以为程序搭配音效&#xff0c;提高程序的交互能力。 19.1 Java绘图类 绘图是高级程序设…

管理类联考——数学——真题篇——按题型分类——充分性判断题——蒙猜C

老规矩&#xff0c;先看目录&#xff0c;平均每个3-4C&#xff08;C是月饼&#xff0c;月饼一般分为4块&#xff09; C是什么&#xff0c;是两个都不行了&#xff0c;但联合起来可以&#xff0c;联合的英文是combined&#xff0c;好的&#xff0c;我知道这个英文也记不住&#…