可弯曲软管的动态生成

最近在做一个化学VR实验室项目,其中要求用一个橡胶软管连接两个导管,两个导管可以任意移动旋转,而连接它们的橡胶软管需要跟随做弯曲和拉伸
美术给了一个直的软管模型

如何实现软管的弯曲和拉伸呢,想起了仰慕已久的Mega-Fiers插件
研究使用了半天,Mega-Fiers插件确实能弯曲和拉伸软管模型,但是
1.Mega-Fiers插件只对模型顶点做位置变化,不会增加和删除模型顶点,所以在对软管局部做角度偏大的弯曲时,会有明显的折痕(这个软管模型有2k+的顶点,增加模型横向顶点数折痕会有缓解)

2.Mega-Fiers插件很难对同一模型用多个形变组件组合出想要的效果,我试了很久没试出怎么组合出软管两头弯曲加中段拉伸的效果,更不要说在程序运行时动态组合效果了
3.Mega-Fiers插件无法满足一些精确的控制要求,比如无法让弯曲后的软管口和导管口无缝衔接

对静态模型做形变不能达到要求,于是想是否能运行时动态生成软管模型,研究了一下,发现确实可行,设计思路如下
1.如下图,以A点为一个导管口端点,DirA方向为导管延伸方向,在DirA方向上取一个合适的距离A-CA,B点为另一个导管口端点取同样的距离B-CB,以CA->CB方向取同样的距离CA-EA,以CB->CA为方向取同样距离CB-EB,CAB为CA-CB中心点


2.以A和EA点为一段贝塞尔曲线的顶点,CA为其控制点,获取到一串贝塞尔曲线点,同样以B和EB点为顶点,CB为控制点获取一串点,以EA,EB为顶点,CAB为控制点获取一串点

3.在以当前曲线点到下一个曲线点的方向为法线,穿过当前曲线点的平面上,以当前曲线点为圆心获取一圈圆的顶点

4.以获取到的所有圆的顶点为软管的模型顶点,再设置三角面和法线信息,最终生成软管模型的网格

中间一段是直线,可以只生成三个曲线点就可以


代码

using UnityEngine;
using System.Collections.Generic;

public struct BezierLineSegment
{
    public Vector3 fromPoint;
    public Vector3 toPoint;
    public Vector3 controlPoint;
    public Quaternion fromDir;
    public Quaternion toDir;

    public void CalculateDir()
    {
        fromDir = Quaternion.FromToRotation(Vector3.forward, controlPoint - fromPoint);
        toDir = Quaternion.FromToRotation(Vector3.forward, toPoint - controlPoint);
    }

    public bool IsStraight()
    {
        return fromDir == toDir;
    }
}

[ExecuteInEditMode]
public class BezierPipe : MonoBehaviour
{

    public float cornerScale = 1f;
    [Range(1,100)]
    public int cornerStep = 10;
    [Range(2,100)]
    public int circleStep = 10;

    public float r = 0.1f;

    public Transform point1;
    public Vector3 point1Dir = Vector3.up;
    public Transform point2;
    public Vector3 point2Dir = Vector3.up;

    public bool update = false;

    public Mesh mesh;
    List<Vector3> verts = new List<Vector3>();
    List<int> triangles = new List<int>();
    private MeshCollider mc;

    // Use this for initialization
    void Start ()
    {
        mesh = new Mesh();
        mesh.name = "Pipe";
        MeshFilter mf = GetComponent<MeshFilter>();
        if (mf != null)
        {
            mf.sharedMesh = mesh;
        }
        mc = GetComponent<MeshCollider>();
        if (mc != null)
        {
            mc.sharedMesh = mesh;
        }
        BuildMesh();
    }

    // Update is called once per frame
    void Update()
    {
        if (update)
        {
            BuildMesh();
        }
    }

    void GetCirclePoint(Vector3 pos, Quaternion dir,bool draw)
    {
        for (int a = 0; a <= circleStep; a++)
        {
            float p = 2 * Mathf.PI * a / circleStep;
            Vector3 cp = new Vector3(r * Mathf.Cos(p), r * Mathf.Sin(p), 0);
            cp = dir* cp + pos;
            //if(draw)
            //    Gizmos.DrawSphere(cp, 0.0005f);
            cp = transform.worldToLocalMatrix.MultiplyPoint(cp);
            //cp += transform.position;
            verts.Add(cp);
        }
    }

    void SetTriangles()
    {
        triangles.Clear();
        for (int i = 0; i < verts.Count - circleStep - 2; i ++)
        {
            triangles.Add(i);
            triangles.Add(i+1);
            triangles.Add(i+circleStep + 1);
            triangles.Add(i+circleStep + 1);
            triangles.Add(i+1);
            triangles.Add(i+circleStep + 2);
        }
        mesh.triangles = triangles.ToArray();
    }

    public void BuildMesh()
    {
        if (point1 != null && point2 != null)
        {
            verts.Clear();
            float scale = cornerScale;
            float length = (point1.position - point2.position).magnitude/4;
            if (scale > length)
            {
                scale = length;
            }
            BezierLineSegment[] segments = new BezierLineSegment[3];
            segments[0].fromPoint = point1.position;
            point1Dir.Normalize();
            segments[0].controlPoint = point1.position + point1.rotation * point1Dir * scale;

            segments[2].toPoint = point2.position;
            point2Dir.Normalize();
            segments[2].controlPoint = point2.position + point2.rotation * point2Dir * scale;

            segments[1].controlPoint = (segments[0].controlPoint + segments[2].controlPoint) / 2;

            segments[0].toPoint = segments[1].fromPoint = segments[0].controlPoint + (segments[1].controlPoint - segments[0].controlPoint).normalized * scale;
            segments[1].toPoint = segments[2].fromPoint = segments[2].controlPoint + (segments[1].controlPoint - segments[2].controlPoint).normalized * scale;

            transform.position = segments[1].controlPoint;
            segments[1].CalculateDir();
            transform.rotation = segments[1].fromDir;
//            Debug.Log (transform.eulerAngles);

            foreach (var segment in segments)
            {
                segment.CalculateDir();
                if (segment.IsStraight())
                {
                    GetCirclePoint(segment.fromPoint, segment.fromDir,true);
                    GetCirclePoint(segment.controlPoint, segment.fromDir,false);
                    GetCirclePoint(segment.toPoint, segment.toDir, true);
                    //Gizmos.DrawLine(segment.fromPoint, segment.controlPoint);
                    //Gizmos.DrawLine(segment.controlPoint, segment.toPoint);
                }
                else
                {
                    GetCirclePoint(segment.fromPoint, segment.fromDir, true);
                    Vector3 p1 = segment.fromPoint;
                    for (int s = 1; s < cornerStep; s++)
                    {
                        float t = (float) s/cornerStep;
                        Vector3 p2 = Bezier.GetPoint(segment.fromPoint, segment.controlPoint, segment.toPoint, t);
                        //Quaternion dir = Quaternion.FromToRotation(Vector3.forward, p2 - p1);
                        Quaternion dir = Quaternion.Lerp(segment.fromDir, segment.toDir, t);
                        GetCirclePoint(p2,dir,false);
                        //Gizmos.DrawLine(p1, p2);
                        p1 = p2;
                    }
                    GetCirclePoint(segment.toPoint, segment.toDir, true);
                    //Gizmos.DrawLine(p1,segment.toPoint);
                }
            }
            mesh.Clear();
            mesh.vertices = verts.ToArray();
            SetTriangles();
            mesh.RecalculateNormals();

            if (mc != null)
            {
                mc.sharedMesh = mesh;
            }
        }
    }
}



CornerScale即A-CA,CA-EA,B-CB,CB-EB的长度,越长软管曲线部分越长,直线部分越短,运行时CornerScale将不大于两个端点间距离的四分之一
CornerStep即A-EA间贝塞尔曲线要取的曲线点数,越高软管弯曲部分越顺滑
CircleStep即绕曲线点圆的顶点数,越高软管横截面越圆
R即绕曲线点圆的半径,即软管的粗度
两个Point即两个导管的头,两个Dir即两个导管头在未旋转时的延伸方向

效果


  • 45
    点赞
  • 113
    收藏
    觉得还不错? 一键收藏
  • 23
    评论
这是Unity 2019以后非常受欢迎的MegaFiers资产的全新版本。它具有完全重写核心系统的功能,以利用Unity Burst和Jobs,所有变形的CPU使用量减少5至40倍。 MegaFiers是完整的网格变形,动画和变形系统,包括超过50个修改器,例如弯曲,扭曲,FFD,位移,锥度等等。它们可以以任何组合堆叠,并且可以将任意数量的修改器应用于网格以实现复杂的结果。无论您的想象力如何,物体都可以拉伸,挤压或弯曲变形。或者,当对象在空间中移动时,使用空间扭曲使其自动变形。该套件的其他新增功能包括Point Cache动画支持,动态水波纹和浮动对象系统。 所有变形都可以在编辑器模式和播放模式下工作,因此在构建场景时,可以使用变形器为模型添加多样性。 MegaFiers还是变形TextMeshPro对象(Mesh和现在的UI版本)的理想选择,因此您可以用有趣的方式对文本进行动画处理。并且还可以与ProBuilder一起使用,使您可以直接在Unity中创建更多有趣的关卡。一个新功能是可以使用修改器使Sprite变形。 另一个强大的功能是我们的“网格包裹”系统,该系统允许一个网格被另一个变形和设置动画,使其非常适合服装等。该包裹系统还可以与Unity Blendshapes和蒙皮网格一起使用。 MegaWrap也已被重写为使用Jobs and Burst的速度提高了5倍以上。 MegaFiers用C#编写,并且包含所有源代码,并支持所有同时支持Burst和Jobs的Unity平台。从2019年起兼容所有版本的Unity,并且可以在IOS和Android以及VR和AR平台上使用。 MegaFiers还与所有Unity Rendering管道完全兼容。我们还将为Unity的所有新发行版不断更新资产。如果您有关于修改器的想法,请让我们知道或编写。 还包括先进的花键系统,该系统还允许路径跟随或将花键转换为网格,动态软管系统和用于履带车辆的系统。 新功能包括: 组变形 充分利用所有核心系统 修改器现在可以使用“连发”和“作业”,最多可以加速40倍 所有全新的使用Burst的法线计算系统 自动禁用修饰符以提高性能 编辑器响应更快 与Prefab系统无缝集成 复制对象就可以了 完整的动画窗口支持 完全还原系统 与所有渲染管线兼容 与精灵一起使用 适用于Text Mesh Pro网格 与ProBuilder对象一起使用 适用于AR和VR 改进的添加/删除/编辑修饰符的工作流程 全新的演示场景包含MegaLiner火箭飞船 包含的修饰符 吸引者贝塞尔 弯曲 气泡 凸 碰撞变形 符合 符合多 弄皱 曲线变形 曲线造型 曲线雕刻层 圆柱化 可变形 置换 位移限制 取代RT 取代网路摄影机 动态纹波 FFD 2x2x2 FFD 3x3x3 FFD 4x4x4 精灵的2D FFD修改器 地球 变形 驼峰 熔化 噪音 翻页 画 路径变形 枢轴调整 点缓存 推 径向偏斜 放松 波纹 卷起 绳变形 橡胶 规模 选择修饰符 简单测试 窦曲线 球形化 挤 拉紧 锥度 树弯曲 捻 紫外线改性剂 顶点动画 垂直噪声 扭曲(大多数修改器的扭曲版本) 海浪 挥手 世界路径变形 随附空间扭曲 弯曲 气泡 FFD 2x2x2 FFD 3x3x3 FFD 4x4x4 地球 驼峰 熔化 噪音 波纹 窦曲线 斯库 球形化 挤 拉紧 锥度 捻 海浪 挥手 Zstretch 其他功能包括 MegaShapes Lite,完整的贝塞尔曲线样条和网格划分系统 KML,SVG,OSM样条导入 Maya,Max和Blender的自定义样条线导出器 动画样条线 样条线到网格选项 将对象附加到变形网格系统 包裹网眼,非常适合衣物 动画书 纸卷轴系统 贝塞尔曲线图像变形 样条路径跟随系统 动态软管系统 履带车辆系统 火车追踪系统 导出到OBJ文件 将刚体约束到样条路径 包括沿路径行驶的RigidBody汽车的示例 角色控制器遵循样条线示例 样条API

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 23
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值