画像の描画(テクスチャ・マッピング)

この章で使用するプログラムおよび画像ファイル

描画処理の考え方

先に学んだとおり、DirectX Graphicsではポリゴンの描画しか行えない。例えばビットマップファイルなどに用意したの画像を、バック・バッファの任意の位置(座標)に転送する(書き込む)ことはできない。
ただし、画像をポリゴンに貼り付けることはできる。つまり、DirectX Graphicsにおける画像の描画とは、画像を貼り付けたポリゴンをバック・バッファに転送することにより行うのである。

DirectX Graphicsでは、ビットマップ・ファイルをビデオ・メモリ上に読み込んでから利用する。ビデオ・メモリ上に読み込まれた画像データをテクスチャと呼ぶ。また、テクスチャをポリゴンに貼り付けることをテクスチャ・マッピングと呼ぶ。

ここでは、テクスチャの作成方法やテクスチャ・マッピングを利用した描画方法について説明する。

テクスチャの基礎知識

テクスチャ・マッピングを行うには、ポリゴンの頂点の位置にテクスチャ画像のどの位置をマッピングするかを設定しなければならない。このテクスチャ画像の位置をテクスチャ座標と呼ぶ。

DirectX Graphicsでは、テクスチャ画像の左上座標を原点として、横方向をtu、縦方向をtvとし、それぞれ0.0~1.0の浮動小数点(実数)で表現する。つまり、右下座標のテクスチャ座標はtu=1.0、tv=1.0である。

例えば32×32ピクセルのポリゴンにテクスチャを貼り付けたいとき、ポリゴンの頂点A,B,C,Dそれぞれにtu、tvの値を指定することになる。

上図のように頂点A,B,C,Dそれぞれに該当するテクスチャのtu、tvの値を設定すると、次の値になる。

これにより、テクスチャ・マッピングにおいて、テクスチャサイズが縦横何ピクセルなのかを気にする必要はないことが分かる。(ただし、ポリゴンの作成時には、テクスチャのサイズに合わせてポリゴンの大きさを指定しなければならない)

テクスチャ画像の作成

テクスチャは、IDirect3DTexture8インターフェイスで表わし、テクスチャを作成するには、IDirect3DDevice8::CreateTextureメソッドを使う。このメソッドを使ってテクスチャを作成し、その後、テクスチャの画像をテクスチャのサーフェイスに書き込むことでテクスチャを使うことになる。
しかし、Direct3DXユーティリティ・ライブラリには、画像ファイルからテクスチャ画像を簡単に作ることができるライブラリがあらかじめ用意されているため、この講座ではそちらを利用してテクスチャ画像を作成することにする。

テクスチャの作成方法は次のとおり。

  1. テクスチャ・オブジェクトの宣言
  2. 画像データを読み込み、テクスチャ・オブジェクトを生成
  3. テクスチャ座標をセット
  4. ポリゴンへ貼り付け

四角形ポリゴンを描画するプログラムに、上記修正を行う手順を解説する。前準備として、この画像ファイルをダウンロードし、WinMain.cppファイルと同じ場所に配置すること。

1.テクスチャ・オブジェクトの宣言

テクスチャ・オブジェクトをグローバル変数として次のように宣言する。

// グローバル変数
HWND hWnd;                              // ウィンドウハンドル
BOOL g_appActive = FALSE;               // ウィンドウの状態
char szWinName[] = "Exer004";           // ウィンドウクラス用文字列
char szWinTitle[] = "DirectX Graphicsでの2D表示"; // ウィンドウクラス用文字列
LPDIRECT3D8 gl_lpD3d = NULL;            // Direct3D8インターフェイス
LPDIRECT3DDEVICE8 gl_lpD3ddev = NULL;   // Direct3DDevice8インターフェイス
D3DPRESENT_PARAMETERS gl_d3dpp;         // ディスプレイパラメータ
LPDIRECT3DTEXTURE8 gl_Texture = NULL;   // テクスチャ・オブジェクト
BYTE g_FrameNo = START_INIT;            // フレーム選択用
BYTE gl_KeyTbl[256];                    // キーボードの状態を格納
TLVERTX VertexDataTbl[4];               // 頂点情報配列

2.スタート画面用のテクスチャ・オブジェクトを生成

画像データを読み込んでテクスチャを作成する。ファイルから読み込む方法と、リソースから読み込む方法の2通りがあるがここでは、ファイルから読み込んでテクスチャを作成する方法を解説する(リソースから読み込む方法は別項で解説する)。

ウィンドウが復帰した際にテクスチャを再生することを考え、スタート画面で使用するテクスチャをまとめて作成する関数(CreateStartTexture()関数)を作成する。プロトタイプ宣言も行うこと。
この関数を、スタート画面初期化処理(StartInit()関数)から呼び出す。

//-----------------------------------------------------------------------------
// 関数名 : CreateStartTexture()
// 機能概要: スタート画面用テクスチャの作成・再生成
//-----------------------------------------------------------------------------
void CreateStartTexture(void)
{
    HRESULT hr;

    // キャラクタ1
    hr = D3DXCreateTextureFromFile(gl_lpD3ddev, "sample1.bmp", &gl_Texture);
    if ( FAILED(hr) ) {
        MessageBox(hWnd, "sample1.bmp をテクスチャとして読み込めませんでした", "ERROR", MB_OK);
        return;
    }

}

《POINT》

《D3DXCreateTextureFromFile関数》

ファイルから画像データを読み込み、テクスチャを作成する。引数は3つ。

Direct3DXで扱うことのできる画像フォーマットは、ビットマップ形式(BMP形式)、TGA形式(α付きビットマップ)、DDS形式(DirectDrawサーフェイス形式)の3種類。これらの画像ファイルからテクスチャ用のサーフェイスを自動的に生成し、サーフェイスに画像を読み込む。
正常に読込ができると、戻り値に定数「D3D_OK」が返るので、それ以外の値ならエラーを表示する。

3.スタート画面初期化処理関数から、テクスチャ作成関数を呼び出す

スタート画面初期化処理関数(StartInit()関数)から、先ほど作成したスタート画面用テクスチャ作成関数(CreateStartTexture()関数)を呼び出す。

//-----------------------------------------------------------------------------
// 関数名 : StartInit()
// 機能概要: スタート画面初期化処理
//-----------------------------------------------------------------------------
void StartInit(void)
{
    //--------------------------------------------------- 各変数の初期化
    // スタート画面で使用するテクスチャの作成
    CreateStartTexture();
    // 頂点データを格納する
    InitVertex(VertexDataTbl, 100.0f, 100.0f, 400.0f, 400.0f);

    //--------------------------------------------------- フレームナンバーセット
    g_FrameNo = START_FRAME;

}

4.テクスチャ座標をポリゴンに設定

テクスチャの各座標を、ポリゴンのどの座標に一致させるかを設定する。

//----------------------------------------------------------------------------------------
// 関数名 : InitVertex() 
// 機能概要: 頂点データを格納する
//----------------------------------------------------------------------------------------
void InitVertex(LPTLVERTEX v, float x1, float y1, float x2, float y2)
{
    //頂点配列をゼロクリア
    ZeroMemory(v, sizeof(TLVERTX));
    //---頂点A1のデータ定義
    v[0].x      =  x1;                         // 頂点X座標
    v[0].y      =  y1;                         // 頂点Y座標
    v[0].z      =  0.0f;                       // 頂点Z座標
    v[0].rhw    =  1.0f;                       // 2Dを扱うときの値
    v[0].tu     =  0.0f;                       // テクスチャのX座標
    v[0].tv     =  0.0f;                       // テクスチャのY座標
    v[0].color  =  D3DCOLOR_XRGB(255,255,255); // 頂点色
    //---頂点A2のデータ定義
    v[1].x      =  x2;
    v[1].y      =  y1;
    v[1].z      =  0.0f;
    v[1].rhw    =  1.0f;
    v[1].tu     =  1.0f;
    v[1].tv     =  0.0f;
    v[1].color  =  D3DCOLOR_XRGB(255,255,255);
    //---頂点A3のデータ定義
    v[2].x      =  x2;
    v[2].y      =  y2;
    v[2].z      =  0.0f;
    v[2].rhw    =  1.0f;
    v[2].tu     =  1.0f;
    v[2].tv     =  1.0f;
    v[2].color  =  D3DCOLOR_XRGB(255,255,255);
    //---頂点A4のデータ定義
    v[3].x      =  x1;
    v[3].y      =  y2;
    v[3].z      =  0.0f;
    v[3].rhw    =  1.0f;
    v[3].tu     =  0.0f;
    v[3].tv     =  1.0f;
    v[3].color  =  D3DCOLOR_XRGB(255,255,255);
}

《POINT》

5.テクスチャ画像をポリゴンへ貼り付けて描画する

頂点データにテクスチャ座標が正しく設定されていれば、テクスチャをデバイスにセットするだけで、テクスチャ・マッピングを行うことができる。よって、ポリゴンを描画する直前に、描画したいテクスチャをデバイスにセットする。

//-----------------------------------------------------------------------------
// 関数名 : StartFrame()
// 機能概要: スタート画面処理
//-----------------------------------------------------------------------------
void StartFrame(void)
{

    /* 描画処理 */
    // 描画するテクスチャをデバイスにセット
    gl_lpD3ddev->SetTexture(0, gl_Texture);
    // 四角形ポリゴンをバックバッファに転送
    gl_lpD3ddev->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, VertexDataTbl, sizeof(TLVERTX));

    // リターンキーが押されたら、ゲーム開始
    if ( gl_KeyTbl[VK_RETURN] & 0x80 ) g_FrameNo = GAME_INIT;

}

《POINT》

6.テクスチャ・オブジェクトの開放

作成したオブジェクトは、プログラム終了時に必ず開放しなければならない。とりあえず、DirectX8オブジェクトの開放処理関数(ReleaseD3D関数)でテクスチャ・オブジェクトを開放するように修正する。

//-----------------------------------------------------------------------------
// 関数名 : ReleaseD3D()
// 機能概要: DirectX8オブジェクトの開放
//-----------------------------------------------------------------------------
void ReleaseD3D(void)
{
    // テクスチャの開放
    if ( gl_Texture != NULL ) {
        gl_Texture->Release();
        gl_Texture = NULL;
    }
    // デバイスオブジェクトの開放
    if ( gl_lpD3ddev != NULL ) {
        gl_lpD3ddev->Release();
        gl_lpD3ddev = NULL;
    }
    // DirectX8オブジェクトの開放
    if ( gl_lpD3d != NULL ) {
        gl_lpD3d->Release();
        gl_lpD3d = NULL;
    }
}

《POINT》

※動作確認

  1. (5)で作成したプログラムに上記修正を行い、ポリゴン上に画像が表示されることを確認する
  2. ポリゴンの大きさを変更し、どのように表示されるかを確認する

リソースから読み込んだテクスチャの描画(参考)

画像データをファイルからではなくリソースから読み込むためには、次のような手順でリソースに画像を格納する必要がある。

  1. プロジェクトで使用するリソースを作成し、画像ファイルを登録する
  2. プログラムからリソースを呼び出す

プロジェクトで使用するリソースを作成し、画像ファイルを登録する

プロジェクトを作成したら、次の要領でリソースを作成する。

1.テクスチャデータをリソースとしてプロジェクトに組み込む
「挿入」→「リソース」を選択し、「リソースのタイプ」ウィンドウでBitmapを選択して、新たに画像を作るなら「新規作成」、すでにある画像を読み込むなら「インポート」をクリックする。
2.画像データの作成
「新規作成」ならエディタが開くので、画像を作成する。
3.リソース名を付ける
アイコンリソース名を、デフォルトの名前からダブルクォーテーションを付けた"XXXXXX"の形(例、"TEX01")に変更する。リソースを右クリックし、プロパティを開き、IDを変更する。
4.リソースファイルを保存
「保存」ボタンをクリックし、「Script1.rc」という名前(デフォルト)で保存。保存場所はWinMain.cppと同じ場所になるように注意する。
5.作成したりソースをプロジェクトへ追加
「プロジェクト」→「プロジェクトへ追加」→「ファイル」→「ファイルの選択」で、作成したリソースファイル「Script1.rc」とヘッダファイル「resource.h」を選択。

プログラムからリソースを呼び出す

リソースを作成したら、プログラムを修正してリソースを呼び出すようにする。

1.リソースファイルの組み込み
「resource.h」のヘッダーファイルを読み込むよう、プログラムに#include "resource.h"を追加。
2.リソースから画像データを読み込み、テクスチャを作成
リソースから画像データを読み込むには、D3DXCreateTextureFromResource関数を使用する。TEX01というリソース名で画像データを作成した場合、次のように作成する。

D3DXCreateTextureFromResource(gl_lpD3ddev, NULL, "TEX01", &gl_Texture);

リソースを使う場合のメリットとデメリットについて

プログラムをビルドすると、実行可能ファイル(exeファイル)が作成される。通常は、その実行可能ファイルを別のマシンに配置し、ダブルクリックすればプログラムが実行される。これにより、作成したゲームを配布することができる。
しかし、画像データや音データなどを扱う場合は注意しなければならない。何故なら、それらのファイルをプログラム上でファイル名を直接指定して利用する場合と、リソースとして登録して利用する場合では、データの配布の仕方が違うからだ。

ファイル名を直接指定して画像などのデータファイルを呼び出す場合、配布する際には実行可能ファイルのほかに、すべてのデータファイルを用意しなければならない。しかも、ファイル名を指定する際にはファイルの場所(パス)を指定しなければならないため、その指定したパス以外の場所にデータファイルを配置してしまうと、プログラムから利用することはできない。この章で作成したプログラムの場合、ファイル名を指定する際にパスは指定していないため、配布の際には実行可能ファイルと同じ場所にデータファイルが存在しなければならない。

それに対してデータファイルをリソースで管理する場合、リソースに登録されたデータはすべてビルド時に実行可能ファイルに組み込まれる。よって、いくつデータファイルがあろうと、配布時に必要なファイルは実行可能ファイルだけであり、リソースを作成する元となった各データファイルは必要なくなる。ただし、データファイルが複数ある場合、実行可能ファイルのサイズは大きくなってしまう。

実行可能ファイルのサイズが小さければ、プログラム実行時に読み込むデータ量は少なくて済み、すばやい起動が可能である。しかし、サイズが大きければ、それだけ読み込みに時間がかかってしまう。ただし、一度メモリに読み込んでしまえば2度とハードディスクのアクセスは必要なくなるため、例えばテクスチャを作成する際、ファイルを直接指定している場合はハードディスクから一度メイン・メモリに読み込み、その後、ビデオ・メモリに読み込まれるが、リソースから読み込む場合はプログラム実行時に既にメイン・メモリに読み込まれているため、ビデオ・メモリに読み込むだけでよい。

このように、ファイルを指定する場合とリソースを使用する場合では、それぞれメリットとデメリットが存在する。自分が作りたいゲームの規模と内容に応じてCase by Caseで使い分けよう。


BACK(頂点情報初期化関数について) NEXT(複数のキャラクタを表示する)