VTK 圖形基本操作進階_網格模型的特征邊 與 封閉性檢測


1.封閉性檢測

由於受原始數據、重建方法的限制,得到的網格模型並不是封閉的。有時為了顯示或者處理某些要求,需要網格必須是封閉的。
封閉性網格應該比較好理解,比如一個球形網格。

1.1網格模型邊的分類

之前也有提到過邊界邊的概念:如果一條邊只被一個多邊形包含,那么這條邊就是邊界邊。是否存在邊界邊是檢測一個網格模型是否封閉的重要特征。
vtkFeatureEdges是一個非常重要的類,該類能夠提取多邊形網格模型中四種類型的邊。
  • 邊界邊:只被一個多邊形或者一條邊包圍的邊。
  • 非流形邊:被三個或者三個以上多邊形包圍的邊;
  • 特征邊:需要設置一個特征角的閾值,當包含同一條邊的兩個三角形的法向量的夾角大於該閾值時,即為一個特征邊。
  • 流行邊:只被兩個多邊形包含的邊。

1.2 網格封閉性判斷

可以通過使用vtkFeatureEdges類檢測是否存在邊界邊,潔兒判斷網格是否封閉。
示例代碼如下:
 1 #include <vtkAutoInit.h>
 2 VTK_MODULE_INIT(vtkRenderingOpenGL);  3 VTK_MODULE_INIT(vtkRenderingFreeType);  4 VTK_MODULE_INIT(vtkInteractionStyle);  5  
 6 #include <vtkSmartPointer.h>
 7 #include <vtkSphereSource.h>
 8 #include <vtkIdTypeArray.h>
 9 #include <vtkSelectionNode.h>
 10 #include <vtkSelection.h>
 11 #include <vtkExtractSelection.h>
 12 #include <vtkDataSetSurfaceFilter.h>
 13 #include <vtkInformation.h>
 14 #include <vtkProperty.h>
 15 //生成帶孔洞的網格球
 16 void GenerateData(vtkSmartPointer<vtkPolyData> input)  17 {  18     vtkSmartPointer<vtkSphereSource> sphereSource =
 19         vtkSmartPointer<vtkSphereSource>::New();  20     sphereSource->Update();  21  
 22     //提供了插入和檢索值的方法,並會自動調整大小以保存新數據
 23     vtkSmartPointer<vtkIdTypeArray> ids =
 24         vtkSmartPointer<vtkIdTypeArray>::New();  25     ids->SetNumberOfComponents(1);  26     ids->InsertNextValue(2);  27     ids->InsertNextValue(10);  28  
 29     //選擇樹中的節,用於存儲選擇結果
 30     vtkSmartPointer<vtkSelectionNode> selectionNode =
 31         vtkSmartPointer<vtkSelectionNode>::New();  32     selectionNode->SetFieldType(vtkSelectionNode::CELL);  33     selectionNode->SetContentType(vtkSelectionNode::INDICES);  34     selectionNode->SetSelectionList(ids);  35     selectionNode->GetProperties()->Set(vtkSelectionNode::INVERSE(), 1);  36  
 37     vtkSmartPointer<vtkSelection> selection =
 38         vtkSmartPointer<vtkSelection>::New();  39     selection->AddNode(selectionNode);  40  
 41     //從vtkdataset提取子集,刪除操作
 42     vtkSmartPointer<vtkExtractSelection> extractSelection =
 43         vtkSmartPointer<vtkExtractSelection>::New();  44     extractSelection->SetInputData(0, sphereSource->GetOutput());  45     extractSelection->SetInputData(1, selection);  46     extractSelection->Update();  47  
 48     //vtkDataSetSurfaceFilter是更快版本的vtkgeometry濾波器  49     //但它沒有一個選擇范圍。比vtkGeometryFilter使用更多的內存  50     //只有一個選擇:輸入結構類型時是否使用三角形條
 51     vtkSmartPointer<vtkDataSetSurfaceFilter> surfaceFilter =
 52         vtkSmartPointer<vtkDataSetSurfaceFilter>::New();  53     surfaceFilter->SetInputConnection(extractSelection->GetOutputPort());  54     surfaceFilter->Update();  55  
 56     input->ShallowCopy(surfaceFilter->GetOutput());  57 }  58 #include <vtkPolyData.h>
 59 #include <vtkFeatureEdges.h>
 60 #include <vtkPolyDataMapper.h>
 61 #include <vtkFillHolesFilter.h>
 62 #include <vtkPolyDataNormals.h>
 63 #include <vtkActor.h>
 64 #include <vtkCamera.h>
 65 #include <vtkRenderer.h>
 66 #include <vtkRenderWindow.h>
 67 #include <vtkRenderWindowInteractor.h>
 68  
 69 int main()  70 {  71     vtkSmartPointer<vtkPolyData> input =
 72         vtkSmartPointer<vtkPolyData>::New();  73  GenerateData(input);  74  
 75     vtkSmartPointer<vtkFeatureEdges> featureEdges =
 76         vtkSmartPointer<vtkFeatureEdges>::New();  77     featureEdges->SetInputData(input);  78     featureEdges->BoundaryEdgesOn();  79     featureEdges->FeatureEdgesOff();  80     featureEdges->ManifoldEdgesOff();  81     featureEdges->NonManifoldEdgesOff();  82     featureEdges->Update();  83  
 84     int numberOfOpenEdges = featureEdges->GetOutput()->GetNumberOfCells();  85     if (numberOfOpenEdges)  86  {  87         std::cout << "該網格模型不是封閉的..." << std::endl;  88  }  89     else
 90  {  91         std::cout << "該網格模型是封閉的..." << std::endl;  92         return EXIT_SUCCESS;  93  }  94     vtkSmartPointer<vtkFillHolesFilter> fillHolesFilter =
 95         vtkSmartPointer<vtkFillHolesFilter>::New();  96     fillHolesFilter->SetInputData(input);  97     fillHolesFilter->Update();  98  
 99     vtkSmartPointer<vtkPolyDataNormals> normals =
100         vtkSmartPointer<vtkPolyDataNormals>::New(); 101     normals->SetInputConnection(fillHolesFilter->GetOutputPort()); 102     normals->ConsistencyOn(); //很重要,根據其他單元點的順序調整補充點的順序
103     normals->SplittingOff(); 104     normals->Update(); 105     /
106     double leftViewport[4] = { 0.0, 0.0, 0.5, 1.0 }; 107     double rightViewport[4] = { 0.5, 0.0, 1.0, 1.0 }; 108  
109     vtkSmartPointer<vtkPolyDataMapper> originalMapper =
110         vtkSmartPointer<vtkPolyDataMapper>::New(); 111     originalMapper->SetInputData(input); 112     vtkSmartPointer<vtkProperty> backfaceProp =
113         vtkSmartPointer<vtkProperty>::New(); 114     backfaceProp->SetDiffuseColor(0.89, 0.81, 0.34); 115     vtkSmartPointer<vtkActor> originalActor =
116         vtkSmartPointer<vtkActor>::New(); 117     originalActor->SetMapper(originalMapper); 118     originalActor->SetBackfaceProperty(backfaceProp); 119     originalActor->GetProperty()->SetDiffuseColor(1.0, 0.3882, 0.2784); 120  
121     vtkSmartPointer<vtkPolyDataMapper> edgeMapper =
122         vtkSmartPointer<vtkPolyDataMapper>::New(); 123     edgeMapper->SetInputData(featureEdges->GetOutput()); 124     vtkSmartPointer<vtkActor> edgeActor =
125         vtkSmartPointer<vtkActor>::New(); 126     edgeActor->SetMapper(edgeMapper); 127     edgeActor->GetProperty()->SetEdgeColor(0., 0., 1.0); 128     edgeActor->GetProperty()->SetEdgeVisibility(1); 129     edgeActor->GetProperty()->SetLineWidth(5); 130  
131     vtkSmartPointer<vtkPolyDataMapper> filledMapper =
132         vtkSmartPointer<vtkPolyDataMapper>::New(); 133     filledMapper->SetInputData(normals->GetOutput()); 134     vtkSmartPointer<vtkActor> filledActor =
135         vtkSmartPointer<vtkActor>::New(); 136     filledActor->SetMapper(filledMapper); 137     filledActor->GetProperty()->SetDiffuseColor(1.0, 0.3882, 0.2784); 138     /// 139     vtkSmartPointer<vtkRenderer> leftRenderer =
140         vtkSmartPointer<vtkRenderer>::New(); 141     leftRenderer->SetViewport(leftViewport); 142     leftRenderer->AddActor(originalActor); 143     leftRenderer->AddActor(edgeActor); 144     leftRenderer->SetBackground(1.0, 1.0, 1.0); 145  
146     vtkSmartPointer<vtkRenderer> rightRenderer =
147         vtkSmartPointer<vtkRenderer>::New(); 148     rightRenderer->SetViewport(rightViewport); 149     rightRenderer->AddActor(filledActor); 150     rightRenderer->SetBackground(0, 0, 0); 151  
152     vtkSmartPointer<vtkRenderWindow> renderWindow =
153         vtkSmartPointer<vtkRenderWindow>::New(); 154     renderWindow->AddRenderer(leftRenderer); 155     renderWindow->AddRenderer(rightRenderer); 156     renderWindow->SetSize(640, 320); 157     renderWindow->Render(); 158     renderWindow->SetWindowName("Poly Data Closed"); 159  
160     vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor =
161         vtkSmartPointer<vtkRenderWindowInteractor>::New(); 162     renderWindowInteractor->SetRenderWindow(renderWindow); 163  
164     leftRenderer->GetActiveCamera()->SetPosition(0, -1, 0); 165     leftRenderer->GetActiveCamera()->SetFocalPoint(0, 0, 0); 166     leftRenderer->GetActiveCamera()->SetViewUp(0, 0, 1); 167     leftRenderer->GetActiveCamera()->Azimuth(30); 168     leftRenderer->GetActiveCamera()->Elevation(30); 169     leftRenderer->ResetCamera(); 170     rightRenderer->SetActiveCamera(leftRenderer->GetActiveCamera()); //同步響應
171     renderWindowInteractor->Start(); 172  
173     return 0; 174 }
為了方便看到效果,我建立了一個球面網格,並去除樂其中另個三角面片(單元)。結果如下:
將該數據作為vtkFeatureEdges的輸入,vtkBoundaryOn()函數設置提取邊界邊,本例無需考慮其他類型的邊。執行完畢后,其輸出GetOutput()為一個包含邊信息的vtkPolyData數據。可以通過判斷邊界邊的數目來確定網格是否封閉:
int numberOfOpenEdges = featureEdges->GetOutput()->GetNumberOfCells();

1.3 漏洞填補

很多情況下,檢測出是否封閉還是不夠的,還需將這些漏洞填補起來。VTK中有現成的類來完成這個功能——vtkFillHolesFilter。
其內部執行過程是首先檢測出網格中的所有邊界邊,然后找出這些邊界邊中的每一個閉合回路,最后將這些閉合回路進行三角化(即生成三角網格)以實現填補的目的。這個類也是非常簡單的,只需要設置需要填補的網格數據即可。
需要注意的是,有些邊界的閉合回路是不需要三角化的,例如一個平面網格,若填補其四周的邊界邊,則會與原網格產生覆蓋。vtkFillHolesFilters()中的SetHoleSize()函數可用於控制需要修補的漏洞面積的最大值,大於該值的漏洞則不需要填補處理。
現在,我們需要討論的一個重要的問題是為什么要使用vtkPolyDataNormals?
這個事之前也提到過,在這里復習一遍。法向量這個東西和光照與陰影的計算密切相關。單元的法向量朝向則與單元的點順序相關!只有保持所有的單元的點順序一致才能得到正確的法向量,否則在網格模型顯示時會得到意外的結果!如下所示:
由於經過漏洞填充,模型的所有單元的點順序並不一致,因此使用vtkPolyDataNormals::ConsisitencyOn()進行調整。這樣才能避免上面的問題。


免責聲明!

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



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