Direct3D10でティーポットを出してみた。

まず初めに…Direct3D10は…
うへあ('A`)
って感じ。


まあ自分にシェーダの知識がないってのもだいぶうへあになる原因なんだけど…。
ティーポットを出すだけですっごい苦労した。
前の記事のMyApp.h,MyApp.cppにコードを付け加える形で、
色々コードをメモってきます。相変わらず理解はしてません。


で、ティーポットを作る関数といえば、D3DXCreateTeapotなわけで。
が、D3D10からは、この定型メッシュ作成関数が一切無い!!
意味わかんねー(;゚Д゚)


色々見たり聞いたりして回った結果、
SDKについている「DXUT(DirectX Utility?)」ってのに全部関数が
移動してて、「DXUTCreateTeapot」として定義されているみたいなので、
コレを利用する事にする。


DXUTは、SDKディレクトリの下の「Samples\C++\DXUT」にあるので、
「Core」の方と「Optional」の方をビルドする。
出力ファイルは適当に「DXUT.lib(DXUTd.lib)」と「DXUTOpt.lib(DXUTOptd.lib)」にしておく。
プロジェクトにはDirectXのインクルードパスが通っていない('A`)ので、
通さないとビルド出来ない。
ビルドしたら、MyApp.hにこんな感じでコードを書く。

//// MyApp.h

...

// Direct3D10
#include
#include
#include

#pragma comment(lib, "d3d10.lib")
#pragma comment(lib, "d3dx10.lib")
#pragma comment( lib, "dxerr.lib" )

// DXUT
#include
#include // for DXUTCreateTeapot()

#ifdef _DEBUG
#pragma comment(lib, "DXUTd.lib")
#pragma comment(lib, "DXUTOptd.lib")
#else
#pragma comment(lib, "DXUT.lib")
#pragma comment(lib, "DXUTOpt.lib")
#endif
#pragma comment(lib, "comctl32.lib") // for InitCommonControls()

...

class MyApp
{
...
// Direct3D10 objects
...
ID3DX10Mesh* _pMesh; // メッシュ
...
};

お次はメッシュ作成部分。
//// MyApp.cpp

...

bool MyApp::Initialize()
{
//// Direct3D10 初期化
...
// メッシュ作成
hr = DXUTCreateTeapot(_pDevice, &_pMesh);
if (FAILED(hr)) {
assert(0);
return false;
}
...
}

...

bool MyApp::Finalize()
{
// 解放の順番は順番どうがいいのかな…
RELEASE(_pMesh);

if (_pDevice) _pDevice->ClearState();
RELEASE(_pRTView);
RELEASE(_pSwapChain);
RELEASE(_pDevice);
return true;
}


さてここまで来たら後はDrawSubsetするだけだー!
と思ったら大間違いで…。
D3D10では、3Dオブジェクトを描く際は必ずシェーダを使わないとダメらしい。
マジ萎える。


とりあえずシェーダを書く。
ちなみにシェーダに関する知識は大して無い。
シェーダを書く事自体初めてだ!


//// MyShader.fx
/**
適当シェーダ
*/


// トランスフォーム行列
float4x4 mWorld;
float4x4 mView;
float4x4 mProj;

// ピクセルシェーダ入力パラメータ
struct PS_INPUT
{
float4 Pos : SV_POSITION;
float4 Color : COLOR0;
};

// 頂点シェーダ
PS_INPUT VS(float4 Pos : POSITION)
{
PS_INPUT psIn;

// 頂点をトランスフォームする
psIn.Pos = mul(Pos, mWorld);
psIn.Pos = mul(psIn.Pos, mView);
psIn.Pos = mul(psIn.Pos, mProj);

// 頂点カラーを赤にする
psIn.Color = float4(1.0f, 0.0f, 0.0f, 1.0f);

return psIn;
}

// ピクセルシェーダ
float4 PS(PS_INPUT psIn) : SV_Target
{
return psIn.Color;
}

// テクニック
technique10 Render
{
// パス(って何?)
pass p0
{
SetVertexShader(CompileShader(vs_4_0, VS()));
SetGeometryShader(NULL);
SetPixelShader(CompileShader(ps_4_0, PS()));
}
}

とりあえず、プログラム側から入力されたトランスフォーム行列を
頂点にかけて、頂点カラーを赤一色にしてしまうだけの適当シェーダ。
float4が、ベクトルやカラー情報(RGBA)として、
float4x4が、マトリクスとして使われるみたい。


さてコイツを読み込まして使うタメには、エフェクトオブジェクトと
頂点データレイアウトを決めてやらにゃあかんそうだ…あーもうマジめんどくせえ。

//// MyApp.h

...

class MyApp
{
...
// Direct3D10 objects
...
ID3D10Effect* _pEffect; // エフェクト
ID3D10InputLayout* _pLayout; // 頂点データレイアウト
...
};

//// MyApp.cpp

...

namespace
{
// エフェクト(シェーダー)ファイル名
const TCHAR EFFECT_FILE_NAME[] = _T("MyShader.fx");

// シェーダーモデル
const char SHADER_MODEL[] = "fx_4_0";

// エフェクトのテクニック名
const char TECHNIQUE_NAME[] = "Render";
}

...

bool MyApp::Initialize()
{
//// Direct3D10 初期化
...
// エフェクトの読み込み
hr = D3DX10CreateEffectFromFile(EFFECT_FILE_NAME,
NULL, NULL,
SHADER_MODEL,
D3D10_SHADER_ENABLE_STRICTNESS,
0,
_pDevice,
NULL, NULL,
&_pEffect,
NULL, &hr);
if (FAILED(hr)) {
assert(0);
return false;
}

// 頂点データのレイアウト定義(FVFに代わるものみたい?)
//  頂点バッファ等で使われ、シェーダではセマンティック(POSITION,COLOR)に使われるそうな。

D3D10_INPUT_ELEMENT_DESC layout[] =
{
{"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0},
{"COLOR" , 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D10_INPUT_PER_VERTEX_DATA, 0 },
};

UINT numElements = sizeof(layout)/sizeof(layout[0]);

ID3D10EffectTechnique* pTechnique;
pTechnique = _pEffect->GetTechniqueByName(TECHNIQUE_NAME);

D3D10_PASS_DESC PassDesc;
pTechnique->GetPassByIndex(0)->GetDesc(&PassDesc);

hr = _pDevice->CreateInputLayout(
layout,
numElements,
PassDesc.pIAInputSignature,
PassDesc.IAInputSignatureSize,
&_pLayout);

if (FAILED(hr)) {
assert(0);
return false;
}

_pDevice->IASetInputLayout(_pLayout); // デバイスに設定する

//RELEASE(pTechnique); // テクニックのReleaseは要らないらしい
...
}

...

bool MyApp::Finalize()
{
// 解放の順番は順番どうがいいのかな…
RELEASE(_pLayout);
RELEASE(_pEffect);
RELEASE(_pMesh);

if (_pDevice) _pDevice->ClearState();
RELEASE(_pRTView);
RELEASE(_pSwapChain);
RELEASE(_pDevice);
return true;
}

ようやく準備ができましたよ('A`)
最後は、描画部分。


//// MyApp.cpp

...

void MyApp::Update()
{
// シーンクリア
...

// シーン描画
{
// シェーダにトランスフォームのパラメータを設定する
D3DXMATRIX mWorld; // ワールド変換行列
D3DXMatrixIdentity(&mWorld);
_pEffect->GetVariableByName("mWorld")->AsMatrix()->SetMatrix((float*)&mWorld);

D3DXMATRIX mView; // ビュー変換行列
D3DXVECTOR3 Eye(0.0f, 2.0f, -4.0f); // カメラ座標
D3DXVECTOR3 At(0.0f, 0.0f, 0.0f); // 注視点
D3DXVECTOR3 Up(0.0f, 1.0f, 0.0f);
D3DXMatrixLookAtLH(&mView, &Eye, &At, &Up);
_pEffect->GetVariableByName("mView")->AsMatrix()->SetMatrix((float*)&mView);

D3DXMATRIX mProj; // 射影変換行列
D3DXMatrixPerspectiveFovLH(
&mProj,
D3DXToRadian(60.0f), // 視野角
(float)SCREEN_W/(float)SCREEN_H, // アスペクト比
0.1f, // ニアクリップ
100.0f);// ファークリップ
_pEffect->GetVariableByName("mProj")->AsMatrix()->SetMatrix((float*)&mProj);

// テクニックを取得する
ID3D10EffectTechnique* pTechnique;
pTechnique = _pEffect->GetTechniqueByName(TECHNIQUE_NAME);

D3D10_TECHNIQUE_DESC desc;
pTechnique->GetDesc(&desc);
for (UINT i = 0; i < desc.Passes; ++i) {
// テクニックに書いたパスの事と思われ
pTechnique->GetPassByIndex(i)->Apply(0);

// メッシュ描画
_pMesh->DrawSubset(0);
}
}

// シーンの表示
_pSwapChain->Present(0, 0);
...
}

これだけ書いてようやくなんのシェーディングもされていないティーポットが出ます('A`)


えー…っと…


Direct3D9万歳。

※あくまで、やってみた程度の記事で、詳細な事は自分もよくわかってないので、→のブクマから頑張って調べてみて下さい…気が向いたら追記します。
※※日記のタイトル変えすぎでさーせん。なかなか気に入らなくて…