「ゲームプログラミング/3Dグラフィック」の版間の差分

削除された内容 追加された内容
typo
449 行
 
=== プログラム例 ===
高校3年や大学1年理系では、回転行列を習う場合が多いので、ついつい回転のプログラムを早く書きたいと、読者は思いがちであろう。
==== すごく簡単な例から ====
 
しかし、さきに回転のプログラムを書いてしまうと、コード中の式が複雑になるので、デバッグがしづらくなる。
 
 
回転のプログラムを書くのは、後回しにしよう。
 
==== 回転に入る前の前準備 ====
===== すごく簡単な例から =====
まず、Windowsプログラミングで画像を表示したい場合、Windowa API の機能を使う。
 
571 ⟶ 579行目:
 
 
===== 被写体の奥行き方向に複数ある場合 =====
<pre>
□ 被写体1
619 ⟶ 627行目:
int hisyatai_onViewX = 200; int hisyatai_onViewY = 150; // これは単なるウィンドウ内での初期の表示位置の調整用
 
Rectangle(hdc, hisyatai_onViewX - (hisyatai1_Z / screen_z_Zahyou) * Camera_x_deffer, hisyatai_onViewY, hisyatai_onViewX+20 - (hisyatai1_Z / screen_z_Zahyou) * Camera_x_deffer, hisyatai_onViewY+30); // 基準の状態
 
 
627 ⟶ 635行目:
 
 
Rectangle(hdc, hisyatai_onViewX - (hisyatai2_Z / screen_z_Zahyou) * Camera_x_deffer, hisyatai_onViewY, hisyatai_onViewX + 10 - (hisyatai2_Z / screen_z_Zahyou) * Camera_x_deffer, hisyatai_onViewY + 20); // 基準の状態
 
EndPaint(hWnd, &ps);
646 ⟶ 654行目:
 
 
===== 被写体に近づく場合 =====
カメラが被写体に近づいた場合、投影面もカメラと一緒に近づく。
 
787 ⟶ 795行目:
裏を返せば、線分ABを平行にセットして、カメラをカニ歩きで左右に動かしてみたときに、もし線分ABの大きさが変わってしまえば、計算をミスってる。
 
===== 構造体にしよう =====
このあとに回転処理を導入すると式が複雑になってくるので、まだ式の簡単な今のうちに、構造体を導入したコードにして構造化しておこう。
 
874 ⟶ 882行目:
 
 
===== 上面図がデバッグに必要 =====
デバッグのために、上面図が必要です。上空から見下ろした視点でのカメラ位置と、カメラ向きと、被写体の位置とを図示した、上面図(じょうめんず、top view)が必要です。
 
894 ⟶ 902行目:
 
これらのプログラムの作成は簡単で、まずプログラムで矢印の作図プログラムでも書いておき、その矢印を、カメラ現在位置の変更にともなって矢印を移動させたり、カメラの向き変更にともなって、矢印の向きも同じ向きの同じ角度だけ回転させるプログラムを作ればいいだけです。
 
下記のような感じのコードを、WM_PAINT: 節に加えればいいだけです。
<syntaxhighlight lang="c">
 
 
int jyoumen_x = 300; int jyoumen_z = 100; // 上面図の基準点
 
MoveToEx(hdc, jyoumen_x + Camera_x, jyoumen_z + Camera_z, NULL); // 矢軸の矢先側
LineTo(hdc, jyoumen_x + Camera_x, 150 + Camera_z); // 矢の尻
 
MoveToEx(hdc, jyoumen_x + Camera_x, jyoumen_z + Camera_z, NULL); // 矢先
LineTo(hdc, jyoumen_x + Camera_x+10, jyoumen_z + Camera_z+10);// 矢先の右側
 
MoveToEx(hdc, jyoumen_x + hisyatai_n1p1_X, jyoumen_z + hisyatai_n1p1_Z, NULL); // 被写体1の片側
LineTo(hdc, jyoumen_x + hisyatai_n1p2_X, jyoumen_z + hisyatai_n1p1_Z);
 
 
MoveToEx(hdc, jyoumen_x + hisyatai_n2p1_X, jyoumen_z + hisyatai_n2p1_Z, NULL); // 被写体11の片側
LineTo(hdc, jyoumen_x + hisyatai_n2p2_X, jyoumen_z + hisyatai_n2p1_Z);
 
 
 
MoveToEx(hdc, jyoumen_x + 150, jyoumen_z + screen_z_Zahyou, NULL); // スクリーンの片側
LineTo(hdc, jyoumen_x + 300, jyoumen_z + screen_z_Zahyou);
 
</syntaxhighlight>
 
 
 
 
909 ⟶ 946行目:
 
 
 
==== 被写体は2個以上でプログラムしよう ====
 
===== 被写体は2個以上でプログラムしよう =====
今までの説明では、説明の単純化のために被写体の個数を1個に限ってきたが、実際にデバッグする場合には、被写体を位置を変えて2個にしてみて、カメラを移動してみて、正しく見えるかを確認してみよう。
 
968 ⟶ 1,007行目:
 
なお、コンピュータ処理では先に描画した画像は、あとから描画した画像によって上書きされる。なので、上記のカメラ例の配置の場合には、奥にある被写体1のほうを先に描画する必要がある。
 
 
* まとめ
ここまでの説明を、コードにすると、下記のようになるでしょう。まだ回転は導入してない状態です。
 
;コード例
<syntaxhighlight lang="c">
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// TODO: HDC を使用する描画コードをここに追加してください...
 
/* ここから上面図の描画コード */
int jyoumen_x = 300; int jyoumen_z = 100; // 上面図(マッパー)の基準点
 
MoveToEx(hdc, jyoumen_x + Camera_x, jyoumen_z + Camera_z, NULL); // 矢軸の矢先側
LineTo(hdc, jyoumen_x + Camera_x, 150 + Camera_z); // 矢の尻
 
MoveToEx(hdc, jyoumen_x + Camera_x, jyoumen_z + Camera_z, NULL); // 矢先
LineTo(hdc, jyoumen_x + Camera_x+10, jyoumen_z + Camera_z+10);// 矢先の右側
 
MoveToEx(hdc, jyoumen_x + hisyatai_n1p1_X, jyoumen_z + hisyatai_n1p1_Z, NULL); // 被写体1の片側
LineTo(hdc, jyoumen_x + hisyatai_n1p2_X, jyoumen_z + hisyatai_n1p1_Z);
 
 
MoveToEx(hdc, jyoumen_x + hisyatai_n2p1_X, jyoumen_z + hisyatai_n2p1_Z, NULL); // 被写体11の片側
LineTo(hdc, jyoumen_x + hisyatai_n2p2_X, jyoumen_z + hisyatai_n2p1_Z);
 
MoveToEx(hdc, jyoumen_x + 150, jyoumen_z + screen_z_Zahyou, NULL); // スクリーンの片側
LineTo(hdc, jyoumen_x + 300, jyoumen_z + screen_z_Zahyou);
 
 
/* ここから投影の座標計算のコード */
 
// 投影面の内での座標
// pro1 // 被写体1の第1点
touei_zahyou_list[1].x_zahyou = (screen_z_Zahyou - Camera_z) / (point_zahyou_list[1].z_zahyou - Camera_z) * (point_zahyou_list[1].x_zahyou - Camera_x) ;
touei_zahyou_list[1].y_zahyou = (screen_z_Zahyou - Camera_z) / (point_zahyou_list[1].z_zahyou - Camera_z) * (point_zahyou_list[1].y_zahyou - Camera_y) ;
 
 
// pro2X // 被写体1の第2点
touei_zahyou_list[2].x_zahyou = (screen_z_Zahyou - Camera_z) / (point_zahyou_list[2].z_zahyou - Camera_z) * (point_zahyou_list[2].x_zahyou - Camera_x) ;
touei_zahyou_list[2].y_zahyou = (screen_z_Zahyou - Camera_z) / (point_zahyou_list[2].z_zahyou - Camera_z) * (point_zahyou_list[2].y_zahyou - Camera_y) ;
 
 
 
// pro3 // 被写体2の第1点
touei_zahyou_list[3].x_zahyou = (screen_z_Zahyou - Camera_z) / (point_zahyou_list[3].z_zahyou - Camera_z) * (point_zahyou_list[3].x_zahyou - Camera_x) ;
touei_zahyou_list[3].y_zahyou = (screen_z_Zahyou - Camera_z) / (point_zahyou_list[3].z_zahyou - Camera_z) * (point_zahyou_list[3].y_zahyou - Camera_y) ;
 
// pro4 // 被写体2の第2点
touei_zahyou_list[4].x_zahyou = (screen_z_Zahyou - Camera_z) / (point_zahyou_list[4].z_zahyou - Camera_z) * (point_zahyou_list[4].x_zahyou - Camera_x) ;
touei_zahyou_list[4].y_zahyou = (screen_z_Zahyou - Camera_z) / (point_zahyou_list[4].z_zahyou - Camera_z) * (point_zahyou_list[4].y_zahyou - Camera_y) ;
 
 
/* ここから視界画像の描画処理のコード */
HBRUSH brasi_parts_2;
int hisyatai_onViewX = 350; int hisyatai_onViewY = 150;
 
// 奥の被写体は緑色にした
brasi_parts_2 = CreateSolidBrush(RGB(100, 255, 100)); // 壁の表示用の緑色のブラシを作成
SelectObject(hdc, brasi_parts_2); // ウィンドウhdcと、さきほど作成したブラシを関連づけ
 
Rectangle(hdc,
hisyatai_onViewX + touei_zahyou_list[3].x_zahyou, hisyatai_onViewY + touei_zahyou_list[3].y_zahyou,
hisyatai_onViewX + touei_zahyou_list[4].x_zahyou, hisyatai_onViewY + touei_zahyou_list[4].y_zahyou + 40); // 基準の状態
 
 
// 手前の被写体はピンクにした
brasi_parts_2 = CreateSolidBrush(RGB(100, 100, 255)); // 壁の表示用のブルー色のブラシを作成
SelectObject(hdc, brasi_parts_2); // ウィンドウhdcと、さきほど作成したブラシを関連づけ
 
Rectangle(hdc,
hisyatai_onViewX + touei_zahyou_list[1].x_zahyou, hisyatai_onViewY + touei_zahyou_list[1].y_zahyou,
hisyatai_onViewX + touei_zahyou_list[2].x_zahyou, hisyatai_onViewY + touei_zahyou_list[2].y_zahyou + 30); // 基準の状態
 
brasi_parts_2 = CreateSolidBrush(RGB(255, 100, 100)); // 壁の表示用のピンク色のブラシを作成
SelectObject(hdc, brasi_parts_2); // ウィンドウhdcと、さきほど作成したブラシを関連づけ
 
 
EndPaint(hWnd, &ps);
}
break;
 
case WM_KEYDOWN:
 
switch (wParam) {
case VK_UP: // 「上キーが入力されたら」という意味
Camera_z = Camera_z -5;
screen_z_Zahyou = screen_z_Zahyou - 5;
 
break; // これは VK_UP: からのbreak
case VK_DOWN:
Camera_z = Camera_z + 5;
screen_z_Zahyou = screen_z_Zahyou + 5;
 
break; // これは VK_DOWN: からのbreak
case VK_RIGHT:
Camera_x = Camera_x + 10;
 
break; // これは VK_RIGHT からのbreak
case VK_LEFT:
 
Camera_x = Camera_x - 10;
 
break; // これは VK_LEFT: からのbreak
} // これは switch (wParam) の終わりのカッコ
 
InvalidateRect(hWnd, NULL, TRUE);
UpdateWindow(hWnd);
 
 
break; // これは WM_KEYDOWN: からのbreak
 
</syntaxhighlight>
 
 
 
このほかグローバル変数に必要な構造体および構造体変数の定義をしておいてください。また、 wWinMain 節などに、それぞれの構造体変数の初期値を代入しておいてください。