Безье Кубические кривые: перемещение с равномерным ускорением
Скажем, у меня есть кривая Безье B(u)
, если я увеличиваю параметр u
с постоянной скоростью, я не получаю перемещение скорости движения вдоль кривой, так как связь между параметром u
и точкой, полученной при оценке кривой, не является линейной.
Я читал и реализовал статью Дэвида Эберли . В нем объясняется, как двигаться с постоянной скоростью по параметрической кривой.
Предположим, что у меня есть функция F(t)
, которая принимает в качестве входного значения значение времени t
и функцию скорости sigma
, которая возвращает значение скорости в момент t
, я могу получить перемещение скорости транзакции вдоль кривой, изменяя параметр t с постоянной скоростью: B(F(t))
Ядро статьи, которую я использую, - это следующая функция:
float umin, umax; // The curve parameter interval [umin,umax].
Point Y (float u); // The position Y(u), umin <= u <= umax.
Point DY (float u); // The derivative dY(u)/du, umin <= u <= umax.
float LengthDY (float u) { return Length(DY(u)); }
float ArcLength (float t) { return Integral(umin,u,LengthDY()); }
float L = ArcLength(umax); // The total length of the curve.
float tmin, tmax; // The user-specified time interval [tmin,tmax]
float Sigma (float t); // The user-specified speed at time t.
float GetU (float t) // tmin <= t <= tmax
{
float h = (t - tmin)/n; // step size, `n' is application-specified
float u = umin; // initial condition
t = tmin; // initial condition
for (int i = 1; i <= n; i++)
{
// The divisions here might be a problem if the divisors are
// nearly zero.
float k1 = h*Sigma(t)/LengthDY(u);
float k2 = h*Sigma(t + h/2)/LengthDY(u + k1/2);
float k3 = h*Sigma(t + h/2)/LengthDY(u + k2/2);
float k4 = h*Sigma(t + h)/LengthDY(u + k3);
t += h;
u += (k1 + 2*(k2 + k3) + k4)/6;
}
return u;
}
Это позволяет мне вычислить параметр кривой u
, рассчитанный с использованием времени t
и сигма-функции.
Теперь функция отлично работает, когда сигма скорости является дорогостоящей. Если сигма представляет собой единое ускорение, я получаю неправильные значения от него.
Здесь приведен пример прямой кривой Безье, где P0 и P1 - контрольные точки, T0 T1 - касательная. Определенная кривая:
[x,y,z]= B(u) =(1–u)3P0 + 3(1–u)2uT0 + 3(1–u)u2T1 + u3P2
![enter image description here]()
Скажем, я хочу знать положение вдоль кривой в момент времени t = 3
.
Если я постоянная скорость:
float sigma(float t)
{
return 1f;
}
и следующие данные:
V0 = 1;
V1 = 1;
t0 = 0;
L = 10;
Я могу аналитически вычислить положение:
px = v0 * t = 1 * 3 = 3
Если я решаю одно и то же уравнение, используя сплайн Безье и алгоритм выше с n =5
, я получаю:
px = 3.002595;
Учитывая численное приближение, значение является довольно точным (я сделал много тестов на этом.Я опускаю детали, но Безье моя реализация кривых прекрасна, и сама длина кривой вычисляется достаточно точно, используя Гауссовская квадратура).
Теперь, если я попытаюсь определить сигму как единую функцию ускорения, я получаю плохие результаты.
Рассмотрим следующие данные:
V0 = 1;
V1 = 2;
t0 = 0;
L = 10;
Я могу вычислить время, когда частица достигнет P1, используя уравнения линейного движения:
L = 0.5 * (V0 + V1) * t1 =>
t1 = 2 * L / (V1 + V0) = 2 * 10 / 3 = 6.6666666
Имея t
, я могу рассчитать ускорение:
a = (V1 - V0) / (t1 - t0) = (2 - 1) / 6.6666666 = 0.15
У меня есть все данные для определения моей сигма-функции:
float sigma (float t)
{
float speed = V0 + a * t;
}
Если аналитически решить это, я ожидаю следующую скорость частицы после времени t =3
:
Vx = V0 + a * t = 1 + 0.15 * 3 = 1.45
и позиция будет:
px = 0.5 * (V0 + Vx) * t = 0.5 * (1 + 1.45) * 3 = 3.675
Но если я вычислил его с вышеописанным алоритом, результаты позиции:
px = 4.358587
что сильно отличается от ожидаемого.
Извините за длинный пост, если у кого хватит терпения прочитать его, я буду рад.
Есть ли у вас какие-либо предложения? Что мне не хватает? Кто-нибудь может сказать мне, что я делаю неправильно?
EDIT:
Я пытаюсь с кривой 3D Безье. Определено следующим образом:
public Vector3 Bezier(float t)
{
float a = 1f - t;
float a_2 = a * a;
float a_3 = a_2 *a;
float t_2 = t * t;
Vector3 point = (P0 * a_3) + (3f * a_2 * t * T0) + (3f * a * t_2 * T1) + t_2 * t * P1 ;
return point;
}
и производная:
public Vector3 Derivative(float t)
{
float a = 1f - t;
float a_2 = a * a;
float t_2 = t * t;
float t6 = 6f*t;
Vector3 der = -3f * a_2 * P0 + 3f * a_2 * T0 - t6 * a * T0 - 3f* t_2 * T1 + t6 * a * T1 + 3f * t_2 * P1;
return der;
}
Ответы
Ответ 1
Я предполагаю, что n=5
просто не дает вам достаточной точности для этой проблемы. Тот факт, что это нормально для случая с постоянной скоростью, не означает, что это также относится к случаю с постоянным ускорением. К сожалению, вам нужно будет определить компромисс, который предоставляет значение n
, которое соответствует вашим потребностям и ресурсам.
В любом случае, если у вас действительно есть параметризация постоянной скорости X (u (t)), которая работает с достаточной точностью, вы можете переименовать этот параметр времени t
a "space" ( distance) s
, так что у вас действительно есть X (ы), и вам нужно только подключить s (t), который вам нужен: X (ы (т)). В вашем случае (постоянное ускорение) s (t) = s 0 + ut + at 2/2, где u и a легко определяются из ваших входных данных.
Ответ 2
Я думаю, что у вас просто есть опечатка где-то в функциях, не показанных в вашей реализации, Y и DY. Я попробовал одномерную кривую с P0 = 0, T0 = 1, T1 = 9, P1 = 10 и получил 3.6963165 с n = 5, что улучшилось до 3.675044 с n = 30 и 3.6750002 при n = 100.
Если ваша реализация двумерна, попробуйте с P0 = (0, 0), T0 = (1, 0), T1 = (9, 0) и P1 = (10, 0). Затем повторите попытку с P0 = (0, 0), T0 = (0, 1), T1 = (0, 9) и P1 = (0, 10).
Если вы используете C, помните, что оператор ^ НЕ означает экспоненту. Вы должны использовать pow (u, 3) или u * u * u, чтобы получить куб u.
Попробуйте распечатать значения как можно большего количества материалов на каждой итерации. Вот что я получил:
i=1
h=0.6
t=0.0
u=0.0
LengthDY(u)=3.0
sigma(t)=1.0
k1=0.2
sigma(t+h/2)=1.045
LengthDY(u+k1/2)=6.78
k2=0.09247787
LengthDY(u+k2/2)=4.8522377
k3=0.12921873
sigma(t+h)=1.09
LengthDY(u+k3)=7.7258916
k4=0.08465043
t_new=0.6
u_new=0.12134061
i=2
h=0.6
t=0.6
u=0.12134061
LengthDY(u)=7.4779167
sigma(t)=1.09
k1=0.08745752
sigma(t+h/2)=1.135
LengthDY(u+k1/2)=8.788503
k2=0.0774876
LengthDY(u+k2/2)=8.64721
k3=0.078753725
sigma(t+h)=1.1800001
LengthDY(u+k3)=9.722377
k4=0.07282171
t_new=1.2
u_new=0.20013426
i=3
h=0.6
t=1.2
u=0.20013426
LengthDY(u)=9.723383
sigma(t)=1.1800001
k1=0.072814174
sigma(t+h/2)=1.225
LengthDY(u+k1/2)=10.584761
k2=0.069439456
LengthDY(u+k2/2)=10.547299
k3=0.069686085
sigma(t+h)=1.27
LengthDY(u+k3)=11.274727
k4=0.06758479
t_new=1.8000001
u_new=0.26990926
i=4
h=0.6
t=1.8000001
u=0.26990926
LengthDY(u)=11.276448
sigma(t)=1.27
k1=0.06757447
sigma(t+h/2)=1.315
LengthDY(u+k1/2)=11.881528
k2=0.06640561
LengthDY(u+k2/2)=11.871877
k3=0.066459596
sigma(t+h)=1.36
LengthDY(u+k3)=12.375444
k4=0.06593703
t_new=2.4
u_new=0.3364496
i=5
h=0.6
t=2.4
u=0.3364496
LengthDY(u)=12.376553
sigma(t)=1.36
k1=0.06593113
sigma(t+h/2)=1.405
LengthDY(u+k1/2)=12.7838
k2=0.06594283
LengthDY(u+k2/2)=12.783864
k3=0.0659425
sigma(t+h)=1.45
LengthDY(u+k3)=13.0998535
k4=0.06641296
t_new=3.0
u_new=0.4024687
Я отлаживал много таких программ, просто распечатывая массу переменных, затем вычисляя каждое значение вручную и удостоверяясь, что они одинаковы.