OpenVDB初探(一)

OpenVDB初探(一)

Tree
OpenVDB的tree是管理三维体素阵列的稀疏表示组件,其中每个元素(int、float、vec等等)都通过离散的三维索引空间坐标进行处理,通常以Coord进行标识:

openvdb::FloatGrid grid = ...;
openvdb::FloatGrid::Accessor accessor = grid.getAccessor();
openvdb::Coord ijk(1,2,3);
float value = accessor.getValue(ijk);

其中,Transform组件‎将索引空间坐标与世界空间坐标联系起来,为离散数据提供空间信息。‎
举例:我想找(1,2,3)的那个体素,它对应空间坐标是(5,5,5),这中间的换算就是Transform提供的,可以调用。

// Create a linear transform that scales i, j and k by 0.1
openvdb::math::Transform::Ptr linearTransform =
    openvdb::math::Transform::createLinearTransform(0.1);
// Compute the location in world space that is the image of (1,2,3).
// The result will be (0.1, 0.2, 0.3).
openvdb::Coord ijk(1,2,3);
openvdb::Vec3d worldSpacePoint = linearTransform->indexToWorld(ijk);
// Compute the location in index space that is the pre-image of (0.1, 0.2, 0.3).
// The result will be (1.0, 2.0, 3.0).
openvdb::Vec3d indexSpacePoint = linearTransform->worldToIndex(worldSpacePoint);

上面的例子里有两个地方要注意,首先,‎世界空间位置指定为一个double的三维浮点矢量‎,其次,worldToIndex ‎不返回离散坐标(i,j,k),而是浮动点向量。‎这说明Transform生成的索引与体素并非一一离散对应!(使得worldToIndex的返回值就好像插值一样)

Linear Transforms
‎目前支持两种不同类型的Transform:线性 linear 和节点转换 frustum transforms 。

linear 支持缩放、平移、旋转和裁剪,这总的来说可以用一个4x4的仿射矩阵表示。
它可以均匀的对整个空间进行索引编码。(一个包围盒均匀的切成很多个同等体素依次编码)

// Create a linear transform from a 4x4 matrix (identity in this example).
openvdb::math::Mat4d mat = openvdb::math::Mat4d::identity();
openvdb::math::Transform::Ptr linearTransform =
    openvdb::math::Transform::createLinearTransform(mat);
// Rotate the transform by 90 degrees about the X axis.
// As a result the j-index will now map into the -z physical direction,
// and the k-index will map to the +y physical direction.
linearTransform->preRotate(M_PI/2, openvdb::math::X_AXIS);

frustum transforms是对不同索引点进行非均匀索引编码,被设计用于空间子集上运行。
‎具体来说,它将索引空间中的包围盒转换为世界空间中的圆锥形包围盒,进行节点的索引转换。‎(Transform生成的索引与体素一一对应)

// Create the bounding box that will be mapped by the transform into
// the shape of a frustum.
// The points (0,0,0), (100,0,0), (0,50,0) and (100,50,0) will map to
// the corners of the near plane of the frustum, while the corners
// of the far plane will be the images of (0,0,120), (100,0,120),
// (0,50,120) and (100,50,120).
const openvdb::math::BBoxd bbox(/*min=*/openvdb::math::Vec3d(0,0,0),
                                /*max=*/openvdb::math::Vec3d(100,50,120));
// The far plane of the frustum will be twice as big as the near plane.
const double taper = 2;
// The depth of the frustum will be 10 times the x-width of the near plane.
cosnt double depth = 10;
// The x-width of the frustum in world space units
const double xWidth = 100;
// Construct a frustum transform that results in a frustum whose
// near plane is centered on the origin in world space.
openvdb::math::Transform::Ptr frustumTransform =
    openvdb::math:::Transform::createFrustumTransform(
        bbox, taper, depth, xWidth);
// The frustum shape can be rotated, scaled, translated and even
// sheared if desired.  For example, the following call translates
// the frustum by 10,15,0 in world space:
frustumTransform->postTranslate(openvdb::math::Vec3d(10,15,0));
// Compute the world space image of a given point within
// the index space bounding box that defines the frustum.
openvdb::Coord ijk(20,10,18);
openvdb::Vec3d worldLocation = frustumTransform->indexToWorld(ijk);

Cell-Centered vs. Vertex-Centered Transforms
究竟是体素为中心进行索引,还是以顶点为中心进行索引?
前者的(i,j,k)对应坐标是对应体素立方体的中心, (0,0,0) -》(Δ/2, Δ/2, Δ/2)

// -- Constructing a uniform, cell-centered transform --
// The grid spacing
const double delta = 0.1;
// The offset to cell-center points
const openvdb::math::Vec3d offset(delta/2., delta/2., delta/2.);
// A linear transform with the correct spacing
openvdb::math::Transform::Ptr transform =
    openvdb::math:::Transform::createLinearTransform(delta);
// Add the offset.
transform->postTranslate(offset);

后者的(i,j,k)对应顶点是对应体素立方体的边角点 (Δi, Δj, Δk)

// -- Constructing a uniform, vertex-centered transform --
// The grid spacing
const double delta = 0.1;
// A linear transform with the correct spacing
openvdb::math::Transform::Ptr transform =
    openvdb::math:::Transform::createLinearTransform(delta);

Transform类包含像voxelSize和 voxelVolume这样的方法来对体素进行调整,假设索引空间中是顶点为中心的体素立方体,那么voxelSize方法返回顶点相连的线长度(世界坐标系)

openvdb::Coord ijk(0,0,0);
openvdb::Coord tmp0(1,0,0), tmp1(0,1,0), tmp2(0,0,1);
openvdb::math::Vec3d size;
size.x() = (xform.indexToWorld(ijk + tmp0) - xform.indexToWorld(ijk)).length();
size.y() = (xform.indexToWorld(ijk + tmp1) - xform.indexToWorld(ijk)).length();
size.z() = (xform.indexToWorld(ijk + tmp2) - xform.indexToWorld(ijk)).length();
// The voxelSize() for the voxel at (0,0,0) is consistent with
// the computation above.
assert(xform.voxelSize(ijk) == size);

上述代码,linear的 voxelSize 是常数, frustum 是可变参数,Transform由Map执行实现。
Map是一个多态对象,其衍生类型旨在以最佳方式表示最常见的转换‎。通过对水平集进行几何操作来更新Transform时,‎如果可能,将重新计算并简化实施映射‎。‎例如,在许多以水平设置为导向的应用中,索引空间和世界空间之间的转换只是索引点的统一缩放‎,这就可以用 UniformScaleMap 来映射。

// Create a linear transform that scales i, j and k by 0.1
openvdb::math::Transform::Ptr linearTransform =
    openvdb::math::Transform::createLinearTransform(0.1);
// Create an equivalent map.
openvdb::math::UniformScaleMap uniformScale(0.1);
// At this time the map holds a openvdb::math::UniformScaleMap.
assert(linearTransform->mapType() == openvdb::math::UniformScaleMap::type());
openvdb::Coord ijk(1,2,3);
// Applying the transform...
openvdb::math::Vec3d transformResult = linearTransform->indexToWorld(ijk);
// ...is equivalent to applying the map.
openvdb::math::Vec3d mapResult = uniformScale.applyMap(ijk);
assert(mapResult == transformResult);

An Equivalent Matrix Representation
‎OpenVDB 中使用的矩阵表示符合矩阵对向量进行右乘法的规则:‎

// Example matrix transform that scales, next translates,
// and finally rotates an incoming vector
openvdb::math::Mat4d transform = openvdb::math::Mat4d::identity();
transform.preScale(openvdb::math::Vec3d(2,3,2));
transform.postTranslate(openvdb::math::Vec3d(1,0,0));
transform.postRotate(openvdb::math::X_AXIS, M_PI/3.0);
// Location of a point in index space
openvdb::math::Vec3d indexSpace(1,2,3);
// Apply the transform by right-multiplying the matrix.
openvdb::math::Vec3d worldSpace = indexSpace * transform;

‎任何线性Map都可以生成等效‎‎的 AffineMap,而 AffineMap‎‎又可以生成等效的 4×4 矩阵。从线性转换开始,可以生成如下一致的矩阵:‎

openvdb::math::Mat4d matrix;
if (transform->isLinear()) {
   // Get the equivalent 4x4 matrix.
   matrix = transform->getBaseMap()->getAffineMap()->getMat4();
}

‎当通过结合旧的线性转换构建新的线性转换时,这可以用作中间形式。‎

// Get the matrix equivalent to linearTransformA.
openvdb::math::Mat4d matrixA =
    linearTransformA->getBaseMap()->getAffineMap()->getMat4();
// Invert the matrix equivalent to linearTransformB.
openvdb::math::Mat4d matrixBinv =
    (linearTransformB->getBaseMap()->getAffineMap()->getMat4()).inverse();
// Create a new transform that maps the index space of linearTransformA
// to the index space of linearTransformB.
openvdb::math::Transform::Ptr linearTransformAtoB =
    openvdb::math::Trasform::createLinearTransform(matrixA * matrixBinv);

‎请注意,在上述示例中,如果可能,请简化变换使用的内部表示,以使用各种Map类型。‎

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值