使用CGAL网格化OpenVDB文件时高宽比模型出现噪点问题求助
问题:CGAL网格化OpenVDB高宽比模型时出现噪点
我尝试用CGAL对OpenVDB(.vdb)文件做网格化处理,中等分辨率的有机模型处理效果正常,但处理高宽比模型时,输出网格会有大量噪点。一开始以为是体素分辨率不够,但调整分辨率后问题没改善。
以下是可复现该问题的最简脚本(需依赖CGAL和OpenVDB库,建议以Release模式编译,Debug模式下编译可能遇到问题):
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h> #include <CGAL/Mesh_triangulation_3.h> #include <CGAL/Mesh_complex_3_in_triangulation_3.h> #include <CGAL/Mesh_criteria_3.h> #include <CGAL/Labeled_mesh_domain_3.h> #include <CGAL/make_mesh_3.h> #include <openvdb/openvdb.h> #include <openvdb/tools/Interpolation.h> typedef CGAL::Exact_predicates_inexact_constructions_kernel K; typedef CGAL::Labeled_mesh_domain_3<K> Mesh_domain; typedef CGAL::Mesh_triangulation_3<Mesh_domain>::type Tr; typedef CGAL::Mesh_complex_3_in_triangulation_3<Tr> C3t3; typedef CGAL::Mesh_criteria_3<Tr> Mesh_criteria; namespace params = CGAL::parameters; int main() { // --- Load VDB --- openvdb::initialize(); openvdb::io::File file("../data/Cylinder.vdb"); file.open(); auto grid = openvdb::gridPtrCast<openvdb::FloatGrid>( file.readGrid(file.beginName().gridName())); file.close(); const double voxelSize = grid->transform().voxelSize()[0]; const openvdb::math::Transform& xform = grid->transform(); // --- Bounding box --- openvdb::CoordBBox bbox = grid->evalActiveVoxelBoundingBox(); openvdb::Vec3d worldMin = xform.indexToWorld(bbox.min().asVec3d()); openvdb::Vec3d worldMax = xform.indexToWorld(bbox.max().asVec3d()); K::Iso_cuboid_3 boundingBox( K::Point_3(worldMin[0], worldMin[1], worldMin[2]), K::Point_3(worldMax[0], worldMax[1], worldMax[2])); // --- Implicit domain from VDB SDF --- auto accessor = grid->getConstAccessor(); Mesh_domain domain = Mesh_domain::create_implicit_mesh_domain( [&accessor, &xform, voxelSize](const K::Point_3& p) -> K::FT { openvdb::Vec3d idx = xform.worldToIndex( openvdb::Vec3d(p.x(), p.y(), p.z())); float val = openvdb::tools::PointSampler::sample(accessor, idx); return static_cast<K::FT>(val * voxelSize); }, boundingBox); // --- Mesh criteria --- Mesh_criteria criteria( params::facet_angle(20) .facet_size(voxelSize * 300) .facet_distance(voxelSize * 3) .cell_radius_edge_ratio(2) .cell_size(voxelSize * 300)); C3t3 c3t3 = CGAL::make_mesh_3<C3t3>(domain, criteria); std::ofstream out("out.mesh"); CGAL::IO::write_MEDIT(out, c3t3); return 0; }
网格化效果示例:

问题原因分析
高宽比模型出现噪点的核心原因通常是SDF采样精度与CGAL网格化准则不匹配,或者高宽比场景下单一的体素缩放系数导致网格准则在不同维度上约束失效。另外OpenVDB的点采样(PointSampler)在高梯度区域精度不足,也会引入噪点。
解决方案
1. 优化SDF采样方式
替换PointSampler为三线性插值采样,它能在体素间提供更平滑的SDF值,减少高梯度区域噪点:
float val = openvdb::tools::TrilinearSampler::sample(accessor, idx);
2. 调整网格化准则的维度相关性
当前代码中facet_size和cell_size用固定缩放系数,高宽比模型不同维度体素密度差异大,需动态调整:
- 先计算模型高宽比:
double dx = worldMax[0] - worldMin[0]; double dy = worldMax[1] - worldMin[1]; double dz = worldMax[2] - worldMin[2]; double maxDim = std::max({dx, dy, dz}); double minDim = std::min({dx, dy, dz}); double aspectRatio = maxDim / minDim;
- 基于高宽比调整准则参数,对窄维度使用更小尺寸:
Mesh_criteria criteria( params::facet_angle(20) .facet_size(std::min(voxelSize * 100, minDim / 20)) // 限制最小面尺寸 .facet_distance(voxelSize * 1) // 缩小面距离阈值,提高贴合度 .cell_radius_edge_ratio(2) .cell_size(std::min(voxelSize * 100, minDim / 15)));
3. 修正SDF单位转换
OpenVDB的SDF值若已为世界空间距离,无需额外乘以体素大小,否则会导致梯度异常:
// 注释单位转换,直接返回原始SDF值 return static_cast<K::FT>(val);
若原始SDF是体素空间的,再保留* voxelSize。
4. 启用自适应网格化
让CGAL在高梯度区域自动细化网格:
Mesh_criteria criteria( params::facet_angle(20) .facet_size(voxelSize * 100) .facet_distance(voxelSize * 1) .cell_radius_edge_ratio(2) .cell_size(voxelSize * 100) .adaptive(true)); // 启用自适应细化
5. 预处理OpenVDB网格
对VDB网格做高斯平滑,减少SDF高频噪声:
#include <openvdb/tools/Smooth.h> // 迭代次数可根据需求调整 openvdb::tools::smoothGrid(*grid, 3);
替代方案
若上述调整效果不佳,可尝试先提取等值面点云再用CGAL泊松重建:
// 提取等值面点云 std::vector<K::Point_3> points; openvdb::tools::extractLevelSetPoints(*grid, points); // 泊松重建 CGAL::Poisson_reconstruction_function<K> function(points.begin(), points.end()); if (!function.compute_implicit_function()) { // 处理错误逻辑 } CGAL::Surface_mesh<K::Point_3> output_mesh; CGAL::poisson_surface_reconstruction_delaunay(function, output_mesh); // 保存网格 std::ofstream out("out_poisson.obj"); out << output_mesh;
内容的提问来源于stack exchange,提问作者user32692817
相关产品推荐
相关产品推荐

