クォータニオンで指定した自機の回転から、追従するカメラの回転を指定したい

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
珈琲

クォータニオンで指定した自機の回転から、追従するカメラの回転を指定したい

#1

投稿記事 by 珈琲 » 6年前

現在フライトシューティングゲームを作っているのですが、自機のクォータニオンで指定した角度から、カメラの回転行列で指定する角度が計算できなくて悩んでいます。



カメラは、自機の位置の後方に存在するイメージです。
エースコンバットみたいなものをそのまま再現したいです。

どんなふうに回転しているかの説明なのですが
自機クラスでは自機から見た、ピッチ、ロール、ヨーの動作を実現するためにベクトル型のAxisX,Y,Zをメンバとして持ってます。
そしてこれらは、どれか1つの軸が回転するとそれに合わせて回転します。

例えばZ軸を回転させると、3Dモデルはロールに相当する機首の回転をさせます。
つまり、X軸とY軸(ピッチとヨー)が、Z軸に合わせてX軸とY軸のベクトルが回転し、ピッチ・ヨーの回転方向が変わるというものです。



こんなかんじで、自機からみたx,y,z軸を基準に回転させると、操作と回転を直結できて便利だったのですが、
自機の後方を維持して回転をそのままトレースするようなカメラの動きをさせたいと思った時に3つのベクトルから回転行列を作成するにはどうしたらいいかわかりません。

半日ぐらい悩んで
自機の座標から-Z軸方向にある程度距離を作って(平行移動)、自機の回転行列をそのまま適用すればできる気がしなくもないのですが、中々コードで表現できません。

1つのベクトルの回転率はそのまま他の2つのベクトルで表現しているため数値化されていないからでしょうか?
ただ、回転を数値化しても回転する順番で結果が変わる気がするので、あまり意味無いような気もします。


どうしたらいいでしょうか?


メンバ変数

コード:

//自機クラス
class CFighter{

	friend class CCamera;

private:
	//位置座標
	VECTOR Position;

	//角度
	VECTOR AxisX;
	VECTOR AxisY;
	VECTOR AxisZ; //実際に自機が向いている方向はZ方向

	CModel* Model;
}

実装

コード:

CFighter::CFighter(string ModelPath){
	Model = new CModel(ModelPath);

	Position=VGet(0.0f, 0.0f, 600.0f);

	AxisX= VGet(1,0,0);
    AxisY= VGet(0,1,0);
    AxisZ= VGet(0,0,1);
}

//位置座標
void CFighter::SetPosition(VECTOR tmpPosition){
	Position = tmpPosition;
}

VECTOR CFighter::ReactionQuaternion(VECTOR Axis,VECTOR EffectVector,float Rotation){

	//Q = (q; V) 指定軸の回転
    float rotaq=cos(Rotation/2);
	VECTOR vectq=VGet(Axis.x*sin(Rotation/2),Axis.y*sin(Rotation/2),Axis.z*sin(Rotation/2));

	//R = (r; W) Qのベクトルを反転したもの。
    float rotar=cos(Rotation/2);
    VECTOR vectr=VGet(-Axis.x*sin(Rotation/2),-Axis.y*sin(Rotation/2),-Axis.z*sin(Rotation/2));

	//RとPの掛け算をしてCクォータニオンを合成
	float rotac=-VDot(vectr,EffectVector);
    VECTOR vectc=VAdd(VGet(EffectVector.x*rotar,EffectVector.y*rotar,EffectVector.z*rotar),VCross(vectr,EffectVector));

	//CクォータニオンとQクォータニオンを合成してベクトルを返す
	return VAdd(VAdd(VGet(vectq.x*rotac,vectq.y*rotac,vectq.z*rotac),VGet(rotaq*vectc.x,rotaq*vectc.y,rotaq*vectc.z)),VCross(vectc,vectq));

}

void CFighter::Update(){

    MV1SetRotationZYAxis(ModelHandle,AxisZ,AxisY,0.0f);

}

//ピッチ 引数は -1.0~1.0
void CFighter::Pitch(float Rotation){
	float Rot=(DX_PI_F/145);
	AxisY=ReactionQuaternion(AxisX,AxisY,Rot*Rotation);
	AxisZ=ReactionQuaternion(AxisX,AxisZ,Rot*Rotation);
}

//ロール 引数は -1.0~1.0
void CFighter::Roll(float Rotation){
	float Rot=(DX_PI_F/60);
	AxisX=ReactionQuaternion(AxisZ,AxisX,Rot*Rotation);
	AxisY=ReactionQuaternion(AxisZ,AxisY,Rot*Rotation);
}

//ヨー 引数は -1.0~1.0
void CFighter::Yaw(float Rotation){
	float Rot=(DX_PI_F/180);    
	AxisX=ReactionQuaternion(AxisY,AxisX,Rot*Rotation);
	AxisZ=ReactionQuaternion(AxisY,AxisZ,Rot*Rotation);
}


アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 9年前
住所: 東海地方
連絡を取る:

Re: クォータニオンで指定した自機の回転から、追従するカメラの回転を指定したい

#2

投稿記事 by softya(ソフト屋) » 6年前

そういえば過去に似たようなことを書いたなぁ。と思い出しました。
「誘導されて フライトシミュのカメラについて DxLib使用 • C言語交流フォーラム ~ mixC++ ~」
http://dixq.net/forum/viewtopic.php?f=3&t=11564#p93795
これでどうでしょうか?
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

珈琲

Re: クォータニオンで指定した自機の回転から、追従するカメラの回転を指定したい

#3

投稿記事 by 珈琲 » 6年前

回答ありがとうございます

リンク先のコードを見てみました。

いくつか疑問が湧いてきたので、質問させて頂きます。

コード:

mat = MGetScale(VGet(2000.0,2000.0,2000.0));
この処理は何を目的としているのでしょうか?
ここから、

コード:

//  機体の回転
matRot = QuaternionToMatrix(Quaternion);
mat = MMult( mat, matRot );
ここらへんのコードを見るとロールやピッチと言うより、絶対軸で回転しているようにみえます。


コード:


        zAxis = VGet(mat.m[2][0], mat.m[2][1], mat.m[2][2]);
        Quaternion = Quaternion * CreateRotationQuaternion(roll, zAxis);
        //ピッチ
        xAxis = VGet(mat.m[0][0], mat.m[0][1], mat.m[0][2]);
        Quaternion = Quaternion * CreateRotationQuaternion(pitch, xAxis);
        //ヨー
        yAxis = VGet(mat.m[1][0], mat.m[1][1], mat.m[1][2]);
        Quaternion = Quaternion * CreateRotationQuaternion(yaw, yAxis);
そうするとQuaternionが、現在の回転を持っているのかと予想しているのですが
Quaternionの型がわからないので、もしかしたらクォータニオンクラスが作られててOperatorで掛け算していたりしているのでしょうか?
ただ、1つのクォータニオンですべての軸の回転を表現できるとは思えません・・・
また、CreateRotationQuaternion()の動作がいまいち予想できません、ベクトルに回転率を付けて返してるだけなのでしょうか?

[hr]

疑問点を質問としてまとめると、
1行目の

コード:

mat = MGetScale(VGet(2000.0,2000.0,2000.0));
が何を意味しているのか、というのと

3~11行目の

コード:

        //ロール
        zAxis = VGet(mat.m[2][0], mat.m[2][1], mat.m[2][2]);
        Quaternion = Quaternion * CreateRotationQuaternion(roll, zAxis);
        //ピッチ
        xAxis = VGet(mat.m[0][0], mat.m[0][1], mat.m[0][2]);
        Quaternion = Quaternion * CreateRotationQuaternion(pitch, xAxis);
        //ヨー
        yAxis = VGet(mat.m[1][0], mat.m[1][1], mat.m[1][2]);
        Quaternion = Quaternion * CreateRotationQuaternion(yaw, yAxis);
のQuaternionは自機の回転を表現しているのか?
というのと

51行目の

コード:

        MATRIX imatCam = MInverse(matCam);  //逆行列。
は、そのままmatCamをカメラのビュー行列に指定してはいけないのか?という疑問です。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 9年前
住所: 東海地方
連絡を取る:

Re: クォータニオンで指定した自機の回転から、追従するカメラの回転を指定したい

#4

投稿記事 by softya(ソフト屋) » 6年前

ちょっとあとで回答しますね。
時間が勿体無いので、試してみてください。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

珈琲

Re: クォータニオンで指定した自機の回転から、追従するカメラの回転を指定したい

#5

投稿記事 by 珈琲 » 6年前

テストプロジェクトを作って、見よう見まねで真似してみましたが、
一瞬だけ描画されて後は真っ暗、という感じでわかりません。

自機は移動してないはずなので、
カメラが勝手に動き続けてるか、モデルが極端に大きい・小さいかだと思うのですが、
自機の座標にskydomeを置いても描画されなかったので、描画自体されてないような・・・

コード:

#include "DxLib.h"
#include "math.h"

typedef struct {
	float Rot;
	VECTOR Axis;
}Quaternion_t;

typedef struct {
    int model;
    VECTOR position;
    Quaternion_t quaternion;
} my_t;
 
my_t my;

Quaternion_t CreateRotationQuaternion(float rot,VECTOR vector){
	Quaternion_t quaternion;

	quaternion.Axis=vector;
	quaternion.Rot=rot;

	return quaternion;
}

Quaternion_t QuaternionMult(Quaternion_t Q,Quaternion_t R){
/*
	Q = (q; V);
	R = (r; W);
	QR = (qr - V ・ W; qW + rV + V × W);
*/

	Quaternion_t Return;
	Return.Rot = Q.Rot*R.Rot - VDot(Q.Axis,R.Axis);
	Return.Axis = VAdd(VAdd(VScale(R.Axis,Q.Rot),VScale(Q.Axis,R.Rot)),VCross(Q.Axis,R.Axis));

	return Return;

}

void func(){
	MATRIX mat = MGetScale(VGet(2000.0,2000.0,2000.0));

	float roll	=0;
	float pitch =0;
	float yaw	=0;

	if(1==CheckHitKey(KEY_INPUT_LEFT)){
		roll+=DX_PI_F/180;
	}

	if(1==CheckHitKey(KEY_INPUT_RIGHT)){
		roll-=DX_PI_F/180;
	}

	if(1==CheckHitKey(KEY_INPUT_DOWN)){
		pitch+=DX_PI_F/180;
	}

	if(1==CheckHitKey(KEY_INPUT_UP)){
		pitch-=DX_PI_F/180;
	}

	if(1==CheckHitKey(KEY_INPUT_A)){
		yaw+=DX_PI_F/180;
	}

	if(1==CheckHitKey(KEY_INPUT_D)){
		yaw-=DX_PI_F/180;
	}

	    //ロール
        VECTOR zAxis = VGet(mat.m[2][0], mat.m[2][1], mat.m[2][2]);
        my.quaternion = QuaternionMult(my.quaternion,CreateRotationQuaternion(roll, zAxis));
        //ピッチ
        VECTOR xAxis = VGet(mat.m[0][0], mat.m[0][1], mat.m[0][2]);
        my.quaternion = QuaternionMult(my.quaternion,CreateRotationQuaternion(pitch, xAxis));
        //ヨー
        VECTOR yAxis = VGet(mat.m[1][0], mat.m[1][1], mat.m[1][2]);
        my.quaternion = QuaternionMult(my.quaternion,CreateRotationQuaternion(yaw, yAxis));

        //  機体の回転
		MATRIX matRot = MGetRotAxis(my.quaternion.Axis,my.quaternion.Rot);
        mat = MMult( mat, matRot );

		MATRIX matTrans = MGetTranslate(my.position);
        mat = MMult( mat, matTrans );

		
        //  移動ベクトル
		VECTOR mat1 = VTransform(my.quaternion.Axis,matRot);
 
        //  機体のマトリックス設定
		MV1SetMatrix( my.model, mat );

///////////////////////////////////

		
        //  カメラを機体後方に移動
        MATRIX matCam = MGetIdent();//単位行列
        matCam = MMult(matCam,MGetTranslate(VGet(0.0f,0.0f,-1000.0f))); //機体のZ後方に。
 
        //  カメラの回転
        matCam = MMult( matCam, matRot );
        
        //  カメラの平行移動
		matTrans = MGetTranslate(my.position);
        matCam = MMult( matCam, matTrans );
 
        // カメラの処理
        MATRIX imatCam = MInverse(matCam);  //逆行列。
        SetCameraViewMatrix(imatCam);

}



int WINAPI WinMain( HINSTANCE, HINSTANCE, LPSTR, int )
{
    ChangeWindowMode( TRUE );
	SetGraphMode(1280,720,32);
	DxLib_Init();
	SetDrawScreen( DX_SCREEN_BACK );
    my.model = MV1LoadModel( "test.mqo" );
	int stage = MV1LoadModel("NightSea.mv1");
	MV1SetUseZBuffer(stage,false);
	SetCameraNearFar( 100.0f, 1000000.0f ) ;

	//初期の単位ベクトル角度?
	my.quaternion.Axis = VGet(0,0,1);
	//回転は無し
	my.quaternion.Rot = 0.0;

    my.position = VGet( 0.0f, 0.0f, 600.0f );
    
    while( ScreenFlip() == 0 && ProcessMessage() == 0 && ClearDrawScreen() == 0 ) {

		func();

		MV1DrawModel(stage);
        MV1DrawModel( my.model );

    }
 
    DxLib_End();
    return 0;
}

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 9年前
住所: 東海地方
連絡を取る:

Re: クォータニオンで指定した自機の回転から、追従するカメラの回転を指定したい

#6

投稿記事 by softya(ソフト屋) » 6年前

とりあえず、ここだけ真似するだけで良いはずなのですが。

コード:

        //  カメラを機体後方に移動
        MATRIX matCam = MGetIdent();//単位行列
        matCam = MMult(matCam,MGetTranslate(VGet(0.0f,0.0f,-1000.0f))); //機体のZ後方に。
 
        //  カメラの回転
        matCam = MMult( matCam, matRot );//自機の回転を掛け合わせる
        
        //  カメラの平行移動
//       matTrans = MGetTranslate( VGet(pl.Position.x,pl.Position.y,pl.Position.z) );
        matCam = MMult( matCam, matTrans );//自機の移動を掛け合わせる
 
        // カメラの処理
//      Camera_Process() ;
        //SetCameraViewMatrix(MMult(matRot,matTrans));
        MATRIX imatCam = MInverse(matCam);  //逆行列。
        SetCameraViewMatrix(imatCam);
こうすれば、自機が動かない限りはカメラも動きません。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

珈琲

Re: クォータニオンで指定した自機の回転から、追従するカメラの回転を指定したい

#7

投稿記事 by 珈琲 » 6年前

元々

コード:


//自機クラス
class CFighter{
 
    friend class CCamera;
 
private:
    //位置座標
    VECTOR Position;
 
    //角度
    VECTOR AxisX;
    VECTOR AxisY;
    VECTOR AxisZ; //実際に自機が向いている方向はZ方向
 
    CModel* Model;
}

のようにして回転を持っていたので、それを

コード:

typedef struct {
    int model;
    VECTOR position;
    Quaternion_t quaternion;
} my_t;
と、リンク先のコードを再現するためにクォータニオンを使って回転を保存するように、コードの半分ぐらい変えないと使えませんでした。

>ただ、1つのクォータニオンですべての軸の回転を表現できるとは思えません・・・
あたりの疑問もここから来てます・・

クォータニオン1つで回転を保持するというのは、戦闘機の進行方向と、その回転だけ保持して、
単位ベクトルと指定回転率のクォータニオンを掛けあわせてピッチ、ロール、ヨーを再現しているのかと予想しています。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 9年前
住所: 東海地方
連絡を取る:

Re: クォータニオンで指定した自機の回転から、追従するカメラの回転を指定したい

#8

投稿記事 by softya(ソフト屋) » 6年前

クォータニオンは回転のジンバルロック対策なので戦闘機ものならクォータニオンで管理しないと垂直上昇した時などに自由に回転できなくなります。
なので、クォータニオン→回転行列変換は必須だと思うのですが。

「その58 やっぱり欲しい回転行列⇔クォータニオン相互変換」
http://marupeke296.com/DXG_No58_RotQuaternionTrans.html
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

珈琲

Re: クォータニオンで指定した自機の回転から、追従するカメラの回転を指定したい

#9

投稿記事 by 珈琲 » 6年前

えーと、つまり
No.1で
>MV1SetRotationZYAxis(ModelHandle,AxisZ,AxisY,0.0f);
を使っている時点でクォータニオンの利点がなくなっていると・・

それと、結局のところクォータニオン1つで回転を全て管理できるのでしょうか?


>「その58 やっぱり欲しい回転行列⇔クォータニオン相互変換」
>http://marupeke296.com/DXG_No58_RotQuaternionTrans.html
ここを参考にして回転行列に変換しようとしていた所、
「3つのベクトルでしか指定しないじゃん!?」と思い、質問しました・・・
Dxlib::MGetRotAxis()が、多分そのままの関数だと思います。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 9年前
住所: 東海地方
連絡を取る:

Re: クォータニオンで指定した自機の回転から、追従するカメラの回転を指定したい

#10

投稿記事 by softya(ソフト屋) » 6年前

ちょっと時間が取れないので、自分で普通の回転とクォータニオンの回転を実験して比べてみてください。
※ ジンバルロックを、まずちゃんと把握した上で実験してみてください。
この手のは自分で実感しないとモヤモヤのままです。
なぜ、クォータニオンなのか? 何処で使うのか? って事を身をもって感じる必要があります。
そのためには、普通の回転でダメな時を知る必要が有るわけです。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

珈琲

Re: クォータニオンで指定した自機の回転から、追従するカメラの回転を指定したい

#11

投稿記事 by 珈琲 » 6年前

答え合わせではないですけれど、
「クォータニオン1つで回転を全て管理できる」のならば、(理屈はわかりませんが)

回転したい分のfloat値と回転したい絶対軸のクォータニオンと、
現在の自機の角度を保持しているクォータニオンを掛け合わせれば、
”自機から見た絶対軸”を軸にして回転させられるということですよね。


回転行列 = MMult(単位行列,MGetRotAxis(クォータニオン));
という感じ

そうすると、リンク先の

コード:

MATRIX mat = MGetScale(VGet(2000.0,2000.0,2000.0));
は単位行列になりそうです
なぜVGet(2000,2000,2000)を基準にしているのか未だよくわかりませんけど

逆行列もまだいまいちよくわからないです




[hr]
時間がない所質問に答えていただきありがとうございます。
ご回答はいつでもいいので、よろしくお願いします。
(プロジェクトの納期が迫ってるとかで1~2日ではないのかもしれませんが(^_^;))

珈琲

Re: クォータニオンで指定した自機の回転から、追従するカメラの回転を指定したい

#12

投稿記事 by 珈琲 » 6年前

とりあえず、進展

>テストプロジェクトを作って、見よう見まねで真似してみましたが、
>一瞬だけ描画されて後は真っ暗、という感じでわかりません。

という不具合は

>MATRIX mat = MGetScale(VGet(2000.0,2000.0,2000.0));
>なぜVGet(2000,2000,2000)を基準にしているのか未だよくわかりませんけど

という疑問で
MATRIX mat = MGetIdent();
にした所、正常に表示されました。


次に浮き出た不具合は、
回転する角度がおかしいということです。(おそらく軸も)

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 9年前
住所: 東海地方
連絡を取る:

Re: クォータニオンで指定した自機の回転から、追従するカメラの回転を指定したい

#13

投稿記事 by softya(ソフト屋) » 6年前

ちょっと今日は、クォータニオンをぱっぱっと扱えるほど頭が回っていないようです。
時間を見つけて確認しますので、気長にお待ちください。
先に自分で色々実験したほうが早いと思うんですけどね。どうでしょうか?
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

アバター
GRAM
記事: 164
登録日時: 9年前
住所: 大阪

Re: クォータニオンで指定した自機の回転から、追従するカメラの回転を指定したい

#14

投稿記事 by GRAM » 6年前

あまり流れを理解していなく、パッと見た感想なんですが、別にクオータニオンじゃない最初のコードでも別に
ジンバルロックは起きないんじゃないんですかね?
そもそも機体姿勢の決定には最低6変数(座標および回転角度)が必要なのに対し、
最初のコードは変数が12個ありますから。(座標及び各軸方向つまり回転行列そのもの)
もちろんクオータニオンのほうが変数の総量が少ないというのはありますが。(こっちなら合計7個)

ただしもとのコードを使うなら、ソフト屋さんの例示したコードをそのまま使えるような気がします。

コード:

        //  カメラを機体後方に移動
        MATRIX matCam = MGetIdent();//単位行列
        matCam = MMult(matCam,MGetTranslate(VGet(0.0f,0.0f,-1000.0f))); //機体のZ後方に。
 
        //  カメラの回転
        matCam = MMult( matCam, matRot );//[color=#FF0000]自機の回転を掛け合わせる[/color]
        
        //  カメラの平行移動
//       matTrans = MGetTranslate( VGet(pl.Position.x,pl.Position.y,pl.Position.z) );
        matCam = MMult( matCam, matTrans );//自機の移動を掛け合わせる
 
        // カメラの処理
//      Camera_Process() ;
        //SetCameraViewMatrix(MMult(matRot,matTrans));
        MATRIX imatCam = MInverse(matCam);  //逆行列。
        SetCameraViewMatrix(imatCam);
このコードのmatRotの中身はCFighterの

コード:

    //角度
    VECTOR AxisX;
    VECTOR AxisY;
    VECTOR AxisZ; //実際に自機が向いている方向はZ方向
この部分そのものだと思います。
もちろんクオータニオンを行列に変換したものでも同じことだと思いますが。

アバター
usao
記事: 1552
登録日時: 6年前

Re: クォータニオンで指定した自機の回転から、追従するカメラの回転を指定したい

#15

投稿記事 by usao » 6年前

>自機の後方を維持して回転をそのままトレースするようなカメラの動き
これがどういう意味なのかわからないので何に困っておられるのかもよく把握できていないのですが
>AxisX,Y,Z
というのは自機に固定された座標系の基底ベクトルなのでしょうか.
>3つのベクトルから回転行列を作成するにはどうしたらいいか
だとしたら,それそのものが世界座標系との間の回転マトリクスだと思います.
(3次元nベクトル*3 → 3*3マトリクス)

珈琲

Re: クォータニオンで指定した自機の回転から、追従するカメラの回転を指定したい

#16

投稿記事 by 珈琲 » 6年前

GRAM さんが書きました:あまり流れを理解していなく、パッと見た感想なんですが、別にクオータニオンじゃない最初のコードでも別に
ジンバルロックは起きないんじゃないんですかね?
そもそも機体姿勢の決定には最低6変数(座標および回転角度)が必要なのに対し、
最初のコードは変数が12個ありますから。(座標及び各軸方向つまり回転行列そのもの)
もちろんクオータニオンのほうが変数の総量が少ないというのはありますが。(こっちなら合計7個)

ただしもとのコードを使うなら、ソフト屋さんの例示したコードをそのまま使えるような気がします。

コード:

        //  カメラを機体後方に移動
        MATRIX matCam = MGetIdent();//単位行列
        matCam = MMult(matCam,MGetTranslate(VGet(0.0f,0.0f,-1000.0f))); //機体のZ後方に。
 
        //  カメラの回転
        matCam = MMult( matCam, matRot );//[color=#FF0000]自機の回転を掛け合わせる[/color]
        
        //  カメラの平行移動
//       matTrans = MGetTranslate( VGet(pl.Position.x,pl.Position.y,pl.Position.z) );
        matCam = MMult( matCam, matTrans );//自機の移動を掛け合わせる
 
        // カメラの処理
//      Camera_Process() ;
        //SetCameraViewMatrix(MMult(matRot,matTrans));
        MATRIX imatCam = MInverse(matCam);  //逆行列。
        SetCameraViewMatrix(imatCam);
このコードのmatRotの中身はCFighterの

コード:

    //角度
    VECTOR AxisX;
    VECTOR AxisY;
    VECTOR AxisZ; //実際に自機が向いている方向はZ方向
この部分そのものだと思います。
もちろんクオータニオンを行列に変換したものでも同じことだと思いますが。
回答ありがとうございます

↓こんな感じで、ベクトルをスカラにして、一個一個行列に入れていく感じですか?

コード:

        VECTOR zAxis = VGet(mat.m[2][0], mat.m[2][1], mat.m[2][2]);
        my.quaternion = QuaternionMult(my.quaternion,CreateRotationQuaternion(roll, zAxis));
        //ピッチ
        VECTOR xAxis = VGet(mat.m[0][0], mat.m[0][1], mat.m[0][2]);
        my.quaternion = QuaternionMult(my.quaternion,CreateRotationQuaternion(pitch, xAxis));
        //ヨー
        VECTOR yAxis = VGet(mat.m[1][0], mat.m[1][1], mat.m[1][2]);
        my.quaternion = QuaternionMult(my.quaternion,CreateRotationQuaternion(yaw, yAxis));
変数の個数もぴったしで合う気がします

>もちろんクオータニオンのほうが変数の総量が少ないというのはありますが。(こっちなら合計7個)
クラスで保管するパラメータは少ないほうがスッキリするので、やっぱりクォータニオンと座標の7個のほうがいいんですよね・・・

アルバイトから帰ってきてからになりますが、試してみようと思います。

珈琲

Re: クォータニオンで指定した自機の回転から、追従するカメラの回転を指定したい

#17

投稿記事 by 珈琲 » 6年前

usao さんが書きました:>自機の後方を維持して回転をそのままトレースするようなカメラの動き
これがどういう意味なのかわからないので何に困っておられるのかもよく把握できていないのですが
>AxisX,Y,Z
というのは自機に固定された座標系の基底ベクトルなのでしょうか.
>3つのベクトルから回転行列を作成するにはどうしたらいいか
だとしたら,それそのものが世界座標系との間の回転マトリクスだと思います.
(3次元nベクトル*3 → 3*3マトリクス)
ありがとうございます。

No.1で悩んでいる問題とはちょっとズレてきて、
保持してある3方向のベクトルからどうやって行列を生成するか
ではなく
保持してあるクォータニオンを原点にした座標系をどうやって指定軸で回転できるのか
です。

回転を保持しているクォータニオンのRot成分を回転させれば「ロール」として表現できるのは想像つくのですが
ピッチを表現しようと、回転を保持しているクォータニオンを、Xの正規化ベクトルと任意の回転率分、回転させているのですが(多分)
結果が違うのです。

コード:

        //ピッチ
        VECTOR xAxis = VGet(mat.m[0][0], mat.m[0][1], mat.m[0][2]);
        my.quaternion = QuaternionMult(my.quaternion,CreateRotationQuaternion(pitch, xAxis));
↑ここらへんの処理の話です

ややこしくしてしまい申し訳ありません。

珈琲

Re: クォータニオンで指定した自機の回転から、追従するカメラの回転を指定したい

#18

投稿記事 by 珈琲 » 6年前

うーん?
クォータニオン同士の掛け算は、あるクォータニオンが別のクォータニオンに向かってX,Y,Z軸をどれぐらい回せばいいか、を求めるものですよね

・・・・ということは、ついさっき作成した絶対軸回転の単位行列と掛けあわせても全く意味がない!?
コードを見ながら言うならば、Z方向±PI/180しか回転していない、
つまり風に煽られている糸みたいな動きしかできない・・・

これを解決するには、
回転させたい軸 例:VGet(0,0,1)と、任意の回転率でクォータニオンを作りそれをそのまま行列化し、
それを「自機の回転を保持しているクォータニオン」ではなく「自機の回転を保持しているクォータニオンから作った行列」と掛けあわせれば
自機から見てVGet(0,0,1)を軸にした回転ができるのでは・・・!?


具体的には、

コード:

    MATRIX mat = MGetIdent();
 
    float roll  =0;
    float pitch =0;
    float yaw   =0;
 
    if(1==CheckHitKey(KEY_INPUT_LEFT)){
        roll+=DX_PI_F/180;
    }
 
    if(1==CheckHitKey(KEY_INPUT_RIGHT)){
        roll-=DX_PI_F/180;
    }
 
    if(1==CheckHitKey(KEY_INPUT_DOWN)){
        pitch+=DX_PI_F/180;
    }
 
    if(1==CheckHitKey(KEY_INPUT_UP)){
        pitch-=DX_PI_F/180;
    }
 
    if(1==CheckHitKey(KEY_INPUT_A)){
        yaw+=DX_PI_F/180;
    }
 
    if(1==CheckHitKey(KEY_INPUT_D)){
        yaw-=DX_PI_F/180;
    }
 
        quaternion_t AllQuaternion;
        AllQuaternion.rot=0;
        AllQuaternion.Axis=VGet(0,0,0);

        //ロール
        VECTOR zAxis = VGet(mat.m[2][0], mat.m[2][1], mat.m[2][2]);
        AllQuaternion = QuaternionMult(AllQuaternion,CreateRotationQuaternion(roll,zAxis));
        
        //ピッチ
        MATRIX VECTOR xAxis = VGet(mat.m[0][0], mat.m[0][1], mat.m[0][2]);
        AllQuaternion = QuaternionMult(AllQuaternion,CreateRotationQuaternion(pitch,xAxis));
        
        //ヨー
        VECTOR yAxis = VGet(mat.m[1][0], mat.m[1][1], mat.m[1][2]);
        AllQuaternion = QuaternionMult(AllQuaternion,CreateRotationQuaternion(yaw,yAxis));

        //  機体の回転
        MATRIX matRot = MGetRotAxis(AllQuaternion.rot, AllQuaternion.Axis);
        mat = MMult( mat, matRot );
 
        MATRIX matTrans = MGetTranslate(my.position);
        mat = MMult( mat, matTrans );
 
        
        //  移動ベクトル
        VECTOR mat1 = VTransform(my.quaternion.Axis,matRot);
 
        //  機体のマトリックス設定
        MV1SetMatrix( my.model, mat );
 
こんな感じです。
自機のクォータニオンと、たった今回転したクォータニオンを行列でかけるようにしました。

今学校なので試すことはできませんが、よろしくお願いします

珈琲

Re: クォータニオンで指定した自機の回転から、追従するカメラの回転を指定したい

#19

投稿記事 by 珈琲 » 6年前

あれ?自機のクォータニオンが関わっていないような・・・

珈琲

Re: クォータニオンで指定した自機の回転から、追従するカメラの回転を指定したい

#20

投稿記事 by 珈琲 » 6年前

それに、自機からみて回転させたいがために行列を作成したのだから、
回転させたその行列からクォータニオンを取り出す必要がある気が・・

アバター
usao
記事: 1552
登録日時: 6年前

Re: クォータニオンで指定した自機の回転から、追従するカメラの回転を指定したい

#21

投稿記事 by usao » 6年前

四元数を使う縛りとかあるんでしょうか?
行列同士を掛けていく操作をやりたいのなら行列で状態持てばいいんじゃ…??


#世界 に対しての 自機 の姿勢が回転行列R1で表されているときに
 自機の姿勢を更新したい.その際,その回転差分量(?)R2を,自機に固定された座標軸で考えたい.
 …という話ではないのかなぁ?と思ったのですが違うのかも??
 (R2を右から掛けて R1 = R1*R2; みたいな)

珈琲

Re: クォータニオンで指定した自機の回転から、追従するカメラの回転を指定したい

#22

投稿記事 by 珈琲 » 6年前

行列で角度・座標を持って、回転・移動したい時にそれらの働き分の行列をかけるということですか?

できるかどうか想像つきませんが、クォータニオンで自機の向きを直接ベクトルで持っているというのは、シューティングゲームの移動や前方範囲の選択とかやりやすそうだなーと

そういった理由で、角度をクォータニオン1つで持つことにこだわっていたのですが




>#世界 に対しての 自機 の姿勢が回転行列R1で表されているときに
>自機の姿勢を更新したい.その際,その回転差分量(?)R2を,自機に固定された座標軸で考えたい.
>…という話ではないのかなぁ?と思ったのですが違うのかも??

そのとおりです。
ただ、上記の理由なども含まれているのですが
・自機の向いている方向をベクトルで持っていると他の処理が楽になるので、クォータニオン1つで角度を表現・保持し、操作したい
・カメラは、Zソートで半透明のモデルをソートしているのでビュー行列で直接指定したい(クォータニオンから行列を生成する部分はカメラで使う)
という条件があり、その上でおっしゃったとおりの
・絶対座標ではなく自機の姿勢から見た座標系で回転させたい
というものです。

一週間ぐらいずっとうまく操作できないので半ばあきらめているのですが。
正解のコードください・・・・・

ISLe
記事: 2645
登録日時: 9年前
連絡を取る:

Re: クォータニオンで指定した自機の回転から、追従するカメラの回転を指定したい

#23

投稿記事 by ISLe » 6年前

クォータニオンは、ポリゴンの頂点座標とか大量に計算する際に行列を使うより計算量が少なくておいしいのですが、計算量が少ない分ひねりというかねじれというか姿勢制御に必要な情報が欠落します。

ポリゴンだと複数の頂点で構成されていて、頂点を結ぶ順番で表裏が決まるので、もともと無駄な情報です。

クォータニオンで姿勢を制御するには、正面ともうひとつの方向(例えば真上)を指すベクトルにクォータニオンを掛けて、その両方が重なる回転行列を求める必要があります。
そうしないとひねり具合は無限の可能性から適当に選ばれたものになります。
実際にはクォータニオンの計算式から決まるものですが。

風に煽られている糸みたいな動きになるのはそのせいじゃないですかね。

アバター
usao
記事: 1552
登録日時: 6年前

Re: クォータニオンで指定した自機の回転から、追従するカメラの回転を指定したい

#24

投稿記事 by usao » 6年前

なんか検索したら
http://hooktail.sub.jp/mathInPhys/quaternion/

>回転 q1 に引き続いて回転 q2 を行う場合は次のように書けばよいのです
> x' = (q2 * q1) * x * (q1^ * q2^ )

という一文がありましたが,これがやりたいことでしょうか?
(ここに書くにあたって,共役を^であらわしました)

これだとq2が q1を考えたときの座標系で表されている(?) ように思いますが,
q2とq1の場所を逆にして
 x' = (q1 * q2) * x * (q2^ * q1^ )
にすれば,q2が q1で回転された座標系で表した 形になるのかな??

珈琲

Re: クォータニオンで指定した自機の回転から、追従するカメラの回転を指定したい

#25

投稿記事 by 珈琲 » 6年前

ISLe さんが書きました:クォータニオンは、ポリゴンの頂点座標とか大量に計算する際に行列を使うより計算量が少なくておいしいのですが、計算量が少ない分ひねりというかねじれというか姿勢制御に必要な情報が欠落します。

ポリゴンだと複数の頂点で構成されていて、頂点を結ぶ順番で表裏が決まるので、もともと無駄な情報です。

クォータニオンで姿勢を制御するには、正面ともうひとつの方向(例えば真上)を指すベクトルにクォータニオンを掛けて、その両方が重なる回転行列を求める必要があります。
そうしないとひねり具合は無限の可能性から適当に選ばれたものになります。
実際にはクォータニオンの計算式から決まるものですが。

風に煽られている糸みたいな動きになるのはそのせいじゃないですかね。
うーん
確かに、球の表面に存在する点を、ぐりぐり球面を動きまわった後元の位置に戻すとなぜか回転しているイメージはありますが・・・
SetCameraPositionAndTargetAndUpVec()みたいなもんですかね・・・
できたらコードを書いてくれると嬉しいです・・・

珈琲

Re: クォータニオンで指定した自機の回転から、追従するカメラの回転を指定したい

#26

投稿記事 by 珈琲 » 6年前

usao さんが書きました:なんか検索したら
http://hooktail.sub.jp/mathInPhys/quaternion/

>回転 q1 に引き続いて回転 q2 を行う場合は次のように書けばよいのです
> x' = (q2 * q1) * x * (q1^ * q2^ )

という一文がありましたが,これがやりたいことでしょうか?
(ここに書くにあたって,共役を^であらわしました)

これだとq2が q1を考えたときの座標系で表されている(?) ように思いますが,
q2とq1の場所を逆にして
 x' = (q1 * q2) * x * (q2^ * q1^ )
にすれば,q2が q1で回転された座標系で表した 形になるのかな??
共役というのがよくわかりませんが、とりあえず全てのパラメータを反転させればいいのですか?

コード:

        //ロール
        VECTOR zAxis = VGet(mat.m[2][0], mat.m[2][1], mat.m[2][2]);
        AllQuaternion = QuaternionMult(AllQuaternion,CreateRotationQuaternion(roll,zAxis));
        
        //ピッチ
        MATRIX VECTOR xAxis = VGet(mat.m[0][0], mat.m[0][1], mat.m[0][2]);
        AllQuaternion = QuaternionMult(AllQuaternion,CreateRotationQuaternion(pitch,xAxis));
        
        //ヨー
        VECTOR yAxis = VGet(mat.m[1][0], mat.m[1][1], mat.m[1][2]);
        AllQuaternion = QuaternionMult(AllQuaternion,CreateRotationQuaternion(yaw,yAxis));
ここらへんのクォータニオンの合成の計算式ですか
よく考えたらQuaternionMult関数の中身って別にクォータニオンを合成しているわけではなく、クォータニオンからクォータニオンへの回転ですね・・・
そうすると、No2のリンク先の
Quaternion * CreateRotationQuaternion(roll, zAxis);
という計算は、QuaternionMult関数ではなく、それだったのかもしれませんね

アバター
usao
記事: 1552
登録日時: 6年前

Re: クォータニオンで指定した自機の回転から、追従するカメラの回転を指定したい

#27

投稿記事 by usao » 6年前

>共役
リンクを示した先にそこらへんから書いてあるのですが.
というかそういった知識すらない状態で使おうとしているのですか!?
(四元数使ったことない自分が言うのもなんですけど)

>クォータニオンからクォータニオンへの回転
というのが何のことなのか,知識のない私にはわかりかねますが…
自分で使う関数なりクラスが何の演算をしてるのかを把握せずにコーディングしているのですか??

珈琲

Re: クォータニオンで指定した自機の回転から、追従するカメラの回転を指定したい

#28

投稿記事 by 珈琲 » 6年前

usao さんが書きました:>共役
リンクを示した先にそこらへんから書いてあるのですが.
というかそういった知識すらない状態で使おうとしているのですか!?
(四元数使ったことない自分が言うのもなんですけど)
使い方を覚えた後にゆっくり理解していけばいいかなと
体で覚えろ理論です

まぁ、要求が増えるに連れて中身を理解する必要はありますね・・・

あと、共役っていうのは共役四元数の事でしたか、5文字で一単語かと思ってました。
確かに書いてありました、すいません。
usao さんが書きました: >クォータニオンからクォータニオンへの回転
というのが何のことなのか,知識のない私にはわかりかねますが…
自分で使う関数なりクラスが何の演算をしてるのかを把握せずにコーディングしているのですか??
他人が使っていた定義不明の演算子を自分なりに予想した結果です
クォータニオン同士の掛け算で求められるのはクォータニオンAからクォータニオンBへの回転ベクトル(らしい)です
そして、私はアスタリスクで定義されたクォータニオン同士の演算を掛け算と誤釈したということです。


ベクトル2つの合成で3次元の回転を表すのは
 x' = (q1 * q2) * x * (q2^ * q1^ )
と書かれましたが、
ロールピッチヨーの3つのベクトルを合成します。
その際の計算式は
 x' = (q1 * q2 * q3) * x * (q3^ * q2^ * q1^)
で良いのでしょうか?

また、
http://wgld.org/d/webgl/w031.html
で紹介されている計算式
(0; X, Y, Z) = Q^ * P * Q

x' = q1 * x * q1^
で、掛け算の順番が違うのはなんででしょうか
かける順番に違いがあると結果が変わるんですよね?

珈琲

Re: クォータニオンで指定した自機の回転から、追従するカメラの回転を指定したい

#29

投稿記事 by 珈琲 » 6年前

 x' = (q1 * q2 * q3) * x * (q3^ * q2^ * q1^)

の式が正しいと仮定して関数を作ってみます。

バイトの時間なので、
中途半端ですが過程を投稿します

コード:

VECTOR SynthesisQuaternion(Quaternion_t q_main,Quaternion_t q_z,Quaternion_t q_x,Quaternion_t q_y){
 
    //それぞれのQを作成
	float rotaq_z=cos(q_z.Rot/2);
	VECTOR vectq_z=VGet(q_z.Axis.x*sin(q_z.Rot/2),q_z.Axis.y*sin(q_z.Rot/2),q_z.Axis.z*sin(q_z.Rot/2));

	float rotaq_x=cos(q_x.Rot/2);
	VECTOR vectq_x=VGet(q_x.Axis.x*sin(q_x.Rot/2),q_x.Axis.y*sin(q_x.Rot/2),q_x.Axis.x*sin(q_x.Rot/2));

	float rotaq_y=cos(q_y.Rot/2);
	VECTOR vectq_y=VGet(q_y.Axis.y*sin(q_y.Rot/2),q_y.Axis.y*sin(q_y.Rot/2),q_y.Axis.y*sin(q_y.Rot/2));


    //それぞれの共役Rを作成
	float rotar_z=cos(q_z.Rot/2);
	VECTOR vectr_z=VGet(-q_z.Axis.x*sin(q_z.Rot/2),-q_z.Axis.y*sin(q_z.Rot/2),-q_z.Axis.z*sin(q_z.Rot/2));

	float rotar_x=cos(q_x.Rot/2);
	VECTOR vectr_x=VGet(-q_x.Axis.x*sin(q_x.Rot/2),-q_x.Axis.y*sin(q_x.Rot/2),-q_x.Axis.x*sin(q_x.Rot/2));

	float rotar_y=cos(q_y.Rot/2);
	VECTOR vectr_y=VGet(-q_y.Axis.y*sin(q_y.Rot/2),-q_y.Axis.y*sin(q_y.Rot/2),-q_y.Axis.y*sin(q_y.Rot/2));


アバター
usao
記事: 1552
登録日時: 6年前

Re: クォータニオンで指定した自機の回転から、追従するカメラの回転を指定したい

#30

投稿記事 by usao » 6年前

やりたいことが
>#世界 に対しての 自機 の姿勢が回転行列R1で表されているときに
>自機の姿勢を更新したい.その際,その回転差分量(?)R2を,自機に固定された座標軸で考えたい.
だということで合ってるようなので…

>ベクトル2つの合成で3次元の回転を表すのは
> x' = (q1 * q2) * x * (q2^ * q1^ )
私のイメージとしては(前述のとおり,使ったことないのであってるのかどうか自信ないですが…),
q1 : 現在の自機の姿勢を表す四元数
q2 : 今回のフレーム(?)での操作入力による回転量(を,自機固定座標系で記述した)の四元数
という感じです.
で,上式の今回の回転量q2の部分を(Roll,Pitch,Yawが個別に求められている状況から)求める方法としては,
> x' = (q1 * q2 * q3) * x * (q3^ * q2^ * q1^)
でいいんじゃないでしょうか
2つの式で使ってる記号が同じで混乱しそうなので書き直すと,例えば
q2 = ( q_roll * q_pitch * q_yaw )
q2^ = ( q_yaw^ * q_pitch^ * q_roll^ )
みたいな.

>掛け算の順番が違うのはなんででしょうか
2つのサイト間では 回転軸を考えている方向が違う(逆向き)んじゃないでしょうか.
右ねじで考えてるか左ねじで考えてるか,の違い?

閉鎖

“C言語何でも質問掲示板” へ戻る