Inside Geometry Instancing(下)

此教程版权归我所有,仅供个人学习使用,请勿转载,勿用于任何商业用途。商业应用请同我联系。
由于本人水平有限,难免出错,不清楚的地方请大家以原著为准。也欢迎大家和我多多交流。
其中部分图片来自网络,尽量保证了和原书中插图一致。
特别感谢mtt重现了文章中的流程图^_^
翻译:clayman
Blog:http://blog.csdn.net/soilwork
 

3.3.3 Vertex Constants Instancing

在vertex constants instancing方法中,我们利用顶点常量来储存实体属性。就渲染性能而言,顶点常量批次是非常快的,同时支持实体位置的移动,但这些特点都是以牺牲可控性为代价的。

以下是这种方法主要的限制:

个实体。但是,这足以满足减少CPU调用绘图函数的负载。

l          不支持skinning;顶点常量全部用于储存实体属性了

l          需要支持vertex shaders的硬件

首先,需要准备一块静态的顶点缓冲(同样包括索引缓冲)来储存同一几何包的多个副本,每个副本都以模型坐标空间保存,并且对应批次中的一个实体。

必须更新最初的顶点格式,为每个顶点添加一个整数索引值。对每个实体来说,这个值将是一个常量,标志了特定几何包属于哪个实体。这和palette skinning有些类似,每个顶点都包含了一个索引,指向将会影响他的一个或多个骨骼。

更新之后的顶点格式如下:

Stuct InstanceVertex

{

D3DVECTOR3  mPosition;

//other properties……

WORD     mInstanceIndex[4];  //Direct3D requires SHORT4

};

在所有实体数据都添加到几何批次之后,Commit()方法将按照正确的设计,准备好顶点缓冲。

接下来就是为每个需要渲染的实体加载属性。我们假设属性只包括描述实体位置和朝向的模型矩阵,以及实体颜色。

个实体。

以下是Update()方法。实际的实体将在vertex shader进行处理。

D3DVECTOR4  instancesData[MAX_NUMBER_OF_CONSTANTS];

unsigned int count = 0;

for(unsigned int i=0; i<GetInstancesCount(); ++i)

{

//write model matrix

instancesData[count++] = *(D3DXVECTOR4*) & mInstances[i].mModeMatrix.m11;

instancesData[count++] = *(D3DXVECTOR4*) & mInstances[i].mModelMatrix.m21;

instancesData[count++] = *(D3DXVECTOR4*) & mInstances[i].mModelMatrix.m31;

instancesData[count++] = *(D3DXVECTOR4*) & mInstances[i].mModelMatrix.m41;

//write instance color

instaceData[count++] = ConverColorToVec4(mInstances[i].mColor);

}

lpDevice->SetVertexConstants(INSTANCES_DATA_FIRST_CONSTANT, instancesData, count);

下面是vertex shader:

//vertex input declaration

struct vsInput

{

float4 postion : POSITON;

float3 normal : NORMAL;

//other vertex data

int4 instance_index : BLENDINDICES;

};

vsOutput VertexConstantsInstancingVS( in vsInput input)

{

//get the instance index; the index is premultiplied by 5 to take account of the number of constants used by each instance

int instanceIndex = ((int[4])(input.instance_index))[0];

//access each row of the instance model matrix

float4 m0 = InstanceData[instanceIndex + 0];

float4 m1 = InstanceData[instanceIndex + 1];

float4 m2 = InstanceData[instanceIndex + 2];

float4 m3 = InstanceData[instanceIndex + 3];

//construct the model matrix

float4x4 modelMatrix = {m0, m1, m2, m3}

//get the instance color

float instanceColor = InstanceData[instanceIndex + 4];

//transform input position and normal to world space with the instance model matrix

float4 worldPostion = mul(input.position, modelMatrix);

float3 worldNormal = mul(input.normal, modelMatrix;

//output posion, normal and color

output.position = mul(worldPostion, ViewProjectionMatrix);

output.normal = mul(worldPostion,ViewProjectionMatrix);

output.color = instanceColor;

//output other vertex data

}

Render()方法设置观察和投影矩阵,并且调用一次DrawIndexedPrimitive()方法提交所有实体。

左右。之后,在vertex
shader中重新构造矩阵,当然,这也增加了编码的复杂度和执行时间。

3.3.4 Batching with the Geometry Instancing API

最后介绍的一种方法就是在DirectX9中引入的,完全可由Geforce
6系列GPU硬件实现的几何实体API批次。随着原来越多的硬件支持几何实体API,这项技术将变的更加有趣,它只需要占用非常少的内存,另外也不需要太多CPU的干涉。它唯一的缺点就是只能处理来自同一几何包的实体。

DirectX9提供了以下函数来访问几何实体API:

HRESULT SetStreamSourceFreq( UINT StreamNumber, UINT FrequencyParameter);

StreamNumber是目标数据流的索引,FrequencyParameter表示每个顶点包含的实体数量。

快顶点缓冲:一块静态缓冲,用来储存将被多次实体化的单一几何包;一块动态缓冲,用来储存实体数据。两个数据流如下图所示:

Commit()必须保证所有几何体都使用了同一几何包,并且把几何体的信息复制到静态缓冲中。

Update()只需简单的把所有实体属性复制到动态缓冲中。虽然它和动态批次中的Update()方法很类似,但是却最小化了CPU的干涉和图形总线(AGP或者PCI-E)带宽。此外,我们可以分配一块足够大的顶点缓冲,来满足所有实体属性的需求,而不必担心显存消耗,因为每个实体属性只会占用整个几何包内存消耗的一小部分。

Render()方法使用正确流频率(stream frequency)设置好两个流,之后调用DrawIndexedPrimitive()方法渲染同一批次中的所有实体,其代码如下:

unsigned int instancesCount = GetInstancesCount();

//set u stream source frequency for the first stream to render instancesCount instances

//D3DSTREAMSOURCE_INDEXEDDATA tell Direct3D we’ll use indexed geometry for instancing

lpDevice->SetStreamSourceFreq(0, D3DSTREAMSOURCE_INDEXEDDATA | instancesCount);

//set up first stream source with the vertex buffer containing geometry for the geometry packet

lpDevice->setStreamSource(0, mGeometryInstancingVB[0], 0, mGeometryPacketDeck);

//set up stream source frequency for the second stream; each set of instance attributes describes one instance to be rendered

lpDevice->SetstreamSouceFreq(1, D3DSTREAMSOURCE_INDEXEDDATA | 1);

// set up second stream source with the vertex buffer containing all instances’ attributes

pd3dDevice->SetStreamSource(1, mGeometryInstancingVB[0], 0, mInstancesDataVertexDecl);

GPU通过虚拟复制(virtually duplicating)把顶点从第一个流打包到第二个流中。vertex
shader的输入参数包括顶点在模型空间下的位置,以及额外的用来把模型矩阵变换到世界空间下的实体属性。代码如下:

// vertex input declaration

struct vsInput

{

//stream 0

float4 position : POSITION;

float3 normal  : NORMAL;

//stream 1

float4 model_matrix0   :  TEXCOORD0;

float4 model_matrix1   :  TEXCOORD1;

float4 model_matrix2   :  TEXCOORD2;

float4 model_matrix3   :  TEXCOORD3;

float4 instance_color    :  D3DCOLOR;

};

vsOutput geometryInstancingVS(in vsInput input)

{

//construct the model matrix

float4x4 modelMatrix =

{

input.model_matrix0,

input.model_matrix1,

input.model_matrix2,

input.model_matrix3,

}

//transform inut position and normal to world space with the instance model matrix

float4 worldPosition = mul(input.position, modelMatrix);

float3 worldNormal = mul(input.normal,modelMatrix);

//output positon, normal ,and color

output.positon = mul(worldPostion,ViewProjectionMatrix);

output.normal = mul(worldNormal,ViewProjectionMatrix);

output.color = int.instance_color;

//output other vertex data…..

}

由于最小化了CPU负载和内存占用,这种技术能高效的渲染同一几何体的大量副本,因此,也是游戏中理想的解决方案。当然,它的缺点在于需要硬件功能的支持,此外,也不能轻易实现skinning。

如果需要实现skinning,可以尝试把所有实体的所有骨骼信息储存为一张纹理,之后为相应的实体选择正确的骨骼,这需要用到Shader
Model3.0中的顶点纹理访问功能。如果使用这种技术,那么访问顶点纹理带来的性能消耗是不确定的,应该实现进行测试。

3.4 结论

中不同的技术,来达到高效渲染同一几何体多次的目的。每一种技术都有有点和缺点,没有哪种单一的方法能完美解决游戏场景中可能遇到的问题。应该根据应用程序的类型和渲染的物体种类来选择相应的方法。

一下是一些场景中建议使用的方法:

l          对于包含了同一几何体大量静态实体的室内场景,由于他们很少移动,静态批次是最好的选择。

l          包含了大量动画实体的户外场景,比如包含了数百战士的即时战略游戏,动态批次也许是最好的选择。

l          包含了大量蔬菜和树木的户外场景,通常需要对他们的属性进行修改(比如实现随风而动的效果),以及一些粒子系统,几何批次API也许就是最好的选择。

通常,同一应用程序会用到两个以上的方法。这种情况下,使用一个抽象的几何批次接口隐藏具体实现,能让引擎更容易进行模块化和管理。这样,对整个程序来说,几何实体化的实现工作也能减少很多。

(图中,静态的建筑使用了静态批次,而树则使用了几何实体API)

点击这里可以下载完整的PDF文档,完整的demo大家可以参考NVIDIA SDK中的示例Instancing,也可以直接在这里下载。另外也可参考DirectX SDK中的示例Instancing。

Inside Geometry Instancing(下)的更多相关文章

  1. Inside Geometry Instancing(上)

    Inside Geometry Instancing(上) http://blog.csdn.net/soilwork/article/details/598335 翻译:claymanclayman ...

  2. Unity GPU Instancing的使用尝试

    似乎是在Unity5.4中开始支持GPU Instacing,但如果要比较好的使用推荐用unity5.6版本,因为这几个版本一直在改. 这里测试也是使用unity5.6.2进行测试 在5.6的版本里, ...

  3. ArcGIS Engine开发前基础知识(1)

    ArcGIS二次开发是当前gis领域的一项重要必不可少的技能.下面介绍它的基本功能 一.ArcGIS Engine功能 在使用之前首先安装和部署arcgis sdk,(在这里不在赘述相关知识)可以实现 ...

  4. ZBrush中的动态网格该怎么进行运用

    DynaMesh是ZBrush最新的基础模型创建工具,该命令用于基本模型的起稿到中模的制作.使用DynaMesh完全不启用考虑模型的拓扑,可以从一个图形拉扯出整个模型的分支,本文将以一个实例简单介绍Z ...

  5. nginx常见内部参数,错误总结

    1.日志简介 nginx日志主要有两种:访问日志和错误日志.访问日志主要记录客户端访问nginx的每一个请求,格式可以自定义:错误日志主要记录客户端访问nginx出错时的日志,格式不支持自定义.两种日 ...

  6. D3D9 GPU Hacks (转载)

    D3D9 GPU Hacks I’ve been trying to catch up what hacks GPU vendors have exposed in Direct3D9, and tu ...

  7. [转]GLES 3.0 新特性

    转自: http://www.ifanr.com/131333 OpenGL ES 3.0 带来很多新特性,根据 AnandTech 的解释: 支持更多缓冲区对象.在 OpenGL ES 2.0 时中 ...

  8. nginx模块开发(18)—日志分析

    1.日志简介 nginx日志主要有两种:访问日志和错误日志.访问日志主要记录客户端访问nginx的每一个请求,格式可以自定义:错误日志主要记录客户端访问nginx出错时的日志,格式不支持自定义.两种日 ...

  9. nginx 错误日志分析 以及说明

    1.日志简介 nginx日志主要有两种:访问日志和错误日志.访问日志主要记录客户端访问nginx的每一个请求,格式可以自定义:错误日志主要记录客户端访问nginx出错时的日志,格式不支持自定义.两种日 ...

随机推荐

  1. MFC HTTP(S)请求笔记

    GET示例 #include <afxinet.h> #include <iostream> #include <vector> #ifdef _UNICODE # ...

  2. mongodb学习之:安全和认证

    mongodb默认是不认证的,默认没有账号,只要能连接上服务就可以对数据库进行各种操作,mongodb认为安全最好的方法就是在一个可信的环境中运行它,保证之后可信的机器才能访问它.因此需要在登录的时候 ...

  3. 我的前端之旅-nodejs 安装静态的文件服务器 (1)

    一个最简单的 Web Server 之功能包含下列三个步骤:步骤一 : 接收浏览器所传来的网址:步骤二 : 取出相对应的文件:步骤三 : 将文件内容传回给浏览器.然而.在这个接收与传回的过程中,所有的 ...

  4. Label标签 自动触发onclick,点击内部的Input

    最近项目遇到了一个bug,点击外层元素会直接触发元素内部的input框.(外层元素用的是label包裹的).找了很久才发现是label标签造成的. label定义和用法: label 标签为 inpu ...

  5. html video api控件总结

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  6. Codeforces Round #373 (Div. 2) Anatoly and Cockroaches —— 贪心

    题目链接:http://codeforces.com/contest/719/problem/B B. Anatoly and Cockroaches time limit per test 1 se ...

  7. IPFS中文简介

    ipfs是什么? 它是一个协议也是一个网络,已经运行了2年半,并非虚无缥缈的空气. 它像比特币网络一样,并没有发明什么新技术,他只是将很多种技术(P2P网络技术,bt传输技术,Git版本控制,自证明文 ...

  8. codeforces D. Toy Sum 解题报告

    题目链接:http://codeforces.com/problemset/problem/405/D 题目意思:从 1 - 1000000 中选择 n 个数:x1,x2,...,xn,对 x1-1, ...

  9. MySql服务未知原因消失了的终极解决办法

    刚才突然MySQL-Front打开不了MySql,果不其然发现是MySql的服务不见了.想起可能是昨晚安装了PhpStudy的点了不要MySql的选项,它把我本身的MYSQL服务给卸载了. 解决办法  ...

  10. 动态调试Android程序

    最近好几天来一直在看动态调试.首先是这一篇(http://www.52pojie.cn/forum.php?mod=viewthread&tid=293648)里面介绍了多种IDA动态调试的情 ...