Inside Geometry Instancing(下)

GPU Instancing 原理解析与 Unity 批量优化指南 GPU Instancing 是一种渲染优化技术,允许使用单一绘制调用(Draw Call)渲染多个相同网格的不同实例。它通过一次性将模型数据和多个实例的变换数据一起发送到 GPU,显著减少了 CPU 与 GPU 之间的通信开销。 阅读详情
Inside Geometry Instancing(下)
此教程版权归我所有,仅供个人学习使用,请勿转载,勿用于任何商业用途。商业应用请同我联系。
由于本人水平有限,难免出错,不清楚的地方请大家以原著为准。也欢迎大家和我多多交流。
其中部分图片来自网络,尽量保证了和原书中插图一致。
特别感谢mtt重现了文章中的流程图^_^
翻译:clayman
Blog:http://blog.youkuaiyun.com/soilwork
 
 

3.3.3 Vertex Constants Instancing

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

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

l          根据常理数值的大小,每批次的实体数量是受限制的;通常对一次方法调用来说,批次中不会超过50到100个实体。但是,这足以满足减少CPU调用绘图函数的负载。

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

l          需要支持vertex shaders的硬件

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

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

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

Stuct InstanceVertex

{

         D3DVECTOR3  mPosition;

         //other properties……

         WORD     mInstanceIndex[4];  //Direct3D requires SHORT4

};

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

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

对于支持DirectX9系列的GPU来说,最多能使用256个顶点常量:我们使用其中的200个来保存实体属性。在我们所举的例子中,每个实体需要4个常量储存模型矩阵,1个常量储存颜色,这样每个实体需要5个常量,因此每批次最多包含40个实体。

以下是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

                   float 4 m 0 = InstanceData[instanceIndex + 0];

                   float 4 m 1 = InstanceData[instanceIndex + 1];

                   float 4 m2 = InstanceData[instanceIndex + 2];

                   float 4 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()方法提交所有实体。

         实际代码中,可以把模型空间的旋转部分储存为一个四元数(quaternion),从而节约2个常量,把最大实体数增加到70左右。之后,在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表示每个顶点包含的实体数量。

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

         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 结论

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

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

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

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

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

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

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

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

Unity 性能优化之GPU Instancing(五) GPU Instancing也是一种Draw call的优化方案,使用一个Draw call就能渲染具有多个相同材质的网格对象。而这些网格的每个copy称为一个实例(Instancing)。使用GPU Instancing可以在一个Draw Call中同时渲染多个相同或类似的物体,从而减少CPU和GPU的开销。 阅读详情

相关推荐

推荐系统召回总结(持续更新)

推荐系统架构 候选池:可供推荐的物料候选池,对于图文、短视频、新闻等来说通常是最近N天的新物料组成的集合,定期进行物料更新。 召回:从候选池中选出和用户相关的物料,交给后面的排序模块进行打分。召回面对的候选集通常是数十万或者百万甚至千万的量级,检索过程必须轻量快速。通常由多路召回组成,保证不遗漏且相关性、多样性尽量好,可以分为非个性化召回和个性化召回。 粗排:对召回选择的几千或者几百候选物料进行排序,快速打分选出topN,减少精排的压力,兼顾精准与效率。 精排:对粗排的结果进行精准排序,这个环节需要特征尽可

whgyxy的博客 2515

GPU Instancing详解

一种Draw call的优化方案,使用一个Draw call就能渲染具有多个相同材质的网格对象。而这些网格的每个copy称为一个实例(Instancing)。此技术在一个场景中对于需要绘制多个相同对象来说是一个行之有效办法,例如树木 灌木丛 或者大量 重复性物体。PropertiesSubShaderLOD 100PassCGPROGRAM//第一步: sharder 增加变体使用shader可以支持instance//第二步:instancID 加入顶点着色器输入结构。

qqaazzzqaz的专栏 1800

Android 13/14/15 默认授权应用权限的实现方法

Android版本修改方式实现原理13/14修改Permission.java将所有权限标记为Normal权限15修改DeviceIdleController.java + DefaultPermissionGrantPolicy.java监听应用安装并主动授权。

m0_60898338的博客 2909

GPU Instancing

(GPU 实例化)是一种现代图形渲染技术,允许你用一次 Draw Call在 GPU 上同时渲染大量相同 Mesh 和材质但变换(位置、旋转、缩放)不同的物体。典型应用:大面积草地、森林、士兵、金币、粒子等。是现代渲染中高效渲染大量相同物体的利器。它比动态合批更高效,适合大规模重复物体。只需保证 Mesh 和材质一致,Shader 支持 Instancing,即可享受极低 Draw Call 的高性能渲染。下面我将继续深入讲解。

本博客聚焦游戏开发技巧、创业实战心得及大学热门课程干货。这里既有技术深度,也有思维广度,无论你是开发者、创业者还是高校学子,都能在这里找到适合自己的成长之路! 646

【代码】获取高级行情数据,并通过 qmt 进行实际下单交易,自动实现止盈止损管理

【代码】获取高级行情数据,并通过 qmt 进行实际下单交易,自动实现止盈止损管理

XD1996CD的博客 4108

微信服务号配置总是提示“参数错误”?我就是这样一步步查出问题的

前两天,我遇到了一个很诡异的问题:在微信服务号后台配置服务器地址时,总是提示“参数错误,请重新填写”。

代码简单说 Vue、JAVA、PHP、Node.js 熟练运用,接口、架构、性能全搞定。 1277

Inside Geometry Instancing(上)

Inside Geometry Instancing(上) 翻译:claymanclayman_joe@yahoo.com.cn仅供个人学习使用,勿用于任何商业用途,转载请注明作者^_^注:呵呵,发现我对翻译东西上瘾了。这次翻译了《GPU Gem2》中第三章的内容,大家共同学习^_^在交互式程序中,丰富用户体验的重要方法之一就是呈现一个充满大量各种有趣物体的世界。从数不清的草丛、树

Clayman的专栏 9581

解锁VS2022!手把手教你本地部署Deepseek

在 VS2022 中本地部署 Deepseek,我们首先需要确保硬件资源的充足,如高性能的 GPU、大容量内存和高速存储等,这是模型稳定运行的基石。如果生成的摘要能够准确概括原文的核心内容,就表明 Deepseek 模型在文本分析方面的功能正常,部署稳定可靠。这对于开发者来说,能够大大提高开发效率,当遇到一些常见的功能实现时,可以借助 Deepseek 模型快速生成基础代码框架,减少手动编写的工作量。在大语言模型的蓬勃发展的当下,Deepseek 凭借其卓越的性能和创新的技术,已然成为该领域的佼佼者。

远程部署调试 运行安装 项目调试 二次开发 项目技术新 持续迭代 部分源码免费分享 1万+

Inside Geometry Instancing

<br />翻译:clayman<br />clayman_joe@yahoo.com.cn<br />仅供个人学习使用,勿用于任何商业用途,转载请注明作者^_^ <br />注:这篇文章是《GPU Gems2》中的第三章,本人水平有限,不清楚的地方请大家以原文为准<br />  在交互式程序中,丰富用户体验的重要方法之一就是呈现一个充满大量各种有趣物体的世界。从数不清的草丛、树木到普通杂物:所有这些都能提高画面最终的效果,让用户保持“幻想状态(suspension of disbelief)”。只有用户相

痞子龙3D编程 2684

从 VL02N 拣配到 ABAP 自动化,读懂 SD_DELIVERY_UPDATE_PICKING 的位置和风险

文章摘要:SAP SD模块中,SD_DELIVERY_UPDATE_PICKING函数模块用于处理出库交货单的拣配确认,更新交货项目的拣配数量。该函数适用于未启用WM/EWM的传统SD流程,能确保拣配状态与标准交货处理逻辑同步,比直接修改数据库更安全。但在S/4HANA Cloud等新环境中,应优先使用API_OUTBOUND_DELIVERY_SRV等公开发布接口。使用该函数时需注意参数完整性和按交货单整体处理的原则,避免逐项提交导致的数据不一致问题。(149字)

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 245

WblockCloneObjects克隆一个图纸内的实体包括块参照,同名块参照的处理

public static void HeBingTu2(double lieJu, List<string> filePaths) { Document doc = Application.DocumentManager.MdiActiveDocument; Database db = doc.Database; using (DocumentLock docLock = doc.Lock...

1108
上一篇: Inside Geometry Instancing(上)
下一篇: Using Vertex Texture Displacement for Realistic Water Rendering(上)
soilwork
博客等级 码龄23年 290粉丝 116原创
评论 7
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值