多边形数据,特别是三角形网格是常被用到的数据格式。有时会面临网格数据大,难以处理这样的问题,比如在一些交互使用中难以快速响应。为了解决这类问题,就要用到数据抽取技术。
抽取也被称作多边形削减,网格简化或者多分辨率建模,其在于保持近似的原始网格条件下对三角网格的三角形进行削减。
vtkDecimatePro
vtkDecimatePro是一个用于削减三角形数量的类,得到一个很好的近似原始形状的几何。相对(vtkQuadricDecimation)来说速度比较快,而且在削减过程中可以修改拓扑结构。它采用边塌陷的方法来消除顶点和三角形。它的误差度量是基于到平面或者边的距离。
vtkDecimatePro的实现类似于Proc Siggraph `92最初在"Decimation of Triangle Meshes"中描述的算法,但有三个主要区别:
1)该算法不能保证网格的拓扑结构;
2)它保证了用户给定的网格系数;
3)它建立了生成渐进网格的方法,这是一个操作流,可以很容易的传输和增量更新(详见Hugues Hoppe Siggraph '96关于"progressive meshes"的论文)。
用法
要使用这个类,至少要设定TargetReduction这个值,它代表要削减的三角形的百分比(范围为0到1)。比如,
有100个三角形,设置TargetReduction为(. 90),那么,最后将大约留下10个三角形,减少90%。
auto decimate = vsp<vtkDecimatePro>::New();
decimate->SetInputData(inputPD);
decimate->SetTargetReduction(reduciton);
decimate->Update();
另外,vtkDecimatePro的输入只能是三角形网格数据,如果是多边形数据,一开始要将它三角化(vtkTriangleFilter )。
auto triangles = vsp<vtkTriangleFilter>::New();
triangles->SetInputConnection(reader->GetOutputPort());
triangles->Update();
其它的一些重要参数:
PreserveTopology:是否保持原始的拓扑结构,如果on,将不会发生网格分裂和孔洞。
Splitting
BoundaryVertexDeletion
MaximumError
FeatureAngle
SplitAngle
示例
下面的演示了对一只牛模型进行数据抽取后的效果及数据大小的对比:
Decimation.cxx
#include <vtkSmartPointer.h>
#include <vtkPolyData.h>
#include <vtkXMLPolyDataReader.h>
#include <vtkSphereSource.h>
#include <vtkTriangleFilter.h>
#include <vtkDecimatePro.h>
#include <vtkNamedColors.h>
#include <vtkPolyDataMapper.h>
#include <vtkProperty.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkRenderer.h>
#include <vtkCamera.h>
#define vsp vtkSmartPointer
int main(int argc, char* argv[])
{
vsp<vtkPolyData> inputPD;
if (argc > 1)
{
auto reader = vsp<vtkXMLPolyDataReader>::New();
reader->SetFileName(argv[1]);
auto triangles = vsp<vtkTriangleFilter>::New();
triangles->SetInputConnection(reader->GetOutputPort());
triangles->Update();
inputPD = triangles->GetOutput();
}
else
{
auto sphereSource = vsp<vtkSphereSource>::New();
sphereSource->SetThetaResolution(30);
sphereSource->SetPhiResolution(15);
sphereSource->Update();
inputPD = sphereSource->GetOutput();
}
std::cout << "========" << "Before decimation" << "========" << std::endl;
std::cout << "There are " << inputPD->GetNumberOfPoints() << " points." << std::endl;
std::cout << "There are " << inputPD->GetNumberOfPolys() << " polygons." << std::endl;
float reduciton = .3f;
if (argc > 2)
{
reduciton = atof(argv[2]);
}
auto decimate = vsp<vtkDecimatePro>::New();
decimate->SetInputData(inputPD);
decimate->SetTargetReduction(reduciton);
decimate->PreserveTopologyOn();
decimate->Update();
auto decimatedPD = vsp<vtkPolyData>::New();
decimatedPD->ShallowCopy(decimate->GetOutput());
std::cout << "========" << "After decimation" << "========" << std::endl;
std::cout << "There are " << decimatedPD->GetNumberOfPoints() << " points." << std::endl;
std::cout << "There are " << decimatedPD->GetNumberOfPolys() << " polygons." << std::endl;
std::cout << "Reduction: " <<
static_cast<double>((inputPD->GetNumberOfPolys() - decimatedPD->GetNumberOfPolys())) /
static_cast<double>(inputPD->GetNumberOfPolys()) << std::endl;
auto colors = vsp<vtkNamedColors>::New();
auto backFace = vsp<vtkProperty>::New();
backFace->SetColor(colors->GetColor3d("gold").GetData());
auto inputMapper = vsp<vtkPolyDataMapper>::New();
inputMapper->SetInputData(inputPD);
auto inputActor = vsp<vtkActor>::New();
inputActor->SetMapper(inputMapper);
inputActor->GetProperty()->SetInterpolationToFlat();
inputActor->GetProperty()->SetColor(colors->GetColor3d("flesh").GetData());
inputActor->SetBackfaceProperty(backFace);
auto decimatedMapper = vsp<vtkPolyDataMapper>::New();
decimatedMapper->SetInputData(decimatedPD);
auto decimatedActor = vsp<vtkActor>::New();
decimatedActor->SetMapper(decimatedMapper);
decimatedActor->GetProperty()->SetInterpolationToFlat();
decimatedActor->GetProperty()->SetColor(colors->GetColor3d("flesh").GetData());
decimatedActor->SetBackfaceProperty(backFace);
auto renderWindow = vsp<vtkRenderWindow>::New();
renderWindow->SetSize(800, 400);
auto interactor = vsp<vtkRenderWindowInteractor>::New();
interactor->SetRenderWindow(renderWindow);
double leftViewport[4] = { 0.0, 0.0, 0.5, 1.0 };
double rightViewport[4] = { 0.5, 0.0, 1.0, 1.0 };
auto leftRenderer = vsp<vtkRenderer>::New();
renderWindow->AddRenderer(leftRenderer);
leftRenderer->SetViewport(leftViewport);
leftRenderer->SetBackground(.6, .5, .4);
auto rightRenderer = vsp<vtkRenderer>::New();
renderWindow->AddRenderer(rightRenderer);
rightRenderer->SetViewport(rightViewport);
rightRenderer->SetBackground(.4, .5, .6);
leftRenderer->AddActor(inputActor);
rightRenderer->AddActor(decimatedActor);
auto camera = vsp<vtkCamera>::New();
camera->SetPosition(0, -1, 0);
camera->SetFocalPoint(0, 0, 0);
camera->SetViewUp(0, 0, 1);
camera->Elevation(30);
camera->Azimuth(30);
leftRenderer->SetActiveCamera(camera);
rightRenderer->SetActiveCamera(camera);
leftRenderer->ResetCamera();
leftRenderer->ResetCameraClippingRange();
renderWindow->Render();
interactor->Start();
return EXIT_SUCCESS;
}