OpenVDB 代码示例与使用技巧详解
【免费下载链接】openvdb 项目地址: https://gitcode.com/gh_mirrors/op/openvdb
OpenVDB 是一个开源的 C++ 库,用于高效存储和操作稀疏的、多维的体数据。本文将通过一系列代码示例,深入讲解 OpenVDB 的核心功能和使用技巧,帮助开发者快速掌握这一强大的工具。
基础入门
初始化与简单网格操作
任何使用 OpenVDB 的程序都需要首先初始化库:
#include <openvdb/openvdb.h>
int main() {
openvdb::initialize(); // 必须调用,可安全多次调用
// 创建背景值为0的浮点网格
openvdb::FloatGrid::Ptr grid = openvdb::FloatGrid::create();
// 获取访问器进行坐标访问
openvdb::FloatGrid::Accessor accessor = grid->getAccessor();
// 设置和获取体素值
openvdb::Coord xyz(1000, -200000000, 30000000);
accessor.setValue(xyz, 1.0);
float value = accessor.getValue(xyz); // 获取值为1.0
// 遍历所有活跃体素
for (auto iter = grid->cbeginValueOn(); iter; ++iter) {
std::cout << "Grid" << iter.getCoord() << " = " << *iter << std::endl;
}
}
创建和写入网格
创建网格并写入文件是常见操作:
openvdb::FloatGrid::Ptr grid = openvdb::FloatGrid::create(2.0);
// 设置网格属性
grid->insertMeta("radius", openvdb::FloatMetadata(50.0));
grid->setTransform(openvdb::math::Transform::createLinearTransform(0.5));
grid->setGridClass(openvdb::GRID_LEVEL_SET);
grid->setName("LevelSetSphere");
// 写入文件
openvdb::io::File file("mygrids.vdb");
file.write({grid});
file.close();
OpenVDB 提供了优化工具函数简化操作:
auto grid = openvdb::tools::createLevelSetSphere<openvdb::FloatGrid>(
50.0, openvdb::Vec3f(1.5, 2, 3), 0.5, 4.0);
高级功能
网格填充算法
以下模板函数展示了如何填充球体距离场:
template<class GridType>
void makeSphere(GridType& grid, float radius, const openvdb::Vec3f& c) {
using ValueT = typename GridType::ValueType;
const ValueT outside = grid.background();
const ValueT inside = -outside;
// 计算窄带范围
int padding = int(openvdb::math::RoundUp(openvdb::math::Abs(outside)));
int dim = int(radius + padding);
// 遍历体素计算距离
auto accessor = grid.getAccessor();
openvdb::Coord ijk;
for (ijk[0] = c[0]-dim; ijk[0] < c[0]+dim; ++ijk[0]) {
// ... 类似处理y和z坐标
float dist = /* 计算距离 */;
ValueT val = ValueT(dist);
if (val >= inside && val <= outside) {
accessor.setValue(ijk, val);
}
}
// 填充内部/外部标记
openvdb::tools::signedFloodFill(grid.tree());
}
网格读取与修改
读取并修改网格数据的示例:
openvdb::io::File file("mygrids.vdb");
file.open();
// 查找特定网格
openvdb::GridBase::Ptr baseGrid;
for (auto nameIter = file.beginName(); nameIter != file.endName(); ++nameIter) {
if (nameIter.gridName() == "LevelSetSphere") {
baseGrid = file.readGrid(nameIter.gridName());
}
}
// 转换为浮点网格
auto grid = openvdb::gridPtrCast<openvdb::FloatGrid>(baseGrid);
// 修改网格值
const float outside = grid->background();
const float width = 2.0 * outside;
// 修改活跃值
for (auto iter = grid->beginValueOn(); iter; ++iter) {
float dist = iter.getValue();
iter.setValue((outside - dist) / width);
}
// 修改非活跃值
for (auto iter = grid->beginValueOff(); iter; ++iter) {
if (iter.getValue() < 0.0) iter.setValue(1.0);
}
// 修改背景值
openvdb::tools::changeBackground(grid->tree(), 0.0);
元数据处理
添加和获取元数据
grid->insertMeta("vector_type", openvdb::StringMetadata("covariant"));
grid->insertMeta("radius", openvdb::FloatMetadata(50.0));
// 获取元数据
std::string type = grid->metaValue<std::string>("vector_type");
float radius = grid->metaValue<float>("radius");
// 遍历所有元数据
for (auto iter = grid->beginMeta(); iter != grid->endMeta(); ++iter) {
std::cout << iter->first << " = " << iter->second->str() << std::endl;
}
动态类型处理
auto metadata = grid["center"];
if (metadata->typeName() == "vec3s") {
auto vec = static_cast<openvdb::Vec3SMetadata&>(*metadata).value();
}
迭代器使用
节点迭代器
using GridType = openvdb::FloatGrid;
using TreeType = GridType::TreeType;
for (auto iter = grid->tree().beginNode(); iter; ++iter) {
switch (iter.getDepth()) {
case 0: { /* 处理根节点 */ break; }
case 1: { /* 处理内部节点 */ break; }
// ... 其他层级
}
}
叶节点迭代器
for (auto leaf = grid->tree().beginLeaf(); leaf; ++leaf) {
// 处理每个叶节点
}
流式I/O
OpenVDB 支持流式读写:
// 写入流
std::ostringstream ostr(std::ios_base::binary);
openvdb::io::Stream(ostr).write(*grids);
// 从流读取
std::istringstream istr(ostr.str(), std::ios_base::binary);
auto grids = openvdb::io::Stream(istr).getGrids();
总结
本文详细介绍了 OpenVDB 的核心功能,从基础操作到高级技巧。通过示例代码展示了如何创建、修改、存储和读取网格数据,以及如何处理元数据和高效遍历数据结构。掌握这些技术后,开发者可以充分利用 OpenVDB 的高效稀疏数据结构来处理复杂的体数据问题。
【免费下载链接】openvdb 项目地址: https://gitcode.com/gh_mirrors/op/openvdb
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



