有些时候,咱们须要在场景中渲染大量的重复的物体,好比体育场中的观众,森林里面的树木等等,这些物体具备类似的形状,好比不少树木,只是位置不一样,或者贴图不一样而已,若是重复渲染这些树木,用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