用DirectX 11绘制一个Cube
之前一篇文章讲了如何初始化DirectX 11,现在在此基础上绘制一个Cube,总体可概括为以下几个步骤:
- 定义Cube顶点数据结构
- 创建Vertex Buffer和Index Buffer
- 编写应用于Cube的effect file
- 读取effect,编译创建
- 根据顶点数据结构,创建input layout,将顶点与effect中VS的input signature关联
- 开始绘制,clear掉render target view和depth stencil view
- 设置若干IA参数,input layout,primitive toplogy,vertex buffer,index buffer
- 将一些必要参数设置给effect的cbuffer中,如worldViewProjMatrix
- 将effect的每个pass依次apply到context中,进行绘制
- 绘制提交,结束绘制
定义Cube顶点数据结构
当前只需要顶点的position和color属性,position是三维向量,color是四维向量,定义如下:
struct Vertex
{
XMFLOAT3 position;
XMFLOAT4 color;
};
这里用的是DirectXmath.h
中的XMFLOAT3
和XMFLOAT4
,也可以用自定义的向量类。
创建Vertex Buffer和Index Buffer
HRESULT CreateBuffer(
const D3D11_BUFFER_DESC *pDesc,
const D3D11_SUBRESOURCE_DATA *pInitialData,
ID3D11Buffer **ppBuffer
);
主要用到的是ID3D11Device::CreateBuffer这一函数。其中,pDesc
主要设置要创建的buffer大小,用途,是否cpu可访问等等,D3D11_BIND_VERTEX_BUFFER用于创建vertex buffer,D3D11_BIND_INDEX_BUFFER用于创建index buffer;pInitialData
存放的是初始化数据。
编写应用于Cube的effect file
这个effect效果很简单,就是将Cube变换到正确的屏幕空间位置,设置为插值后的颜色。大致包括的内容如下:
cbuffer cbPerObject
{
float4x4 gWorldViewProj;
};
struct VertexIn
{
float3 posL : POSITION;
float4 color : COLOR;
};
struct VertexOut
{
float4 posH : SV_POSITION;
float4 color : COLOR;
};
VertexOut VS(VertexIn vIn)
{
VertexOut vOut;
vOut.posH = mul(float4(vIn.posL, 1.0f), gWorldViewProj);
vOut.color = vIn.color;
return vOut;
}
float4 PS(VertexOut pIn) : SV_TARGET
{
return pIn.color;
}
technique11 ColorTech
{
pass P0
{
SetVertexShader(CompileShader(vs_5_0, VS()));
SetPixelShader(CompileShader(ps_5_0, PS()));
}
}
cBuffer
存放的是要从外部读取的数据,这里就是个MVP矩阵了。
读取effect,编译创建
HRESULT D3DX11CompileFromFile(
_In_ LPCTSTR pSrcFile,
_In_ const D3D10_SHADER_MACRO *pDefines,
_In_ LPD3D10INCLUDE pInclude,
_In_ LPCSTR pFunctionName,
_In_ LPCSTR pProfile,
_In_ UINT Flags1,
_In_ UINT Flags2,
_In_ ID3DX11ThreadPump *pPump,
_Out_ ID3D10Blob **ppShader,
_Out_ ID3D10Blob **ppErrorMsgs,
_Out_ HRESULT *pHResult
);
这里,我们要编译整个effect而不是某个shader,因而将pFunctionName
设置为nullptr
。得到编译后的effect后:
HRESULT D3DX11CreateEffectFromMemory(
void *pData,
SIZE_T DataLength,
UINT FXFlags,
ID3D11Device *pDevice,
ID3DX11Effect **ppEffect
);
effect创建完成。
创建input layout
input layout将定义的顶点结构与effect中VS的input结构关联,为VS中的input指定输入来源。
HRESULT CreateInputLayout(
const D3D11_INPUT_ELEMENT_DESC *pInputElementDescs,
UINT NumElements,
const void *pShaderBytecodeWithInputSignature,
SIZE_T BytecodeLength,
ID3D11InputLayout **ppInputLayout
);
pInputElementDescs
可以被当作是两者关联的桥梁,现在顶点只有position
和color
两个属性,pInputElementDescs
定义如下:
D3D11_INPUT_ELEMENT_DESC VertexDesc[] =
{
{"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0},
{"COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0},
};
D3D11_APPEND_ALIGNED_ELEMENT
这个宏省去了自己计算每个分量偏移的麻烦。
绘制
首先,清空back buffer和depth/stencil buffer;
void ClearRenderTargetView(
ID3D11RenderTargetView *pRenderTargetView,
const FLOAT [4] ColorRGBA
);
void ClearDepthStencilView(
ID3D11DepthStencilView *pDepthStencilView,
UINT ClearFlags,
FLOAT Depth,
UINT8 Stencil
);
接下来,设置若干IA参数,这里我们需要的就是input layout关联,绘制基本primitive的类型,还有设置vertex buffer和index buffer。
void IASetInputLayout(
ID3D11InputLayout *pInputLayout
);
void IASetPrimitiveTopology(
D3D11_PRIMITIVE_TOPOLOGY Topology
);
void IASetVertexBuffers(
UINT StartSlot,
UINT NumBuffers,
ID3D11Buffer * const *ppVertexBuffers,
const UINT *pStrides,
const UINT *pOffsets
);
void IASetIndexBuffer(
ID3D11Buffer *pIndexBuffer,
DXGI_FORMAT Format,
UINT Offset
);
值得一提的是,vertex buffer可以存在多个,index buffer只能存在一个。不同的vertex buffer会分配到不同的input slots中去,这里只用到了一个vertex buffer,暂时不考虑多个input slots的情况。
在effect渲染之前,往往存在需要设置一些参数给effect的情况,比如我们这个例子,就需要把MVP矩阵传给effect:
XMMATRIX worldViewProj = world * view * proj;
shaderMVP->SetMatrix(reinterpret_cast<float*>(&worldViewProj));
然后就到了正式的绘制阶段,将effect的每个pass依次apply到context中,进行绘制:
D3DX11_TECHNIQUE_DESC techDesc;
shaderTech->GetDesc(&techDesc);
for (UINT i = 0; i < techDesc.Passes; ++i)
{
shaderTech->GetPassByIndex(i)->Apply(0, mD3dImmediateContext);
mD3dImmediateContext->DrawIndexed(36, 0, 0);
}
一个Cube有6个面,描述一个面需要6个index,所以总共是36个index。
最后不要忘记将绘制内容提交:
mSwapChain->Present(0, 0);
运行效果如图: