细分贝塞尔曲线
Subdivide Bezier Curves
我想实现的是,我可以用给定的距离细分贝塞尔曲线。现在,如果贝塞尔曲线是一条直线,它就可以工作了,但是如果我改变一个控制点(B&C),那么贝塞尔曲线就会弯曲,计算出的点之间的差距不再像给定的距离!
我浏览了网络,但没有遇到类似的问题。
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;
}
我现在已经设法获得相当准确的方法来分割贝塞尔曲线并获得位置 => 但它的性能消耗随着它获得的准确性而增加。所以这可以在这段代码中得到改进:
//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;
}
无论如何你都需要画出你的曲线,所以保持曲线的查找 table 并预先计算每个条目的距离,或者通过选择一个 t
值二进制搜索你的胜利之路,计算距离,然后移动 t
值 up/down 的一半(如果您关闭),并重复该操作,直到达到所需的精度。而且因为二进制搜索非常高效,所以你可以在微不足道的尝试次数中找到它。
有关详细信息,请参阅 https://pomax.github.io/bezierinfo/#tracing for a rationale, https://pomax.github.io/bezierinfo/#arclength for computing the length of a curve (with https://pomax.github.io/bezierinfo/#splitting as obvious section on getting the value you need to determine the length of a curve at some point t
), and "all of https://pomax.github.io/bezierinfo”,真的。
我想实现的是,我可以用给定的距离细分贝塞尔曲线。现在,如果贝塞尔曲线是一条直线,它就可以工作了,但是如果我改变一个控制点(B&C),那么贝塞尔曲线就会弯曲,计算出的点之间的差距不再像给定的距离!
我浏览了网络,但没有遇到类似的问题。
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;
}
我现在已经设法获得相当准确的方法来分割贝塞尔曲线并获得位置 => 但它的性能消耗随着它获得的准确性而增加。所以这可以在这段代码中得到改进:
//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;
}
无论如何你都需要画出你的曲线,所以保持曲线的查找 table 并预先计算每个条目的距离,或者通过选择一个 t
值二进制搜索你的胜利之路,计算距离,然后移动 t
值 up/down 的一半(如果您关闭),并重复该操作,直到达到所需的精度。而且因为二进制搜索非常高效,所以你可以在微不足道的尝试次数中找到它。
有关详细信息,请参阅 https://pomax.github.io/bezierinfo/#tracing for a rationale, https://pomax.github.io/bezierinfo/#arclength for computing the length of a curve (with https://pomax.github.io/bezierinfo/#splitting as obvious section on getting the value you need to determine the length of a curve at some point t
), and "all of https://pomax.github.io/bezierinfo”,真的。