はじめまして shutoと申します。C言語は二年前から授業で扱っていて
入門書に書いてあるようなことは理解できていますが3Dの知識をあまり持っていません。
ベクトルについては授業でベクトル解析を習い、
gradやdiv、rotなどが出ていますがふつうに理解できています。
(そんな知識はこの話ではいらない気もしますが一応空間ベクトルの話なので)
自宅のwindowsVistaにインストールしたVC2008で色々試していたところ
自分ではどうすべきか判断に困ったので質問します。
DXライブラリをつかって3Dモデルの表示をしようとして
MV1LoadModel でメタセコイアで作成したxファイルのモデルを読み込み、
SetCameraPositionAndTarget_UpVecY でカメラを調整して
MV1DrawModel でモデルを表示しました。
コンパイル時にエラーは無く普通に動作しモデルの表示もされるのですが、
モデルの位置ベクトルを
SetCameraPositionAndTarget_UpVecY に与える引数の
引数1 VECTOR Position : カメラの位置 を VGet( 0.0f, 2000.0f, 0.0f )
引数2 VECTOR Target : カメラの注視点 を VGet( 0.0f, 0.0f, 0,0f )
にして、真上からカメラを向けるとモデルが表示されませんでした。
引数2をVGet( 1.0f, 2000.0f, 0,0f )にして、ほんの少しずらすと正しく表示されました。
原因を予想するとカメラを真下に向けるとカメラの上下がおかしくなっているんだと思うのですが、
もしかして自分だけこの状況になるのかもしれないと思い質問しました。
原因がしっかりわかる方・考えてくれる方が居ましたら教えてほしいです。
それと3Dゲーム内では真上からの視点で操作するということもあると思うので
これが正しい動作なのであればどう対処するのがよいのか教えてほしいです。
Dxlibでの3dモデルの表示
Re:Dxlibでの3dモデルの表示
すみません、xファイルじゃなくてmqoファイルでした。
しかし、やっぱり表示できない現象は起こるので
同じ現象が起こるコードを載せます。
#include "DxLib.h"
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nCmdShow ){
ChangeWindowMode( TRUE ) ;
if( DxLib_Init() == -1 ) return -1;
int ModelHandle = MV1LoadModel( "../stage1.mqo" );
VECTOR Camera = VGet( 0.0f, 2000.0f, 0.0f );
while( ProcessMessage() == 0 && CheckHitKey( KEY_INPUT_ESCAPE ) == 0 )
{
ClearDrawScreen() ;
if( CheckHitKey( KEY_INPUT_UP ) == 1 ){
Camera.x += 1.0f ;
}
if( CheckHitKey( KEY_INPUT_DOWN ) == 1 ){
Camera.x -= 1.0f ;
}
SetCameraPositionAndTarget_UpVecY( Camera, VGet( 0.0f, 0.0f, 0.0f ) );
MV1DrawModel( ModelHandle );
DrawFormatString( 0, 0, GetColor(0,0,255),"Camera.x = %f",Camera.x) ;
ScreenFlip();
}
// DXライブラリの後始末
DxLib_End() ;
// ソフトの終了
return 0 ;
}
しかし、やっぱり表示できない現象は起こるので
同じ現象が起こるコードを載せます。
#include "DxLib.h"
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nCmdShow ){
ChangeWindowMode( TRUE ) ;
if( DxLib_Init() == -1 ) return -1;
int ModelHandle = MV1LoadModel( "../stage1.mqo" );
VECTOR Camera = VGet( 0.0f, 2000.0f, 0.0f );
while( ProcessMessage() == 0 && CheckHitKey( KEY_INPUT_ESCAPE ) == 0 )
{
ClearDrawScreen() ;
if( CheckHitKey( KEY_INPUT_UP ) == 1 ){
Camera.x += 1.0f ;
}
if( CheckHitKey( KEY_INPUT_DOWN ) == 1 ){
Camera.x -= 1.0f ;
}
SetCameraPositionAndTarget_UpVecY( Camera, VGet( 0.0f, 0.0f, 0.0f ) );
MV1DrawModel( ModelHandle );
DrawFormatString( 0, 0, GetColor(0,0,255),"Camera.x = %f",Camera.x) ;
ScreenFlip();
}
// DXライブラリの後始末
DxLib_End() ;
// ソフトの終了
return 0 ;
}
Re:Dxlibでの3dモデルの表示
その関数を使って真下に向けると内部でビュー行列の計算が破綻します。
SetCameraPositionAndTarget_UpVecY関数は SetCameraPositionAndTargetAndUpVec関数の
第三引数の上方ベクトル指定を [0, 1, 0]としたもの、つまりカメラの上方向がワールドのY軸の方向に
あることを前提とした関数だからです。
例えば通常カメラが真正面を向いているとき、カメラの向き(カメラベクトル)は [0, 0, 1]、
カメラの上の方向(上方ベクトル)は [0, 1, 0]になっているはずです。
当然向いている向きとカメラの上方向の向きは異なりますし、直行しています。
しかしカメラを下を向ける時に SetCameraPositionAndTarget_UpVecYを使ってしまうと
カメラベクトルが [0, -1, 0]、上方ベクトルは [0, 1, 0]となり完全に正反対の方向を向いてしまいます。
内部的には指定した上方ベクトルを補正して最終的な上方ベクトルを計算しているのですが
カメラベクトルと同じ方向や 180度反対の方向を指しているとその計算に失敗し、破綻してしまうというわけです。
従って、改善方法としては
・ SetCameraPositionAndTargetAndUpVecを使って(ある程度)正しい上向きのベクトルを指定する
例)SetCameraPositionAndTargetAndUpVec( Camera, VGet( 0.0f, 0.0f, 0.0f ), VGet( 0.0f, 0.0f, 1.0f ) );
・ SetCameraPositionAndAngleを使う
例)SetCameraPositionAndAngle( Camera, PHI_F/2, 0, 0 );
・ SetCameraViewMatrixを使う
自前でビュー行列を計算して設定します。
あたりがいいかと思います。
ちなみに SetCameraPositionAndTarget_UpVecY関数の内部処理を再現したコードを載せておきます。
最終的に CreateLookAtMatrixでビュー行列を求めて matCamera変数に代入しています。
引数 posや targetにいろいろな値を入れて、どう破綻しているか調べて見るといいでしょう。
[color=#d0d0ff" face="monospace]
int SetCameraPositionAndTarget_UpVecY_(VECTOR pos, VECTOR target)
{
VECTOR vecAt, vecRight, vecUp = { 0.0f, 1.0f, 0.0f };
VectorSub(&vecAt, &target, &pos);
VectorOuterProduct(&vecRight, &vecAt, &vecUp);
VectorOuterProduct(&vecUp, &vecRight, &vecAt);
VectorNormalize(&vecUp, &vecUp);
MATRIX matCamera ;
CreateLookAtMatrix(&matCamera, &pos, &target, &vecUp);
SetTransformToView(&matCamera);
return 0;
}
[/color]
SetCameraPositionAndTarget_UpVecY関数は SetCameraPositionAndTargetAndUpVec関数の
第三引数の上方ベクトル指定を [0, 1, 0]としたもの、つまりカメラの上方向がワールドのY軸の方向に
あることを前提とした関数だからです。
例えば通常カメラが真正面を向いているとき、カメラの向き(カメラベクトル)は [0, 0, 1]、
カメラの上の方向(上方ベクトル)は [0, 1, 0]になっているはずです。
当然向いている向きとカメラの上方向の向きは異なりますし、直行しています。
しかしカメラを下を向ける時に SetCameraPositionAndTarget_UpVecYを使ってしまうと
カメラベクトルが [0, -1, 0]、上方ベクトルは [0, 1, 0]となり完全に正反対の方向を向いてしまいます。
内部的には指定した上方ベクトルを補正して最終的な上方ベクトルを計算しているのですが
カメラベクトルと同じ方向や 180度反対の方向を指しているとその計算に失敗し、破綻してしまうというわけです。
従って、改善方法としては
・ SetCameraPositionAndTargetAndUpVecを使って(ある程度)正しい上向きのベクトルを指定する
例)SetCameraPositionAndTargetAndUpVec( Camera, VGet( 0.0f, 0.0f, 0.0f ), VGet( 0.0f, 0.0f, 1.0f ) );
・ SetCameraPositionAndAngleを使う
例)SetCameraPositionAndAngle( Camera, PHI_F/2, 0, 0 );
・ SetCameraViewMatrixを使う
自前でビュー行列を計算して設定します。
あたりがいいかと思います。
ちなみに SetCameraPositionAndTarget_UpVecY関数の内部処理を再現したコードを載せておきます。
最終的に CreateLookAtMatrixでビュー行列を求めて matCamera変数に代入しています。
引数 posや targetにいろいろな値を入れて、どう破綻しているか調べて見るといいでしょう。
[color=#d0d0ff" face="monospace]
int SetCameraPositionAndTarget_UpVecY_(VECTOR pos, VECTOR target)
{
VECTOR vecAt, vecRight, vecUp = { 0.0f, 1.0f, 0.0f };
VectorSub(&vecAt, &target, &pos);
VectorOuterProduct(&vecRight, &vecAt, &vecUp);
VectorOuterProduct(&vecUp, &vecRight, &vecAt);
VectorNormalize(&vecUp, &vecUp);
MATRIX matCamera ;
CreateLookAtMatrix(&matCamera, &pos, &target, &vecUp);
SetTransformToView(&matCamera);
return 0;
}
[/color]
Re:Dxlibでの3dモデルの表示
なるほど・・・Justyさん解説ありがとうございました。
おかしくなってしまう理由が自分なりに十分理解できました。
確かにカメラの上をY軸にしてしまうとカメラを下向きにしたときにおかしくなってしまいますね。
よく考えればカメラに対してカメラの座標と注視点の二つしか無ければカメラの捻りが作りでせなく
さらにカメラを真上・真下に向ければカメラの捻りがないと写せませんね。
自分をカメラにして上向いたり(物理的な意味で)頭ひねったりして考えてました。
自分のプログラムではSetCameraPositionAndTargetAndUpVecを通常では使い
視点が完全には対象の真上・真下に行かないようにし、
真上からの視点のときはSetCameraPositionAndAngleを使おうと思います。
自分はまだまだ3Dの知識や考え方などに慣れていないので
また質問することがありそうですがその時はまたよろしくお願いします。
では、これで解決とさせていただきます。ありがとうございました。
おかしくなってしまう理由が自分なりに十分理解できました。
確かにカメラの上をY軸にしてしまうとカメラを下向きにしたときにおかしくなってしまいますね。
よく考えればカメラに対してカメラの座標と注視点の二つしか無ければカメラの捻りが作りでせなく
さらにカメラを真上・真下に向ければカメラの捻りがないと写せませんね。
自分をカメラにして上向いたり(物理的な意味で)頭ひねったりして考えてました。
自分のプログラムではSetCameraPositionAndTargetAndUpVecを通常では使い
視点が完全には対象の真上・真下に行かないようにし、
真上からの視点のときはSetCameraPositionAndAngleを使おうと思います。
自分はまだまだ3Dの知識や考え方などに慣れていないので
また質問することがありそうですがその時はまたよろしくお願いします。
では、これで解決とさせていただきます。ありがとうございました。