[PR]
×
[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。
プログラミング、3DCGとその他いろいろについて
[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。
#using<System.Windows.Forms.dll>
#using<System.dll>
#include<d3d10.h>
#include<d3dx10.h>
using namespace System;
using namespace System::Windows::Forms;
ref class Game : Form
{
ID3D10Device* graphicsDevice;
IDXGISwapChain* swapChain;
ID3D10RenderTargetView* renderTargetView;
ID3D10Effect *effect;
ID3D10EffectTechnique *effectTechnique;
ID3D10InputLayout* vertexLayout;
ID3D10Buffer* vertexBuffer;
public:
void Run()
{
InitDevice();
Show();
while(Created)
{
Draw();
Application::DoEvents();
}
}
private:
void InitDevice()
{
createGraphicsDevice();
createRenderTargetView();
setupViewport();
LoadGraphicsContent();
}
void LoadGraphicsContent()
{
//シェーダーの設定
//「グラフィックスカードへおくる頂点バッファが
//どのように処理されて画面に表示されるか」
//を表すエフェクト・オブジェクトを
//(外部のHLSLで書かれたファイルMyShader.fxから)作り出します。
ID3D10Effect* effect;
HRESULT result = D3DX10CreateEffectFromFile(
"MyShader.fx",
NULL,
NULL,
"fx_4_0",
D3D10_SHADER_ENABLE_STRICTNESS,
0,
graphicsDevice,
NULL,
NULL,
&effect,
NULL,
NULL
);
if(FAILED(result))
throw gcnew Exception("couldn't create effect object");
this->effect = effect;
this->effectTechnique = effect->GetTechniqueByName("MyTechnique");
//描画のときデバイスに入力する頂点データのレイアウトの設定
//普通なら頂点データは座標のほかに、色やテクスチャ座標を持っていますが、
//ここでは話を単純にするために座標データのみです。
D3D10_INPUT_ELEMENT_DESC vertexElements[] =
{
{"SV_POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0}
};
D3D10_PASS_DESC passDesc;
effectTechnique->GetPassByIndex(0)->GetDesc(&passDesc);
ID3D10InputLayout *vertexLayout;
result = graphicsDevice->CreateInputLayout(
vertexElements,
1,
passDesc.pIAInputSignature,
passDesc.IAInputSignatureSize,
&vertexLayout
);
if(FAILED(result))
throw gcnew Exception("couldn't create InputLayout");
this->vertexLayout = vertexLayout;
graphicsDevice->IASetInputLayout(vertexLayout);
//頂点バッファの設定
//3Dモデルの頂点データを格納する頂点バッファを
//グラフィックスカード内に作り、
//それをDrawのとき使われるようにセットします。
D3DXVECTOR3 vertices[] =
{
D3DXVECTOR3(0, 0.5f, 0),
D3DXVECTOR3(0.5f, 0, 0),
D3DXVECTOR3(-0.5f, 0, 0)
};
D3D10_SUBRESOURCE_DATA initData;
initData.pSysMem = vertices;
D3D10_BUFFER_DESC bufferDesc;
ZeroMemory(&bufferDesc, sizeof(bufferDesc));
bufferDesc.Usage = D3D10_USAGE_DEFAULT;
bufferDesc.ByteWidth = sizeof(D3DXVECTOR3) * 3;
bufferDesc.BindFlags = D3D10_BIND_VERTEX_BUFFER;
ID3D10Buffer *vertexBuffer;
result = graphicsDevice->CreateBuffer(
&bufferDesc,
&initData,
&vertexBuffer
);
if(FAILED(result))
throw gcnew Exception("couldn't create Vertex Buffer");
this->vertexBuffer = vertexBuffer;
unsigned int stride = sizeof(D3DXVECTOR3);
unsigned int offset = 0;
graphicsDevice->IASetVertexBuffers(0, 1, &vertexBuffer, &stride, &offset);
//頂点バッファが三角形のリストとして描画されるようにセットします。
//ここでは三角形のリストですが、ほかにも
//点や線のリストとして描画されるようにセットすることもできます。
graphicsDevice->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
}
~Game(){ this->!Game(); }
!Game()
{
if(graphicsDevice) graphicsDevice->ClearState();
if(vertexBuffer) vertexBuffer->Release();
if(vertexLayout) vertexLayout->Release();
if(effect) effect->Release();
if(renderTargetView) renderTargetView->Release();
if(swapChain) swapChain->Release();
if(graphicsDevice) graphicsDevice->Release();
}
void Draw()
{
float blue[] = {0, 0, 1, 1};
graphicsDevice->ClearRenderTargetView(renderTargetView, blue);
D3D10_TECHNIQUE_DESC techniqueDesc;
effectTechnique->GetDesc(&techniqueDesc);
for(int i = 0; i < techniqueDesc.Passes; i++)
{
effectTechnique->GetPassByIndex(i)->Apply(0);
graphicsDevice->Draw( 3, 0);
}
swapChain->Present(0, 0);
}
void createGraphicsDevice()
{
DXGI_SWAP_CHAIN_DESC swapChainDescription;
ZeroMemory( &swapChainDescription, sizeof(swapChainDescription) );
swapChainDescription.BufferCount = 1;
swapChainDescription.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
swapChainDescription.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDescription.OutputWindow = (HWND)(this->Handle.ToInt32());
swapChainDescription.SampleDesc.Count = 1;
swapChainDescription.Windowed = TRUE;
ID3D10Device *graphicsDevice;
IDXGISwapChain *swapChain;
HRESULT result = D3D10CreateDeviceAndSwapChain(
NULL,
D3D10_DRIVER_TYPE_REFERENCE,
NULL,
0,
D3D10_SDK_VERSION,
&swapChainDescription,
&swapChain,
&graphicsDevice
);
if( FAILED(result) )
throw gcnew Exception("Couldn't create Graphics Device");
this->graphicsDevice = graphicsDevice;
this->swapChain = swapChain;
}
void createRenderTargetView()
{
ID3D10Texture2D *backBuffer;
HRESULT result = swapChain->GetBuffer(0, __uuidof(ID3D10Texture2D), (LPVOID*)&backBuffer);
if( FAILED(result) )
throw gcnew Exception("Couldn't get BackBuffer from swap chain.");
ID3D10RenderTargetView* renderTargetView;
result = graphicsDevice->CreateRenderTargetView(backBuffer, NULL, &renderTargetView);
backBuffer->Release();
if( FAILED(result) )
throw gcnew Exception("Couldn't create RenderTargetView.");
this->renderTargetView = renderTargetView;
graphicsDevice->OMSetRenderTargets(1, &renderTargetView, NULL);
}
void setupViewport()
{
D3D10_VIEWPORT viewport;
viewport.Width = Width;
viewport.Height = Height;
viewport.MinDepth = 0.0f;
viewport.MaxDepth = 1.0f;
viewport.TopLeftX = 0;
viewport.TopLeftY = 0;
graphicsDevice->RSSetViewports( 1, &viewport );
}
};
int main()
{
Game ^game = gcnew Game();
game->Run();
}
float4 MyVertexShader(float4 position : SV_Position) : SV_Position
{
return position;
}
float4 MyPixelShader() : SV_Target
{
return float4(1, 1, 1, 1);
}
technique10 MyTechnique
{
pass
{
SetVertexShader(CompileShader(vs_4_0, MyVertexShader()));
SetPixelShader(CompileShader(ps_4_0, MyPixelShader()));
}
}
void OMSetRenderTargets(
UINT numViews,
ID3D10RenderTargetView *const *renderTargetViews,
ID3D10DepthStencilView *depthStencilView
);
HRESULT D3DX10CreateEffectFromFile(
LPCTSTR fileName,
CONST D3D10_SHADER_MACRO *defines,
ID3D10Include *shaderIncludeFile,
LPCSTR profile,
UINT hlslFlags,
UINT fxFlags,
ID3D10Device *graphicsDevice,
ID3D10EffectPool *effectPool,
ID3DX10ThreadPump *pump,
ID3D10Effect **outEffect,
ID3D10Blob **outErrors
);
| 変数名 | 説明 |
| D3D10_SHADER_DEBUG | デバッグ情報を挿入します。 |
| D3D10_SHADER_SKIP_VALIDATION | 過去にコンパイルできたコードをまたコンパイルする時、VALIDATIONをスキップする目的で使います。 |
| D3D10_SHADER_SKIP_OPTIMIZATION | デバッグしたい時に、最適化をスキップします。 |
| D3D10_SHADER_PACK_MATRIX_ROW_MAJOR | 例外が明示されない限り、マトリックスは行が優先的にレジスタに入ります。 |
| D3D10_SHADER_PACK_MATRIX_COLUMN_MAJOR | 例外が明示されない限り、マトリックスは列が優先的にレジスタに入ります。内積の計算方法の関係で、たいていこっちの方が効率的です。 |
| D3D10_SHADER_PARTIAL_PRECISION | 計算の正確性を削って速さを優先します。(速くなるかどうかは実際には保障されていませんが・・・。速くなる「かもしれない」レベルです) |
| D3D10_SHADER_FORCE_VS_SOFTWARE_NO_OPT | 頂点シェーダを次に高いシェーダープロファイルでコンパイルします。これは最適化をoffにしてデバッグをonにします。 |
| D3D10_SHADER_FORCE_PS_SOFTWARE_NO_OPT | ピクセルシェーダを次に高いシェーダープロファイルでコンパイルします。これは最適化をoffにしてデバッグをonにします。 |
| D3D10_SHADER_NO_PRESHADER | Preshader(CPUで出来る計算は1回だけCPUでやって残りの計算をGPUで毎回行う)を無効にします。 |
| D3D10_SHADER_AVOID_FLOW_CONTROL | 可能ならば、フロー制御を使わないようにします。 |
| D3D10_SHADER_PREFER_FLOW_CONTROL | 可能ならば、フロー制御を使うようにします。 |
| D3D10_SHADER_ENABLE_STRICTNESS | 古いシンタックスを使えなくします。(デフォルトでは、HLSLのコンパイラは古いシンタックスがあってもコンパイルしてくれます) |
| D3D10_SHADER_ENABLE_BACKWARDS_COMPATIBILITY | 後方互換を有効にして、古いシェーダーを4_0でコンパイルできるようにします。 |
| D3D10_SHADER_IEEE_STRICTNESS | IEEEに厳密にします。 |
| D3D10_SHADER_OPTIMIZATION_LEVEL0 | 1番低い最適化のレベルです。 |
| D3D10_SHADER_OPTIMIZATION_LEVEL1 | 2番目に低い最適化のレベルです。 |
| D3D10_SHADER_OPTIMIZATION_LEVEL2 | 2番目に高い最適化のレベルです。 |
| D3D10_SHADER_OPTIMIZATION_LEVEL3 | 1番高い最適化のレベルです。パフォーマンスがとっても重要な時にだけ使います。 |
| 定数名 | 値 | 説明 |
| D3D10_EFFECT_COMPILE_CHILD_EFFECT | 1 << 0 | .fxファイルを子エフェクトにコンパイルします。子エフェクトは共有された値に対する初期化を行いません。それらは、エフェクトプールで初期化されるからです。 |
| D3D10_EFFECT_COMPILE_ALLOW_SLOW_OPS | 1 << 1 | ミュータブルなステート・オブジェクトを作ることが出来ます。 どういうことかというと、デフォルトではパフォーマンスのため、ステートオブジェクトはリテラルを使ってしか定義できないのですが、このフラグを使えばそれを無効に出来ます。 |
| D3D10_EFFECT_SINGLE_THREADED | 1 << 3 | 同じプールにエフェクトをロードしたほかのスレッドと同期しようとしません。 |
HRESULT CreateInputLayout(
const D3D10_INPUT_ELEMENT_DESC *inputElementDescs,
UINT numElements,
const void *shaderBytecodeWithInputSignature,
SIZE_T bytecodeLength,
ID3D10InputLayout **outInputLayout
);
inputElementDescsは、頂点バッファに格納される頂点のレイアウトをあらわすD3D10_INPUT_ELEMENT_DESC構造体の配列です。基本的には、頂点の構造体を構成するメンバ変数1つが1つのD3D10_INPUT_ELEMENT_DESCに対応します。例えば、頂点が3次元ベクトル1つだけならこの配列の長さも1で、もし頂点が3次元ベクトルと色を表すデータの2つを持っているならこの配列の長さは2です(そして、座標と色と、テクスチャ座標も持っていたりして3つなら・・・もちろん3です)。
D3D10_INPUT_ELEMENT_DESC 構造体
typedef struct D3D10_INPUT_ELEMENT_DESC { LPCSTR SemanticName; UINT SemanticIndex; DXGI_FORMAT Format; UINT InputSlot; UINT AlignedByteOffset; D3D10_INPUT_CLASSIFICATION InputSlotClass; UINT InstanceDataStepRate; } D3D10_INPUT_ELEMENT_DESC;
SemanticNameはHLSLでシェーダーの入力や出力の変数に付けるセマンティック、つまりこの要素がどのような役割を果たすかをあらわす文字列です。たとえば、座標を表すのなら"SV_Position"、色を表すのなら"Color"、テクスチャ座標を表すのなら"TexCoord"といった具合です。これは、大文字小文字の区別が結構ゆるいようです。MSDNにはCOLORやTEXCOORDと全部大文字で書いていますが、そうでなくてもいけます。
SemanticIndexは、セマンティックに付ける数です。この数によって、同じ名前のセマンティックでも別々に扱うことができます(Color0とColor1は違う)。セマンティックとは要素の役割をあらわすわけで、同じ役割を持つものが複数あるのは変に思えるかもしれませんが、たとえばColor0は乱反射の色、Color1は鏡面反射の色という風に分けたい時だってあるでしょう。あるいは、4x4のマトリックスを、4つの4次元ベクトルとしてシェーダーに流し込みたいとき、セマンティクスは全部同じにしてこのインデックスをそれぞれ変えて入力する、なんてのもありです(たしかXNAのサンプルにそんなのがありました)。
Formatは、この要素のフォーマットです。このメンバの型は、スワップチェーンを作るときにテクスチャを構成するピクセル(テクセル)のフォーマットを決めるのに使った、あのDXGI_FORMATです。つまり、どのくらいのサイズの要素なのかといったことを決めるんですね。
InputSlotは、頂点バッファのインデックスのようなものです(0から15まで。たいてい0)。GPUがポリゴンを描画するとき、(普通は1つだけですが)複数の頂点バッファをセットすることができます。これによって、3DのモデルをGPUの中で複製したりといったことができる(0番目の頂点バッファはモデルの頂点、1番目の頂点バッファは各モデルのインスタンスの情報)のですが、このメンバがあらわしているのはその使う頂点バッファのインデックスです。そういうわけで、普通に3Dモデルを描画したいときにはここは0です。テクニカルなことをしたいときに、ここは0以外になります。
AlignedByteOffsetは、要素間のオフセットだそうです。これはあくまでもオプションで、必ず指定しなければいけないということはありません。自分で値を指定したくない場合は、D3D10_APPEND_ALIGNED_ELEMENTをセットすると、この要素が前の要素のすぐ後にくるようになります。
InputSlotClassには、D3D10_INPUT_CLASSIFICATION列挙型、つまりD3D10_INPUT_PER_VERTEX_DATA(=0)かD3D10_INPUT_PER_INSTANCE_DATA(=1)を指定します。それぞれ、インプットデータが、頂点ごとのデータ、インスタンスごとのデータであることをあらわします。たぶんこれは3DモデルのGPUによる複製、つまりハードウェア・インスタンシングをするときに使うのではないでしょうか(たぶん)。普通に3Dモデルをあらわす頂点バッファから描画するときにはD3D10_INPUT_PER_VERTEX_DATAを指定していいでしょう。
InstanceDataStepRateは、上のメンバInputSlotClassがD3D10_INPUT_PER_INSTANCE_DATAにセットされているときだけ意味を持ちます。D3D10_INPUT_PER_VERTEX_DATAがセットされているときには、このメンバは0でなくてはいけません。このメンバが何を意味するのかというと、MSDNによると、「同じインスタンス毎のデータを使って、バッファをひとつの要素分進む前に、描画するインスタンスの数」だそうです。なんのこっちゃ……。とりあえずハードウェア・インスタンシングをしないのならあんまり意味がなさそうです。そんなに心配する必要はないでしょう。たぶん
void IASetInputLayout (
ID3D10InputLayout *inputLayout
);
HRESULT CreateBuffer(
const D3D10_BUFFER_DESC *desc,
const D3D10_SUBRESOURCE_DATA *initialData,
ID3D10Buffer **outBuffer
);
D3D10_BUFFER_DESC構造体
typedef struct D3D10_BUFFER_DESC { UINT ByteWidth; D3D10_USAGE Usage; UINT BindFlags; UINT CPUAccessFlags; UINT MiscFlags; } D3D10_BUFFER_DESC;
ByteWidthは、バッファのサイズ(単位はbyte)です。
Usageは、このバッファをどのようにつかわれるかです。
これには4つ選択肢があって、それぞれCPUやGPUからのアクセスできたりできなかったりが違います。
GPUから読む GPUから書く CPUから読む CPUから書く ステージの入力として使える(ポリゴンに貼るテクスチャとか?) ステージの出力として使える(レンダーターゲットのことかな?) D3D10_USAGE_DEFAULT ○ ○(制限あり) ○ ○ D3D10_USAGE_IMMUTABLE ○ ○ D3D10_USAGE_DYNAMIC ○(制限あり) ○ ○(サブリソースは1つだけでなければいけません。テクスチャの配列やmipmapチェーンにはできません) D3D10_USAGE_STAGING ○(制限あり) ○(制限あり) ○(制限あり) ○(制限あり)
CPUからのアクセスは、ID3D10Buffer::Map、ID3D10Texture1D::Map、ID3D10Texture2D::Map、ID3D10Texture3D::Mapメソッドで行います。
GPUからのアクセスは、ID3D10Device::CopySubresourceRegion, ID3D10Device::CopyResource、ID3D10Device::UpdateSubresourceで行えます。
ただし!Defaultの書き込みとDynamic、そしてStagingでは、最初の2つだけに制限されます。
なお、Stagingのバッファは、深度ステンシルバッファにはできませんし、マルチサンプルのレンダーターゲットにもできません。
BindFlagsは、どんなふうにバインドできるかをあらわします。
たとえば、頂点バッファとして使えるか、インデックスバッファとして使えるか、定数バッファとして使えるか…といったぐあいです。
これには、D3D10_BIND_FLAGを使います。
typedef enum D3D10_BIND_FLAG { D3D10_BIND_VERTEX_BUFFER = 0x1L, D3D10_BIND_INDEX_BUFFER = 0x2L, D3D10_BIND_CONSTANT_BUFFER = 0x4L, D3D10_BIND_SHADER_RESOURCE = 0x8L, D3D10_BIND_STREAM_OUTPUT = 0x10L, D3D10_BIND_RENDER_TARGET = 0x20L, D3D10_BIND_DEPTH_STENCIL = 0x40L } D3D10_BIND_FLAG;
CPUAccessFlagsは、CPUからの書き込みや読み込みを許可するかを指定します(D3D10_CPU_ACCESS_FLAG列挙型を使います)。
もし、CPUからまったくアクセスしないのであれば、0を指定します。
書き込みを許可するのはD3D10_CPU_ACCESS_WRITEで、これを使うとパイプラインの出力としては使えなくなり、またUsageメンバがDynamicかStagingになってなければいけません。
読み込みを許可するのはD3D10_CPU_ACCESS_READで、これを使うとパイプラインの入力としても出力としても使えなくなり、またUsageメンバがStagingになっていなければいけません。
MiscFlagsは、あんまり使われない、「その他」な機能を表すフラグです。
使わないのなら0です。
このメンバにはD3D10_RESOURCE_MISC_FLAG列挙型を使います。
typedef enum D3D10_RESOURCE_MISC_FLAG { D3D10_RESOURCE_MISC_GENERATE_MIPS = 0x1L, D3D10_RESOURCE_MISC_SHARED = 0x2L, D3D10_RESOURCE_MISC_TEXTURECUBE = 0x4L } D3D10_RESOURCE_MISC_FLAG;
D3D10_SUBRESOURCE_DATA 構造体
typedef struct D3D10_SUBRESOURCE_DATA { const void *pSysMem; UINT SysMemPitch; UINT SysMemSlicePitch; } D3D10_SUBRESOURCE_DATA;
pSysMemは、これから作るサブリソースを初期化する値です。たとえば、作るサブリソースがインデックスバッファならインデックスの配列、テクスチャなら色の配列です。
SysMemPitchは、2Dや3Dのテクスチャを作るときのみ使われます。頂点バッファやインデックスバッファでは使いません。このメンバが何を意味するのかというと、テクスチャの横幅です(ただし単位はbyteらしいです。そのままwidthとかを突っ込んではいけません。つまり、テクスチャのピクセル1つのデータのサイズを×しなきゃいけないとうことです。)。
SysMemSlicePitchは、3Dのテクスチャを作るときのみ使われます。これはテクスチャの深さ(単位はbyteで)ですね。
void IASetVertexBuffers(
UINT startSlot,
UINT numBuffers,
ID3D10Buffer *const *vertexBuffers,
const UINT *strides,
const UINT *offsets
);
void IASetPrimitiveTopology(
D3D10_PRIMITIVE_TOPOLOGY topology
);
typedef enum D3D10_PRIMITIVE_TOPOLOGY
{
D3D10_PRIMITIVE_TOPOLOGY_UNDEFINED = 0,
D3D10_PRIMITIVE_TOPOLOGY_POINTLIST = 1,
D3D10_PRIMITIVE_TOPOLOGY_LINELIST = 2,
D3D10_PRIMITIVE_TOPOLOGY_LINESTRIP = 3,
D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST = 4,
D3D10_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP = 5,
D3D10_PRIMITIVE_TOPOLOGY_LINELIST_ADJ = 10,
D3D10_PRIMITIVE_TOPOLOGY_LINESTRIP_ADJ = 11,
D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST_ADJ = 12,
D3D10_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP_ADJ = 13
} D3D10_PRIMITIVE_TOPOLOGY;
HRESULT Apply(UINT flags);
ID3D10EffectPass* GetPassByIndex( UINT index );
ID3D10EffectTechnique* GetTechniqueByName(
LPCSTR name
);
void Draw(
UINT vertexCount,
UINT startVertexLocation
);