DirectX11 使用Instancing技术提高重复模型的绘制效率
来源:互联网 发布:通过 相信 马克思 知乎 编辑:程序博客网 时间:2024/05/18 14:12
在游戏场景中,经常需要我们绘制大量相同的模型,比如英雄联盟中,战场上的小兵可以达到非常多的数量。
(图为英雄联盟游戏截图)
如果我们用以前的方法绘制,一个模型draw call一次,那么就会造成巨大的性能损耗。因为每次数据从内存传入显存都需要不少时间;而且每次draw call需要CPU和GPU进行周期同步,会导致CPU或GPU在等待;每次传入顶点数据后,都需要走一遍渲染管线,上下文也要进行切换等等。如果我们有一种方法,对于模型相同的游戏对象,只需要传一次模型的顶点数据,每个实例也可以包含不同的位置信息、颜色信息等,那么就可以极大增加渲染效率。DirectX10开始有Instancing技术的硬件支持,帮助我们完成解决这个问题。(这个问题在之前要通过程序员自己batch合并模型顶点来一次draw call来完成,而现在是通过硬件支持,更加方便)
一、指定输入布局
顶点元素描述如下:
typedef struct D3D11_INPUT_ELEMENT_DESC { LPCSTR SemanticName; UINT SemanticIndex; DXGI_FORMAT Format; UINT InputSlot; UINT AlignedByteOffset; D3D11_INPUT_CLASSIFICATION InputSlotClass; UINT InstanceDataStepRate;} D3D11_INPUT_ELEMENT_DESC;
有几个新增的字段需要介绍一下,
- InputSlotClass:为了区分顶点数据和实例数据,在输入布局元素中的InputSlotClass字段应当用
D3D11_INPUT_PER_VERTEX_DATA
和D3D11_INPUT_PER_INSTANCE_DATA
进行区分。 - InstanceDataStepRate:指定每个实例数据有多少个实例要绘制。比如说我们有3种不同的实例颜色,但是有6个模型,我们可以设置2,那么前2个、中间2个、后面2个都分别占有不同的三种颜色。如果每个实例都有一个实例数据,那么就设为1。顶点数据只需要设为0。
我们的Demo中,实例数据包含了世界矩阵和每个实例的颜色,下面是一段代码示例:
const D3D11_INPUT_ELEMENT_DESC InputLayoutDesc::InstancedBasic32[8] ={{"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0},{"NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0},{"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 24, D3D11_INPUT_PER_VERTEX_DATA, 0},{"WORLD", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, 0, D3D11_INPUT_PER_INSTANCE_DATA, 1},{"WORLD", 1, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, 16, D3D11_INPUT_PER_INSTANCE_DATA, 1},{"WORLD", 2, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, 32, D3D11_INPUT_PER_INSTANCE_DATA, 1},{"WORLD", 3, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, 48, D3D11_INPUT_PER_INSTANCE_DATA, 1},{"COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, 64, D3D11_INPUT_PER_INSTANCE_DATA, 1}};
二、顶点结构
因为矩阵包含了四行的四列浮点数,所以上面有四个WORD实例数据,代表着不同行。我们在着色器中定义的顶点结构如下:
struct VertexIn{ float3 PosL : POSITION; float3 NormalL : NORMAL; float2 Tex : TEXCOORD; row_major float4x4 World : WORLD; float4 Color : COLOR; uint InstanceId : SV_InstanceID;};
三、绘制
我们不再用以前的Draw或DrawIndexed方法来绘制,而是要用DrawIndexedInstanced来使用instancing技术。它的函数声明如下:
void ID3D11DeviceContext::DrawIndexedInstanced( UINT IndexCountPerInstance, UINT InstanceCount, UINT StartIndexLocation, INT BaseVertexLocation, UINT StartInstanceLocation);
IndexCountPerInstance:每个实例的索引个数。我们不需要指定所有实例的索引了,因为每绘制一个实例都会使用相同的索引。
InstanceCount:绘制实例的个数。
StartIndexLocation:索引的开始位置。
BaseVertexLocation:添加到索引开始前的基本顶点。
StartInstanceLocation:第一个实例在实例缓存开始的位置。
下面是一个调用实例:
md3dImmediateContext->DrawIndexedInstanced(mSkullIndexCount, // number of indices in the skull meshmVisibleObjectCount, // number of instances to draw0, 0, 0);
顶点着色器等函数与以前的也没有多大的区别,因为DirectX会自动处理不同的实例,将其当做一个模型的普通的顶点来处理即可:
struct VertexIn{ float3 PosL : POSITION; float3 NormalL : NORMAL; float2 Tex : TEXCOORD; row_major float4x4 World : WORLD; float4 Color : COLOR; uint InstanceId : SV_InstanceID;};struct VertexOut{ float4 PosH : SV_POSITION; float3 PosW : POSITION; float3 NormalW : NORMAL; float2 Tex : TEXCOORD; float4 Color : COLOR;};VertexOut VS(VertexIn vin){ VertexOut vout; // Transform to world space space. vout.PosW = mul(float4(vin.PosL, 1.0f), vin.World).xyz; vout.NormalW = mul(vin.NormalL, (float3x3)vin.World); // Transform to homogeneous clip space. vout.PosH = mul(float4(vout.PosW, 1.0f), gViewProj); // Output vertex attributes for interpolation across triangle. vout.Tex = mul(float4(vin.Tex, 0.0f, 1.0f), gTexTransform).xy; vout.Color = vin.Color; return vout;}
最后程序运行结果:
程序示例中在不同的位置绘制不同颜色的大量相同模型,每帧只需要一次DrawIndexedInstanced调用,因此比起以前效率高了很多呢。
项目源代码:
https://github.com/ljcduo/Introduction-to-3D-Game-Programming-With-DirectX11/tree/master/Chapter%2015%20Instancing%20and%20Frustum%20Culling/InstancingAndCulling
- DirectX11 使用Instancing技术提高重复模型的绘制效率
- 提高GDI+绘制效率的一些方法
- 提高SVN的使用效率
- C#中提高绘制控件时的效率
- Opengl instancing技术
- 使用bitblt提高GDI+绘图的效率
- 【分享Windows7提高使用效率的快捷键】
- 使用 Emmet 提高编写 CSS 的效率
- 使用 Emmet 提高编写 CSS 的效率
- 使用ViewHolder提高ListView的效率
- 提高UBUNTU使用效率
- 提高eclipse使用效率
- 提高eclipse使用效率
- 提高Eclipse使用效率
- 提高eclipse使用效率
- Directx11的XMVECTOR使用规则
- 利用GDI+的双缓冲技术来提高绘图效率
- 利用GDI+的双缓冲技术来提高绘图效率
- R语言入门学习--数据类型
- timer的缺陷及使用禁区
- JS方式检验输入的IP
- require.js用法详解
- 如何用proto生成文件
- DirectX11 使用Instancing技术提高重复模型的绘制效率
- Kotlin使用小结
- 我的英语之旅(开始)
- iOS表视图分割线的总结
- 底部导航栏
- 将所有异常打印到日志
- DateTimeField *** received a naive datetime (***) while time zone support is active
- java ssm框架学习——三大框架整合
- android studio中application module,library module,java module的区别