UVマッピング


概要

UVマッピングとはテクスチャマッピングの1種で、
3Dモデル等のポリゴンの各頂点にテクスチャのUV値の指定を行い、
その指定されたUV値からテクスチャをポリゴンにマッピングして描画します。
このUVマッピングを使用して「UVアニメーション」「UVスクロール」といった
ゲームでよく使用される手法を使うことができます。

gmpg_0221

UV座標軸

UV座標軸は左上を原点とした縦、横の2直線が交差する座標軸で、
横方向をU軸、縦方向をV軸と呼んでいます。
そのため、UV座標軸で設定したテクスチャ座標のことを
「UV値」や「UV座標」と呼びます。

gmpg_0222

UVの範囲

下の画像のように1枚のテクスチャのUV値は
縦横ともに全て「0.0~1.0」の範囲とされています。

gmpg_0223

ピクセル座標からUV座標変換

3Dモデルの場合は各頂点にマッピングするUV値はMaya等のモデルツールが
行ってくれますが、2Dゲームの統合画像の一部分をマッピングしたい場合など、
UV値の計算を開発者側でしなくてはいけない事もあります。
その際にピクセル単位の座標であるテクセル座標からUV座標に変換が必要です。
以下はその変換の計算式です。

gmpg_0224

例として以下の画像の一番左のキャラクターのみ四角形ポリゴンに
UVマッピングするための各頂点のUV値をテクセル座標から変換してみます。

gmpg_0225

UVアニメーション

UVアニメーションはUVマッピングする矩形を一定フレーム毎に変更する手法です。
この手法を使用することで、キャラクターやエフェクトのアニメーションを表現できます。

gmpg_0226

ループ

アニメーションのループはアニメーションが最後に到達した際に
そのままにせずに先頭や途中の矩形に戻すことで可能です。

gmpg_0227

実装方法

アニメーションの実装にはアニメーションの矩形情報などのデータ側と
それを使用する再生側の実装が必要となります。

データ側

データ側は「矩形情報」「矩形切り替え時間」「次の矩形に関する情報」が
必要なので、それが保存できる構造体やクラスを用意します。

struct UVAnimationRect
{
	// 矩形に必要な情報
	float m_TexturePosX;		//!< テクスチャ座標X
	float m_TexturePosY;		//!< テクスチャ座標Y
	float m_RectWidth;		//!< 矩形横幅
	float m_RectHeight;		//!< 矩形縦幅
	TextureList m_TextureId;	//!< 対象のテクスチャID

	// アニメーションに必要な情報
	int m_ChangeFrame;		//!< 切り替えフレーム
	intt m_NextRectId;		//!< 次のアニメーションID
};


用意したデータ構造を使用して「待機」や「移動」など、
各アニメーション単位のデータを用意します。

// 待機アニメーション
UVAnimationRect CharacterIdleAnimations[] =
{
	{ 0.0f, 0.0f, 128.0f, 128.0f, TextureList::CharacterTex, 5, 1 },
	{ 128.0f, 0.0f, 128.0f, 128.0f, TextureList::CharacterTex, 5, 2) },
	{ 256.0f, 0.0f, 128.0f, 128.0f, TextureList::CharacterTex, 5, 3) },
	{ 374.0f, 0.0f, 128.0f, 128.0f, TextureList::CharacterTex, 5, 0) },
};

// 移動アニメーション
UVAnimationRect CharacterRunAnimations[] =
{
	{ 0.0f, 128.0f, 128.0f, 128.0f, TextureList::CharacterTex, 5, 1 },
	{ 128.0f, 128.0f, 128.0f, 128.0f, TextureList::CharacterTex, 5, 2 },
	{ 256.0f, 128.0f, 128.0f, 128.0f, TextureList::CharacterTex, 5, 3 },
	{ 374.0f, 128.0f, 128.0f, 128.0f, TextureList::CharacterTex, 5, 4 },
	{ 0.0f, 256.0f, 128.0f, 128.0f, TextureList::CharacterTex, 5, 0 },
};


この各アニメーション情報をまとめておき、そのまとめた内容をもとにして
enumなどの定数を用意すると管理が楽になります。

// アニメーションリスト
enum AnimationIds
{
	CharacterIdleAnimation,	//!< キャラクター待機
	CharacterRunAnimation,	//!< キャラクター移動
	MaxAnimationIds,
};

// アニメーションリスト
UVAnimationRect* AnimationsList[] =
{
	CharacterIdleAnimations,
	CharacterRunAnimations,
};

再生側

再生は必要な情報は「再生中のアニメーション、矩形、時間」です。
この情報をPlayerやEnemyなどの様々なオブジェクトが使用することになるので、
汎用的に使えるようにしておいた方が楽です。
今回は再生用の構造体と再生実行関数で実装しています。

// アニメーション再生用構造体
struct AnimationPlayer
{
	AnimationIds m_AnimationId;		//!< アニメーションID
	UVAnimationRect* m_Animations;		//!< アニメーション矩形情報
	int m_Frame;				//!< フレーム数
	int m_CurrentId;			//!< m_Animationsのインデックス
};

bool UpdateAnimation(AnimationPlayer* player)
{
	bool is_finish = false;

	player->m_Frame++;

	if (player->m_Frame >= player->m_Animations[player->m_CurrentId].m_ChangeFrame)
	{
		player->m_Frame = 0;
		if (player->m_Animations[player->m_CurrentId].m_NextAnimationId != -1)
		{
			player->m_CurrentId = player->m_Animations[player->m_CurrentId].m_NextAnimationId;
		}
		else
		{
			// アニメーション終了
			is_finish = true;
		}
	}

	return is_finish;
}

UVスクロール

UVスクロールは設定されているUV座標に対しの加算または減算を行い
画像がスクロールしているようにみせる方法です。

gmpg_0083

UVスクロールの種類

UVスクロールには種類がいくつかあり、その種類によって0.0~1.0の範囲を
越えた際の描画の内容が変わります。
スクロールの種類は「ラップ」「ミラー」「クランプ」「ボーダー」があります。
※サンプルではDirectX9を使用しているので、設定の説明もDirectX9で行っています。

設定方法

DirectXでの設定はIDirect3DDeviceのSetSamplerStateを使用します。

SetSamplerState
内容 Samplerの設定を行う
戻り値 初期化の成否(HRESULT)が返る
引数の型 説明
DWORD Stage サンプラステージ
D3DTEXTURESTAGESTATETYPE Type サンプラステートの設定
pValueの影響がU軸やV軸などの設定が可能
DWORD pValue ラッピングモードの種類

ラップ

ラップはUV座標が0.0~1.0の範囲を超えた場合、
範囲を超えた領域はテクスチャを繰り返し描画します。
SetSamplerStateの設定では「D3DTADDRESS_WRAP」を設定します。

gmpg_0084

ミラー

ミラーはUV座標が0.0~1.0の範囲を超えた場合、
上下または左右反転したテクスチャで範囲を超えた領域の描画を行います。
SetSamplerStateの設定では「D3DTADDRESS_MIRROR」を設定します。

gmpg_0085

クランプ

クランプはUV座標が0.0~1.0の範囲を超えた場合、
終端の色情報を使用して範囲を超えた領域の描画を行います。
SetSamplerStateの設定では「D3DTADDRESS_CLAMP」を設定します。

gmpg_0086

ボーダー

ボーダーはUV座標が0.0~1.0の範囲を超えた場合、
特定の色で範囲を超えた領域の描画を行います。
ボーダーのみSetSamplerStateの設定は2つ必要になります。
まず通常と同様に「D3DTADDRESS_BORDER」を設定します。
次にSetSamplerState(0, D3DSAMP_BORDERCOLOR, 領域を埋めたい色)で
描画で使用する色を決めます。

gmpg_0087