用DirectX 11绘制一个Cube

之前一篇文章讲了如何初始化DirectX 11,现在在此基础上绘制一个Cube,总体可概括为以下几个步骤:

  1. 定义Cube顶点数据结构
  2. 创建Vertex Buffer和Index Buffer
  3. 编写应用于Cube的effect file
  4. 读取effect,编译创建
  5. 根据顶点数据结构,创建input layout,将顶点与effect中VS的input signature关联
  6. 开始绘制,clear掉render target view和depth stencil view
  7. 设置若干IA参数,input layout,primitive toplogy,vertex buffer,index buffer
  8. 将一些必要参数设置给effect的cbuffer中,如worldViewProjMatrix
  9. 将effect的每个pass依次apply到context中,进行绘制
  10. 绘制提交,结束绘制

定义Cube顶点数据结构

当前只需要顶点的position和color属性,position是三维向量,color是四维向量,定义如下:

	struct Vertex
	{
		XMFLOAT3 position;
		XMFLOAT4 color;
	};

这里用的是DirectXmath.h中的XMFLOAT3XMFLOAT4,也可以用自定义的向量类。

创建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可以被当作是两者关联的桥梁,现在顶点只有positioncolor两个属性,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);

运行效果如图:

posted @ 2020-05-08 23:14  异次元的归来  阅读(338)  评论(0编辑  收藏  举报