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

news2024/11/25 2:43:04

[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使用暂时保存的方法

读取再编辑

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

曲线浏览

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

22.JSP技术

JSP起源 在很多动态网页中&#xff0c;绝大部分内容都是固定不变的&#xff0c;只有局部内容需要动态产生和改变。如果使用Servlet程序来输出只有局部内容需要动态改变的网页&#xff0c;其中所有的静态内容也需要程序员用Java程序代码产生&#xff0c;整个Servlet程序的代码将…

智能优化算法应用:基于阿基米德优化算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于阿基米德优化算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于阿基米德优化算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.阿基米德优化算法4.实验参数设定…

智能 GPT 图书馆又重生了

智能 GPT 图书馆又重生了 作者&#xff1a;程序员小白条 1&#xff09;概述 自从大二寒假准备开始筹备这个项目&#xff0c;到现在已经一年了&#xff0c;这个项目能维护一年&#xff0c;不愧是我.jpg。本来这个项目只是想练练手&#xff0c;因为那时候刚学完 Spring Boot2 V…

构建强大应用的引擎:深度解析Spring Boot Starter机制

目录 引言1. Spring Boot Starter机制1.1 什么是Spring Boot Starter1.2 为什么要使用Spring Boot Starter1.3.应用场景1.4.自动加载核心注解说明 2. 综合案例配置类制作控制功能实现 总结 引言 在当今互联网时代&#xff0c;构建高性能、可维护的应用已成为开发者的首要任务。…

Ansible自动化运维以及模块使用

ansible的作用&#xff1a; 远程操作主机功能 自动化运维(playbook剧本基于yaml格式书写) ansible是基于python开发的配置管理和应用部署工具。在自动化运维中&#xff0c;现在是异军突起 ansible能够批量配置、部署、管理上千台主机。类似于Xshell的一键输入工具。不需要每…

vscode如何开发微信小程序?(保姆级教学)

1.安装“微信小程序开发工具”扩展 2.安装“vscode weapp api”扩展 3.安装“vscode wxml”扩展 4.安装“vscode-wechat”扩展 5.在终端执行命令&#xff1a; vue create -p dcloudio/uni-preset-vue uniapp-test uniapp-test就是我这里的项目名称了 6.如果遇到了这个错误&a…

【C盘清理】Jetbrains全家桶(PyCharm、Clion……)更改 IDE 特定文件(配置、缓存、插件、日志等)存储位置

文章目录 一、官网说明二、更改 IDE 目录的位置1. 转到“帮助”|“编辑自定义属性”2. 各文件位置3. 以PyCharm系统目录为例4. 修改idea.properties 三、清理旧的 IDE 目录 一、官网说明 IDE 使用的目录官网说明 二、更改 IDE 目录的位置 默认情况下&#xff0c;PyCharm 将每…

c语言:输出26个英文字母|练习题

一、题目 分两排&#xff0c;输出26个英文字母 如图&#xff1a; 二、思路分析 1、从第13个字母分行显示 2、从A开始&#xff0c;在A的ASC码后面&#xff0c;按顺序加1~26 三、代码图片【带注释】 四、源代码【带注释】 #include <stdio.h> //题目:输入26个字母&#x…

企业电子招标采购系统源码Spring Cloud + Spring Boot + 前后端分离 + 二次开发

项目说明 随着公司的快速发展&#xff0c;企业人员和经营规模不断壮大&#xff0c;公司对内部招采管理的提升提出了更高的要求。在企业里建立一个公平、公开、公正的采购环境&#xff0c;最大限度控制采购成本至关重要。符合国家电子招投标法律法规及相关规范&#xff0c;以及审…

各种不同语言分别整理的拿来开箱即用的8个开源免费单点登录(SSO)系统

各种不同语言分别整理的拿来开箱即用的8个开源免费单点登录&#xff08;SSO&#xff09;系统。 单点登录&#xff08;SSO&#xff09;是一个登录服务层&#xff0c;通过一次登录访问多个应用。使用SSO服务可以提高多系统使用的用户体验和安全性&#xff0c;用户不必记忆多个密…

2023_Spark_实验二十九:Flume配置KafkaSink

实验目的&#xff1a;掌握Flume采集数据发送到Kafka的方法 实验方法&#xff1a;通过配置Flume的KafkaSink采集数据到Kafka中 实验步骤&#xff1a; 一、明确日志采集方式 一般Flume采集日志source有两种方式&#xff1a; 1.Exec类型的Source 可以将命令产生的输出作为源&…

HarmonyOS4.0从零开始的开发教程18后台代理提醒

HarmonyOS&#xff08;十六&#xff09;后台代理提醒 简介 随着生活节奏的加快&#xff0c;我们有时会忘记一些重要的事情或日子&#xff0c;所以提醒功能必不可少。应用可能需要在指定的时刻&#xff0c;向用户发送一些业务提醒通知。例如购物类应用&#xff0c;希望在指定时…

搭建Eureka服务

搭建Eureka服务 文章目录 搭建Eureka服务搭建EurekaServer注册user-service注册多个实例 在order-service中完成服务拉取和负载均衡 搭建EurekaServer <dependency><!--eureka服务器--><groupId>org.springframework.cloud</groupId><artifactId>…

QUIC在零信任解决方案的落地实践

一 前言 ZTNA为以“网络为中心”的传统企业体系架构向以“身份为中心”的新型企业安全体系架构转变&#xff0c;提供解决方案。随着传统网络边界不断弱化&#xff0c;企业SaaS规模化日益增多&#xff0c;给终端安全访问接入创造了多元化的空间。其中BYOD办公方式尤为突出&#…

什么是uniapp?如何开发uniapp?

大家好&#xff01;我是咕噜铁蛋&#xff01;随着移动应用市场的持续发展&#xff0c;开发者们面临着不断增长的需求和多样化的平台选择。在这个背景下&#xff0c;UniApp应运而生&#xff0c;成为一种跨平台开发框架&#xff0c;为开发者提供了一种高效、简便的方式来开发移动…

前端框架的单文件组件(Single File Component)

聚沙成塔每天进步一点点 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦&#xff01;这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发…

机器学习之线性回归(Linear Regression)

概念 线性回归(Linear Regression)是机器学习中的一种基本的监督学习算法,用于建立输入变量(特征)与输出变量(目标)之间的线性关系。它假设输入变量与输出变量之间存在线性关系,并试图找到最佳拟合线来描述这种关系。 在简单线性回归中,只涉及两个变量:一个是自变量…

【赠书活动】OpenCV4工业缺陷检测的六种方法

文章目录 前言机器视觉缺陷检测工业上常见缺陷检测方法延伸阅读推荐语 赠书活动 前言 随着工业制造的发展&#xff0c;对产品质量的要求越来越高。工业缺陷检测是确保产品质量的重要环节&#xff0c;而计算机视觉技术的应用能够有效提升工业缺陷检测的效率和精度。 OpenCV是一…

AWS解决方案架构师学习与备考

系列文章目录 送书第一期 《用户画像&#xff1a;平台构建与业务实践》 送书活动之抽奖工具的打造 《获取博客评论用户抽取幸运中奖者》 送书第二期 《Spring Cloud Alibaba核心技术与实战案例》 送书第三期 《深入浅出Java虚拟机》 送书第四期 《AI时代项目经理成长之道》 …

Vue 项目中使用 debugger 在 chrome 谷歌浏览器中失效以及 console.log 指向去了 vue.js 代码

问题 今天在代码里面输出 console.log 信息直接指向了 vue.js&#xff0c;并且代码里面写了 debgger 也不生效 解决 f12 找到浏览器的这个设置图标 找到这个 ignore list 的 custom exclusion rules 取消掉 /node_modules/|/bower_components/ 这样就正常了