六狼论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

新浪微博账号登陆

只需一步,快速开始

搜索
查看: 346|回复: 0

Directx11教程(52) 实例(instancing)的简单应用

[复制链接]

升级  91.33%

154

主题

154

主题

154

主题

举人

Rank: 3Rank: 3

积分
474
 楼主| 发表于 2012-12-30 11:51:55 | 显示全部楼层 |阅读模式
Directx11教程(52) 实例(instancing)的简单应用

<div class="postbody"><div id="cnblogs_post_body">     有些时候,我们需要在场景中渲染大量的重复的物体,比如体育场中的观众,森林里面的树木等等,这些物体具有相似的形状,比如很多树木,只是位置不同,或者贴图不同而已,如果重复渲染这些树木,用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
      
  
  
  
  
  
  
  
  
  
   
您需要登录后才可以回帖 登录 | 立即注册 新浪微博账号登陆

本版积分规则

快速回复 返回顶部 返回列表