1. 程式人生 > >Directx11教程(52) 實例(instancing)的簡單應用

Directx11教程(52) 實例(instancing)的簡單應用

dir amp manage utl 攝像機 con res mage 直觀

原文:Directx11教程(52) 實例(instancing)的簡單應用

有些時候,我們需要在場景中渲染大量的重復的物體,比如體育場中的觀眾,森林裏面的樹木等等,這些物體具有相似的形狀,比如很多樹木,只是位置不同,或者貼圖不同而已,如果重復渲染這些樹木,用billboard技術,n棵樹,就要輸入n*4個頂點,在樹木很多的時候,這也是比不小的開銷,因為每次都要在system memory和gpu之間傳輸數據。

在D3D11中,通過使用實例技術,可以有效的減少這種開銷。

實例技術的主要實現方式:定義一個頂點緩沖,然後再定義第二個緩沖,稱作實例緩沖,只記錄物體變化的信息,比如改變位置的信息等等。這樣只需傳入少量的頂點就可以有效的渲染大量的物體

下面,我們在myTutorialD3D11_45的基礎上,通過實例技術,畫4棵樹。

1、首先修改MirrorModelClass,因為我們的樹通過billboard實現,這個類用來定義頂點和實例緩沖信息(因為我們的代碼是修改來的,所以我並沒有改類的名字,按道理說,用TreeModelClass或許更直觀一些)

定義實例類結構,我們只改變物體的位置,所以結構非常簡單,然後再定義實例緩沖和實例計數(去掉了索引緩沖)。

struct InstanceType { D3DXVECTOR3 position; };

ID3D11Buffer* m_instanceBuffer; //實例緩沖
int m_instanceCount;

實例緩沖定義的代碼如下:

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

http://files.cnblogs.com/mikewolf2002/pictures.zip

Directx11教程(52) 實例(instancing)的簡單應用