幾個經典的數學庫之一學習---VCGlib(1)


 

1. VCG Libary是Visulization and Computer Graphics Libary(可視化與計算機圖形學庫)的縮寫,是一個開源的C++模板庫,用於三角網格和四面體網格的控制、處理和OpenGL顯示。其中包含了超過100 000行的代碼。基於該庫,Visual Computing Lab開發了幾個著名的工具,如metro和MeshLab。

VCG Libary是專門為處理三角網格而設計的,庫很大,且提供了許多最先進的處理網格的功能,如:

            *基於邊坍塌(edge-collapse)二次誤差的高質量網格簡化(simplfication);
            *高效的空間檢索數據結構(uniform grids, hashed grids, kdtree, ...);
            *先進的網格平滑和光順算法;
            *曲率計算;
            *紋理坐標優化;
            *Hausdorff距離計算;
            *測地路徑;
            *網格修復能力
            *等直面抽取和前沿的網格划分算法;
            *泊松圓盤采樣和其他的網格點采樣算法;
       *細分曲面。
2. 可以從VCG的官網中獲取:
  或者從github中下載:
 
  下載后的vcg的庫結構如下。
 
     

      VCG Lib的大部分由頭文件組成,下載后,將文件解壓到一個vcglib文件夾中,然后將vcglib文件夾放到你的編譯器的“include”目錄中。然后使用時,只要包含其中需要的文件即可。
同上圖,vcglib文件夾中,總共包含5個子文件夾:

        *vcg:這是整個庫的核心,其中定義了所有的算法和數據結構。該部分所有的C++代碼都是STL支持的普通數據結構和算法,不包含任何其它標准庫之外的庫而且可以發現,該部分只包含頭文件(.h)
        *wrap:這里包含一些針對特定需求/上下文/庫的VCG概念的封裝。例如,所有的用於計算機硬盤上很多格式的網格數據的導入和導出;用OpenGL渲染三角形網格的代碼;普通GUI工具如跟蹤球,等等;
        *apps:這個文件夾包含一些用VCG Lib開發的命令行程序應用。

  很多例子都能在MeshLab中找到apps/simple文件夾包含了這些程序的一個基礎的子集,是一個初學者很好的入口點;
        *docs:文檔(包括這個教程)
        *eigenLib:線性代數的eigen庫最近的穩定版本的一個副本(相當於就是借用第三方庫了),VCGLib中的高級矩陣操作都是基於這個庫的。

 
 3 VCG中的鄰接關系
 
  VCG庫不通過硬編碼的方式來位置三角形與面的鄰接關系。VCG依賴於保存在各種“Simplices”(簡素)中的屬性來位置。如在每一個面中定義vcg::face::VertexRef,該屬性保存了三個指針,指向Vertex。所有的算法都依賴於vcg::face::VertexRef。去過缺少次屬性,則定義沒有錯誤,但是算法將不會執行。
  VCG中有量大臨街關系face-face和vertex-face鄰接關系。通過這兩個臨街關系,可以遍歷網格拓撲關系。
 
1. FF Adjacent(Face-Face 鄰接關系)
  面之間的鄰接關系存儲於vcg::face::FFAdj(正四面體為vcg::face::TTAdj),該屬性通過邊來記錄面之間的鄰接關系。
  

圖中頂點編號從0到2,以逆時針順序編號,邊i(i=0..2)的兩個端點分別為i和(i+1)%3,因此圖中面f0和面f1的公共邊對f0而言是0邊,對f1而言是1邊。

對於面f的每個邊i,vcg::face::FFAdj存儲以下信息:

  • FFp(i):指向共享邊i的面的指針,若i是border,則該指針指向自己;
  • FFi(i):在指向的面中i的索引。在共享邊的面中的索引。

例如在上圖中,有:

  • f1->FFp(1) == f0
  • f1->FFi(1) == 0
  • f0->FFp(0) == f1
  • f0->FFi(0) == 1
  需要說明的是, FFp(i)指向一個鄰接面。如果是下圖中的non-manifld 邊的情況,(奇異邊)則在一邊上有多余兩個面。這看起來在VCG中沒有支持。
  VCG提供了一種方法來檢查mesh是否是manifold(可組裝的)在指定的邊中。因為FFAdj的關系是相互的,如,f0指向面f1,則面f1也一定指向f0,則在對應的邊上二者是manifold。
  【說明】mainfold是針對邊來做區分的。一般指的是以下兩種情況:
  (1)Non-Mainfold類型的邊是多個面共邊的情況。
  (2)計算一個vertex鄰接的面的數量,通過Pos計算的結果比VFIterator計算的數量少。
 
  

 

 

bool IsManifold(MyFace *f,int e) { return (f0 == f0->FFp(0)->FFp(f0->FFi(0)))}
  

 

Pos關系

   三角網格中,Pos為一三元組,pos = {v,e,f},e是f的邊,v是e的端點。
  
  從一個pos移動到該pos的一個鄰居的操作稱為 Flip,把改變 頂點的Flip操作分別記做 FlipVFlipEFlipF
  

   

  下圖以小三角形的形式顯示了三角網格中的一些pos,在每個面中,每個小三角形指向一個頂點倚靠一條邊,且在一個三角形面片中

  這樣就能保證任意給定一個pos c,若只改變c的三元組的一個分量,能夠唯一確定一個鄰居pos。

  C2與C1,的Pos只有Vertex Component不同,即 C2 = FlipV(C1)

  

c2 = FlipV(c1),定點分量不同,即換頂點則在同一個三角形中同一條邊的另一個定點。
c0 = FlipE(c1),邊分量不同,即換邊則在同一個三角形中同一個定點的另一個邊。
c3 = FlipF(c0), 面分量不同,即換面則通過邊臨街另一個面。

CCW around v,(逆時針旋轉V)
c4 = FlipE(FlipF(c0)),即C0換面換邊后為C4
c5 = FlipE(FlipF(c4)),即C4換面換邊后C5
Bounce(彈跳,遇到邊界情況)
c6 = FlipE(FlipF(c5)),遇到邊界后彈回
CW around v (順時針旋轉V)
c3 = FlipE(FlipF(c6))
c1 = FlipE(FlipF(c3))
Bounce(彈跳)
c0 = FlipE(FlipF(c1)),遇到邊界彈回

  當兩個flip嵌套操作的時候,根據pos與面的關系,可以實現順時針或逆時針的pos轉換。並且由於面-面鄰接關系的定義方式,當pos在border上的時候,FlipF操作會返回到pos本身所在的面。

  VCG庫中廣泛使用成對的flip,mannifold定點的相鄰的ring環。

  下面的例子使用pos來迭代一個點的周圍相關性。

sf/apps/sample/trimesh_pos_demo/trimesh_pos_demo.cpp

#include <vcg/simplex/face/pos.h> // include the definition of pos

...includes to define your mesh type

class MyVertex: ...
class MyFace: public vcg::FaceSimp2<MyVertex,MyEdge,MyFace, vcg::face::VertexRef, vcg::face::FFAdj>{};

void OneRingNeighborhood( MyFace * f)
{
  MyVertex * v = f->V(0);
  MyFace* start = f;
  vcg::face::Pos<MyFace> p(f,0,v);// constructor that takes face, edge and vertex
  do
  {
    p.FlipF();
    p.FlipE();
  }while(p.f!=start);
}

 注意:

  (1)使用屬性vcg::vertex::VFAdj屬性,可以關聯的頂點指針指向面。(2)如果定點再邊界上,則上述方法行不通。即從C4開始,將會找到C5,C6,C3,即C3余C4在同一個面中。但是,如果遇到邊界,則會“彈”回來,即在這個圖中,可能會得到C5,C6,C3, C1,C0, C4的序列,這或許不能夠完成對一個定點的便利序列。

VCG提供以下的方法解決

Jumping Pos

  Jumping Pos類似Pos,但在遇到border的時候不會反彈回去,而是跨到下一個border-face上去。下面這個例子中,上圖中的p會從f0跨到f2。

sf/apps/sample/trimesh_pos_demo/trimesh_pos_demo.cpp
    #include <vcg/simplex/face/jumping_pos.h> // include the definition of jumping pos
    //...includes to define your mesh type
    //class MyVertex: ...
    class MyFace: public vcg::FaceSimp2<MyVertex,MyEdge,MyFace, vcg::face::VertexRef,vcg::face::FFAdj>{};
    void OneRingNeighborhoodJP( MyFace * f)
    {
      MyVertex * v = f->V(0);
      MyFace* start = f;
      vcg::face::JumpingPos<MyFace> p(f,0,v);// constructor that takes face, edge and vertex
      do
      {
        p.NextFE();
      }while(p.f!=start);
    }

 2. VF Adjacency(頂點-面, Vertex-Face 鄰接關系)

   VCG lib中,頂點與面之間也實現了鄰接關系,即給定一個頂點v,可以找出所有與其關聯的面。

  設v_star = (f0,f1,f2,…,fk)為一個列表,該列表中的元素都是與頂點v關聯的面。可以通過以下屬性索引v_star:

  • vcg::vertex::VFAdj:這是一個頂點的屬性,包含了指向f0的指針
  • vcg::face::VFAdj:這是面的屬性,該面三個頂點中的每一個頂點都包含了指向v_star列表中下一個面的指針

  

  這兩個屬性不單是指針,他們同樣包含在指向面中定點的索引,和vcg::face::FFAdj同樣的效果。如上圖展示了實際的例子,與Face-Face adjacent相似,需要使用vcg::tri::UpdateTopology::VertexFace(MeshType& m)來初始化。

Vertex的VF鄰接關系
v.VFp() == f2  頂點的VFp是一個指針,指向此定點關聯的面的列表的第一個面。 v.VFi() == 0  定點的VFi是一個索引值,是所指向的面中的定點所在的序號
face的VF鄰接關系,即,面中的指定索引值i頂點,其所對應的鄰接面的序列的首個面的指針 f2
->VFp(0) == f3 ,即f2面中的0索引定點,其所對應的臨界面的序列的首個面的指針,即面f3 f2->VFi(0) == 1 ,即f2面中的0索引點,其再所對應的鄰接面的首個面中的相應的該(本身)定點所在的序號。 f3->VFp(1) == f1 f3->VFi(1) == 2 f1->VFp(2) == f0 f1->VFi(2) == 2 f0->VFp(2) == NULL f0->VFi(2) == -1

VFIterator

  

  VFIterator是一個簡單的迭代器,來遍歷一個頂點的所有鄰接三角形面,通過使用VFAdjacency屬性。其用法如下圖:

sf/apps/sample/trimesh_pos_demo/trimesh_vfiter_demo.cpp
#include <vcg/simplex/face/pos.h> // include the definition of  VFIterator
//...includes to define your mesh type
class MyVertex: public vcg::VertexSimp2<MyVertex,MyEdge,MyFace, vcg::vertex::VFAdj /*,... other attributes*/ >{};
class MyFace: public vcg::FaceSimp2<MyVertex,MyEdge,MyFace, vcg::face::VertexRef,vcg::face::VFAd>{};
void OneRingNeighborhoodVF( MyVertex * v)
{
  vcg::face::VFIterator<MyFace> vfi(v); //initialize the iterator tohe first face
  for(;!vfi.End();++vfi)
  {
    MyFace* f = vfi.F();
    // ...do something with face f
  }
}

Stars and Rings

  端點頂點的face和vertices集合,可以使用以下方式函數:

面面鄰接與點面鄰接中的少量的面的處理

   以下陳述可以避免“沖突”,來幫助選擇最適合的臨街對象
  如果mesh是 manifold,則通過使用Pos計算出的頂點的臨街ring(環)與使用VFIterator計算出的一致。
  如果使用Pos,所有的面的集合的訪問順序可以是順時針,或者是逆時針。但是如果使用VIterator則順序不能指定,如果mesh是non-manifold。
  Pos可能不會找到一個定點的所有鄰接面,但是VFIterator可以。
邊界關系與鄰接關系
  在許多的算法中,你應該簡化三角面的邊界。例如,如果一個給定的面F,有許多的鄰接面在指定的邊中。使用FF 鄰接的過程中,可以使用face::isBorder(f,e)靜態函數,可以簡化合適面f中的點。
  *如果使用Pos來遍歷網格,通過Pos的城管函數IsBorder()可以核實當前點的邊界情況。
  *同樣,通過mesh中指定區域,測試manifoldness,有一個函數face::IsManifold(f,e)和IsManifold(e) ,Pos類的函數。
 
   如果使用Face-Face鄰接關系來處理“邊界”條件,則效率不高,因此VCG庫提供了將mesh的邊界條件“標識flag”添加在頂點和面的標識中。使用類的靜態方法UpdateFlags()來計算可以反映當前網格狀態的標識,通過使用面的類的靜態成員函數IsB(e)可以后去這些標識值。如果改變mesh網格的拓撲結構信息,則這些標識flag則無效。另一方面,對於許多non-mesh-modifying算法,不要求顯示的Face-Face鄰接關系,但是對於邊界信息(典型的許多網格光滑和曲率計算算法)
  
  對於non manifold 條件,邊界的標識被設置為true。
 
  http://vcg.isti.cnr.it/vcglib/adjacency.html

 

 

  


 

 

 

endl;

 
 
 
 
 
 
 
 
 
 
 
endl;


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM