【问题标题】:Subdivide Bezier Curves细分贝塞尔曲线
【发布时间】:2019-02-08 14:27:44
【问题描述】:

我想实现我可以用给定的距离细分贝塞尔曲线。现在,如果 Bezier 是一条直线,它就可以工作,但是如果我更改 Controll-Point(B&C) 使 Bezier 变得弯曲,则计算点之间的差距不再像给定的距离!

我浏览过网络,但没有遇到类似的问题。

float t = Distance between subdividedParts / bezier length;

//A,B,C,D = ControllPoints of Bezier

GetPoint(A,B,C,D,t);

//GetPoint equation:
public static Vector3 GetPoint (Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t) {
         t = Mathf.Clamp01(t);
         float OneMinusT = 1f - t;
         return
             OneMinusT * OneMinusT * OneMinusT * p0 +
             3f * OneMinusT * OneMinusT * t * p1 +
             3f * OneMinusT * t * t * p2 +
             t * t * t * p3;
     }

【问题讨论】:

  • 如果您正在寻找在曲线上的某个点细分三次贝塞尔曲线的算法,请先查看这篇文章 (math.stackexchange.com/questions/527005/…),然后再查看这篇文章 (stackoverflow.com/questions/33928808/…)
  • 谢谢,但我不需要新曲线,我需要在实际贝塞尔曲线上找到点,就像我有一个长度为 100 的贝塞尔曲线一样,我希望每 10 个浮点数后的 vector3 位置(Vector3.距离(这个,下一个)== 10)。
  • @fang 当我有一个数组,例如在 0 和 1(t) 之间有 20 个浮点数时,如何创建新的贝塞尔曲线?
  • 您在 2 月 12 日不是已经接受了您自己的帖子作为答案吗?如果您有更多问题,请创建一个新问题,而不是在已标记为已回答的旧问题下提问。
  • 感谢您的提示,我会就此开启一个新的讨论。

标签: c# unity3d division bezier curve


【解决方案1】:

我现在已经设法获得了相当准确的方法来分割贝塞尔曲线并获得位置 => 但它的性能消耗随着它的准确度而增加。所以这可以在这段代码中改进:

 //if accuracy is 0.001 = good performance | if 0.000001 laggy performance
    public Vector3[] GetPoints (float gap,float accuracy){
     SimpsonVec sv = SV_Setup(0);
     Vector3 last_spawn = Bezier.GetPoint(sv.A,sv.B,sv.C,sv.D,0);

     List<Vector3> allPoints = new List<Vector3>();
     allPoints.Add(last_spawn);

     for(float t = accuracy;t <= 1.0f; t +=accuracy){
         Vector3 trial = Bezier.GetPoint(sv.A,sv.B,sv.C,sv.D,t);
         if(Vector3.Distance(trial,last_spawn) >= gap){
             last_spawn = trial;
             allPoints.Add(trial);
         }
     }
     return allPoints.ToArray();
 }

为了获得更好的性能,我现在这样做了:

Vector3[] 数组输出的代码

public Vector3[] GetAllPoints(float gap,float acc){

    SimpsonVector = SV_SETUP_ALL();
    BezierPoints bp = new BezierPoints();
    bp.bp_vector3 = new List<Vector3>();
    bp.bp_lastSpawn = new List<Vector3>();

    for(int i = 0; i<points.Length / 3;i++){

        Vector3 ls = new Vector3();
        if(i == 0){
            ls = Bezier.GetPoint(SimpsonVector[0].A,SimpsonVector[0].B,SimpsonVector[0].C,SimpsonVector[0].D,0);
        }if (i > 0){
            ls = bp.bp_lastSpawn[i-1];
        }
        BezierPoints bp_temp = GetSegmentPoints(gap,acc,i,ls);
        bp.bp_lastSpawn.Add(bp_temp.bp_lastSpawn[0]);
        bp.bp_vector3.AddRange(bp_temp.bp_vector3);
        SimpsonVector_TEMP = SimpsonVector;
    }


    return bp.bp_vector3.ToArray();
}

BezierPoints GetSegmentPoints (float gap,float acc,int index, Vector3 ls)
{
    SimpsonVec sv = SimpsonVector[index];
    Vector3 last_spawn = ls;

    BezierPoints bp = new BezierPoints();
    bp.bp_vector3 = new List<Vector3>();
    bp.bp_lastSpawn = new List<Vector3>();

    float step = 0.1f;
    float t = step;
    float lastT = new float();

    while (t >= 0 && t <= 1f)
    {
        while (t < 1f && Vector3.Distance(Bezier.GetPoint(sv.A,sv.B,sv.C,sv.D,t), last_spawn) < gap){
            t += step;}
        step /= acc;
        while (t > lastT && Vector3.Distance(Bezier.GetPoint(sv.A,sv.B,sv.C,sv.D,t), last_spawn) > gap){
            t -= step;}
        step /= acc;
        if (t > 1f || t < lastT){
            break;}
        if(step < 0.000001f){
            last_spawn = Bezier.GetPoint(sv.A,sv.B,sv.C,sv.D,t);
            bp.bp_vector3.Add(last_spawn + transform.position);
            lastT = t;
            step = 0.1f;
        }
    }
    bp.bp_lastSpawn.Add(last_spawn);
    return bp;
}

结构:

public struct SimpsonVec{
    [SerializeField] public Vector3 A;
    [SerializeField] public Vector3 B;
    [SerializeField] public Vector3 C;
    [SerializeField] public Vector3 D;
}

public struct  BezierPoints
{
    [SerializeField] public List<Vector3> bp_vector3;
    [SerializeField] public List<Vector3> bp_lastSpawn; 
}

辅助方法:

public SimpsonVec SV_Setup(int index){
     SimpsonVec sv;
     sv.A = points[index];
     sv.B = points[index+1];
     sv.C = points[index+2];
     sv.D = points[index+3];
     return sv;

 }
 public SimpsonVec[] SV_SETUP_ALL(){
     SimpsonVec[] sv = new SimpsonVec[points.Length / 3];
     for(int i = 0; i<points.Length / 3;i++){
         sv[i] = SV_Setup(i*3);
     }
     return sv;
 }
 public Vector3 GetPoint (Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t) {
     t = Mathf.Clamp01(t);
     float OneMinusT = 1f - t;
     return
         OneMinusT * OneMinusT * OneMinusT * p0 +
         3f * OneMinusT * OneMinusT * t * p1 +
         3f * OneMinusT * t * t * p2 +
         t * t * t * p3;
 }

【讨论】:

    【解决方案2】:

    无论如何,您都需要绘制曲线,因此请保留曲线的查找表并预先计算每个条目的距离,或者通过选择t 值、计算距离然后移动来对胜利进行二分搜索如果您关闭,则将 t 的一半值向上/向下,并重复此操作,直到您达到所需的精度。而且由于二进制搜索非常高效,因此您只需极少的尝试次数即可到达那里。

    请参阅https://pomax.github.io/bezierinfo/#tracing 了解基本原理,https://pomax.github.io/bezierinfo/#arclength 了解计算曲线的长度(https://pomax.github.io/bezierinfo/#splitting 是获取确定某个点处的曲线长度所需的值的明显部分t),和“所有https://pomax.github.io/bezierinfo 了解更多信息”,真的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-03-10
      • 1970-01-01
      • 1970-01-01
      • 2013-01-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多