ページ 11

3D空間でのカメラの向きと進行方向について

Posted: 2012年7月23日(月) 03:14
by すけたろう
DXライブラリ、C言語、VC++2008EE、WindowsXP、メタセコイアという環境で3Dモデルを使ったゲームを作成しようと思っているのですが、今回カメラの扱いに慣れるためのプログラムを組んでみたところ、カメラの向きと進行方向が上手く一致せず困っています。

普通に「右が押されたらx++」「下が押されたらz--」という処理をしたところ、カメラの向きが左を向いているにも関わらず、右を押したらx軸通りに右へ、下を押したらz軸通りに下へ、という感じで進んでしまいます。
そこで「ゲームプログラミングの館 3.7章 特定の角度で弾を飛ばす」のプログラムを参考に、プログラムを以下のように組みました。

コード:

#include "./DxLib/DxLib.h"
#include <math.h>

//フルスクリーンで起動するかのメッセージボックス
int ChangeWindowMessageBox();

int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int){
	ChangeWindowMessageBox(),DxLib_Init(),SetDrawScreen(DX_SCREEN_BACK);

	int ModelHandle1 = MV1LoadModel( "Model/test1.mqo" );
	int ModelHandle2 = MV1LoadModel( "Model/test2.mqo" );

	//カメラの位置
	float Camera_Pos_X = 250.00f, Camera_Pos_Y = 350.00f, Camera_Pos_Z = 0.00f;

	//カメラの注視点
	float Camera_Rota_V = -0.00f, Camera_Rota_H = -0.00f, Camera_Rota_T = -0.00f;

	//カメラの視野(っていうか双眼鏡みたいな役目)
	float Camera_Fov = 50.0f, Camera_Fov_Parcent = 100.0f;

	while( !ScreenFlip() && !ProcessMessage() && !ClearDrawScreen() && !CheckHitKey( KEY_INPUT_ESCAPE ) ){

		//シフトと同時押しの処理
		if( CheckHitKey( KEY_INPUT_LSHIFT ) || CheckHitKey( KEY_INPUT_RSHIFT ) ){
			if( CheckHitKey( KEY_INPUT_UP ) && Camera_Rota_V >= -90.0f*PHI_F/180.0f )		
											Camera_Rota_V -= PHI_F/180.0f;
			if( CheckHitKey( KEY_INPUT_DOWN ) && Camera_Rota_V <= 90.0f*PHI_F/180.0f )		
											Camera_Rota_V += PHI_F/135.0f;
			if( CheckHitKey( KEY_INPUT_LEFT ) )		
											Camera_Rota_H -= PHI_F/135.0f;
			if( CheckHitKey( KEY_INPUT_RIGHT ) )	
											Camera_Rota_H += PHI_F/135.0f;
		}

		//Ctrlと同時押しの処理
		else if( CheckHitKey( KEY_INPUT_LCONTROL ) || CheckHitKey( KEY_INPUT_RCONTROL ) ){
			if( CheckHitKey( KEY_INPUT_UP ) && Camera_Fov > 10.0f )	{
											Camera_Fov--;
											Camera_Fov_Parcent += 100.0f / 40.0f;
			}
			if( CheckHitKey( KEY_INPUT_DOWN ) && Camera_Fov < 50.0f ){	
											Camera_Fov++;
											Camera_Fov_Parcent -= 100.0f / 40.0f;
			}

			if( CheckHitKey( KEY_INPUT_RIGHT ) && Camera_Rota_T > -90.0f*PHI_F/180.0f )
											Camera_Rota_T -= PHI_F/135.0f;

			if( CheckHitKey( KEY_INPUT_LEFT ) && Camera_Rota_T < 90.0f*PHI_F/180.f )
											Camera_Rota_T += PHI_F/135.0f;
		}

		//単体で押された時の処理
		else{
			if( CheckHitKey( KEY_INPUT_F1 ) || CheckHitKey( KEY_INPUT_SPACE ) )	Camera_Rota_V = -0.0f;
			if( CheckHitKey( KEY_INPUT_F2 ) || CheckHitKey( KEY_INPUT_SPACE ) )	Camera_Rota_H = -0.0f;
			if( CheckHitKey( KEY_INPUT_F3 ) || CheckHitKey( KEY_INPUT_SPACE ) )	Camera_Rota_T = -0.0f;
			if( CheckHitKey( KEY_INPUT_F4 ) || CheckHitKey( KEY_INPUT_SPACE ) )	Camera_Pos_X  = 250.0f;
			if( CheckHitKey( KEY_INPUT_F5 ) || CheckHitKey( KEY_INPUT_SPACE ) )	Camera_Pos_Y  = 350.0f;
			if( CheckHitKey( KEY_INPUT_F6 ) || CheckHitKey( KEY_INPUT_SPACE ) )	Camera_Pos_Z  = 0.0f;
			if( CheckHitKey( KEY_INPUT_F7 ) || CheckHitKey( KEY_INPUT_SPACE ) ){
																				Camera_Fov	= 50.0f;
																				Camera_Fov_Parcent = 100.0f;
			}	

			if( CheckHitKey( KEY_INPUT_UP ) )		Camera_Pos_Z += 10.0f;
			if( CheckHitKey( KEY_INPUT_DOWN ) )		Camera_Pos_Z -= 10.0f;
			if( CheckHitKey( KEY_INPUT_LEFT ) )		Camera_Pos_X -= 10.0f;
			if( CheckHitKey( KEY_INPUT_RIGHT ) )	Camera_Pos_X += 10.0f;

		}

		//モデルの変形処理
		MV1SetPosition( ModelHandle1, VGet( -50,240,600 ) );
		MV1SetRotationXYZ( ModelHandle1, VGet( -30.0f*PHI_F/180.0f,-90.0f*PHI_F/180.0f, 0.0f) ); 

		MV1SetPosition( ModelHandle2, VGet( 500,240,600 ) );
		MV1SetRotationXYZ( ModelHandle2, VGet( 0.0f,90.0f*PHI_F/180.0f, 0.0f) ); 
	
		//カメラの処理
		SetCameraNearFar( 10, 1000 );
		SetupCamera_Perspective( Camera_Fov*PHI_F/180.0f );
										//↓ここでx=cos(angle)*r、y=sin(angle)*rを応用してみた
		SetCameraPositionAndAngle( VGet( cos( Camera_Rota_H )*Camera_Pos_X, Camera_Pos_Y, sin( Camera_Rota_H )*Camera_Pos_Z ), 
														Camera_Rota_V, Camera_Rota_H, Camera_Rota_T );
		//モデルの描画
		MV1DrawModel( ModelHandle1 );
		MV1DrawModel( ModelHandle2 );

		//数値が見えるように
		DrawFormatString( 5, 0, GetColor( 255,255,255 ), "CameraRota[V] : %f <%.1f°>", -Camera_Rota_V, -Camera_Rota_V*180.0f/PHI_F ); 
		DrawFormatString( 5,20, GetColor( 255,255,255 ), "CameraRota[H] : %f <%.1f°>", -Camera_Rota_H, -Camera_Rota_H*180.0f/PHI_F );
		DrawFormatString( 5,40, GetColor( 255,255,255 ), "CameraRota[T] : %f <%.1f°>", -Camera_Rota_T, -Camera_Rota_T*180.0f/PHI_F );
		DrawFormatString( 5,60, GetColor( 255,255,255 ), "CameraFov   : %.1f <%.1f%%>", Camera_Fov, Camera_Fov_Parcent );
		DrawFormatString( 5,80, GetColor( 255,255,255 ), "CameraPos [X] : %.1f", Camera_Pos_X );
		DrawFormatString( 5,100, GetColor( 255,255,255 ),"CameraPos [Y] : %.1f", Camera_Pos_Y );
		DrawFormatString( 5,120,GetColor( 255,255,255 ), "CameraPos [Z] : %.1f", Camera_Pos_Z );

	}

	DxLib_End();
	return 0;
}

//フルスクリーンで起動するかのメッセージボックス
int ChangeWindowMessageBox(){
	int Flag = MessageBox( NULL,
						   TEXT("フルスクリーンで表示しますか?"),
						   TEXT("スクリーン設定"),
						   MB_YESNO | MB_ICONQUESTION
						 );
	if( Flag == IDNO )
		if( ChangeWindowMode( TRUE ) != 0 )
			return -1;
	
	return 0;
}
85-86行目の部分

コード:

SetCameraPositionAndAngle( VGet( cos( Camera_Rota_H )*Camera_Pos_X, Camera_Pos_Y, sin( Camera_Rota_H )*Camera_Pos_Z ), 
														Camera_Rota_V, Camera_Rota_H, Camera_Rota_T );
ここで2Dのプログラムをそのまま応用してみたのですが、やっぱりうまい具合に動いてくれませんでした。
カメラの注視点に向かって進むにはどういった式にすればいいのでしょうか?

F欄工業高に通っているため、高3でありながら「ベクトル」や「行列」「空間図形」と言ったものを習わずにここまで来ました。
プログラムで3Dを扱うとなるとやはり独学でも「ベクトル」や「行列」、せめて「空間図形」は学んでおくべきですかね・・・。
とにかくカメラが向いている先に向かって進む方法を教えてもらいたいです。お願いします。

Re: 3D空間でのカメラの向きと進行方向について

Posted: 2012年7月23日(月) 11:20
by softya(ソフト屋)
そうですね。ベクトルと行列は教科書レベルじゃなくても使い方ぐらいは知っていないと話になりません。
※ 2Dの移動も結局のところベクトルなんですが、カメラを回転して動かすことがないと思うのでイメージがつかめていないんだと思います。

現状の方法はカメラの現在座標を回してますが移動方向(移動ベクトル)を回さないとダメです。この違いは分かりますか?
紙に書いてみるとイメージが掴めると思うんですけどね(上から見た図の平面で良いです)。

●参考
「ベクトル」
http://gakuen.gifu-net.ed.jp/~contents/ ... 120_0.html

Re: 3D空間でのカメラの向きと進行方向について

Posted: 2012年7月23日(月) 17:21
by すけたろう
>>現状の方法はカメラの現在座標を回してますが移動方向(移動ベクトル)を回さないとダメです。この違いは分かりますか?
描いてみたのですが、この解釈であってますか?
画像
カメラ自体の方向転換をするんではなくて、ベクトルの方向を変えてやるんだったらこんな感じなのかなぁと思ったのですが・・・

これからベクトルについて勉強しようと思うのですが、問題が解決した訳ではないので、とりあえずはまだ解決にチェック入れないでおこうと思います。

Re: 3D空間でのカメラの向きと進行方向について

Posted: 2012年7月23日(月) 17:43
by softya(ソフト屋)
ごめんなさい。私の説明が悪かったですね。
カメラの座標をワールド原点を中心に回転させているって事です。
その図にワールド原点(0,0,0)を書き加えてみてください。

カメラの座標(Camera_Pos_X,Camera_Pos_Z)はワールド原点から離れた距離(一種のベクトルです)。
この処理は、そのベクトルがワールド原点を中心にしてCamera_Rota_H回転する処理になっています。

コード:

SetCameraPositionAndAngle( VGet( cos( Camera_Rota_H )*Camera_Pos_X, Camera_Pos_Y, sin( Camera_Rota_H )*Camera_Pos_Z ), 
                                                        Camera_Rota_V, Camera_Rota_H, Camera_Rota_T );
【補足】
原点から棒が伸ばしてあって、その先にカメラが付いていると思って下さい。
で回転処理は、その棒を原点中心で回していることになります。カメラはカメラで棒の先で回転しています。

Re: 3D空間でのカメラの向きと進行方向について

Posted: 2012年7月23日(月) 19:09
by すけたろう
理解できた気がします、新しく描きなおしてみました。
画像

この解釈で合っているといいのですが・・・
僕が「カメラの方向回転」と思っていた処理は、実は「カメラの位置をワールド原点を中心に回しているだけ」だったってことですね
カメラ自体の向きを回転させる処理さえ分かればうまい具合に進むことが出来る・・・?

Re: 3D空間でのカメラの向きと進行方向について

Posted: 2012年7月23日(月) 20:46
by softya(ソフト屋)
こっちが棒を回す処理ですね。 VGet( cos( Camera_Rota_H )*Camera_Pos_X, Camera_Pos_Y, sin( Camera_Rota_H )*Camera_Pos_Z ),
こちらはカメラ自体を回しています。Camera_Rota_V, Camera_Rota_H, Camera_Rota_T );
なので、棒の先に付いたカメラ自体も棒の先に固定されていないでくるくる回っているんです。

こういう棒を回すような回転をワールド回転、棒の先のカメラだけが回るような回転をローカル回転といいます。
よく出てくる言葉・概念なので覚えておいてください。

3Dでややこしい場合2Dで勉強したほうが良いかも知れません。
俗に言えばラジコンカーに近い動きなのですが車の向いている向きに↑前進・↓後進が来て、左右で←→車の回転が出来るようになればカメラも動かせます。
回転したら前後の方向は変わらないと行けません。
・移動は座標に対する移動ベクトルの加算と減算です。
・移動方向の回転は移動ベクトルの回転です。
・スピードは移動ベクトルの大きさです。

Re: 3D空間でのカメラの向きと進行方向について

Posted: 2012年8月05日(日) 16:26
by すけたろう
何とか思うようにカメラが動いてくれました!!以下がプログラムです。

コード:

#include <math.h>
#include "DxLib.h"

//* マクロ定義 *///////////////////////////////////////////////////////////////////////////

#define		CAMERA_L		170.0f		//カメラの初めの高さ(170.0cm)
#define		CAMERA_SPEED	10.0		
#define		CAMERA_NEAR		10.0		
#define		CAMERA_FAR		1000.0		
#define		CAMERA_FOV		50.0		//カメラの最高視野角

//* 構造体定義 *///////////////////////////////////////////////////////////////////////////

typedef struct{
	int Handle;
	float Pos_X, Pos_Y, Pos_Z;
	float Rot_X, Rot_Y, Rot_Z;
}Model_t;

typedef	struct{
	float Pos_X, Pos_Y, Pos_Z;
	float Rot_V, Rot_H, Rot_T;
	float Fov, Move;
}Camera_t;

//* 変数宣言 */////////////////////////////////////////////////////////////////////////////

Model_t Model;
Camera_t Camera;

int Key[256];

//* プロトタイプ宣言 *////////////////////////////////////////////////////////////////////

int GetHitKeyStateAll_2();		//キー入力のやつ
int ChangeWindowMessageBox();	//ウィンドウ切替のやつ

float rad( float );                        //ラジアンに変換するやつ

void Model_Init( int, float, float, float, float, float, float );		//モデルを設定するやつ
void Camera_Init( float, float, float, float, float, float, float );	//カメラを設定するやつ

//* main */////////////////////////////////////////////////////////////////////////////////

int WINAPI WinMain( HINSTANCE, HINSTANCE, LPSTR, int ){
	ChangeWindowMessageBox(), DxLib_Init(), SetDrawScreen( DX_SCREEN_BACK );
	SetDrawMode( DX_DRAWMODE_BILINEAR );

	//カメラとモデルの初期設定
	Camera_Init( 400.0f, CAMERA_L/2, 0.0f, 0.0f, 0.0f, 0.0f, CAMERA_FOV );
	Model_Init( MV1LoadModel("Model/Field.mqo"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f );

	while( !ScreenFlip() && !ProcessMessage() && !ClearDrawScreen() 
									&& !GetHitKeyStateAll_2() && !Key[KEY_INPUT_ESCAPE] ){
	
                //カメラの設置
		SetCameraNearFar( CAMERA_NEAR, CAMERA_FAR );
		SetupCamera_Perspective( rad( Camera.Fov ) );
		SetCameraPositionAndAngle( VGet( Camera.Pos_X, Camera.Pos_Y, Camera.Pos_Z ),
									rad( Camera.Rot_V ), rad( Camera.Rot_H ), rad( Camera.Rot_T ) );

                //カメラの動きを制御
		Camera.Move = 0.0f;

                //前進、後退
		if	   ( Key[KEY_INPUT_UP] > 0 )		Camera.Move =  CAMERA_SPEED;
		else if( Key[KEY_INPUT_DOWN] > 0 )		Camera.Move = -CAMERA_SPEED;

                //旋回
		if	   ( Key[KEY_INPUT_LEFT] > 0 )		Camera.Rot_H -= 2.0f;
		else if( Key[KEY_INPUT_RIGHT] > 0 )		Camera.Rot_H += 2.0f;

		//カメラの座標を計算
		Camera.Pos_X += Camera.Move * sin( rad( Camera.Rot_H ) );
		Camera.Pos_Z += Camera.Move * cos( rad( Camera.Rot_H ) );

                //モデルを設置
		MV1SetPosition( Model.Handle, VGet( Model.Pos_X, Model.Pos_Y, Model.Pos_Z ) );
		MV1SetRotationXYZ( Model.Handle, VGet( rad( Model.Rot_X ), rad( Model.Rot_Y ), rad( Model.Rot_Z ) ) );
		MV1DrawModel( Model.Handle );
	}

	DxLib_End();
	return 0;
}

//* 自作関数 *//////////////////////////////////////////////////////////////////////////////////////////////

int GetHitKeyStateAll_2(){
	char KeyBuf[256];
	GetHitKeyStateAll( KeyBuf );

	for( int i=0; i<256; i++ ){
		if( KeyBuf[i] != 0 )	Key[i]++;
		else					Key[i]=0;
	}

	return 0;
}

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

int ChangeWindowMessageBox(){
	int Flag = MessageBox( NULL,
						   TEXT( "フルスクリーンで表示しますか?" ),
						   TEXT( "スクリーン設定" ),
						   MB_YESNO | MB_ICONQUESTION );

	if( Flag == IDNO )
		if( ChangeWindowMode( TRUE ) != 0 )
			return -1;

	return 0;
}

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

float rad( float angle ){
	return angle * PHI_F/180.0f;
}

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

void Model_Init( int Handle, float Px, float Py, float Pz, float Rx, float Ry, float Rz ){

	Model.Handle = Handle;
	Model.Pos_X  = Px;
	Model.Pos_Y  = Py;
	Model.Pos_Z  = Pz;
	Model.Rot_X  = Rx;
	Model.Rot_Y  = Ry;
	Model.Rot_Z  = Rz;

}

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

void Camera_Init( float Px, float Py, float Pz, float Rv, float Rh, float Rt, float F ){

	Camera.Pos_X = Px;
	Camera.Pos_Y = Py;
	Camera.Pos_Z = Pz;
	Camera.Rot_V = Rv;
	Camera.Rot_H = Rh;
	Camera.Rot_T = Rt;
	Camera.Fov   = F;

}
これで解決とします。
アドバイス下さったソフト屋様、ありがとうございました!

Re: 3D空間でのカメラの向きと進行方向について

Posted: 2012年8月05日(日) 16:27
by すけたろう
解決チェックしてませんでしたorz