ConstanbBufferのUtilityを作って、World, View Projection行列を渡して3次元描画する。
shader
とりあえずシェーダー
Texture2D diffuseTexture;
SamplerState diffuseTextureSampler;
struct VS_IN
{
float4 Position: POSITION;
float4 Color: COLOR;
float2 Tex: TEXCOORD0;
};
struct VS_OUT
{
float4 Position: SV_POSITION;
float4 Color: COLOR;
float2 Tex: TEXCOORD0;
};
typedef VS_OUT PS_IN;
cbuffer cb0
{
row_major matrix ModelMatrix;
}
cbuffer cb1
{
row_major matrix ViewMatrix;
}
cbuffer cb2
{
row_major matrix ProjectionMatrix;
};
VS_OUT vsMain(VS_IN input)
{
VS_OUT Output;
Output.Position = mul(input.Position, mul(ModelMatrix, mul(ViewMatrix, ProjectionMatrix)));
Output.Color = input.Color;
Output.Tex = input.Tex;
return Output;
}
float4 psMain(PS_IN input) : SV_TARGET
{
float4 texel = diffuseTexture.Sample(diffuseTextureSampler, input.Tex);
return input.Color * texel;
}
定数バッファを3つに増やして(変数名経由で渡せるようにするのでわざと変な定義してある)立体化しますよと。
定数バッファを管理するクラス
定数バッファのシェーダーへの供給は以下の関数で実行できる。
void ID3D11DeviceContext::VSSetConstantBuffers(
[in] UINT StartSlot,
[in] UINT NumBuffers,
[in] ID3D11Buffer *const *ppConstantBuffers
);
これが、PS版、GS版のようにシェーダーステージ毎にあるので使う側が適切なステージとスロットを覚えておく必要がある。
一方、使う側としては
ConstantBuffer->SetValue("ModelMatrix", matrix);
という感じでステージとスロットを気にせずに変数名で渡したい。
シェーダーリフレクションでできる。
下記のような使い勝手の管理クラスを作った。
{
auto m = DirectX::XMMatrixPerspectiveFovLH(DirectX::XMConvertToRadians(45.0f), 1.0f, 1.0f, 1000.0f);
DirectX::XMFLOAT4X4 matrix;
DirectX::XMStoreFloat4x4(&matrix, m);
// 変数の情報を取得
auto v = m_constant->GetCBVariable("ProjectionMatrix");
// 定数バッファの特定のバイト位置を更新
m_constant->SetCBValue(v, matrix);
}
シェーダーコンパイル時にリフレクションで変数の所属する定数バッファのステージ(VS, PS...)とスロット(0, 1, 2...)と定数バッファ内でのバイト位置を記録しておいて、後で変数名からそれらを取り出して値を更新できるようにした。テクスチャ、サンプラもslot内に複数の変数が入らない点を除いて同様。
深度バッファ追加
// デプステクスチャの作成
D3D11_TEXTURE2D_DESC depthDesc;
ZeroMemory(&depthDesc, sizeof(depthDesc));
depthDesc.Width = tdesc.Width;
depthDesc.Height = tdesc.Height;
depthDesc.MipLevels = 1;
depthDesc.ArraySize = 1;
depthDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
depthDesc.SampleDesc.Count = 1;
depthDesc.SampleDesc.Quality = 0;
depthDesc.Usage = D3D11_USAGE_DEFAULT;
depthDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
depthDesc.CPUAccessFlags = 0;
depthDesc.MiscFlags = 0;
hr=pDevice->CreateTexture2D(&depthDesc, NULL, &m_depthStencil);
if (FAILED(hr)){
return false;
}
// DepthStencilViewの作成
D3D11_DEPTH_STENCIL_VIEW_DESC dsvDesc;
ZeroMemory(&dsvDesc, sizeof(dsvDesc));
dsvDesc.Format = depthDesc.Format;
dsvDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DMS;
dsvDesc.Texture2D.MipSlice = 0;
hr=pDevice->CreateDepthStencilView(m_depthStencil.Get(), &dsvDesc, &m_depthStencilView);
if (FAILED(hr)){
return false;
}
行列のrow_major column_major
hlslのmatrixの値格納方式は省略時はcolumn_majorで、入れ物に使ったXMFLOAT4X4はrow_majorなのでここの辻褄を合わせる必要がある。
対策1
シェーダー側の定数定義で
row_major matrix ModelMatrix;
とする。
前回のサンプルまではrow_majorは無かったので逆回転してたのですな・・・
対策2
乗算の向きを反対向きにする。行ベクトルから列ベクトルの世界へ。
OpenGL使いにはこっちの方がわかりやすいかもしれぬ。
//Output.Position = mul(input.Position, mul(ModelMatrix, mul(ViewMatrix, ProjectionMatrix)));
Output.Position = mul(ProjectionMatrix, mul(ViewMatrix, mul(ModelMatrix, input.Position)));
対策3
プログラム側で転置する。
DirectX::XMStoreFloat4x4(&matrix, DirectX::XMMatrixTranspose(m));
わしは、行ベクトルかつRowMajorに慣れているので対策1が好みでござる。
今回はこれまで。
次回、D2DでFPS等の文字列表示をする。予定。