OpenVDB:VFX 体积数据

June 24, 2026 · View on GitHub

简介

OpenVDB 是一个用于存储、操作和渲染稀疏体积数据的开源库。由 DreamWorks Animation 开发,现由 Academy Software Foundation 维护,它已成为电影和视觉效果中体积效果的行业标准。

OpenVDB 使用分层数据结构,仅高效存储包含数据的空间区域,使得处理极大的体积数据集而不耗尽内存成为可能。

为什么体积数据很重要

许多自然现象本质上是三维的,无法用表面网格表示。体积数据可以准确捕捉这些效果。

现象传统方法OpenVDB 方法
烟雾Billboard 精灵完整 3D 模拟
火焰2D 纹理体积发射
分层卡片真实 3D 密度场
液体表面网格SDF + 速度场
均匀密度空间变化密度
爆炸粒子精灵动态体积网格

核心数据结构

OpenVDB 使用具有三级节点的 B+ 树结构。

级别节点大小用途典型数量
5123512^{3}(坐标范围)顶层索引1
内部434^{3}838^{3}中层分支数千
838^{3}(512 体素)实际体素数据数百万

稀疏 vs 密集存储

存储类型内存数据访问用例
密集O(n^3)直接索引小体积
稀疏 (VDB)O(活跃体素)树遍历VFX 体积
稀疏(自适应)不定多分辨率水平集

网格类型

网格类型值类型典型用途
FloatGridfloat密度、温度
Vec3fGridVector3f速度、颜色
Int32Gridint32_t标签、ID
BoolGridbool蒙版
DoubleGriddouble高精度标量
HalfGridhalf节省内存的浮点

安装

从源码构建

git clone https://github.com/AcademySoftwareFoundation/openvdb.git
cd openvdb
mkdir build && cd build
cmake .. \
  -DCMAKE_INSTALL_PREFIX=/usr/local \
  -DOPENVDB_BUILD_PYTHON_MODULE=ON \
  -DOPENVDB_BUILD_BINARIES=ON
make -j$(nproc)
sudo make install

依赖项

依赖必需用途
Boost智能指针、IO
TBB多线程
OpenEXR半精度浮点支持
Zlib压缩
Blosc可选体素压缩
Python可选Python 绑定
NumPy可选数组互操作
GLFW可选查看器

包管理器

平台命令
macOSbrew install openvdb
Ubuntusudo apt install libopenvdb-dev
Condaconda install -c conda-forge openvdb
vcpkgvcpkg install openvdb

Python API

创建网格

import openvdb
import numpy as np

# 创建浮点网格
grid = openvdb.FloatGrid()
grid.name = "density"

# 从 numpy 数组创建
data = np.zeros((100, 100, 100), dtype=np.float32)
data[40:60, 40:60, 40:60] = 1.0  # 密度立方体

grid.copyFromArray(data)
print(f"Active voxel count: {grid.activeVoxelCount}")
print(f"Grid bbox: {grid.evalActiveVoxelBoundingBox()}")

读取和写入 VDB 文件

import openvdb

# 将网格写入文件
density = openvdb.FloatGrid()
velocity = openvdb.Vec3fGrid()

openvdb.write("smoke.vdb", density, velocity)

# 从文件读取网格
grids = openvdb.readAll("smoke.vdb")
for grid in grids:
    print(f"Name: {grid.name}, Type: {grid.type}")
    print(f"Active voxels: {grid.activeVoxelCount}")

访问体素数据

import openvdb
import numpy as np

grid = openvdb.FloatGrid()
# ... 填充网格 ...

# 转换为 numpy 数组
array = grid.copyToArray()
print(f"Array shape: {array.shape}")

# 访问特定坐标
accessor = grid.getAccessor()
value = accessor.getValue((0, 0, 0))
print(f"Value at (0,0,0): {value}")

# 设置值
accessor.setValueOn((10, 10, 10), 5.0)

水平集

水平集将表面表示为有符号距离场的零交叉。

import openvdb
import numpy as np

# 创建球体水平集
sphere = openvdb.createLevelSetSphere(
    radius=50.0,
    center=(0.0, 0.0, 0.0),
    voxelSize=1.0
)

print(f"Active voxels: {sphere.activeVoxelCount}")
print(f"BBox: {sphere.evalActiveVoxelBoundingBox()}")

# 布尔操作
cube = openvdb.createLevelSetBox(
    (-30, -30, -30), (30, 30, 30), voxelSize=1.0
)

# 并集
union = openvdb.tools.levelSetUnion(sphere, cube)

# 交集
intersection = openvdb.tools.levelSetIntersection(sphere, cube)

# 差集
difference = openvdb.tools.levelSetDifference(sphere, cube)

C++ API

创建和填充网格

#include <openvdb/openvdb.h>
#include <openvdb/tools/LevelSetSphere.h>

// 初始化 OpenVDB
openvdb::initialize();

// 创建稀疏网格
openvdb::FloatGrid::Ptr grid = openvdb::FloatGrid::create(0.0);
grid->setName("density");

// 获取访问器以高效访问体素
openvdb::FloatGrid::Accessor accessor = grid->getAccessor();

// 设置单个体素
for (int i = 0; i < 100; ++i) {
    openvdb::Coord xyz(i, i, i);
    accessor.setValue(xyz, 1.0f);
}

// 打印统计
std::cout << "Active voxels: " << grid->activeVoxelCount() << std::endl;

水平集操作

#include <openvdb/tools/LevelSetSphere.h>
#include <openvdb/tools/LevelSetUtil.h>

// 创建球体水平集
openvdb::FloatGrid::Ptr sphere = 
    openvdb::tools::createLevelSetSphere<openvdb::FloatGrid>(
        50.0f, openvdb::Vec3f(0, 0, 0), 1.0f);

// 创建立方体水平集
openvdb::FloatGrid::Ptr box = 
    openvdb::tools::createLevelSetBox<openvdb::FloatGrid>(
        openvdb::BBoxd(openvdb::Vec3d(-30), openvdb::Vec3d(30)), 1.0f);

// 布尔操作
openvdb::FloatGrid::Ptr result = 
    openvdb::tools::csgUnion(*sphere, *box);

工具和操作

过滤操作

操作函数用例
均值tools::mean平滑
高斯tools::gaussian模糊
中值tools::median降噪
膨胀tools::dilate扩展区域
腐蚀tools::erode收缩区域
偏移tools::offset水平集偏移

转换工具

转换用例
网格到 VDB多边形网格体积模拟设置
VDB 到网格体积多边形网格渲染
雾到 SDF密度场水平集表面提取
SDF 到雾水平集密度体积渲染
import openvdb

# 将水平集转换为网格(使用 C++ 绑定)
# mesh = openvdb.tools.volumeToMesh(sphere_grid)

# 将网格转换为水平集
# grid = openvdb.tools.meshToLevelSet(mesh, voxel_size=1.0)

文件格式

VDB 文件格式存储一个或多个网格及其元数据。

功能描述
多网格每个文件多个网格
压缩Blosc、ZIP 或无
元数据每个网格的键值对
部分 I/O读取/写入特定网格
流式传输顺序读取支持

文件结构

部分内容描述
头部魔数、版本文件标识
网格描述符名称、类型、变换网格元数据
树数据B+ 树节点体素数据
元数据键值对自定义属性
尾部校验和完整性验证

与 VFX 工具的集成

工具OpenVDB 集成备注
Houdini原生VDB SOP 节点
Blender原生体积对象和修改器
Maya插件Bifrost 集成
Katana原生体积渲染
3ds Max插件Phoenix FD 集成
Unreal Engine插件Niagara VDB 支持
Arnold原生体积着色器
V-Ray原生VDB 渲染

模拟工作流

烟雾模拟

import openvdb

# 步骤 1:创建密度网格
density = openvdb.FloatGrid()
density.name = "density"

# 步骤 2:创建速度场
velocity = openvdb.Vec3fGrid()
velocity.name = "velocity"

# 步骤 3:创建温度网格
temperature = openvdb.FloatGrid()
temperature.name = "temperature"

# 步骤 4:通过速度场平流密度
# (模拟在外部发生,VDB 存储数据)

# 步骤 5:写入输出
openvdb.write("smoke_frame001.vdb", density, velocity, temperature)

水平集工作流

import openvdb

# 创建初始水平集(例如球体)
sphere = openvdb.createLevelSetSphere(5.0, (0, 0, 0), 0.1)

# 跟踪哪些体素发生了变化
sphere.setGridClass(openvdb.GridClass.LEVEL_SET)

# 写入以进行网格化
openvdb.write("levelset.vdb", sphere)

性能优化

优化技术影响
体素大小使用可接受的最大尺寸
压缩启用 Blosc 压缩
稀疏访问使用访问器,而非直接迭代
变换在变换中使用适当的体素大小
背景值设置为空空间的典型值
线程数TBB 自动检测核心数

最佳实践

实践建议
网格命名为每个网格使用描述性名称
体素大小匹配所需的细节级别
压缩文件存储时启用,计算时禁用
水平集保持窄带宽度一致
元数据在网格元数据中存储模拟参数
内存模拟期间监控活跃体素数量

附加资源

资源URL
文档openvdb.org
GitHubgithub.com/AcademySoftwareFoundation/openvdb
API 参考openvdb.org/documentation
SIGGRAPH 课程openvdb.org/documentation