Objファイルのインデックスバッファと4頂点対応

概要

最終更新日:2020/02/28

Objファイルのインデックスバッファと4頂点ポリゴンの対応について書いた記事です。
主に次の項目に該当する方に向けて書いています。
※頂点や法線、マテリアルの読み込みが完了していることを前提に書いているので
 まだの方は「頂点と法線読み込み」「マテリアル読み込み」
「UVマッピング」を確認してください。
  • インデックスバッファをしっかりと使いたい
  • 4頂点ポリゴンをちゃんと描画したい

サンプル

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

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


インデックスバッファ対応

インデックスバッファは重複した頂点バッファの情報を省くためのバッファです。
その為、以下のような単純な方法で頂点バッファの番号を追加しても
インデックスバッファを作成する意味がありません。

// 頂点バッファに追加
out_custom_vertices.push_back(vertex);
// インデックスバッファに追加		
m_Indices[current_material].push_back((UINT)out_custom_vertices.size() - 1);

重複している頂点情報を省略する方法は、既に同じ情報頂点が登録されていたら
登録済みの頂点をインデックスバッファに登録します。
この時、重複していると判断する頂点情報は座標、法線、UV座標の
全てが同じである必要があります。
例えば以下のような頂点情報があった場合、座標だけで重複と判断してしまうと
法線とUV座標が異なるので、描画の内容がおかしくなってしまいます。

座標 法線 UV座標
(100, 200, 300) (1.0, 0.0, 0.0) (0.0, 0.0)
(100, 200, 300) (-1.0, 0.0, 0.0) (0.5, 0.7)
次の絵は左が座標だけで重複と判断した立方体モデル、 右が座標、法線、UV座標で重複判断をした立方体モデルですが、 左の立方体のUVマッピングがおかしいことが分かります。 model_render_0014 このようにならないように重複頂点の判断をする際は 座標、法線、UV座標が全て同じ情報かどうかを調べます。 サンプルの手順は以下の通りです。
  1. 重複調査用の配列を用意する
  2. 調査用の頂点情報の作成
  3. 重複チェック

重複調査用の配列を用意する

重複している頂点を調べるための配列をmapで用意します。

// 頂点バッファに登録されている頂点の要素番号の配列
std::map<std::string, int>& entry_vertex_ids

キーが調査用の頂点情報(以下、調査頂点情報)で値が頂点バッファの番号です。

調査用の頂点情報の作成

objファイルの「f」キーワードで、頂点座標、法線、UV座標の取得が完了したら、
それらの情報を使用して調査頂点情報を作成します。
調査頂点情報は各情報の要素番号を連結させた文字列です。

for (int i = 0; i < 3; i++)
{
	std::ostringstream sout;
	sout << std::setfill('0') << std::setw(5) << vertex_info[i];
	key += sout.str();
}

上のコードでは各要素の番号を0埋めした5桁の文字列として扱い、
それらを連結をして一つの文字列を作成しています。
    頂点座標要素番号 => 10
    法線要素番号 => 5
    UV座標要素番号 => 2
    連結文字列 => "000100000500002"
この文字列をmapのキーとして利用します。

重複チェック

キーが完成したら配列に登録されているかどうかを調べます。

// 重複チェック
if (index_list.count(key) > 0)
{
	// 登録されている頂点バッファの要素番号をインデックスバッファに保存する
	m_Indices[current_material].push_back(index_list[key]);
}
else
{
	// 頂点情報追加
	out_custom_vertices.push_back(vertex);

	// 頂点バッファの要素番号追加
	m_Indices[current_material].push_back((UINT)out_custom_vertices.size() - 1);
	
	// 頂点バッファの要素番号追加
	index_list[key] = out_custom_vertices.size() - 1;
}

その結果、登録されてたら登録されている頂点番号をインデックスバッファに保存、
登録されていなかったら、新規追加となるので以下の項目に情報を追加します。
  • 頂点バッファ => 頂点情報
  • インデックスバッファ => 頂点バッファの要素番号
  • 調査頂点情報 => 頂点バッファの要素番号
これでインデックスバッファの頂点番号に重複している頂点情報が
追加されることはなくなります。

4頂点ポリゴン対応

DirectXの3Dモデル描画は3頂点で1ポリゴンとして扱うことが多いのですが、
3Dモデルデータは4頂点を1ポリゴンとして作られているデータがあります。
4頂点ポリゴンを3頂点として描画すると、当然ながらモデルの一部がかけた状態で
描画されてしまいます。

model_render_0015

次の項目からこの問題の対応方法の説明をします。

対応方法

対応方法は以下のとおり、非常に単純です。
    4頂点で二つのポリゴンを用意して、インデックスバッファに追加する
4頂点ポリゴンの問題点はインデックスバッファにポリゴンが足りないことです。
ですが、頂点情報はObjファイル内に用意されていますし、
足りないポリゴンの頂点構成についても4頂点ポリゴンの中にヒントがあります。
それらを駆使して足りないポリゴンを追加すれば描画は上手くいきます。

model_render_0016

ポリゴンの法則

Objファイルは右手系で作られているので、「f」キーワードの頂点の順番は
左回りで並べられています。

model_render_0017

この法則を利用して二つのポリゴンを用意します。
まず、一つ目のポリゴンは頂点の順番通り「① => ② => ③」です。

model_render_0018

二つ目は④の頂点を始点として「④ => ① => ③」とします。

model_render_0019

「④ => ① => ②」ではないので、注意してください。
この順番でポリゴンを作成すると二つのポリゴンで四角形が作れません。

model_render_0020

対応

ポリゴンの法則から新しいポリゴンを作るためには「④ => ① => ③」の
頂点番号をインデックスバッファに追加をします。
サンプルでは以下のように①~④まで追加されています。

model_render_0021

新規で追加を行うのは①と③の頂点です。

model_render_0022

①と③はインデックスバッファの配列番号「要素数 - 4」と「要素数 - 2」の
位置にあるので、それらをインデックスバッファに追加します。

// 「f」キーワードの分解結果が3を超えた場合は頂点を追加する
if (space_split.size() > 3)
{
	m_Indices[current_material].push_back(m_Indices[current_material][size - 4]);
	m_Indices[current_material].push_back(m_Indices[current_material][size - 2]);
	add_index_count += 2;
}

これで、4頂点で二つ分のポリゴンのインデックスバッファに追加できました。
あとは、このインデックスバッファを使用して描画を行うだけですが
描画は特に変更の必要はありません。

model_render_0023