目录
- Hello World
- Creating and writing a grid
- Populating a grid with values
- Reading and modifying a grid
- Stream I/O
- Handling metadata
- Adding metadata
- Retrieving metadata
- Removing metadata
- Iteration
- Node Iterator
- Leaf Node Iterator
- Value Iterator
- Iterator Range
- Interpolation of grid values
- Index-space samplers
- Grid Sampler
- Dual Grid Sampler
- Transforming grids
- Geometric transformation
- Value transformation
- Combining grids
- Level set CSG operations
- Compositing operations
- Generic combination
- Generic programming
- Calling Grid methods
- “Hello, World” for OpenVDB Points
- Converting Point Attributes
- Random Point Generation
- Point Iteration, Groups and Filtering
- Point Iteration
- Creating and Assigning Point Groups
- Point Filtering using Groups
- Point Filtering using Custom Filters
- Strided Point Attributes
- Constant Stride Attributes
- Moving Points in Space
- Advecting Points
- Moving Points with a Custom Deformer
代码示例:
https://www.openvdb.org/documentation/doxygen/codeExamples.html#sHelloWorld
Hello World
初始化
创建网格
从网格获取访问器
创建坐标
修改坐标
访问器访问指定坐标
遍历网格
Creating and writing a grid
创建 openvdb::io::File
类型的对象,使用它来读写 vdb 文件
Populating a grid with values
第一步只创建表面周围的 SDF 值,也就是创建一个窄带
窄带的 SDF 值的区间取为 [-background_SDF, background_SDF]
。background_SDF
是正数,是背景的 SDF 值
按照一定的间隔,跳跃着遍历立方体 grid
遍历到的元素的 SDF 值如果在这个区间内,也就是在窄带内,那么就赋值,否则跳过
具体怎么计算遍历到的元素的 SDF 值,是 (x,y,z) - center
的长度减去球的半径
Reading and modifying a grid
遍历一个 openvdb::io::File
对象中的所有网络,根据网格名字查找到某一个网络
用网格基类存储查找到的网格。如果已知网格子类的类型,就可以直接 cast。
遍历 active 的 grid 元素,这些元素对应窄带 narrow band 元素
遍历 inactive 的 grid 元素,这些元素对应内部 interior 的元素
Stream I/O
写入(读取) string 流或者文件流
Handling metadata
元数据
Adding metadata
添加元数据
重复添加相同的名字相同类型的值,那么就是覆盖
如果是重复添加相同的名字不同类型的值,那么就报错
Retrieving metadata
已知元数据的数据类型,根据名字可以获取值
遍历元数据 map
可以获得元数据的 string 形式的类型名称,进而得知某个元数据的数据类型,进而进行类型转换
Removing metadata
删除不存在的元数据,不会报错,但是也没有效果
Iteration
Node Iterator
遍历树
Leaf Node Iterator
遍历叶子节点
遍历常量叶子节点
转换成引用或者指针
Value Iterator
遍历 active 的 grid 元素,这些元素对应窄带 narrow band 元素
遍历 inactive 的 grid 元素,这些元素对应内部 interior 的元素
Iterator Range
怎么使用 tbb 的 parallel_for
并行执行任务
首先要输入 openvdb 提供的迭代器
然后要自己创建一个重载了括号运算符的结构体
输入这个结构体作为任务
这个结构体的括号运算符的重载就是任务主体。它接受一个迭代器
,这个迭代器的范围是由 tbb 划分的,原始迭代范围的子区间
Interpolation of grid values
需要在分数坐标的值的时候,就需要在整数坐标的值之间插值
GridSampler
用于插值
GridTransformer
用于对所有值进行变换
Index-space samplers
在索引空间中插值
零阶插值 最近邻
一阶插值 线性
二阶插值 二次函数
通过树 grid.tree()
来访问是线程安全的,但是因为没有缓存,所以是次优的
最佳的是通过访问器,例如 grid.getConstAccessor()
因为访问器采用了缓存
也因此,访问器必须是一个线程一个访问器
Grid Sampler
GridSampler
可以在索引空间或者世界空间中插值
使用网格来构造 GridSampler
,GridSampler
会自动获得索引空间到世界空间的变换
但是使用树或者访问器来构造 GridSampler
,需要提供变换
用访问器构造 GridSampler
更快
Dual Grid Sampler
有些时候存在两个网格,一个源网格,一个目标网格
给定目标网格中的索引,需要从源网格得到相同索引位置的插值结果
所以提供了工具类 DualGridSampler
这个类还会检查两个网格的变换是不是一样的,如果不一样的话,这个任务就没有意义了
Transforming grids
Geometric transformation
有点不知道为什么,网格从一个变换转到另外一个变换是 源 transform 乘 目标 transform 的逆
Value transformation
openvdb::tools::foreach
可以遍历所有的值
openvdb::tools::transformValues
遍历源网格的值,同时把结果输出到一个目标网格
Combining grids
假设某个索引坐标,在两个网格中都对应同一个世界坐标,才能谈网格的组合
如果不是对应同一个世界坐标,那就是两个网格的变换不同,一般的方法是先将一个网格重新采样到另一个网格的索引空间
Level set CSG operations
Constructive solid geometry, CSG,构造立体几何
并集 交集 差集
Compositing operations
组合操作,取最大值,取最小值,加法,乘法
Generic combination
自定义操作符
使用 Tree::combine
来对两个树进行自定义操作符的操作
Tree::combineExtended
可以设置两个树的迭代元素的值和 active 状态
Tree::combine
将结果写到 A,B 留空;Tree::combine2
将结果写到第三个树
Generic programming
Calling Grid methods
这节演示了怎么泛型编程
processTypedGrid
这个函数用了一个叫作 dependent names 的特性,这个之前我还没见过,很酷
https://en.cppreference.com/w/cpp/language/dependent_name
The template disambiguator for dependent names
Similarly, in a template definition, a dependent name that is not a member of the current instantiation is not considered to be a template name unless the disambiguation keyword template is used or unless it was already established as a template name:
template<typename T>
struct S
{template<typename U>void foo() {}
};template<typename T>
void bar()
{S<T> s;s.foo<T>(); // error: < parsed as less than operators.template foo<T>(); // OK
}
之后的 PruneOp
类中也用到了这个特性
“Hello, World” for OpenVDB Points
对点集进行划分的工具类
对点集划分之后,可以计算体素大小
根据体素大小可以获得网格的变换
然后就可以创建点网格
根据字符串获得属性的这个,有点奇妙啊
const openvdb::points::AttributeArray& array =leafIter->constAttributeArray("P");
他用 “P” 来获得 position,这种缩写的规则,看上去就很容易搞混啊
从点网格获得属性的过程是,先获取叶子节点,然后得到 AttributeArray
,然后得到 AttributeHandle
Converting Point Attributes
要对属性进行操作的时候,需要共通的 openvdb::tools::PointIndexGrid
创建 openvdb::points::PointDataGrid
的时候,还有把新的数组添加的网格的新字段的时候,都需要 openvdb::tools::PointIndexGrid
创建 openvdb::points::PointDataGrid
的时候,看上去是默认创建了一个名字为 “P” 的字段
然后之后如果要对点添加新字段的话,先注册类型
openvdb::points::TypedAttributeArray<float, Codec>::registerType();
然后再 openvdb::points::appendAttribute
添加字段到网格
现在网格有了字段,还是没有对应的数据
还要用 openvdb::points::populateAttribute
添加数据到对应字段
Random Point Generation
创建一个 openvdb::points::PointDataTree
,可以拷贝别的网格的树结构来创建
要想使用 openvdb::points::PointDataTree
来创建 openvdb::points::PointDataGrid
,还需要创建 openvdb::points::AttributeSet::Descriptor
,然后 initializeAttributes
初始化 PointDataTree
的每一个叶节点
Point Iteration, Groups and Filtering
Point Iteration
迭代点网格的方法
两层循环,外层迭代树结构的所有叶子节点,内层对于每一个叶子节点,根据属性的名字构建 openvdb::points::AttributeArray
,进而创建 openvdb::points::AttributeHandle
。内层循环是对于叶子节点的每个索引,根据索引从 AttributeHandle
中 get 或 set 值
要使用 TBB 的并行,TBB 需要迭代器和回调函数,迭代器由 openvdb::tree::LeafManager
提供,回调函数就是自己创建结构体重载括号运算符
为了单线程调试 TBB,可以把输入的范围做成没有拆分的,对于 openvdb::tree::LeafManager
就是把 leafRange
的参数 grainsize
设置得很大,大于所有叶子节点的总数
Creating and Assigning Point Groups
树底下是节点,遍历叶子节点
叶子节点里面可以遍历 index
index 可以添加到 Group
添加到 Group 的 handle 需要从叶子节点这里获取
很神奇噢,属性句柄 AttributeHandle
和顶点组写入句柄 openvdb::points::GroupWriteHandle
都是从叶子节点这里得来的
Point Filtering using Groups
顶点组可以方便过滤迭代
如果已经创建好了一个顶点组
那么使用这个顶点组的名称就可以创建 openvdb::points::GroupFilter
在迭代某个叶子节点的 index 的时候,使用这个 filter
,就可以自动遍历属于这个组的 index
for (auto indexIter = leafIter->beginIndexOn(filter); indexIter; ++indexIter) {
Point Filtering using Custom Filters
不用点组的话,可以自己创建一个结构体,提供 valid 函数,作为过滤器
Strided Point Attributes
步长表示每一个属性能有多少个值
Constant Stride Attributes
在 openvdb::points::appendAttribute
添加属性的时候,就把步长参数传进去了
Moving Points in Space
Advecting Points
使用一个均匀的场来平流点
Moving Points with a Custom Deformer
自定义平流方法
也是自定义一个结构体,提供特定函数的方法,形参是 vec3 和 index