ページ 11

3Dの追跡カメラの特異姿勢問題

Posted: 2014年6月20日(金) 13:04
by OROCHI
こんにちは。現在よくある飛行機の後方に視点を置き、前方に注視点を置くというカメラを制御するプログラムを書いています

そこで、視点と注視点を以下のようなコードで求めました(PIは円周率、EYLENGTHは視点までの距離、ATLENGTHは注視点までの距離です)。描画はDirectXを使っているので左手系になっています

コード:

        // 視点の座標を計算
	D3DXVECTOR3 eyePos;
	eyePos.x = character[ CODE_PLAYER ]->pos_.x + EYELENGTH * cos(character[ CODE_PLAYER ]->rot_.y - PI / 2 );
	eyePos.y = character[ CODE_PLAYER ]->pos_.y - EYELENGTH * sin(character[ CODE_PLAYER ]->rot_.x ) + 1.0f;
	eyePos.z = character[ CODE_PLAYER ]->pos_.z - EYELENGTH * sin(character[ CODE_PLAYER ]->rot_.y - PI / 2 );

	// 注視点の座標を計算
	D3DXVECTOR3 atPos;
	atPos.x = character[ CODE_PLAYER ]->pos_.x - ATLENGTH * cos(character[ CODE_PLAYER ]->rot_.y - PI / 2 );
	atPos.y = character[ CODE_PLAYER ]->pos_.y + ATLENGTH * sin(character[ CODE_PLAYER ]->rot_.x );
	atPos.z = character[ CODE_PLAYER ]->pos_.z + ATLENGTH * sin(character[ CODE_PLAYER ]->rot_.y - PI / 2 );
ですが、この方法ですとピッチ角(X軸周りの回転)を変化させていくと、少しずつ誤差が生じていき、ついにはカメラの視点が機体の真上に来るという現象が起きます
所謂特異姿勢問題であるとは思うのですが、この方法では機体後方に視点を置き、前方に注視点を置くという方法は使えないのでしょうか?3Dに詳しい方、ご回答お願いします

Re: 3Dの追跡カメラの特異姿勢問題

Posted: 2014年6月20日(金) 14:13
by usao
>この方法では...

「この方法」というのが何を指すのかよくわかりません.
(単語に自信がありませんが,
 仰角:X-Z平面からの立ち上がり角 と
 方位角:Y軸周り角度 とで
 回転姿勢を表そうとされているのでしょうか? コード見た感じ.)

回転を表す角度が2つ(で事足りる内容なのだろうか?)存在する場合,
計算式内に,2つの角度が使われる座標計算 が普通は出てくることになると思うのですが
提示コードでは,全ての式が どちらか一方の角度しか使ってないのが気になります.
(例えば,xやzの計算にも rot_.x を用いる必要があるのではないかな)

Re: 3Dの追跡カメラの特異姿勢問題

Posted: 2014年6月20日(金) 14:34
by OROCHI
仰る通り、仰角と方位角のみで計算しています(Z軸周りの回転は考慮に入れていません)

2つの角度が使われる座標計算、とはロボットアームの運動方程式のような2自由度系を考えなくてはだめだということでしょうか?

Re: 3Dの追跡カメラの特異姿勢問題

Posted: 2014年6月20日(金) 15:11
by softya(ソフト屋)
これだとワールド面で回転しているように見えますが。
【補足】 なぜ行列を避けているのかが分かりません。 この方法だとクォータニオンを使ったほうが良いかも。

Re: 3Dの追跡カメラの特異姿勢問題

Posted: 2014年6月20日(金) 15:29
by usao
例えば,自身が紙飛行機(?)になったつもりで,
何か棒なりあるいは片手を前に突き出して,その先端を
>注視点
だと思って,その姿勢から回転してみるとわかりやすいのではないでしょうか?

簡単化のため,自分のいる座標を (0,0,0),
初期の姿勢を無回転姿勢だとして, 注視点 の位置を (0,0,1) だとしましょうか.
ここから仰角(rot_.x)が増える方向に回転することを考えてみましょう.
注視点のy座標が,仰角の関数として sin(仰角) となるlことがイメージできると思います.
さて,ではこのとき,注視点の残りの X,Z 座標はどうなるのでしょう?
X座標は相変わらず0でしょうが,Zの方も「あいかわらず1」で有り続けるでしょうか?
最もわかりやすい例としては,
ぐーっと仰角を増やし続けて90度に達したとき,Zの値は0になるハズです.
(X側についても,最初に少し方位角方向に回転してから同じようなことを考えてみればZと同様であることがわかるはずです)

これに対して,
あなたの提示したコードでは,XやZの計算に,rot_.x の影響が考えられていません.


……ということ.
(あと,視点と注視点の2点「だけ」を計算しても,カメラを配置する段になって
 情報が足りない:カメラのRoll姿勢が定まらない と思うのだけれど,そこらへんは大丈夫なのかな?)

Re: 3Dの追跡カメラの特異姿勢問題

Posted: 2014年6月20日(金) 15:38
by usao
オフトピック
>なぜ行列を避けているのかが分かりません

このように行列計算の結果を書き下して実装した場合だと
何かしら利便性とかに問題があるということでしょうか?(DirectX的な意味で?)

Re: 3Dの追跡カメラの特異姿勢問題

Posted: 2014年6月20日(金) 15:41
by softya(ソフト屋)
usao さんが書きました:
オフトピック
>なぜ行列を避けているのかが分かりません

このように行列計算の結果を書き下して実装した場合だと
何かしら利便性とかに問題があるということでしょうか?(DirectX的な意味で?)
行列を使えば楽なのに避けているか不明とか、SSE(高速化)が使われないよとか。
あと完璧に展開されていないですよとかですね。

Re: 3Dの追跡カメラの特異姿勢問題

Posted: 2014年6月20日(金) 16:43
by usao
オフトピック
>この方法では機体後方に視点を置き、前方に注視点を置くという方法は使えないのでしょうか?

という事柄に対して回答していますが…

この方法 : 仰角と方位角を使う方式 だと
rot_ が「1フレーム前の飛行機座標系での値」とかいう話でないならば,
飛行機のフレーム間姿勢変化を書くのが非常にしんどいように思いますが そんなことは無いのでしょうか?
(実際上は仰角が0にかなり近いところでしか使わないから,あまり問題にしなくてもいい,とか?)

仰角と方位角を使う方式 というのは,
ある決まった座標系の上でのある位置から見た "方向" を表すのには,まぁ良いと思うのですが,
大きさを持ったモデルの3次元な "姿勢" を表す場合には向かないというか,
もっと普通の(?)方法を使った方が良いと思う.

全然関係ないけど,さっきから「ほういかく」が「砲威嚇」って変換されてつらい.どんな威嚇だよ…

Re: 3Dの追跡カメラの特異姿勢問題

Posted: 2014年6月20日(金) 17:00
by OROCHI
行列を使うのなら

平行移動行列

コード:

D3DXMATRIX out;
out._11 = out._22 = out._33 = out._44 = 1.0f;
out._12 = out._13 = out._14 = out._21 = out._23 = out._24 = out._31 = out._32 = out._34 = 0.0f;
out._41 = Trans.x;
out._42 = Trans.y;
out._43 = Trans.z;
回転行列

コード:

D3DXMATRIX outRot;
outRot._11 = ( cos( zrot ) * cos( yrot ) ) + ( sin( zrot ) * sin( xrot ) * sin( yrot ) );
outRot._12 = ( sin( zrot ) * cos( xrot ) );
outRot._13 = ( cos( zrot ) * -sin( yrot ) ) + ( sin( zrot ) * sin( xrot ) * cos( yrot ) );
outRot._14 = 0;
outRot._21 = ( -sin( zrot) * cos( yrot ) ) + ( cos( zrot ) * sin( xrot ) * sin( yrot ) );
outRot._22 = ( cos( zrot ) * cos( xrot ) );
outRot._23 = ( sin( zrot ) * sin( yrot ) ) + ( cos( zrot ) * sin( xrot ) * cos( yrot ) );
outRot._24 = 0;
outRot._31 = ( cos( xrot ) * sin( yrot ) );
outRot._32 = -sin( xrot );
outRot._33 = ( cos( xrot ) * cos( yrot ) );
outRot._34 = 0;
outRot._41 = outRot._42 = outRot._43 = 0;
outRot._44 = 1;
となるので、Transに character[ CODE_PLAYER ] からの相対位置を代入したら回転行列×平行移動行列で移動先が出せると思ったのですが上手くいきませんでした
そもそも使う行列はこれでいいのでしょうか?

Re: 3Dの追跡カメラの特異姿勢問題

Posted: 2014年6月20日(金) 20:58
by softya(ソフト屋)
なぜ、行列系の関数を使わないのでしょうか?

「カメラの回転 - ゲームプログラミングWiki」
http://www.c3.club.kyutech.ac.jp/gamewi ... 2%F3%C5%BE
ココらへんをちょこっと応用するだけだと思います。