您的位置:首页 > 其它

转:骨骼动画教程及微软示例Skinned Mesh的解析(二)

2009-11-19 08:40 344 查看
四. 怎样绘制显示动画?

DrawFrame()用来绘制整个X框架。它遍历各个框架,找到Mesh不为空的进行绘制。(其实整个.x中通常只有一个不为空,见上文所述)
DrawMeshContainer()是绘制函数。

4.1 怎样开启顶点混合?
注意应用有关的Vertex Blending技术。如在索引方式的绘制中,
m_pd3dDevice->SetRenderState(D3DRS_VERTEXBLEND, pMeshContainer->NumInfl - 1);
其实是设定了D3DVBF_2WEIGHTS或D3DVBF_3WEIGHTS
注意要m_pd3dDevice->SetRenderState(D3DRS_INDEXEDVERTEXBLENDENABLE, TRUE);

4.2 矩阵的刷新:
首先,在FrameMove()调用m_pAnimController->SetTime()设置当前时间(或在DX9.0c中用AdvanceTime()设置时间差),从而刷新各个pFrame->TransformationMatrix,即骨骼转换矩阵
其次,调用UpdateFrameMatrices()做乘法累积,计算出各骨骼坐标系到根世界转换矩阵。
最后,在绘制前,将该转换矩阵左乘偏移矩阵,得到最终的转换矩阵。
D3DXMatrixMultiply( &matTemp, &pMeshContainer->pBoneOffsetMatrices[iMatrixIndex], pMeshContainer->ppBoneMatrixPtrs[iMatrixIndex] );

由此可见,你如果注释掉了m_pAnimController->SetTime,画面肯定停了。

4.3 绘制输出 是在DrawMeshContainer()中,调用SkinMesh的DrawSubset进行绘制。一些细节内容如D3DTS_WORLDMATRIX(),在上面已经有说明,不再罗嗦。

4.4 关于示例中多种绘制方式分析
在示例中,用到了多种渲染方式,包括传统的非索引顶点混合,还有新兴的HLSL方式。而且我发现,ATI RADEON 9550 显卡MaxVertexBlendMatrixIndex=37,而价格更高的Gefoce 6600GT MaxVertexBlendMatrixIndex竟然为0,不支持index vertex blending!
所以,还是有必要分析一下该示例中各种vertex blending方式的处理,以便掌握多种绘制方式适应不同显卡。
经测试,示例中所涉及的多种方式,由慢到快,依次是以下几种:
SOFTWARE,
D3DNONINDEXED,
D3DINDEXED,
D3DINDEXEDVS,
D3DINDEXEDHLSLVS,

从最慢的SW到最快的HLSL,大约相差20%,有时会大到40%。 差别不是特别悬殊的原因,主要是顶点混合并不是瓶颈。

关于顶点处理方式,是在创建D3D设备时指定的。共有三种方式:
D3DCREATE_SOFTWARE_VERTEXPROCESSING 软件顶点运算 (简记 sw vp)
D3DCREATE_HARDWARE_VERTEXPROCESSING 硬件顶点运算。必须有这项才支持有HAL (简记 hw vp)
D3DCREATE_MIXED_VERTEXPROCESSING 混合顶点运算,即硬件+软件 (简记 mixed vp)

一旦用D3DCREATE_HARDWARE_VERTEXPROCESSING方式创建设备,就只能在硬件方式下进行顶点处理。如果调用m_pd3dDevice->SetSoftwareVertexProcessing(TRUE)来切换到软件顶点处理,HRESULT会返回失败。
所以,如果你对客户的显卡没有足够的信息,就用D3DCREATE_MIXED_VERTEXPROCESSING方式创建设备。它默认工作方式是HAL。一旦发现进行某种绘制时硬件能力不够,就可以调用调用m_pd3dDevice->SetSoftwareVertexProcessing(TRUE)切换到软件模式。在示例中就是这么做的,启动示例后,运行在mixed模式下。

在Gefoce6600GT显卡中,由于D3DINDEXED方式不支持,采用了软件混合方式,在这种方式下速度甚至比SOFTWARE慢。HLSL还好,还是最快。

要确定设备的硬件顶点处理能力,可以参考D3DCAPS9结构的VertexProcessingCaps成员。可以获取下列属性
MaxActiveLights,MaxUserClipPlanes,MaxVertexBlendMatrices,MaxStreams,MaxVertexIndex

(1)D3DNONINDEXED方式:

首先看GenerateSkinnedMesh()中怎样创建蒙皮网格的。
这种方式下,用ConvertToBlendedMesh()建立蒙皮网格,而不是ConvertToIndexBlendedMesh()

为了绘制蒙皮,在这个函数中对Mesh各子集的顶点再次进行的分组。分组的标准是各顶点(或三角面)所涉及的骨骼矩阵个数不超过pMeshContainer->NumInfl个。(这个数字是由在ConvertToBlendedMesh()时,由参数pMaxFaceInfl返回的)。一个Mesh子集可能被拆开成多个分组。 最后,分组的属性保存在pBoneCombinationBuf中,如子集ID,该子集的各骨骼ID,起始三角面,三角面个数等供绘制时使用,分组的个数保存在pMeshContainer->NumAttributeGroups中。

接下来检查每个分组所涉及的骨骼数,是不是超过硬件允许的最大混合矩阵数---MaxVertexBlendMatrices。如果超过了就把所有分组截为两大部分,前一部分用硬件混合,后一部分采用软件混合。而且,一旦发现有需要软件混合,要采用CloneMeshFVF(D3DXMESH_SOFTWAREPROCESSING|...)的方式重新生成网格。

再来看绘制部分DrawMeshContainer()

用pBoneComb指向骨骼分组属性,扫描各分组。找出其中骨骼数满足硬件性能的用进行绘制。
然后开启软件顶点渲染m_pd3dDevice->SetSoftwareVertexProcessing(TRUE),对那些骨骼数超出硬件性能的进行绘制。
SetSoftwareVertexProcessing()需要当前d3d设备以D3DCREATE_MIXED_VERTEXPROCESSING方式创建。

(2)D3DINDEXED,这种方式上面分析过了,从略。用pMeshContainer->UseSoftwareVP表示是否采用软件绘制。
值得注意的是在这种方式下,一旦硬件性能不足,会彻底使用软件顶点渲染,而不是像上面一样拆为两部分。

(3)D3DINDEXEDVS,D3DINDEXEDHLSLVS
这种情况下使用了着色器和高级着色语言。超出本文主旨,讨论从略。

(4)SOFTWARE--软件方式? 让人有些迷惑,与上面的m_pd3dDevice->SetSoftwareVertexProcessing(TRUE)有何区别?

从代码看,这种方式下反而比较简单。GenerateSkinnedMesh()中,
先直接从原始Mesh克隆一个Mesh,然后读取它的材质属性数组。开辟一个空间m_pBoneMatrices,用以存放各块骨骼的转换矩阵。

在绘制时,从pMeshContainer中的变换矩阵乘以偏移矩阵,放在pBoneMatrices中。把这个矩阵数组,以原Mesh的顶点作为源顶点,以新克隆的MeshData.pMesh做为目标顶点,调用pSkinInfo->UpdateSkinnedMesh(),用软件方式计算各骨骼顶点的新位置(相当于软件计算方式蒙皮)。

然后调用MeshData.pMesh->DrawSubset()绘制。

可见,在SOFTWARE方式下,最终顶点的渲染还是HAL方式的,只不过蒙皮计算是由软件完成的。它和上面的m_pd3dDevice->SetSoftwareVertexProcessing(TRUE)直接设置软件顶点渲染还是有区别的。

注解:

从示例源码看: UpdateFrameMatrices(m_pFrameRoot, &matWorld);
在求矩阵累积时,第一次传入的父矩阵是matWorld, 所以累积完的最终矩阵就是相对于当前世界的变换矩阵.
如果 UpdateFrameMatrices(m_pFrameRoot, NULL);那么累积完是当前物体的根骨骼为准的局部坐标系矩阵

所以,所有参与计算的顶点坐标是mesh的原始坐标,但与该矩阵相乘后,就是当前的新坐标.

新坐标是以什么为原点,就看你调用updateFrameMatrices时第一次传入的父矩阵.

在示例中,用
D3DXMatrixTranslation( &matWorld, -m_vObjectCenter.x,
-m_vObjectCenter.y,
-m_vObjectCenter.z );
D3DXMatrixMultiply( &matWorld, &matWorld, m_ArcBall.GetRotationMatrix() );
D3DXMatrixMultiply( &matWorld, &matWorld, m_ArcBall.GetTranslationMatrix() );
m_pd3dDevice->SetTransform( D3DTS_WORLD, &matWorld );

设定了物体的坐标.说明,matWorld就是当前物体变换到当前世界坐标系的世界转换矩阵,所以Mesh的新坐标是以当前世界坐标系为原点的.

类别:Directx 查看评论
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: