这几天研究D3D骨骼动画,顺便把directx中的骨骼动画的例子翻译一下,有不当的地方,请大家指正..
How the Sample Works
本例主要讲述了通过骨骼索引号索引骨骼变换的四种方法,其中骨骼索引号在模型的点流(vertex stream)中给出。本例并不包含骨骼动画与层级变换。
本例讲述了四种不同的适合GPU蒙皮的索引骨骼变换矩阵的方法。另外,还讲述了怎样stream out,而不使用Geometry shader,当蒙皮的模型需要进行多通道渲染时可以用来减少顶点处理的时间。
Constant Buffer Skinning
在这儿所介绍的四种方法中,Constant Buffer Skinning 与传统的Direct3D 9基于GPU的蒙皮非常相似。变换矩阵加载到shader constants, vertex shader索引这些矩阵基于顶点流中的骨骼索引。就像大多数Direct3D 9实现方式一样,每个顶点的骨骼索引被限制为4. 与direct3d 9的实现方式不同的是,在本例中的蒙皮方法并不创建矩阵的调板,也不会根据相似索引的顶点而划分为不同的部分。这是因为Direct3D 10允许比Direct3D 9更大的缓冲区。只对于拥有几百块骨骼的模型才有必要把骨骼矩阵分成不同的调板。在shader中索引骨骼的代码如下:
mret = g_mConstBoneWorld[ iBone ];
Texture Buffer Skinning
Texture Buffer Skinning 看起来与Constant Buffer Skinning一样。实际上,骨骼在HLSL中被索引的方式也一样:
mret = g_mTexBoneWorld[ iBone ];
唯一的不同点是保存变换矩阵的Buffer被声明为一个texture buffer 而不是一个常规的constant buffer。定义一个Buffer为tBuffer充许像texture一样被访问,对于需要更多随机访问texture的程序来讲,这可以提高性能。
Texture Skinning
基于tBuffer蒙皮的一个逻辑上的扩展是保存骨骼的旋转矩阵到一个浮点型的texture中。取决于显卡的纹理拾取(texture-fetch)性能和是否这个shader 是ALU或者texture-fetch bound,这个方法可以产生惊人的绑定速度的提升。
iBone *= 4;
float4 row1 = g_txTexBoneWorld.Load( float2(iBone, 0) );
float4 row2 = g_txTexBoneWorld.Load( float2(iBone + 1, 0) );
float4 row3 = g_txTexBoneWorld.Load( float2(iBone + 2, 0) );
float4 row4 = g_txTexBoneWorld.Load( float2(iBone + 3, 0) );
mret = float4x4( row1, row2, row3, row4 );
在这里可以看到,通过4个texture fetches来取得变换矩阵的方法可能快于从constent buffer,这取决于硬件。
Buffer Skinning
索引矩阵的最后一个方法是通过 Buffer Skinning. 在buffer skinning中,矩阵被加载到一个ID3D10Buffer 对象中,这个对像被绑定为一个shader resource. 矩阵的数据通过Load命令从Buffer中获取。
iBone *= 4;
float4 row1 = g_BufferBoneWorld.Load( iBone );
float4 row2 = g_BufferBoneWorld.Load( iBone + 1 );
float4 row3 = g_BufferBoneWorld.Load( iBone + 2 );
float4 row4 = g_BufferBoneWorld.Load( iBone + 3 );
mret = float4x4( row1, row2, row3, row4 );
Speed up Multiple Passes with Stream Out
传统的GPU蒙皮存在的一个问题是如果一个物体需要执行多通道渲染,那么这个物体需要在每个通道都进行一次蒙皮。比如,在一个有三个投射阴影的光源与蒙皮角色的场景中,为了计算每一个光源产生的阴影,这个角色需要被渲染3次 -- 每个光源的视角一次。这需要Vertex shader绑定角色三次。不幸的现实是多出来的两次的机率太多了 -- 实际上需只绑定角色一次然后把变换的角色存储。用这样的方法,如果有一个场景需要10个通道来渲染,那么角色将不需要被绑定10次。
幸运的是,Stream Out充许我们这样做。Direct3D 10充许绑定一次,然后把信息存储到显卡的缓存中。然后,对于后面的渲染通道,只需要使用之前绑定的顶点。对于一个需要10个通道渲染的角色来讲,这样做会节约90%的顶点处理时间。
在这个例子中并不做多通道渲染,但是会渲染多个角色实例。这实际上是同一个问题,只不过表达方式不一样。当Stream Out 被禁用了时,程序每渲染一次角色时都要做绑定。然而,当Stream Out启用时,角色只需要绑定一次,在Stream Out中保存结果,渲染其它角色实例时使用预先绑定的模型。