【座標計算の基礎】

 

【座標系】
(1) 2次元座標系
 コンピュータ画面上の2次元座標系は,「ゲームソフトウェア設計論」でも学習したように,一般にはy軸は下向きである。
描画対象領域の左上が原点となり,たいていは整数値で表され,単位としては pixel が使われる。

 



(2) 3次元座標系
 ・ 右手座標系と左手座標系
 3次元座標系は,左手座標系または右手座標系で表される(下図)。ここでは,親指がX軸,人差し指がY軸,中指がZ軸である。

※ここでは,y軸を上方向としたが,zjik卯を上方向とする場合もある。
※座標値は実数値で表されるが,単位は決まっていないことが多く,決まっていてもソフトによってまちまちである(1.0が1.0mを表しているものもあれば,1.0cmを表してることもある)。

なお,XYZの座標軸は光の三原色のRGBになぞらえて,X軸が赤(R)Y軸が緑(G)Z軸が青(B)で表示されるのが一般的である。

x y z
親指 人差し指 中指
赤(R) 緑(G) 青(B)


・ソフトによる座標系の違い
 各ソフトウェアによっての採用している座標系は以下のとおり。

ソフトウェア DirectX/DXライブラリ Unity MMD OpenGL Metasequoia Blender
座標系 左手座標系

右手座標系
(データは左手座標系)

右手座標系
上向き y軸 z軸

【ベクトルと座標変換】
  ベクトル(vector,英語読みだとベクタ)とは,基本的には

  ・複数の数値を組にしたもの (数ベクトル) 例:(1.0, 2.0, 3.0)

であるが,幾何学的には

  ・向きと大きさをもったもの (幾何ベクトル) (下図の矢印)


を言う。幾何ベクタは,「どこにあるか」は問題にならず,あくまで「向き」と「大きさ」だけをもったものである。
したがって,座標系で扱う時はその始点を原点に合わせて扱う(下図)。



上図の様に,幾何ベクトルの始点を原点に揃えると,幾何ベクトルの終点の座標(これ自体は数ベクトル)で表現できる。
3次元でも同様である。


【DXライブラリでのベクトル表現】
・ DXライブラリでは,3次元座標値は単精度浮動小数点数型 ( float型 ) で表されている。
  C/C++では, 1.2 というように小数点を含んだリテラル定数値を書くと double 型の値として扱われる。これをDXライブラリが float型値を要求するところに渡すと,
 double→float の型変換作業が行われることになるので効率が悪い。 1.2f のように,末尾に f を付けると float型の実数値リテラルになるので, DXライブラリ用の
実数リテラルは,できるだけ末尾に f をつけておくこと。

・VECTOR構造体
 DXライブラリでは,ヘッダファイル DxLib.h 内で 3次元ベクトルを表す VECTOR 構造体を次の様に定義している。
また,手軽に VECTOR構造体型の値を作成することができるVGet( ) 関数が用意されている(下図)。



・色の指定
  DXライブラリでは, RGBそれぞれ 0〜255 で指定できる 色を利用できる。その際, GetColor( )関数でRGB値を指定し, unsigned int 型の色コードを返す関数
  GetColor( ) が便利である。

・ベクトル演算関数
 DXライブラリには,様々なベクトル演算関数が用意されている(DXライブラリ 関数リファレンスページ 3D関係関数リファレンス」ページの「算術演算関数」の項を参照のことほうこう)。

●例1
 ゲーム用のプロジェクト「MMD_DX」を使用する。座標軸とXZ平面を描写し,カメラを移動できるプログラム。↑↓キーでZ方向にカメラを移動,←→キーでカメラをX方向に移動。u,nキーでカメラをY方向移動。tキーでカメラのターゲットを標準と原点に切り替える。
いくつかの関数を定義しているので,その内容も把握すること。

List 1
#include <DxLib.h>

#include <cmath>


// 三次元の原点(VECTOR型はDXライブラリのヘッダで定義されている三次元座標を表す構造体)
VECTOR theOrigin = {0.0f, 0.0f, 0.0f};

/***
 *** void draw3DArrow( float x0 = 0.0f, float y0 = 0.0f, float z0 = 0.0f,
 ***                 float x1 = 1.0f, float y1 = 1.0f, float z1 = 1.0f, unsigned int theColor = VGet(255,0,0), float coneSize = 0.0f );
 ***
 ***      三次元の矢印を始点( x0, y0, z0 ) 終点( x1, y1, z1 )間に描写する。色は色コードtheColor。
 ***      矢印の先の円錐の高さを coneSize で指定する。coneSize の値が 0.0f の場合は、円錐の高さは自動調整される。
 ***      withArrow が true なら矢印の先の円錐を描画し、false なら描画しない。
 ***/
void draw3DArrow( float x0 = 0.0f, float y0 = 0.0f, float z0 = 0.0f,
	            float x1 = 1.0f, float y1 = 1.0f, float z1 = 1.0f, unsigned int theColor = GetColor(255,0,0), float coneSize = 0.0f, bool withArrow = true );
void draw3DArrow( float x0, float y0, float z0,
	            float x1, float y1, float z1, unsigned int theColor, float coneSize, bool withArrow ) {
	VECTOR p0 = VGet( x0, y0, z0 );
	VECTOR p1 = VGet( x1, y1, z1 );
	DrawLine3D( p0, p1, theColor );
	float len = sqrt( (x1-x0)* (x1-x0) + (y1-y0)*(y1-y0) + (z1-z0)*(z1-z0) );

	if( withArrow ) {
	    if( coneSize <= 0.0f ) coneSize = len / 10.0f; // 矢印先の円錐の長さが0.0f以下に指定された場合は自動調整する。
	    float r = (len - coneSize) / len;
	    float xp = x0 + (x1-x0)*r;
	    float yp = y0 + (y1-y0)*r;
	    float zp = z0 + (z1-z0)*r;
	    DrawCone3D(VGet(x1, y1,  z1), VGet( xp, yp, zp), coneSize / 4.0f, 12, theColor, theColor, TRUE);
	}
}

/***
 *** draw3DArrow( VECTOR p0 = VGet( 0.0f, 0.0f, 0.0f ), VECTOR p1 = VGet( 1.0f, 1.0f, 1.0f ), 
 ***              unsigned int theColor = GetColor(255,0,0), float coneSize = 0.0f, bool withArrow = true  );
 ***
 ***      三次元の矢印を始点p0 終点p1間に描写する。色は色コードtheColor。矢印の先の円錐の高さを coneSize で指定する。
 ***      coneSize の値が 0.0f の場合は、円錐の高さは自動調整される。
 ***      withArrow が true なら矢印の先の円錐を描画し、false なら描画しない。
 ***/
void draw3DArrow( VECTOR p0 = VGet( 0.0f, 0.0f, 0.0f ), VECTOR p1 = VGet( 1.0f, 1.0f, 1.0f ), unsigned int theColor = GetColor(255,0,0), float coneSize = 0.0f, bool withArrow = true  );
void draw3DArrow( VECTOR p0, VECTOR p1, unsigned int theColor, float coneSize, bool withArrow ) {
	draw3DArrow(p0.x, p0.y, p0.z, p1.x, p1.y, p1.z, theColor, coneSize, withArrow );
}

/***
 *** draw3DAxisArrow( float x0 = 0.0f, float y0 = 0.0f, float z0 = 0.0f, float length = 1.0f, bool withArrow = true );
 ***     (x0, y0, z0) を始点としたXYZ座標軸を描写する。X軸は赤、Y軸は緑、Z軸は青。
 ***     length は座標軸の長さ。withArrow が true の場合は、座標軸を円錐を使った矢印として描写する。
 ***/
void draw3DAxisArrow( float x0 = 0.0f, float y0 = 0.0f, float z0 = 0.0f, float length = 1.0f, bool withArrow = true );
void draw3DAxisArrow( float x0, float y0, float z0, float length, bool withArrow ) {
	draw3DArrow( x0, y0, z0, x0 + length, y0,          z0,          GetColor(255, 0, 0), 0.0f, withArrow  );
	draw3DArrow( x0, y0, z0, x0,          y0 + length, z0,          GetColor(0, 255, 0), 0.0f, withArrow  );
	draw3DArrow( x0, y0, z0, x0,          y0,          z0 + length, GetColor(0, 0, 255), 0.0f, withArrow  );
}

/***
 *** void drawXZPlane( int numOfMesh, float meshInterval, bool FillFlag = true );
 ***     numOfMesh 個の格子(一辺の長さは meshInterval )で、原点を中心としたXZ平面を描写する。
 ***     FillFlag が true の場合は、平面を白く塗りつぶし、false の場合は塗りつぶさない。
 ***/
void drawXZPlane( int numOfMesh, float meshInterval, bool FillFlag = true );
void drawXZPlane( int numOfMesh, float meshInterval, bool FillFlag ) {

	float width = numOfMesh * meshInterval;
	float x0 = - width / 2.0f;
	float z0 = x0;
	unsigned int lineColor = GetColor(220, 220, 220);

	// FillFlag が true なら、平面(原点を中心とした一辺が width の正方形領域)を白で塗りつぶす(実際には白い三角ポリゴンを2個描写する)。
	if( FillFlag ) {
	    DrawTriangle3D( VGet( x0, 0.0f,  z0), VGet(x0, -0.0f, -z0), VGet(-x0, 0.0f, z0), GetColor(255,255,255), true );
	    DrawTriangle3D( VGet(-x0, 0.0f, -z0), VGet(x0, -0.0f, -z0), VGet(-x0, 0.0f, z0), GetColor(255,255,255), true );
		lineColor = GetColor(220, 220, 220);
	}

	// 格子線を描写する。
	for(int n = 0; n <= numOfMesh; n++ ) {// Z軸に平行な平行線分を書く。
		float ratio = n / (1.0f * numOfMesh);
		float offset = ratio * width;
		DrawLine3D( VGet(x0 + offset, 0.0f, z0), VGet(x0 + offset, 0.0f, -z0), lineColor );
	}
	for(int n = 0; n <= numOfMesh; n++ ) {// X軸に平行な平行線分を書く。
		float ratio = n / (1.0f * numOfMesh);
		float offset = ratio * width;
		DrawLine3D( VGet(x0, 0.0f, z0 + offset), VGet(-x0, 0.0f, z0 + offset), lineColor );
	}

}


/***
 *** void drawRectangle( VECTOR p0, VECTOR p1, VECTOR p2, VECTOR p3, unsigned int color = GetColor(255,255,255) );
 ***     4頂点を p0, p1, p2, p3 とする color 色の矩形を描写する。なお、頂点 p0とp2 は対角頂点である事。
 ***
 ***/
void drawRectangle( VECTOR p0, VECTOR p1, VECTOR p2, VECTOR p3, unsigned int color = GetColor(255,255,255) );
void drawRectangle( VECTOR p0, VECTOR p1, VECTOR p2, VECTOR p3, unsigned int color ) {
	DrawTriangle3D( p0, p1, p2, color, true );
	DrawTriangle3D( p2, p3, p0, color, true );
}

/***
 *** void drawCube( VECTOR center, float width, unsigned int color = GetColor(255,255,255) );
 ***    中心点 center を囲む一辺の長さが width の立方体を描写する。面の色は color で指定する。
 ***    尚、各面はXY平面、YZ平面、XZ平面のいずれかに平行である。
 ***
 ***/
void drawCube( VECTOR center, float width, unsigned int color = GetColor(255,255,255) );
void drawCube( VECTOR center, float width, unsigned int color ) {
	float x = center.x;
	float y = center.y;
	float z = center.z;
	float s = width / 2.0f;
	// 立方体の8頂点を取得する。
	VECTOR v_XYZ = VGet( x + s, y + s, z + s );
	VECTOR v_XYz = VGet( x + s, y + s, z - s );
	VECTOR v_XyZ = VGet( x + s, y - s, z + s );
	VECTOR v_xYZ = VGet( x - s, y + s, z + s );
	VECTOR v_Xyz = VGet( x + s, y - s, z - s );
	VECTOR v_xYz = VGet( x - s, y + s, z - s );
	VECTOR v_xyZ = VGet( x - s, y - s, z + s );
	VECTOR v_xyz = VGet( x - s, y - s, z - s );
	// 立方体の面を描画する。
	drawRectangle( v_XYZ, v_XYz, v_Xyz, v_XyZ, color );
	drawRectangle( v_XYZ, v_xYZ, v_xYz, v_XYz, color );
	drawRectangle( v_xyz, v_Xyz, v_XYz, v_xYz, color );
	drawRectangle( v_Xyz, v_XyZ, v_xyZ, v_xyz, color );
	drawRectangle( v_XYZ, v_xYZ, v_xyZ, v_XyZ, color );
	drawRectangle( v_xyz, v_xyZ ,v_xYZ, v_xYz, color );
}

int WINAPI WinMain( HINSTANCE, HINSTANCE, LPSTR, int ){

	ChangeWindowMode( TRUE );        // ゲーム画面をウィンドウで表示するように指定する。
	DxLib_Init();                    // DXライブラリを初期化する。
	SetDrawScreen( DX_SCREEN_BACK ); // 描写先を裏画面にする。

    //カメラの描写範囲(奥行)を0.1〜10000.0に設定する。
    SetCameraNearFar( 0.1f, 10000.0f );

    //( 0.0, 9.0, -22.0 )の位置にカメラを設置し、視線を( 0.0, 10.0, 0.0 )にあるターゲットへ向ける。
	VECTOR cameraPos  = VGet( 0.0f,  9.0f, -22.0f );
	VECTOR theTarget  = VGet( 0.0f, 10.0f,   0.0f );
	bool targeIsOrigin = false;
    SetCameraPositionAndTarget_UpVecY( cameraPos, theTarget );

	//Zバッファを使って図形描写を行う。
	SetUseZBuffer3D( TRUE );
	SetWriteZBuffer3D( TRUE );

    // 繰り返し。
    while( ! ProcessMessage() ){
		
		// 描写先の画面(裏画面)を消去する。
		ClearDrawScreen();

		// 原点を囲むように、一辺が 100.f の白い立方体を描写する。
		drawCube( VGet(0.0f, 0.0f, 0.0f), 200.0f );

		// XZ平面を描写する。
		drawXZPlane( 100, 1.0, false );

		// 座標軸を描写する。
		draw3DAxisArrow( 0.0f, 0.0f, 0.0f, 8.0f );

		// カメラの座標を画面左下に表示する。
		DrawFormatString( 0, 460, GetColor(0,0,0), "%6.2f, %6.2f, %6.2f", cameraPos.x, cameraPos.y, cameraPos.z );

		// 完成した裏画面の内容を表画面(ゲームのウィンドウ)に転写する。
		ScreenFlip();

		// キー(ゲームコントローラー)の状態を得る。
		int keyInput = GetJoypadInputState( DX_INPUT_KEY_PAD1 ); 

		// カメラ(とターゲット)の位置を変更する
		if( keyInput == PAD_INPUT_DOWN  ) {
			cameraPos.z = cameraPos.z - 0.5; theTarget.z = theTarget.z - 0.5f;
		}
		if( keyInput == PAD_INPUT_UP  ) {
			cameraPos.z = cameraPos.z + 0.5; theTarget.z = theTarget.z + 0.5f;
		}
		if( keyInput == PAD_INPUT_LEFT  ) {
			cameraPos.x = cameraPos.x - 0.5; theTarget.x = theTarget.x - 0.5f;
		}
		if( keyInput == PAD_INPUT_RIGHT  ) {
			cameraPos.x = cameraPos.x + 0.5; theTarget.x = theTarget.x + 0.5f;
		}
		char c = GetInputChar( TRUE );
		if( c == 'u' ) {
			cameraPos.y = cameraPos.y + 0.5f; theTarget.y = theTarget.y + 0.5f;
		}
		if( c == 'n' ) {
			cameraPos.y = cameraPos.y - 0.5f; theTarget.y = theTarget.y - 0.5f;
		}
		if( c == 't' ) { // カメラのターゲットを標準位置にするか原点にするか切り替える。
			targeIsOrigin = !targeIsOrigin;
		}

		// カメラの位置を設定しなおす。
		if( targeIsOrigin ) { // targeIsOrigin が true なら、カメラをターゲットに向ける。
			SetCameraPositionAndTarget_UpVecY( cameraPos, VGet(0.0f, 0.0f, 0.0f) );
		}
		else { // targeIsOrigin が false なら、カメラを標準位置に向ける。
			SetCameraPositionAndTarget_UpVecY( cameraPos, theTarget );
		}
		
	}
    
    DxLib_End(); // DXライブラリの後処理を行う.

    return 0 ;
}

では,MMDのキャラクタモデルと背景を描写した上で,座標軸とXZ平面を重ねてみよう。

List 2
#include <DxLib.h>

#include <cmath>


// 三次元の原点(VECTOR型はDXライブラリのヘッダで定義されている三次元座標を表す構造体)
VECTOR theOrigin = { 0.0f, 0.0f, 0.0f };

/***
*** void draw3DArrow( float x0 = 0.0f, float y0 = 0.0f, float z0 = 0.0f,
***                 float x1 = 1.0f, float y1 = 1.0f, float z1 = 1.0f, unsigned int theColor = VGet(255,0,0), float coneSize = 0.0f );
***
***      三次元の矢印を始点( x0, y0, z0 ) 終点( x1, y1, z1 )間に描写する。色は色コードtheColor。
***      矢印の先の円錐の高さを coneSize で指定する。coneSize の値が 0.0f の場合は、円錐の高さは自動調整される。
***      withArrow が true なら矢印の先の円錐を描画し、false なら描画しない。
***/
void draw3DArrow(float x0 = 0.0f, float y0 = 0.0f, float z0 = 0.0f,
	float x1 = 1.0f, float y1 = 1.0f, float z1 = 1.0f, unsigned int theColor = GetColor(255, 0, 0), float coneSize = 0.0f, bool withArrow = true);
void draw3DArrow(float x0, float y0, float z0,
	float x1, float y1, float z1, unsigned int theColor, float coneSize, bool withArrow) {
	VECTOR p0 = VGet(x0, y0, z0);
	VECTOR p1 = VGet(x1, y1, z1);
	DrawLine3D(p0, p1, theColor);
	float len = sqrt((x1 - x0)* (x1 - x0) + (y1 - y0)*(y1 - y0) + (z1 - z0)*(z1 - z0));

	if (withArrow) {
		if (coneSize <= 0.0f) coneSize = len / 10.0f; // 矢印先の円錐の長さが0.0f以下に指定された場合は自動調整する。
		float r = (len - coneSize) / len;
		float xp = x0 + (x1 - x0)*r;
		float yp = y0 + (y1 - y0)*r;
		float zp = z0 + (z1 - z0)*r;
		DrawCone3D(VGet(x1, y1, z1), VGet(xp, yp, zp), coneSize / 4.0f, 12, theColor, theColor, TRUE);
	}
}

/***
*** draw3DArrow( VECTOR p0 = VGet( 0.0f, 0.0f, 0.0f ), VECTOR p1 = VGet( 1.0f, 1.0f, 1.0f ),
***              unsigned int theColor = GetColor(255,0,0), float coneSize = 0.0f, bool withArrow = true  );
***
***      三次元の矢印を始点p0 終点p1間に描写する。色は色コードtheColor。矢印の先の円錐の高さを coneSize で指定する。
***      coneSize の値が 0.0f の場合は、円錐の高さは自動調整される。
***      withArrow が true なら矢印の先の円錐を描画し、false なら描画しない。
***/
void draw3DArrow(VECTOR p0 = VGet(0.0f, 0.0f, 0.0f), VECTOR p1 = VGet(1.0f, 1.0f, 1.0f), unsigned int theColor = GetColor(255, 0, 0), float coneSize = 0.0f, bool withArrow = true);
void draw3DArrow(VECTOR p0, VECTOR p1, unsigned int theColor, float coneSize, bool withArrow) {
	draw3DArrow(p0.x, p0.y, p0.z, p1.x, p1.y, p1.z, theColor, coneSize, withArrow);
}

/***
*** draw3DAxisArrow( float x0 = 0.0f, float y0 = 0.0f, float z0 = 0.0f, float length = 1.0f, bool withArrow = true );
***     (x0, y0, z0) を始点としたXYZ座標軸を描写する。X軸は赤、Y軸は緑、Z軸は青。
***     length は座標軸の長さ。withArrow が true の場合は、座標軸を円錐を使った矢印として描写する。
***/
void draw3DAxisArrow(float x0 = 0.0f, float y0 = 0.0f, float z0 = 0.0f, float length = 1.0f, bool withArrow = true);
void draw3DAxisArrow(float x0, float y0, float z0, float length, bool withArrow) {
	draw3DArrow(x0, y0, z0, x0 + length, y0, z0, GetColor(255, 0, 0), 0.0f, withArrow);
	draw3DArrow(x0, y0, z0, x0, y0 + length, z0, GetColor(0, 255, 0), 0.0f, withArrow);
	draw3DArrow(x0, y0, z0, x0, y0, z0 + length, GetColor(0, 0, 255), 0.0f, withArrow);
}

/***
*** void drawXZPlane( int numOfMesh, float meshInterval, bool FillFlag = true );
***     numOfMesh 個の格子(一辺の長さは meshInterval )で、原点を中心としたXZ平面を描写する。
***     FillFlag が true の場合は、平面を白く塗りつぶし、false の場合は塗りつぶさない。
***/
void drawXZPlane(int numOfMesh, float meshInterval, bool FillFlag = true);
void drawXZPlane(int numOfMesh, float meshInterval, bool FillFlag) {

	float width = numOfMesh * meshInterval;
	float x0 = -width / 2.0f;
	float z0 = x0;
	unsigned int lineColor = GetColor(220, 220, 220);

	// FillFlag が true なら、平面(原点を中心とした一辺が width の正方形領域)を白で塗りつぶす(実際には白い三角ポリゴンを2個描写する)。
	if (FillFlag) {
		DrawTriangle3D(VGet(x0, 0.0f, z0), VGet(x0, -0.0f, -z0), VGet(-x0, 0.0f, z0), GetColor(255, 255, 255), true);
		DrawTriangle3D(VGet(-x0, 0.0f, -z0), VGet(x0, -0.0f, -z0), VGet(-x0, 0.0f, z0), GetColor(255, 255, 255), true);
		lineColor = GetColor(220, 220, 220);
	}

	// 格子線を描写する。
	for (int n = 0; n <= numOfMesh; n++) {// Z軸に平行な平行線分を書く。
		float ratio = n / (1.0f * numOfMesh);
		float offset = ratio * width;
		DrawLine3D(VGet(x0 + offset, 0.0f, z0), VGet(x0 + offset, 0.0f, -z0), lineColor);
	}
	for (int n = 0; n <= numOfMesh; n++) {// X軸に平行な平行線分を書く。
		float ratio = n / (1.0f * numOfMesh);
		float offset = ratio * width;
		DrawLine3D(VGet(x0, 0.0f, z0 + offset), VGet(-x0, 0.0f, z0 + offset), lineColor);
	}

}


/***
*** void drawRectangle( VECTOR p0, VECTOR p1, VECTOR p2, VECTOR p3, unsigned int color = GetColor(255,255,255) );
***     4頂点を p0, p1, p2, p3 とする color 色の矩形を描写する。なお、頂点 p0とp2 は対角頂点である事。
***
***/
void drawRectangle(VECTOR p0, VECTOR p1, VECTOR p2, VECTOR p3, unsigned int color = GetColor(255, 255, 255));
void drawRectangle(VECTOR p0, VECTOR p1, VECTOR p2, VECTOR p3, unsigned int color) {
	DrawTriangle3D(p0, p1, p2, color, true);
	DrawTriangle3D(p2, p3, p0, color, true);
}

/***
*** void drawCube( VECTOR center, float width, unsigned int color = GetColor(255,255,255) );
***    中心点 center を囲む一辺の長さが width の立方体を描写する。面の色は color で指定する。
***    尚、各面はXY平面、YZ平面、XZ平面のいずれかに平行である。
***
***/
void drawCube(VECTOR center, float width, unsigned int color = GetColor(255, 255, 255));
void drawCube(VECTOR center, float width, unsigned int color) {
	float x = center.x;
	float y = center.y;
	float z = center.z;
	float s = width / 2.0f;
	// 立方体の8頂点を取得する。
	VECTOR v_XYZ = VGet(x + s, y + s, z + s);
	VECTOR v_XYz = VGet(x + s, y + s, z - s);
	VECTOR v_XyZ = VGet(x + s, y - s, z + s);
	VECTOR v_xYZ = VGet(x - s, y + s, z + s);
	VECTOR v_Xyz = VGet(x + s, y - s, z - s);
	VECTOR v_xYz = VGet(x - s, y + s, z - s);
	VECTOR v_xyZ = VGet(x - s, y - s, z + s);
	VECTOR v_xyz = VGet(x - s, y - s, z - s);
	// 立方体の面を描画する。
	drawRectangle(v_XYZ, v_XYz, v_Xyz, v_XyZ, color);
	drawRectangle(v_XYZ, v_xYZ, v_xYz, v_XYz, color);
	drawRectangle(v_xyz, v_Xyz, v_XYz, v_xYz, color);
	drawRectangle(v_Xyz, v_XyZ, v_xyZ, v_xyz, color);
	drawRectangle(v_XYZ, v_xYZ, v_xyZ, v_XyZ, color);
	drawRectangle(v_xyz, v_xyZ, v_xYZ, v_xYz, color);
}

int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int){

	ChangeWindowMode(TRUE);        // ゲーム画面をウィンドウで表示するように指定する。
	DxLib_Init();                  // DXライブラリを初期化する。
	SetDrawScreen(DX_SCREEN_BACK); // 描写先を裏画面にする。

	// MMDキャラクタモデルと背景モデルをモーション付きで描画するために必要な変数群
	int modelHandle1, modelHandle_BG; // 読み込んだ3Dモデルデータの番号を記録しておく変数
	int attachIndex;                  // アタッチされたアニメーションの番号を記録しておく変数
	float animTime000;                // アニメーションの総再生時間を記録しておく変数
	float theTime;                    // アニメーションの再生時間を記録しておく変数

	// 以下,(1)〜(8)は, MMDキャラクタモデルと背景モデルを描写するための処理
	// (1)3Dモデルデータの読み込み
	modelHandle1 = MV1LoadModel("那珂ver1.01/那珂ver1.0.1.pmd");
	modelHandle_BG = MV1LoadModel("かぼちゃステージ/カボチャステージ(空セット).pmx");

	// (2) 3Dモデル1に対してアニメーション0番をアタッチする
	attachIndex = MV1AttachAnim(modelHandle1, 0, -1, FALSE);

	// (3) アタッチしたアニメーションの再生時間を得る
	animTime000 = MV1GetAttachAnimTotalTime(modelHandle1, attachIndex);

	// (4) ゲーム内時間の初期化
	theTime = 0.0;

	//カメラの描写範囲(奥行)を0.1〜10000.0に設定する。
	SetCameraNearFar(0.1f, 10000.0f);

	//( 0.0, 9.0, -22.0 )の位置にカメラを設置し、視線を( 0.0, 10.0, 0.0 )にあるターゲットへ向ける。
	VECTOR cameraPos = VGet(0.0f, 9.0f, -22.0f);
	VECTOR theTarget = VGet(0.0f, 10.0f, 0.0f);
	bool targeIsOrigin = false;
	SetCameraPositionAndTarget_UpVecY(cameraPos, theTarget);

	//Zバッファを使って図形描写を行う。
	SetUseZBuffer3D(TRUE);
	SetWriteZBuffer3D(TRUE);

	// 繰り返し。
	while (!ProcessMessage()){

		// 描写先の画面(裏画面)を消去する。
		ClearDrawScreen();

		// 原点を囲むように、一辺が 100.f の白い立方体を描写する。
		drawCube(VGet(0.0f, 0.0f, 0.0f), 200.0f);

		// (5) 現在の再生時間をセットする
		MV1SetAttachAnimTime(modelHandle1, attachIndex, theTime);

		// (6) 3Dモデルの描画
		MV1DrawModel(modelHandle_BG);
		MV1DrawModel(modelHandle1);

		// XZ平面を描写する。
		drawXZPlane(100, 1.0, false);

		// 座標軸を描写する。
		draw3DAxisArrow(0.0f, 0.0f, 0.0f, 20.0f);

		// カメラの座標を画面左下に表示する。
		DrawFormatString(0, 460, GetColor(0, 0, 0), "%6.2f, %6.2f, %6.2f", cameraPos.x, cameraPos.y, cameraPos.z);

		// 完成した裏画面の内容を表画面(ゲームのウィンドウ)に転写する。
		ScreenFlip();

		// キー(ゲームコントローラー)の状態を得る。
		int keyInput = GetJoypadInputState(DX_INPUT_KEY_PAD1);

		// カメラ(とターゲット)の位置を変更する
		if (keyInput == PAD_INPUT_DOWN) {
			cameraPos.z = cameraPos.z - 0.5; theTarget.z = theTarget.z - 0.5f;
		}
		if (keyInput == PAD_INPUT_UP) {
			cameraPos.z = cameraPos.z + 0.5; theTarget.z = theTarget.z + 0.5f;
		}
		if (keyInput == PAD_INPUT_LEFT) {
			cameraPos.x = cameraPos.x - 0.5; theTarget.x = theTarget.x - 0.5f;
		}
		if (keyInput == PAD_INPUT_RIGHT) {
			cameraPos.x = cameraPos.x + 0.5; theTarget.x = theTarget.x + 0.5f;
		}
		char c = GetInputChar(TRUE);
		if (c == 'u') {
			cameraPos.y = cameraPos.y + 0.5f; theTarget.y = theTarget.y + 0.5f;
		}
		if (c == 'n') {
			cameraPos.y = cameraPos.y - 0.5f; theTarget.y = theTarget.y - 0.5f;
		}
		if (c == 't') { // カメラのターゲットを標準位置にするか原点にするか切り替える。
			targeIsOrigin = !targeIsOrigin;
		}

		// カメラの位置を設定しなおす。
		if (targeIsOrigin) { // targeIsOrigin が true なら、カメラをターゲットに向ける。
			SetCameraPositionAndTarget_UpVecY(cameraPos, VGet(0.0f, 0.0f, 0.0f));
		}
		else { // targeIsOrigin が false なら、カメラを標準位置に向ける。
			SetCameraPositionAndTarget_UpVecY(cameraPos, theTarget);
		}

		// (7) 次の描写のためにゲーム内時間を更新する
		theTime = theTime + 0.4;

		// (8) ゲーム内時間がアニメーションの再生時間を超えたら再生時間をリセットする
		if (theTime >= animTime000) theTime = 0.0;
	}

	DxLib_End(); // DXライブラリの後処理を行う.

	return 0;
}


●座標変換と行列



●座標変換を組み合わせた例


●提出課題 第15回(2024/01/19)課題 2024/01/26(金) 23:59 締切
 List 2に処理を追加し,点A(2.0, 0.5, 0.0)を中心としてXY平面と平行な面の上で半径0.2の球を反時計回りに回転させるように表示せよ。なお,Aからこの球の中心までの距離は2.0とする。

球を描写するには,DXライブラリリファレンスページの3D関係関数のDrawSphere3D関数を使えば良い。

こちらのリンク先から提出せよ。締切は 2024/01/26(金) 23:59。

ヒント:
 (0)まず,このサンプル1で,処理を書く場所を確認する(3箇所。1箇所目は必要な変数の宣言。2箇所目は球の描写。3箇所目は座標の変換)。
 (1)次に,原点を中心に半径0.2の球を描写してみよう(サンプル2)。
 (2)次に,以下の様な処理を書き直す。サンプル3は,処理1のX座標に関する処理と処理3のX座標に関する処理だけ記入済み。
  処理1:回転の中心点を原点へ平行移動する(球の中心も同じ量だけ平行移動する)
  処理2:原点を中心としてxy平面上で theta ラジアンだけ回転
  処理3:回転の中心点を元の位置へ平行移動する(球の中心も同じ量だけ平行移動する)