实例化的基本原理 收藏

引言: 

     最近刚好有个项目要用到这个技术,就学习了下DXSDK的Sample里面的Instancing10。 
     实例化的技术是利用多个顶点缓冲区有效的合并,减少Draw Call的次数,从而有效的提高程序的性能。实例化比较适合于相同物体的重复渲染,例如粒子系统,草被模拟等等。 
     本文实现了一个非常简单,甚至简陋的实例化的Demo。虽然Demo非常简单,但是实例化的每个必要的元素都已经被体现出来了。相对于Dx的Sample而言简单的多,不过同样可以把这个技术简单解释清楚。

正文: 
     实例化适合于相同物体的反复渲染,例如草,粒子系统等。传统的渲染方法中,相同物体由于一些细节不同,例如位置,颜色等信息,使得Draw Call被迫调用多次。对于很小的元素,例如草,可能视野里面会有上万根草,那么如果仅仅因为纹理,方向甚至位置不同,就必须调用上万次DrawCall,从而严重降低了程序的性能。利用实例化技术,虽然这些Mesh在一些简单的细节上有不同,但是大体上还是一致的,所以可以仅仅用一个Draw Call就可以把所有的Mesh渲染出来,这样节省了CPU与GPU之间的带宽,非常高效。 
     下面是这次Demo的截图:

新建位图图像

      这个Demo的画面非常简单,就是一个静态的粒子集合的渲染。但是所有的球体都是利用一个Draw Call渲染出来的,就是说每一帧只Draw一次。那么下面我简单介绍下这个技术的基本原理: 
      首先需要了解的一个概念是IA(input assembler)。在VertexShader之前,我们的输入是一个或者几个顶点缓冲区,而这些顶点缓冲区并不是VS的输入,事实上VS的输入是经过IA处理后的信息。IA实际上是不可以编程的,它就像一个状态机一样,只能设置简单的几个状态,但是由于不同的应用程序对于IA的要求基本都一致,所以完全没有可编程的必要。IA的目的就是根据顶点缓冲区,图元拓扑结构以及输入的数据格式(Layout)生成VS所需要的信息。我们在渲染任何物体之前,都需要为IA设置好这几个状态,分别是通过IASetInputLayout,IASetVertexBuffers,IASetPrimitiveTopology来搞定的。 
      在简单的了解了IA之后,我们来看一下Instancing的基本思想。其实,实例化就是通过把每个实例的不同的信息存储在缓冲(可能是顶点缓冲,常量Buffer等)里面,然后利用过个顶点缓冲区来设置,从而使生成的每个顶点都包含有自己Custom的数据定义。举个简单的粒子,我们看到这个程序中的每个球体的位置是不同的。按照传统的做法,伪代码如下:

for( i : 0 to sphereNum ) 

     set world matrix 
     draw sphere i 
}

      上面的方法,也是最笨拙的方法。仅仅因为每次的Draw Call调用中world matrix不同,就必须调用多次。而其实每次的调用都是很相似的。其实我们在set world matrix的时候,我们更新的是某一个constant value。从宏观的角度上来说,就是通过constant value来设置球体的位置信息。但是既然我们可以描述每个顶点的法线,纹理坐标等信息,我们完全可以为每个顶点描述world matrix信息,其实就是在VS的输入里面多加一个float4而已(这里我只用了float4,因为球体不需要旋转。如果需要旋转的话,完全可以加四个float4组成一个matrix)。下面的问题是,我们怎么样利用IA生成我们想要的信息: 
      假设我们需要渲染100个球体,每个球体256个三角形。那么我们不可能在CPU端写入256*100个三角形的顶点数据,因为这样以来,从CPU到GPU端的传输代价会非常大,而这些代价完全可以避免(当然,这里面如果预处理的话,这些代价也可以无视,但是这种方法实际上相当于用CPU去做GPU擅长的事情,代码看着很不舒服)。那么实际上我们应该怎么做呢?其实也很简单,Dx10帮我们提供了非常友好的接口。 
      这个Demo里面需要两个顶点缓冲区,一个来描述球体的顶点信息,256个三角形而已(768个顶点)。另一个用来描述位置信息,100个顶点而已。两个加起来还不到1k个顶点,相对于上面的768000个顶点少了上千倍。这些数据是要走PCIE总线的!那么似乎上面的信息无法描述这么多个球体,但是通过IA处理之后,我们完全可以达到同样的效果。唯一需要设置的就是Input Layout。

//the vertex layout 
D3D10_INPUT_ELEMENT_DESC layout[] = 

    { "POSITION" , 0 , DXGI_FORMAT_R32G32B32_FLOAT , 0 , 0 , D3D10_INPUT_PER_VERTEX_DATA , 0 } , 
    { "mTransform" , 0 , DXGI_FORMAT_R32G32B32_FLOAT , 1 , 0 , D3D10_INPUT_PER_INSTANCE_DATA , 1 } 
};

      利用上面连个顶点缓冲,我们要设置如上的layout。第一个element描述的是球体的顶点信息。第二个element描述的是位置信息。我们注意到第五个参数,D3D10_INPUT_PER_VERTEX_DATA/D3D10_INPUT_PER_INSTANCE_DATA,这里面如果是前者,IA会把顶点缓冲中的每个顶点当做顶点处理。而如果是后者,IA实际上是把顶点缓冲区中的每个元素当做实例来处理的。那么最后IA可以为我们生成 numberOfVertex * numberOfInstance 个顶点数据,这也正是我们想要的数据。 
      有了这些数据,我们就可以进行顶点处理了。看看VS中有什么变化:

//the input struct of the vertex shader 
struct    VS_INPUT 

    //the instance id 
    uint    uInstanceID : SV_InstanceID; 
    //the position of the particle 
    float3    vPosition : POSITION; 
    //the instanced position 
    float3    vTransform : mTransform; 
};

//the default vertex shader 
VS_OUTPUT    DefaultVertexShader( VS_INPUT input ) 

    //the output of the vertex shader 
    VS_OUTPUT vs_out;

    //transform the vertex 
   vs_out.vPosProj = mul( float4( ( input.vPosition + input.vTransform ) , 1.0f ) , ViewProjMatrix );

    //pass the normal 
    vs_out.vNormal = input.vPosition.xyz;

    //copy the color 
    vs_out.vColor = ColorBuffer[input.uInstanceID % 8];

    //return the output struct 
    return vs_out; 
}

     很简单的一个VS。但是这里我们注意加粗体的一行,这里我们为每个顶点变换的矩阵是ViewProjectionMatrix。我们没有做WorldMatrix变换,原因很简单,是因为我们根本就没有WorldMatrix这样一个常量。我们每个顶点的World Matrix都已经存储到了顶点结构中的vTransform中了。实际上input.vPosition + input.vTransform就相当于做了World Matrix的变换了。 
     通过上面的步骤,就可以渲染出来不同位置的球体了。但是我们注意到,VS的Input里面并没有Color这一属性,为什么球体会有不同的颜色呢?其实球体的颜色信息是存储到了Constant Array中的,因为简单的几种颜色就可以满足人的视觉需求了,完全没有必要每个球体都生成不同的颜色。所以这里面我只生成了8中基本的颜色。每个顶点中是有uInstanceID的属性的,通过对于这个属性求模运算,我们可以为每个球体选择出相应的颜色。这里的uInstanceID是IA为我们生成的,而不是我们自己写入的数据。 
     有了这些处理,我们就可以实现一个简单的实例化的Demo了,下面是源代码: 
     http://filer.blogbus.com/4730079/resource_473007912613770374.rar

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值