有些時候,咱們須要在場景中渲染大量的重複的物體,好比體育場中的觀衆,森林裏面的樹木等等,這些物體具備類似的形狀,好比不少樹木,只是位置不一樣,或者貼圖不一樣而已,若是重複渲染這些樹木,用billboard技術,n棵樹,就要輸入n*4個頂點,在樹木不少的時候,這也是比不小的開銷,由於每次都要在system memory和gpu之間傳輸數據。html
在D3D11中,經過使用實例技術,能夠有效的減小這種開銷。ide
實例技術的主要實現方式:定義一個頂點緩衝,而後再定義第二個緩衝,稱做實例緩衝,只記錄物體變化的信息,好比改變位置的信息等等。這樣只需傳入少許的頂點就能夠有效的渲染大量的物體。函數
下面,咱們在myTutorialD3D11_45的基礎上,經過實例技術,畫4棵樹。佈局
一、首先修改MirrorModelClass,由於咱們的樹經過billboard實現,這個類用來定義頂點和實例緩衝信息(由於咱們的代碼是修改來的,因此我並無改類的名字,按道理說,用TreeModelClass或許更直觀一些)spa
定義實例類結構,咱們只改變物體的位置,因此結構很是簡單,而後再定義實例緩衝和實例計數(去掉了索引緩衝)。3d
struct InstanceType { D3DXVECTOR3 position; };orm
…htm
ID3D11Buffer* m_instanceBuffer; //實例緩衝
int m_instanceCount;
blog
實例緩衝定義的代碼以下:教程
instances[0].position = D3DXVECTOR3(-5.5f, 0.0f, 5.0f);
instances[1].position = D3DXVECTOR3(-1.5f, 0.0f, 5.0f);
instances[2].position = D3DXVECTOR3( 5.5f, 0.0f, 15.0f);
instances[3].position = D3DXVECTOR3( 1.5f, 0.0f, 9.0f);
…
// 建立實例緩衝.
instanceBufferDesc.Usage = D3D11_USAGE_DEFAULT;
instanceBufferDesc.ByteWidth = sizeof(InstanceType) * m_instanceCount;
instanceBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
instanceBufferDesc.CPUAccessFlags = 0;
instanceBufferDesc.MiscFlags = 0;
instanceBufferDesc.StructureByteStride = 0;
// 指向實例臨時緩衝.
instanceData.pSysMem = instances;
instanceData.SysMemPitch = 0;
instanceData.SysMemSlicePitch = 0;
result = device->CreateBuffer(&instanceBufferDesc, &instanceData, &m_instanceBuffer);
if(FAILED(result))
{
return false;
}
再就是渲染緩衝函數的改變,這裏面咱們去掉了索引緩衝,還有值得注意的是咱們使用三角形帶(strip)的體元語義。由於在頂點緩衝中咱們定義四個點,經過三角形帶,咱們能夠很好的畫一個矩形出來作billboard。
void MirrorModelClass::RenderBuffers(ID3D11DeviceContext* deviceContext)
{
unsigned int strides[2];
unsigned int offsets[2];
ID3D11Buffer* bufferPointers[2];
// 設置頂點緩衝跨度和偏移.
strides[0] = sizeof(VertexType);
strides[1] = sizeof(InstanceType);
offsets[0] = 0;
offsets[1] = 0;
// 指向頂點緩衝和實例緩衝.
bufferPointers[0] = m_vertexBuffer;
bufferPointers[1] = m_instanceBuffer;
//在input assemberl階段綁定頂點緩衝和實例緩衝,以便可以被渲染
deviceContext->IASetVertexBuffers(0, 2, bufferPointers, strides, offsets);
// 設置體元語義,渲染三角形帶
deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
return;
}
接着新定義一個LightTexInstanceShader類,該類是渲染實例物體的shader類,它的代碼和LightTexShader類類似,我主要指出不一樣的部分,就是輸入佈局中,咱們增長了實例緩衝,它會被傳入vs中。
//實例數據
polygonLayout[5].SemanticName = "TEXCOORD";
polygonLayout[5].SemanticIndex = 1;
polygonLayout[5].Format = DXGI_FORMAT_R32G32B32_FLOAT;
polygonLayout[5].InputSlot = 1;
polygonLayout[5].AlignedByteOffset = 0;
polygonLayout[5].InputSlotClass = D3D11_INPUT_PER_INSTANCE_DATA;
polygonLayout[5].InstanceDataStepRate = 1;
// 獲得layout中的元素數量
numElements = sizeof(polygonLayout) / sizeof(polygonLayout[0]);
// 建立頂點輸入佈局.
result = device->CreateInputLayout(polygonLayout, numElements, vertexShaderBuffer->GetBufferPointer(), vertexShaderBuffer->GetBufferSize(),
&m_layout);
再就是用DrawInstance代替DrawIndex函數。
// 渲染三角形實例
deviceContext->DrawInstanced(indexCount, instanceCount, 0, 0);
最後就是在GraphicsClass類中調用渲染樹木實例:
result = m_LightTexInstanceShader->Render(m_D3D->GetDeviceContext(), m_MirrorModel->GetVertexCount(), m_MirrorModel->GetInstanceCount(), worldMatrix4, viewMatrix, projectionMatrix,
light, material, camera,m_TexManager->createTex(m_D3D->GetDevice(),string("tree1.dds")));
if(!result)
{
return false;
}
在vs中,咱們會讀取實例緩衝中的位置偏移,用它來偏移每一個頂點,從而畫出不一樣的實例:
struct VertexInputType
{
float4 position : POSITION;
float3 normal : NORMAL;
float2 tex : TEXCOORD0; //紋理座標
float4 Kd : DIFFUSE;
float4 Ks: SPECULAR;
float3 instancePosition : TEXCOORD1;
};
…
// 用實例數據更新頂點位置.
input.position.x += input.instancePosition.x;
input.position.y += input.instancePosition.y;
input.position.z += input.instancePosition.z;
注意:對本章中的例子,咱們經過偏移位置獲得不一樣的實例,從而畫出不一樣的樹,可是咱們只用了一個世界矩陣,對這些不一樣實例billboard,咱們應該用不一樣世界矩陣,就是應該考慮每一個實例的位置偏移,以便每一個billboard樹都能面向攝像機的方向。但咱們如今這種方法,在cpu端計算世界矩陣可能並不太適合實例billboard。
程序執行後界面以下:
完整的代碼請參考:
工程文件myTutorialD3D11_46
代碼下載:
http://files.cnblogs.com/mikewolf2002/d3d1139-49.zip