The Design of Model (part 2)

前端之家收集整理的这篇文章主要介绍了The Design of Model (part 2)前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

The Designof Model (part 2)

仅供个人学习使用,请勿转载,勿用于任何商业用途。

继续上次的讨论,所有渲染器支持的对象都必须支持接口Draw,那么Draw应该实现哪些职责呢?由于已经把材质作为单独的对象来管理,所以Draw只需要完成2个任务:设置几何数据,绘图。下面是一个可能的Draw方法实现:

  1. Draw()
  2. {
  3. graphicsDevice.Vertices[0].SetSource(vb,vertexStride);
  4. graphicsDevice.Indices=ib;
  5. graphicsDevice.VertexDeclaration=vertexDecl;
  6. graphicsDevice.DrawIndexPrimitive();
  7. }

也许把Draw更名为SubmitGeometry()更合适。显然,通过这一层封装,渲染器不需要知道所绘制几何体的任何细节。你可以把顶点保存为带索引的顶点缓冲,也可以是简单的顶点数组。这些顶点可以是三角形,也可以是线段,或者点。此外,还应该明确drawable object就是一个最小绘图单元。假设我们使用带索引的顶点缓冲(这也是最常见的方法)来保存几何体数据,那么一个可能的drawable object也许是这样:

  1. classDrawableMesh:IDrawableGeometry
  2. {
  3. GraphicsDevicegraphics;
  4. VertexBuffervb;
  5. IndexBufferib;
  6. VertexDeclarationvertexDecl;
  7. PrimitiveTypeprimitiveType;
  8. intbaseVertex;
  9. intnumVertex;
  10. intprimitiveCount;
  11. intminVertexIndex;
  12. intstartIndex;
  13. intstreamOffset;
  14. intvertexStride;
  15. intstartVertex;
  16. //otherproperties….

  17. publicvoidSubmitGeometry(){}
  18. //othermethod….
  19. }

上面的代码非常直观,DrawableMesh只是包含了绘图所需要的所有基本元素而已。确定了类的大概外观,接下来讨论实现细节。这里的DrawableMesh相当于我们上次讨论的mesh part,一个最小绘图单元。复杂模型通常由一个或者多个mesh part组成。但是为每个mesh part都保存独立的vertex buffer或者index buffer却并不是最高效的做法。每创建一块buffer,DirectX就必须花费一定资源对它进行管理, 因此,大量琐碎的buffer会对性能有所影响。常见的方法是把众多buffer打包为一个大buffer,然后通过索引获得不同mesh part的实际数据。打包的方法和策略是多种多样的,可以选择把某个模型中的几个部分打包为一个buffer,或者把整个模型,甚至几个模型的数据都打包为一个buffer(当然,使用顶点数组保存几何体则不必考虑这个步骤)。为了方便讨论,这里只考虑打包整个模型数据的情况:

  1. classGeometryData
  2. {
  3. GraphicsDevicegraphics;
  4. VertexBuffervb;
  5. IndexBufferib;
  6. VertexDeclarationvertexDecl;
  7. intstartVertex;
  8. }

  9. classDrawableMesh:IDrawableGeometry
  10. {
  11. GeometryDatageometryData;
  12. PrimitiveTypeprimitiveType;
  13. intbaseVertex;
  14. intnumVertex;
  15. intprimitiveCount;
  16. intminVertexIndex;
  17. intstartIndex;
  18. intstreamOffset;
  19. intvertexStride;
  20. //otherproperties….
  21. publicvoidSubmitGeometry(){}
  22. //othermethod….
  23. }

一个模型也许包含了若干mesh part,但每个mesh part的实际几何数据都保存在同一个对象中。

那么LOD呢?在讨论如何实现LOD前,先来看看相关的LOD技术。我把LOD分为三类,1,顶点改变,索引不变;2. 顶点不变,索引改变;3,顶点和索引都改变。第一种听起来有些奇怪,索引不变,那么绘制的图元数量肯定也不会变,如何能达到LOD的目的呢?其实通过改变顶点位置是可以实现LOD的。举个例子来说,假设有2个三角形,ABC和BCD,4个顶点互不重合,2个三角形共享BC边,但是面积上没有重叠部分(呃,懒得画图了),这是2个三角形正常情况下的拓扑结构。现在假设我移动A点或者D点,使其与B点或C点重合会怎样呢?显然,其中一个三角形会变为直线,图形学中,把这样的三角形的称为退化(degeneration)三角形或者0区域三角形。有趣的是对于这样的三角形,硬件在流水线早期阶段就会把它们裁剪去,因此,虽然图元数量没有改变,仍然达到了LOD的目的。第二种LOD类型是处理地形时最常见的方法,通过改变索引,减少三角形数量。第三种也非常容易理解,相当于创建一个全新的模型。

对于LOD数据的计算,又分为两类:预处理或者运行时生成。对预处理来说,可以使用手工建模的方式,也可以通过程序;实时LOD自然只能通过程序计算,比如DX里的progress mesh。我比较倾向于手工预处理的方式,原因有2点:1. 程序LOD通常是很费时的工作,我不愿把cpu资源浪费在LOD计算上,现代计算机已经有足够大的硬盘和内存,可以保存预处理数据;2. 所有程序LOD的结果都不理想,程序无法分析某个面的重要性,常常把关键的面缩减了,而该缩的地方又处理不好。当然,手工预处理会极大的增加模型师的工作量,要模型师为每个模型都建立几个不同的LOD副本显然是不现实的,因此,顶点改变,索引不变的LOD方式就变的非常就用了,毕竟与新建模型相比,简单的移动顶点要快的多。对于储存空间来说,仅仅改变索引的方式是最优的,可以共享同一个vertex buffer,可惜这样的数据不太容易生成,大多数建模软件都不允许直接修改索引,当然,你总是可以自己写工具来实现:) 。如何需要支持连续的LOD(CLOD)怎么办呢?显然,预处理的方式无法获得几何上的CLOD,但是通过一些特别的渲染方法,同样能让不连续的LOD对象在变化时,达到视觉上的连续性,比如alpah blend或者geo-morphing.

如何把LOD集成到我们已有的类中呢?先来看你会如何使用一个支持LOD的模型,下面是2中最有可能的情况:

  1. DrawableMesh.SubmitGeometry(intlod);
  2. 或者
  3. DrawableMesh.SetLod(intlod)
  4. DrawableMesh.SubmitGeometry();

如果使用前者,就意味着要修改接口,而且并不是场景中所有模型都支持LOD,所以我选择了后者,让支持LOD的mesh派生于DrawableMesh:

  1. classDrawableLodMesh:DrawableMesh
  2. {
  3. publicintLod{get;set;}
  4. }

为了保存额外数据,DrawableMesh也需要做相应修改,可以选择把不同的LOD都打包为一块buffer,也可以分开,比如:

  1. publicclassMeshPartInfo
  2. {
  3. intbaseVertex;
  4. intnumVertex;
  5. intprimitiveCount;
  6. intminVertexIndex;
  7. intstartIndex;
  8. intstreamOffset;
  9. intvertexStride;
  10. }
  11. classDrawableMesh:IDrawableGeometry
  12. {
  13. GeometryData[]geometryData;
  14. PrimitiveTypeprimitiveType;
  15. MeshPartInfo[]meshPartInfos;
  16. //otherproperties….
  17. publicvoidSubmitGeometry(){}
  18. //othermethod….
  19. }

可以用相同的方法来处理morph,模型可以既支持lod,也支持morph,Renderer不需要知道这些细节,只需要为不同类型编写相应的SubmitGeometry()方法就可以了。

至今为止,这样的设计看起来还不错,可是,如果要渲染两个相同模型的不同实体时怎么办呢,两个实体的位置和LOD都可能不相同,如何区分呢?

(to be continue..............)

原文链接:https://www.f2er.com/vb/263327.html

猜你在找的VB相关文章