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

 

 

 

 

 

 

 

 

 

 

 

 

时间: 2024-09-20 22:53:48

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

PHP教程.应用实例5

教程|应用实例 php生成WAP页面 WAP(无线通讯协议)是在数字移动电话.个人手持设备(PDA等)及计算机之间进行通讯的开放性全球标准.由于静态的WAP页面在很多方面不能满足用户个性化的服务请求,因此通过WAP服务器端语言产生动态的WML页面,具有很广泛的应用价值和很高的商业价值. WAP应用结构非常类似于Internet,一个典型的WAP应用请求是这样的:首先,具有WAP用户代理功能的移动终端(WAP手机等)通过内部运行的微浏览器(Micro Browser)对某一网站以无线方式发送WAP

PHP教程.应用实例14

教程|应用实例 多文件上载系统程序<?php //多文件上载系统完整版 include("../include/common.inc"); $title = "多个文件的上载程序"; include("../include/header.inc"); //定义允许上载文件的数目 define("UPLOAD_NO", 10); echo("<p align='center'><font siz

PHP教程.应用实例13

教程|应用实例 PHP中用户身份认证实现二法(2)AddType application/x-httpd-php4 .php3AddType application/x-httpd-php4 .php4Action application/x-httpd-php4 /php4/php.exe 要想使PHP支持更多的后缀名,没问题.在给出的配置文件mod_php4.conf已经支持了三种后缀名php,php3,php4,如果你还想支持更多的后缀名可以更改这个文件,很简单的. 4.测试 用<? ph

PHP教程.应用实例11

教程|应用实例 PHP应用提速面面观PHP最大的优点之一显然在于它的快速度.一般情况下PHP总是具有足够的速度支持Web内容动态生成,许多时候你甚至无法找出比它更快的方法.然而,当你不得不面对庞大的访问量.高负荷的应用.有限的带宽以及其他各种带来性能瓶颈的因素时,你可能会问问自己是否可以做点什么让网站运行得更好.或许只要加上一个很不起眼的免费模块,你的PHP应用性能以及Web服务器响应速度就会有显著的改善.本文讨论的就是如何进一步提高php应用的性能,给用户以更美妙的浏览感受.本文分三个方面(代

PHP教程.应用实例18

教程|应用实例 基于PHP的聊天室(三)现在我们有了需要通过$REMOTE_ADDR变量来交叉引用的文件,这样我们可以区分出想要发贴的用户是否已经被扁或没有被扁.很简单: <?php for ($counter=0;$counter<sizeof($banned_array);$counter++) { if ($banned_array[$counter] == $REMOTE_ADDR) { print("<font color="red" face=&

PHP教程.应用实例16

教程|应用实例 基于PHP的聊天室(一) 这个经过精心设计的web聊天室使用了从表单传递过来的变量,并且把它们处理为HTML ,然后将其写入文件.把表单和信息文件放在一个框架中,你可以看到它与一个叫BeSeen的聊天室很象.当然它的好处在于, 我们的聊天室比起它的BeSeen堂兄弟来说要聪明一些. <form action="chat.php3" method="post"> Name : <input type="text"

PHP教程.应用实例15

教程|应用实例 基于Linux的搜索引擎实现搜索引擎是为用户提供快速获取网页信息的工具,其主要的功能是系统通过用户输入关键字,检索后端网页数据库,将相关网页的链接和摘要信息反馈给用户.从搜索的范围上一般分为站内网页搜索和全局网页搜索.随着网页数量的急剧增加,搜索引擎已经成为上网查询信息的必须手段,各个大型网站均已经提供网页数据搜索服务,并且出现了许多为大型网站提供专业搜索引擎服务的公司,如为Yahoo提供搜索服务的Google,为新浪网和263等国内网站提供服务的百度公司等.专业的搜索服务费用高

PHP教程.应用实例8

教程|应用实例 PHP下定制自己的记数器计数器是让 Web 访客知道该网页或者网站的人气指数最直接的方法,同时,访客人数也是给广告商做广告的最好依据.虽然很多网站都提供免费的计数器,可毕竟不是自己亲手制作的,不能体现出自己的特色.下面就PHP环境下记数器的制作过程进行详细的讨论. 访客计数器的流程如下: 第一位使用者浏览某页. 服务器程式从资料库或档案中读取该页被浏览次数. 将次数加一储存,并将它送回第一位使用者. 下一位使用者浏览某页. 服务器程式从资料库或档案中读取该页被浏览次数. 将次数再

Symfony2在Nginx下的配置方法图文教程_php实例

本文详细讲述了Symfony2在Nginx下的配置方法.分享给大家供大家参考,具体如下: 网上有很多关于symfony2在nginx下的配置文章,如果是小白,按照网上贴出来的配置文件配置,却怎么也不成功,我经过多次摸索,写下心得: 1. 首先开启Nginx的pathinfo 至于什么是pathinfo,可以参考文章<nginx下支持PATH_INFO的方法实例详解>,自行脑补.很多人按照教程配置的时候,会报500的错误,查报错日志也查不出来,八成就是没有开启pathinfo. 如果你的主机上安