现在的位置: 首页 > 综合 > 正文

worldwind 三维模型加载优化总结

2014年08月19日 ⁄ 综合 ⁄ 共 6357字 ⁄ 字号 评论关闭

 研究ww的人都知道,ww在加载海量的三维模型方面,性能不行,稳定性很差。要想ww能够稳定快速的加载海量的三维模型,必须学习dx技术 及研究ww 原始设计,修改ww底层,优化其性能,使其满足我们在三维业务方面的需要。

下面从不同的方面,从ww原先的设计与dx技术的角度讲述ww程序的性能优化。 

一、ww三维模型加载设计

    1.1 ww通过序列化xml文件来预加载三维模型对象,并把这些对象存储是树形菜单中(注:程序启动时加载Config\Earth下  
               的模型配置文件 ),利用一个whlie循环不但刷行三维球,在每次刷新的时候判断,树形菜单中的对象是否被选中,是否
               在可是范围,如果在可是范围,开始加载该模型。这些多是单线程的。 其实这样设置会带来很大的问题。

       1) 如果模型量比较大,树形菜单中的对象比较多,那判断这些模型是否在可视范围就比较耗时,就导致,模型已在可视范
                  围还的等待一段时间才显示。

      2)模型加载速度比较慢 在加载的过程很卡。因为加载模型跟刷新三维球在同一个线程,三维模型没加载完 该线程一直处在
                 堵住等待执行中。

      3)加载模型渲染时 加载网格跟贴图加载 是相当的费时。

          想解决上面的问题,首先要设计一个加载模型的算法,类似谷歌地图,openlayer 等加载瓦片的算法来加载模型。其次单
                独 开线程来刷行三维球,保证三维球在任何时候能流畅操作,再者就是要对三维模型数据处理,加速模型的加载 很少资
                源的消耗。下面具体介绍dx方面对模型加载方面的优化 。这也是比较关键的(参考学步图圆的一片文章)。

二、dx技术方面优化性能

1: 光源

    1.1:尽量减少光源数量,使用环境光来提高亮度。

    1.2:方向光源比点光源和聚光灯更高效,因为光的方向是固定的。

    1.3:使用光照范围参数来剔除不受光照影响的物体。

    1.4:镜面高光几乎使光照计算量加倍,因此只在需要时使用:

             将SPECULARENABLE设为FALSE,

             将材质的specular power 设为0,

             将材质的specular color 设为0。         

 

2:纹理     
    2.1:尽量减小纹理(贴图)尺寸,这样可以增加纹理被缓存的可能性。 
             过大的贴图可能造成贴图cache过载, 从而导致贴图cache命中降低.过大的贴图会导致显存过载,

          这时候贴图是从系统内存中取的。 

         
    2.2:纹理不要超过纹理内存大小,否则你的高速缓存机制,会被强迫每帧上传大量的纹理数据。
 
    2.3:尽量使用正方形纹理。最好长宽都是2的n次方,最快的纹理是256×256,将4张128×128的纹理拼接

          成256×256使用。 
 
    2.4:尽量减少纹理的切换,将使用同一纹理的对象集中绘制。   
 
    2.5:上传纹理到设备上会消耗带宽并引起和顶点数据的带宽冲突。
 
    2.6:动态纹理。首先必须要检查DYNAMICTEXTURES来判断硬件是否支持。 
                    动态纹理不能放在MANAGED pool中。动态纹理总是能锁定,甚至是在D3DPOOL_DEFAULT中。

              D3DLOCK_DISCARD是合法的。         
                     注意:只有真正需要修改的贴图才使用Dynamic,并且使用 DISCRAD 和 WRITEONLY 来lock.  

    

    2.7.只要可能就用16位色的贴图, 如环境贴图或者shadow map.它们用32位色的贴图实在是浪费。

 

    2.8:.考虑使用DXT 贴图压缩。

   

    2.9: 如果可能,使用简单的贴图过滤或者mip map, 除非必要否则尽量不要使用三线过滤和各项异性过滤.

           
 light map 和环境贴图基本上都不需要使用它们。  
          

3: 缓存 
    3.1:动态缓存(D3DUSAGE_DYNAMIC)使用DISCARD参数来lock更新, 使用NOOVERWRITE来添加.

             尽量不要使用不带参数的lock调用(0)。 
             当需要在每帧里锁定顶点或索引缓存是,应该使用动态缓存。对动态缓存使用D3DLOCK_DISCARD锁

             定能减少延迟。         
             D3DLOCK_NOOVERWRITE锁定可以用于在缓存空闲处添加新的数据而不修改已经写入的数据。    
 
    3.2:尽量使用顶点和索引缓存而不是完全由程序分配内存,这样可以更有效利用顶点高速缓存。     
             因为至少顶点(索引)缓存的锁定机制能够避免冗余的拷贝操作。在一些驱动中,顶点(索引)

             缓存会被放到更优化的内存中(可能是在显存或者AGP内存中)被硬件读取。

      
    3.3:最小化顶点缓存的切换
 
    3.4:如果可能尽量多的使用static vertex buffer(静态顶点缓存)代替dynamic vertex buffer。
 
    3.5:对静态对象,对每种FVF使用一个大的静态顶点缓存来保存多个对象的顶点数据,而不是每个对象使用

             
 一个顶点缓存。其目的也是减少顶点缓存的切换           
           
    3.6:尽量使顶点格式大小是32字节的倍数.可以考虑使用压缩过的顶点格式然后用vertex shader去解. 或

             者留下冗余的部分,使顶点大小刚好使32字节的倍数。
             特别是如果程序需要随机访问AGP内存中的顶点缓存,顶点格式的大小最好是32bytes的倍数。否

             则,选择合适的最小的格式。32bytes 也就是8个float数据或2个vector4。
        
     3.7:尽量减少无用的顶点数据, 比如贴图坐标, 如果有Object使用2组有的使用1组, 那么不要将他们放在

              一个vertex buffer中, 这样可以减少传输的数据量。 
        
     3.8:顶点在顶点缓冲中的顺序尽量符合绘制的顺序, 可以考虑使用三角条带(strips) 代替 三角列表和三

             角扇。这样,能能更有效利用顶点高速缓存(cache),在排列条带时因考虑尽快重用顶点。    
 
     3.9:一个Vertex buffer 的大小在2M-4M之间最好。

4:  Shader

    4.1: 尽量使用shader来代替Fixed Pipeline。

    4.2: 尽量使用shader来实现来取代Multipass渲染效果。  
    4.3: 如果可能, 尽量使用vertex shader来代替pixel shader.将计算从逐象素变成逐顶点。  
    4.4: 使用Effect时,应该根据Effect,然后根据Technique来安排渲染顺序,也就是使用相同Effect和

           Technique的物体应该集中绘制。这样可以减少状态切换开销。 
    4.5: 避免Vertex shader指令数量太多或者分支过多, 尽量减少vertex shader的长度和复杂程度. 尽量使

           用swizzling代替mov。     
    4.6: 可以考虑使用vertex shader来计算静态VB中的数据.比如SkinMesh的顶点可以放到vectex shader中计

           算, 这样就可以避免每一帧都从AGP内存中向显存传送数据. 这样也可以使用静态VB了。

    4.7: 在shader中判断Z值可以避免绘制不可见的象素, 但是 nvidia 建议简单的shader不要这么做.

    4.8: 按照shader和贴图分组后再渲染.先按照shaders分组再按贴图。 
    4.9: 将计算结果和输出的shader指令合并:    
           //Rather than doing a multiply and add, and then output the data with  two instructions
            mad r2, r1, v0, c0
            mov oD0, r2

           //Combine both in a single instruction, because this eliminates an additional register copy
             mad oD0, r1, v0, c0 
 

 
5:  绘制 
    5.1:批处理数据量(Batch) 
             将使用相同渲染状态和贴图的图元集中在一起绘制,这样能尽量减少顶点缓存和状态的切换。并

            且将状态切换操作集中成一组设置。  
   原因:  
        1:dx对大量图元数据处理有优化。

                    一次函数调用处理的多边形越多,效率越高。故尽量一次画尽可能多的多边形。      
                    一个比较好的规则就是平均每次函数调用处理1000(显卡性能决定)个图元。在这个数量之下

             你很难优化性能,在此之上你则会减少返回次数以及与其他优化考虑的潜在冲突。   
        

          2:尽量减少渲染状态切换。并且将需要进行的状态切换组合在一起设置。
                       改变绘制状态会成为代价昂贵的操作,特别是在改变纹理时。因此要尽可能的减少每帧的状

                态改变操作,同样也要尽量减少顶点缓存或者索引缓存的切换。状态包括RenderState,

             SamplerState,TextureStageState等。
                  注意:和DX8一样,改变顶点缓存不再和从前版本一样耗时,但是减少顶点缓存切换仍然是

             良好的操作。    
  
     5.2:并行操作(Concurrency) 
          如果能尽可能的把绘制操作和其他操作并行执行,那么性能就能得到显著提高。
          这个目标可能和减少绘制状态改变相重度。  
          你需要平衡批处理数据量以减少绘制状态改变,并且在早期就把数据发送到驱动中以获得并行性。
          使用多顶点缓存轮流操作能够促进并行性。

  
 5.3:Clear操作      
          只在必须的时候Clear。IDirect3DDevice9::Clear函数通常需要花费较多的时间,因此要尽量少调

      用,而且只清空的确需要清空的缓存。      
          对于Color Stencil Z buffer尽量在一次Clear调用中清除,例如:深度缓存和模版缓存本来就是一

      块缓冲,尽量一起clear。
     
 5.4:尽量按照front–back(从前至后)的顺序来绘制(渲染)场景中的对象。这样,可以尽可能早地精选出

      不需要绘制的对象和象素。
 
 5.5:尽量减少lock的次数, 有些东西并不一定非要每一帧都更新VB, 比如人物动画一般每秒钟更新30次VB基本

      上就够了。
 
 5.6:坚决不要在渲染循环中调用创建资源。 
 
 5.7:如果是因为需要绘制的顶点数据太多了可以考虑使用LOD, 但是现在的显卡的绘制能力都很强劲, 所以需

      要权衡一下LOD是否能够带来相应的好处, 如果过分的强化LOD很可能将瓶颈转移到CPU这边。

 

5.8:使用多个streamsource, 比如SkinMesh渲染, 可以把顶点坐标和法线这些每一帧都要修改的数据放在一个

     动态VB中, 其它不需要修改的(如贴图坐标)放到一个静态VB中, 这样就减少了数据传输量。

7: 其它:

    7.1:避免过多的顶点计算,比如过多的光源, 过于复杂的光照计算(复杂的光照模型), 纹理自动生成的开

          启也会增加顶点的计算量.

              如果贴图坐标变换矩阵不是单位矩阵, 也会造成顶点计算量的增加, 所以如果纹理变换已经结

          束, 记得要将纹理变换矩阵设为单位矩阵同时调整贴图坐标。   
    7.2:连接World-View Matrix,将ViewMatrix设为Identity减少矩阵乘法运算。  
    7.3:可能的话尽量使用alpha test代替alpha blending。  
    7.4:尽量使用16位的索引缓冲,避免32位的. 一方面浪费带宽, 一方面也不是所有的显卡都支持32位的索

          引缓冲。  
    7.5:减小RenderTarget 贴图的大小, 如shadow map环境贴图. 可能根本不需要那么大效果就很好。
    7.6:如果不需要stencil buffer就尽量使用16位的Z buffer。

    7.7:可以根据所需要据消耗的系统资源来逐步减少特效。  
    7.8:关注渲染的总三角面数,最好先使用最低精度的模型,在保证性能的前提下逐步使用更高精度的模

          型。 

    7.9:如果图象质量方面的计算(pixel shader)范围很大, 并且很复杂, 可以考虑试试全屏反走样。说不定

          更快。     
    7.10:坚决避免使用Draw**UP一族的函数来绘制多边形。  

    7.11:太多的帧缓冲读写可以考虑关闭Z-Writes如有些多pass的渲染中的后续pass或者粒子系统等半透明几

          何物体(如果可以)。  

 

    7.12: 定位瓶颈 
              一般情况,定位渲染通道瓶颈的方法就是改变渲染通道每个步骤的工作量, 如果吞吐量也改变

         了,那个步骤就是瓶颈。找到了瓶颈就要想办 法消除瓶颈, 可以减少该步骤的工作量, 增加其他步骤

         的工作量。  
           一般光栅化之前的瓶颈称作"transform bound", 三角形设置处理后的瓶颈称作"fill bound".

        

定位瓶颈的办法: 
  1.改变帧缓冲或者渲染目标(Render Target)的颜色深度(16 到 32 位), 如果帧速改变了, 那么瓶颈应该在帧

    缓冲(RenderTarget)的填充率上。

  2.否则试试改变贴图大小和贴图过滤设置, 如果帧速变了,那么瓶颈应该是在贴图这里。

  3.否则改变分辨率.如果帧速改变了, 那么改变一下pixel shader的指令数量,如果帧速变了, 那么瓶颈应该就

    是pixel shader. 否则瓶颈就在光栅化过程中。

  4.否则, 改变顶点格式的大小, 如果帧速改变了, 那么瓶颈应该在显卡带宽上。

  5.如果以上都不是, 那么瓶颈就在CPU这一边。

【上篇】
【下篇】

抱歉!评论已关闭.