■ 2004年04月30日 [授業] ゲームグラフィックス特論
2012年03月05日 13:32更新
球面線形補間
4月に入ってなんだか妙に忙しいと思っているうちに,今日はもう30日!なのに,今月は全然ブログ書いとらん…いかん,何か書かねばと思ってもネタがない.仕方がないからゲームグラフィックス特論(なんちゅう講義名だ)の宿題のヒントを.
まず,2つのクォータニオンの補間の実装は,例えばこんな具合になります.
#include <math.h>
#include <float.h>
/*
** 球面線形補間 p ← q と r を t で補間したクォータニオン
*/
void slerp(double p[], const double q[], const double r[],
const double t)
{
double qr = q[0] * r[0] + q[1] * r[1] + q[2] * r[2] + q[3] * r[3];
double ss = 1.0 - qr * qr, sp;
if (ss <= 0.0 || (sp = sqrt(ss)) == 0.0) {
p[0] = q[0];
p[1] = q[1];
p[2] = q[2];
p[3] = q[3];
}
else {
double ph = acos(qr);
double pt = ph * t;
double t1 = sin(pt) / sp;
double t0 = sin(ph - pt) / sp;
p[0] = q[0] * t0 + r[0] * t1;
p[1] = q[1] * t0 + r[1] * t1;
p[2] = q[2] * t0 + r[2] * t1;
p[3] = q[3] * t0 + r[3] * t1;
}
}
宿題のプログラムを完成させるのに必要なのは,時間軸上に配置された複数のクォータニオンを補間して,ある時刻における姿勢を求める手続きです.すなわち,時刻 ti における姿勢 qi が与えられているとき,それらから時刻 t における姿勢 q(t) を求める関数を作ります.
このために,まず t を含む区間 [ti,ti+i) を探します.これには飛行経路を求めるのに使っているスプライン補間同様,二分探索を用いることができます.そして,その区間 i の両端のクォータニオン qi と qi+1 を線形補間して,q(t) を求めます.書いてみると簡単でしょ>某氏.
/*
** 複数のクォータニオン間の球面線形補間(折れ線)
** p ← t[i] におけるクォータニオン q[i], 0 <= i < n に対する
** u における補間値
**
*/
void mslerp(double p[],
const double t[], const double q[][4], const int n,
const double u)
{
int i = 0, j = n - 1;
/* u を含む t の区間 [t[i], t[i+1]) を二分法で求める */
while (i < j) {
int k = (i + j) / 2;
if (t[k] < u)
i = k + 1;
else
j = k;
}
if (i > 0) --i;
slerp(p, q[i], q[i + 1], (u - t[i]) / (t[i + 1] - t[i]));
}
それで,たとえばこういう絵を作ってみてください.
(しかし線形補間だとやっぱりぎこちない)
こんにちは!<br><br>細かい事で申し訳ないのですが…(汗)<br><br>slerp()で <br><br>if ((ss == 0.0) {...} else {...}<br><br>と処理を分けている箇所、ss が0より大きくて、すごく小さい値の場合などで、sqrt(ss) でエラーになったりする可能性がありますね…(というか、なりました)<br><br>以上、ご報告まで。
ご指摘ありがとうございます!ss が負でなかったら,小さくても sqrt(ss) がエラーになることは無いと思うんですが…<br>あ,ss が計算機イプシロンに近くて sqrt(ss) が 0 になってしまったために,sp での割り算でエラーになるということはありそうです.<br>そうならないようにするには sqrt(ss) を計算してから 0.0 との比較をしないといけないですね.