UVマッピングしたObjファイルを描画する

概要

最終更新日:2020/02/27

UVマッピングを行ったObjファイルの描画方法を書いた記事です。
主に次の項目に該当する方に向けて書いています。
※頂点や法線、マテリアルの読み込みが完了していることを前提に書いているので
 まだの方は「頂点と法線読み込み」「マテリアル読み込み」を確認してください。
  • objファイルでテクスチャマッピングする描画方法が知りたい
  • ポリゴン情報にテクスチャ座標を保存させたい
  • なぜ、テクスチャ座標のY軸を反転させるのかを知りたい
  • マテリアルでどのテクスチャが使用されているのかを知りたい

サンプル

サンプルはここからダウンロードできます。
環境については以下の内容となっています。

開発環境
VSのバージョン VisualStudio 2019
DirectXのバージョン DirectX11


ファイル読み込み

UVマッピングを行ったObjファイルを描画する際のファイル読み込みは
以下のポイントを抑えて行います。
  • テクスチャ座標をポリゴンに保存する
  • 使用するテクスチャを読み込む

テクスチャ座標をポリゴンに保存する

Objファイルのテクスチャ座標(以下、UV座標)の情報は「vt」キーワードで設定されています。
vtの後ろに続く文字列がUV座標を表しており、この値をポリゴンに保存します。
vtの書式は以下の通りです。

// 書式
vt U座標 V座標

// 具体例
vt 0.008533 0.983134

次は解析コードです。

// テクスチャ座標
else if (buffer[1] == 't')
{
	ParseVKeywordTag(textures, &parse_point[1]);
	// V軸を反転させる
	textures[textures.size() - 1].Y = (1.0f - textures[textures.size() - 1].Y);
}

V軸を反転している理由は次の項目で説明します。

UV座標のV値を反転する理由

DirectXではObjファイルを描画する場合、UV座標のV値を反転するために
以下の計算を行っています。
    DirectXのUV値 = (ObjファイルのU値, 1.0 - ObjファイルのV値)
この計算をなぜ行っているかというと、Objファイルに展開されたUV値は
原点が左下で設定されているからです。

model_render_0009

DirectXは左上原点です。

model_render_0010

この違いを修正するために上の計算を行っています。

model_render_0011

使用するテクスチャを読み込む

テクスチャを読み込むためにファイル名が必要です。
このファイル名はmtlファイルの「map_Kd」キーワードに設定されています。
map_Kdの後ろに続く文字列がテクスチャのファイル名を表しています。

// 書式
map_Kd テクスチャ名

// 具体例
map_Kd character.png

以下は解析コードです。
ファイル名を取得してパスを追加し、読み込みを行っています。
XFileやFBXでもそうですが、3Dモデルのテクスチャの読み込みは
自動で行われないので、開発側で行う必要があります。

else if (strstr(buffer, "map_Kd") == buffer)
{
	Replace('\n', '\0', buffer);
	std::string texture_name = &buffer[strlen("map_Kd") + 1];
	// ファイルパス込みで保存
	m_Materials[current_material_name].TextureName = file_path + texture_name;

	std::vector split;
	Split('.', (char*)texture_name.c_str(), split);
	if (split.size() > 1)
	{
		m_Materials[current_material_name].TextureKeyWord = split[0];
		
		// 読み込み
		LoadTexture(split[0], m_Materials[current_material_name].TextureName);
	}
}

以上がUVマッピングしたモデル描画の読み込み時に必要な処理でした。

描画方法

UVマッピングしたObjファイルの描画は以下のポイントを抑えて行います。
  • InputLayoutにUV座標を追加
  • マテリアル単位でテクスチャを設定する
  • シェーダーにテクスチャカラー反映計算を追加

InputLayoutにUV座標を追加

テクスチャを描画するには、InputLayoutにUV座標を追加します。

D3D11_INPUT_ELEMENT_DESC vertex_desc[]{
	{ "POSITION",	0, DXGI_FORMAT_R32G32B32_FLOAT,		0,	0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
	{ "NORMAL",	0, DXGI_FORMAT_R32G32B32_FLOAT,		0,	D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
	// UV座標追加
	{ "TEXTURE",	0, DXGI_FORMAT_R32G32_FLOAT,		0,	D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
};

//頂点レイアウト作成
if (FAILED(device->CreateInputLayout(
	vertex_desc,			// レイアウト設定
	ARRAYSIZE(vertex_desc),		// 配列サイズ
	vertex_shader->GetData(),	// レイアウトと関連付ける頂点シェーダのデータ
	vertex_shader->GetSize(),	// レイアウトと関連付ける頂点シェーダのサイズ
	&m_InputLayout)))		// 作成された頂点レイアウトの格納先
{
	return false;
}

return true;

マテリアル単位でテクスチャを設定する

テクスチャはマテリアル単位で設定されているので、
マテリアル毎にテクスチャの設定を「SetTexture」を使用して行います。

// インデックスバッファの数 = マテリアルの数だけメッシュを描画する
for (auto index : m_Indices)
{
	// 途中省略
		
	// マテリアルにテクスチャがあるなら設定する
	if (m_Textures.count(m_Materials[index.first].TextureKeyWord) > 0)
	{
		graphics->SetTexture(m_Textures[m_Materials[index.first].TextureKeyWord]);
	}
	else
	{
		graphics->SetTexture(nullptr);
	}
}

シェーダーにテクスチャカラー反映計算を追加

テクスチャの設定が完了したら、頂点やピクセルでテクスチャカラーを
反映する処理を追加します。
まずは、頂点シェーダのコードです。

// Texture指定
output.texture_pos = input.texture_pos;

次はピクセルシェーダのコードです。

// テクスチャ座標から色情報を取得
float4 tex_color = Texture.Sample(Sampler, input.texture_pos);

これで、UVマッピングしたObjファイルの描画のために必要な処理の追加は終了です。

model_render_0012