Nanashi-softプログラマ専用DirectX11開発


◇DirectX11プログラミング -マテリアルとテクスチャー混在対策は?-

テクスチャーにはもう1つ大きな問題があります
マテリアルとテクスチャーとでは描画時のピクセルシェーダーHLSLが異なります

ピクセルシェーダーを統合しようと考えると,インプットレイアウトにフラグを追加して,描画処理を分岐させなければなりません
それでも良いのですが,HLSLプログラム中に不用意にif文などは入れたくありません

そこで考えた方法なのですが
PMD形式は,↓材質リストというカテゴリがあります
	//材質リスト
unsigned long material_count;
struct t_material{
float diffuse_color[3];
float alpha;
float specularity;
float specular_color[3];
float mirror_color[3];
unsigned char toon_index;
unsigned char edge_flag;
unsigned long face_vert_count;
char texture_file_name[20];
};
t_material *material;
例えば,髪は青,服は灰色と言うように,色毎にポリゴンデータが分けられています
その中には,テクスチャーファイル名を指定する項目もあります

この材質リスト単位で分割して描画するようにすれば,マテリアルとテクスチャーを分離して処理することができます


○処理の流れ
mainクラス

pmdクラスでモデルデータロード

材質リストをマテリアルカウント分に分ける

描画時にピクセルシェーダーをスイッチしながら,インデックスデータを送る


材質リストの扱いとか,この部分だけを抜き出すのは難しいので,新システムのプログラムから抜粋すると,
初期化で,PMDデータからマテリアルとテクスチャーデータを取得して,
		//カラーデータを取得
int COLORSU = modeldata->material_count;
//LPCSTRとかわけわかんないので,普通にCで書くよ
#define MATERIAL_FILENAME_MAX 21
char *texture_file = new char[COLORSU * MATERIAL_FILENAME_MAX];
memset(&texture_file[0], 0, COLORSU * MATERIAL_FILENAME_MAX);

int cnt_idx = 0; //インデックスカウンター
for(int i=0; i < COLORSU; i++){
//マテリアルにあるカウンター分回す
for(unsigned long j=0; j < modeldata->material[i].face_vert_count; j++){
//インデックスから頂点座標の行数を取り出す
int pos_vec = modeldata->face_vert_index[cnt_idx];
cnt_idx++; //インデックスカウンターを進める

//その頂点座標のカラーが現在のマテリアルカラー
hVectorData[pos_vec].col[0] = modeldata->material[i].diffuse_color[0]; //R
hVectorData[pos_vec].col[1] = modeldata->material[i].diffuse_color[1]; //G
hVectorData[pos_vec].col[2] = modeldata->material[i].diffuse_color[2]; //B
hVectorData[pos_vec].col[3] = modeldata->material[i].alpha; //A
}
//テクスチャー設定
//最後にNULLは保障されていないらしいので。。。
char w[MATERIAL_FILENAME_MAX];
memset(&w, 0, sizeof(w));
memcpy(&w, &modeldata->material[i].texture_file_name[0], MATERIAL_FILENAME_MAX - 1);
//テクスチャー名は0クリアされている保障は無かったし。。。
if(w[0] != 0){
//bmpの場合pngに変更
int w2 = 0;
while(w[w2] != 0){
w2++;
}
w2--;
if( (w[w2] == 'p' || w[w2] == 'P')
&& (w[w2-1] == 'm' || w[w2-1] == 'M')
&& (w[w2-2] == 'b' || w[w2-2] == 'B')
&& w[w2-3] == '.'){
w[w2] = 'g';
w[w2-1] = 'n';
w[w2-2] = 'p';
}

memcpy(&texture_file[i*MATERIAL_FILENAME_MAX], w, MATERIAL_FILENAME_MAX-1);
}
}
予めシェーダーの種類を判定して保存しておいて,
		for(int i=0; i < COLORSU; i++){
if(texture_file[i*MATERIAL_FILENAME_MAX] != 0x00){
//テクスチャーファイルを読み込み
hModelData->setPsNum(hModelId, 1); //PSにテクスチャー描画をセット
}else{
hModelData->setPsNum(hModelId, 0); //PSにマテリアル描画をセット
}
ピクセルシェーダーを予め生成しておいて,
	//ピクセルシェーダー生成(テクスチャー描画用)
// ID3D11PixelShader* hpPixelShaders = new ID3D11PixelShader[2]; //←できない
ID3D11PixelShader* hpPixelShader;
if(FAILED(hpDevice->CreatePixelShader(&g_ps_main, sizeof(g_ps_main), NULL, &hpPixelShader))){
MessageBoxW(hWnd, L"CreateVertexShader", L"Err", MB_ICONSTOP);
goto End;
}

//ピクセルシェーダー2生成(マテリアル描画用)
ID3D11PixelShader* hpPixelShader2;
if(FAILED(hpDevice->CreatePixelShader(&g_ps_main2, sizeof(g_ps_main2), NULL, &hpPixelShader2))){
MessageBoxW(hWnd, L"CreateVertexShader 2", L"Err", MB_ICONSTOP);
goto End;
}
メインループで,シェーダーを切り替える
	for(int mdcnt=0; mdcnt < MODELDATA_MAX; mdcnt++){
//ピクセルシェーダー切り替え
if(hModelData->getPsNum(mdcnt) == 0){
hpDeviceContext->PSSetShader(hpPixelShader2, NULL, 0);
}else if(hModelData->getPsNum(mdcnt) == 1){
hpDeviceContext->PSSetShader(hpPixelShader, NULL, 0);
}

//インデックス描画
hpDeviceContext->DrawIndexed(hModelData->getIdxCnt(mdcnt), hModelData->getIdxSta(mdcnt), 0);
}
もはや説明とかできるようなロジックじゃ無くなっている気がする(*'-')


TOPプログラマ専用DirectX11開発