悬链线与弹簧质点模拟绳索

绳索模拟

最近有这么一个需求,模拟绳子。
如果是那种铰链之类的,可以直接用unity物理。如果是软体并且要求物理碰撞,那就得考虑Obi之类的用质点弹簧模型的解算器去解算了。
考虑到webGL也要用,Obi需求的求解器依赖于Burst,所以webgl没法使用。而且Obi不可控因素太多,为了一个简单的,无需物理的绳索动画,也没有必要使用太复杂的Obi。
还一种方式就是让美术在Blender,maya里绑定骨骼,制作动画,但这就是固定死的,也不方便后期调整。
最后就是本文的主题了,悬链线。

悬链线

首先贴出原文。以下基本是翻译,但最后增加补充了在三维中绘制悬链线的方式和代码。

悬链线就是指理想状态下的绳索在固定两端后自然下垂所形成的曲线,具体的公式推导就不赘述了。
曲线函数为:

函数

这里a是一个常数,决定了悬链线的形态,其余的p和q一看就知道只是横向与纵向的偏移。

但这种方程不符合我们的使用习惯,一般的逻辑应该是给出两点,长度。这里记为P1,P2,l。然后通过这些参数来给出曲线函数。
其中p和q可以通过以下方式来构造出:

这里

所以主要问题就是如何求解这个a。

求解a

首先,并没有可以简单的可以直接导出a的公式。
但上式可以转换成:

这里左边就是一个常数。
而右边可以证明是一个关于a的单调递减的函数。
所以可以用二分法快速求出一个近似解。
首先是确定一个二分的区间,确保其中有解。

我们可以从0出发,每次增加一个固定值,比如1,如果新值a1小于左边,就说明解存在与(a1-1,a1)之间。

然后从这个区间出发,用二分法求出一个相对精确的解。
下面贴上代码

private void GetARange()
{
    a = 0;
    currentIteration = 0;
    do
    {
        currentIteration++;
        a += IntervalStep;
    } while (Math.Sqrt(Math.Pow(l, 2) - Math.Pow(v, 2)) < 2 * a * Math.Sinh(h / (2 * a)) &&
             currentIteration < maxIteration);
}


private void GetA()
{
    double a_prev = a - IntervalStep;
    double a_next = a;

    currentIteration = 0;
    do
    {
        currentIteration++;
        a = (a_prev + a_next) / 2f;

        if (Math.Sqrt(Math.Pow(l, 2) - Math.Pow(v, 2)) < 2 * a * Math.Sinh(h / (2 * a)))
            a_prev = a;
        else
            a_next = a;
    } while (a_next - a_prev > Precision && currentIteration < maxIteration);
}

绘制

已经求出了全部参数,那悬链线函数就确定了,绘制可以采用lineRenderer。
也可以考虑使用unity新推出的专用来绘制曲线的Spline。前者简单快捷,后者稍显复杂但是提供了更多的扩展性,比如在两端再增加一些点,用来模拟绳子在两端的朝向。
或者也可以把悬链线参数传入shader中,用GPU来绘制。

这里先以lineRenderer为例。

lineRenderer

悬链线方程是二维平面内的曲线,所以这里把其中一个点作为起点,以这个起点为原点,世界坐标的Y轴为悬链线的Y轴,以起点指向终点在世界坐标下的xz平面中的投影作为X轴。

这样得出的曲线上每个点的坐标,只要先让起点在xz平面上朝向终点,然后用起点的Transform转换一下就可以了。

private void DrawCatenary()
{
    double segmentLength = h / resolution;


    Vector3 dir = endPoint.position - startPoint.position;

    var originPos = startPoint.position;
    var originRot = startPoint.rotation;


    startPoint.right = new Vector3(dir.x, 0, dir.z);
    lineRenderer.positionCount = resolution + 1;


    for (int i = 0; i <= resolution; i++)
    {
        double x = i * segmentLength;
        double y = CalculateCatenaryY(x);
        Vector3 point = new Vector3((float)x, (float)y, 0f);
        point = startPoint.TransformPoint(point);
        lineRenderer.SetPosition(i, point);
    }

    startPoint.SetPositionAndRotation(originPos, originRot);
}

悬链线

动态

todo
这样的模拟只是静态,没有动态惯性。静态看起来没有什么问题,但的一旦动起来,看起来还是比较僵硬。下面就设法为其增加一些动态效果。
先从简单的摇摆开始吧。

摇摆

这里把摇摆简单建模成每个点绕沿着起点与终点的连线的垂直方向上的交点的旋转,所以只要在二维中时计算每个点的旋转幅度即可。
用于一些接触点不变的如电线等物体效果好一些。

悬链线摇摆

惯性

写到这里就才发现,继续用悬链线来做已经不合适了,悬链线只能表示绳索的最终状态,要想完美实现符合基本物理规律的动态绳索,只能依靠质点弹簧了。那就再另起一篇实现个质点弹簧系统吧。

弹簧质点

最终还是要手撸一个弹簧质点。参考自
思路与代码就不贴了,原文很详细了。

弹簧质点模拟

既然都弹簧质点了,等有时间再来试试布料模拟吧。


悬链线与弹簧质点模拟绳索
https://www.kuanmi.top/2023/06/14/Catenary/
作者
KuanMi
发布于
2023年6月15日
许可协议