原文: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

Directx11教程(52) 实例(instancing)的简单应用的更多相关文章

  1. Directx11教程(54) 简单的基于GS的billboard实现

    原文:Directx11教程(54) 简单的基于GS的billboard实现     本章我们用一个billboard的实现来学习D3D11中的GS.     在VS shader中,我们输入的是顶点 ...

  2. Directx11教程(51) 简单的billboard

    原文:Directx11教程(51) 简单的billboard        billboard称作公告板,通常用一个quad(四边形)表示[有的billboard用两个正交的quad表示],它的特点 ...

  3. Directx11教程(42) 纹理映射(12)-简单的bump mapping

    原文:Directx11教程(42) 纹理映射(12)-简单的bump mapping        有时候,我们只有一个粗糙的模型,但是我们想渲染纹理细节,比如一个砖墙,我们如何在只有一个平面的时候 ...

  4. Directx11教程(20) 一个简单的水面

    原文:Directx11教程(20) 一个简单的水面 nnd,以前发的这篇教程怎么没有了?是我自己误删除了,还是被系统删除了? 找不到存稿了,没有心情再写一遍了.      简单说一下,本篇教程就是实 ...

  5. Directx11教程(19) 画一个简单的地形

    原文:Directx11教程(19) 画一个简单的地形       通常我们在xz平面定义一个二维的网格,然后y的值根据一定的函数计算得到,比如正弦.余弦函数的组合等等,可以得到一个看似不错的地形或者 ...

  6. Directx11教程(6) 画一个简单的三角形(2)

    原文:Directx11教程(6) 画一个简单的三角形(2)      在上篇教程中,我们实现了在D3D11中画一个简单的三角形,但是,当我们改变窗口大小时候,三角形形状却随着窗口高宽比例改变而改变, ...

  7. Directx11教程(5) 画一个简单的三角形(1)

    原文:Directx11教程(5) 画一个简单的三角形(1)       在本篇教程中,我们将通过D3D11画一个简单的三角形.在D3D11中,GPU的渲染主要通过shader来操作(当然还有一些操作 ...

  8. Directx11教程(15) D3D11管线(4)

    原文:Directx11教程(15) D3D11管线(4) 本章我们首先了解一下D3D11中的逻辑管线,认识一下管线中每个stage的含义. 参考资料:http://fgiesen.wordpress ...

  9. Directx11 教程(1) 基本的windows应用程序框架(1)

    原文:Directx11 教程(1) 基本的windows应用程序框架(1)        在vs2010中,建立一个新的win32工程,名字是: myTutorialD3D11, 注意:同时勾选Cr ...

随机推荐

  1. 图像复原MATLAB实现

    前言:本篇博客先介绍滤波器滤除噪声,再介绍滤波器复原,侧重于程序的实现. 一:三种常见的噪声 二:空间域滤波 空间域滤波复原是在已知噪声模型的基础上,对噪声的空间域进行滤波.空间域滤波复原方法主要包括 ...

  2. (转载)My97 datepicker使用指南

    这里先显示大家都可以看到的My97DatePicker的用法: WdatePicker日历控件使用方法(http://www.cnblogs.com/yuhanzhong/archive/2011/0 ...

  3. 转:LPC2214的PLL与定时器设置

    原地址:http://blog.sina.com.cn/s/blog_4419d72d0100mu7h.html LPC2214的PLL与定时器设置 http://www.dpj365.cn/bbs/ ...

  4. 转:linux select 多路复用机制

    源地址:http://blog.csdn.net/turkeyzhou/article/details/8609360 2013-02-25 14:18 442人阅读 评论(1) 收藏 举报   目录 ...

  5. 原生js实现头像大屏随机显示

    效果如下图所示: 一.html部分 <div class="myContainer"> <ul> <li class="first" ...

  6. 013- unittest单元测试框架

    unittest单元测试框架 重要的概念 1. TestCase TestCase 是最小的测试单元,用于检查特定输入集合的特定返回值.unittest提供了TestCase基类,我们创建的测试类需要 ...

  7. 单例模式(Singleton)(单一实例)

    单例模式基本要点: 用于确保一个类只有一个实例,并且这个实例易于被访问. 让类自身负责保存他的唯一实例.这个类可以保证没有其他实例创建,并且他可以提供一个访问实例的方法,来实现单例模式. (1)把构造 ...

  8. webServices学习四(---WebService监听工具)

    之前我们使用过HttpWatch获取的HTTP的调用过程,并获得了HTTP的请求头及其他请求的详细信息. 既然WebServie也是通过HTTP进行通信的,能不使用HTTPWatch来获取它的请求过程 ...

  9. MySQL:比较两个数据表不同部分

    简单比较 1.SELECT * FROM t2 WHERE id NOT IN (SELECT id FROM t1); 2.SELECT * FROM t2 WHERE NOT EXISTS(SEL ...

  10. IO流2 --- File类的常用方法1 --- 技术搬运工(尚硅谷)

    File类的获取功能 @Test public void test2(){ File file1 = new File("hello.txt"); File file2 = new ...