二次贝塞尔曲线弧参数化

时间:2017-08-04 18:13:36

标签: c# math unity3d bezier parameterization

目前尝试沿贝塞尔曲线实例化一些对象。这是有效的,除了网格沿曲线不均匀分布的事实。所以我需要做一些弧参数化。

我的信息来自此文档WarpingTextToSplines,因此以下代码是我对伪代码的解释。

首先,找到曲线的(近似)长度,并将其分成几个部分:

private void buildCurveTables()
{
    int curveSegments = 100;
    lengthTable = new float[curveSegments + 1];

    Vector3 previousPoint = GetBezierPoint(0, curvePoints[0], curvePoints[1], curvePoints[2]);

    float sum = 0;
    lengthTable[0] = 0;

    for (int i = 1; i < lengthTable.Length; i++)
    {
        Vector3 currentPoint = GetBezierPoint(i / (float) lengthTable.Length, curvePoints[0], curvePoints[1], curvePoints[2]);
        sum += Vector3.Distance(previousPoint, currentPoint);
        lengthTable[i] = sum;
        previousPoint = currentPoint;
    }

    totalCurveLength = sum;
}

这将沿曲线存储100个长度值。稍后将使用这些来确定曲线上放置网格的正确位置。使用以下内容在表中找到该值:

private float findPositionOnCurve(float u)
{
    float t; //Find t for the given u
    float targetArcLength = u * lengthTable[lengthTable.Length-1];

    //Debug.Log("u is: " + u);

    int index = Array.BinarySearch(lengthTable, targetArcLength);

    if (u >=1)
    {
        return 1;
    }

    if (index < 0)
    {
        index = ~index - 1;
        //No exact match found
        float lengthBefore = lengthTable[index];
        return (index + (targetArcLength - lengthBefore) / (lengthTable[index + 1] - lengthBefore)) / lengthTable.Length;
    }
    else
    {
        //Exact match found
        t = index / (float)lengthTable.Length - 1;
        //Debug.Log("Exact match, returning " + t);
        return t;
    }
}

我尝试了许多不同的二次贝塞尔曲线算法,但这些算法似乎产生了正确的结果。

 private Vector3 GetBezierPoint(float t, Vector3 start, Vector3 control, Vector3 end)
{
    //float x = (((1 - t) * (1 - t)) * start.x) + (2 * t * (1 - t) * control.x) + ((t * t) * end.x);
    //float y = (((1 - t) * (1 - t)) * start.y) + (2 * t * (1 - t) * control.y) + ((t * t) * end.y);
    //float z = (((1 - t) * (1 - t)) * start.z) + (2 * t * (1 - t) * control.z) + ((t * t) * end.z);
    //return new Vector3(x, y, z);

    //http://answers.unity3d.com/questions/990171/curve-between-lerps.html
    float rt = 1 - t;
    return rt * rt * start + 2 * rt * t * control + t * t * end;
}

更新

我还会把我写的代码放到实例化曲线上的对象上,以防我在那里做一些蠢事......

//Only try to curve the objects once the control point has been put down.
 if (curvedMode && createdCurveControlHandle)
 {
 Vector3 startOfCurve = getCurveStartPoint().transform.position;

 curvePoints = new Vector3[3] { startOfCurve, currentCurveControlHandle.transform.position, currentMousePosition };

 buildCurveTables();

//Lerp value is generated through a loop that generates 'n' number of 
objects based on the distance between the start and current mouse position
 float mappedLerp = findPositionOnCurve(lerpValue); 

 Vector3 pointTest = GetBezierPoint(mappedLerp, curvePoints[0], curvePoints[1], curvePoints[2]);

 nextPlacementLocationTarget = pointTest;

 nextPosition = Vector3.Lerp(startOfCurve, nextPlacementLocationTarget,mappedLerp);}

以下是一些lerp查找:

Lerp: 0.32 mappedLerp: 0.1659664
Lerp: 0.36 mappedLerp: 0.1896916
Lerp: 0.3999999 mappedLerp: 0.2143274
Lerp: 0.4399999 mappedLerp: 0.2399609
Lerp: 0.4799999 mappedLerp: 0.2667291
Lerp: 0.5199999 mappedLerp: 0.2948231
Lerp: 0.5599999 mappedLerp: 0.3244205

其他更新

经过一些调整和SO用户'MBo'的帮助后,我现在使用固定长度的十个对象得到了相当均匀的分布。然而,他们仍然在曲线的开头分配更多。

我更新了我的代码,使用第二个答案中提到的表格创建和二元搜索来解决这个问题How-to-achieve-uniform-speed-of-movement-on-a-bezier-curve

我经历了很多次检查,我开始睁大眼睛。我不确定我的数学是否已关闭,所以我误解了一些事情。

曲线上的大多数网格都在开始时分组,然后在一段时间后它们开始均匀。

如果有人有任何想法或建议,我将非常感激。

由于

1 个答案:

答案 0 :(得分:2)

您的Bezier数学和表格构建看起来不错。但搜索是奇怪的。

首先 - 使用一种用于不精确搜索的二进制搜索算法 - 它返回数组/列表中的索引,我们可以在其中插入项目。 Delphi实现示例:

alow := 0;
ahigh := A.Count - 1;
while ahigh - alow > 1 do begin
  j := (ahigh + alow) div 2;
  if Value <= A[j] then
    ahigh := j
  else
    alow := j;
end;

如果按顺序沿曲线放置对象(例如,文本字符),则线性搜索可能更有效 - 您总是向前移动,因此下一次搜索从当前索引开始:

i = 0  // or current index
while (A[i] < Value) && (i < A.Length)
   i++

请注意,沿Bezier弧长的t参数分布可能非常不均匀。示例显示了两条曲线,均带有t步参数1/8。在第二种情况下,t = 0.5对应于全长的0.28。

enter image description here

现在甚至弧长点的范围为0.05..0.95(relativelength = 0.05 + i / 10.0)。我使用过二维立方贝塞尔曲线,但没有主要区别。

enter image description here