Professional Documents
Culture Documents
Openvdb: Ken Museth - Nvidia
Openvdb: Ken Museth - Nvidia
• Tile
- Larger constant domain of index space
- Resides at the upper (non-leaf) tree levels
• Active state
- All values (voxels and tiles) have a binary state
- Interpretation of state is application-defined
Motivation OpenVDB
VDB
Compact Fast
2D y
outside
inside
Tall
1D tree
Dense representation
Adaptive x
Shallow Tree O(1) Access
VDB topology
Root node
Active bit-mask
Child bit-mask
Internal nodes
Leaf nodes
Sparse representation
Encode
Decode
Values
VDB Tree OpenVDB
Active Mask
Child Mask
Internal Node 1
Tile values / Child pointers
Active Mask
Child Mask
Internal Node 2
Tile values / Child pointers
Active Mask
Leaf Node Voxels
tree.getValue(x,y,z)=?
Random Access: Bottom-Up OpenVDB
accessor .getValue(x,y,z)=?
Convolution &
Morphology Operations Interface Tracking
Smoothing
CPU w. 8 cores
Support of Point Data OpenVDB
• PointPartitioner
• PointIndexGrid
• PointDataGrid
Multi-Resolution Grids OpenVDB
Conversion To Polygons OpenVDB
Uniform Mesh
(4.8M points + 4.8M quads)
129 MB
Houdini SOP Nodes OpenVDB
3delight
major.minor.patch
• Patch:
- No change to API, file format or ABI of Grid or its member classes
• Minor:
- Change to API but not Grid ABI; backward-compatible file format
• Major:
- Change to ABI of Grid or non-backward-compatible file format
• Guaranteed Grid and file compatibility with fixed major version
• One major release per year
• Support last three versions on VFX Platform
A Few Highlights in v8 OpenVDB
OpenVDB NanoVDB
16 Bit floats
8 Bit floats
4 Bit floats
RAY-TRACING LEVEL SET WITH 2.2 BILLION VOXELS VOLUME RENDERING DENSITY WITH 1.5 BILLION VOXELS
Wil Braithwaite
NanoVDB in FLOW NanoVDB
Sadjad Rabiee
• http://www.openvdb.org
- Course slides from 2013 - 2021
- Technical papers
- Coding cookbook and doxygen
- vdb assets (points and voxels)
- Houdini hip file with examples
- Build instructions, license, CLA, TSC minutes
• https://github.com/AcademySoftwareFoundation/openvdb
NANOVDB
CASE STUDY IN HOUDINI
• What is NanoVDB
• Why NanoVDB
• Case Study: Blind-Data OpenCL Integration
• Example: Particle collisions
• Example: Smoke Sourcing
• Example: Cloth Collisions
• .nvdb files
• Security Concerns
• Digression on Dithering
• Summary
• OpenVDB compatible
• A Reference Data Layout
• Read Only (-ish)
• Pre-Marshalled (Zero-copy serialization/deserialization)
• Header Only
.vdb files
Simulation Rendering System
System
void *
void *
?
GPU System
Simulation
System
#include <nanovdb/util/OpenToNanoVDB.h>
#include "nanovdb/util/OpenToNanoVDB.h"
void
CE_VDBGrid::initFromVDB(const openvdb::GridBase &grid)
{
UTvdbCallAllType(UTvdbGetGridType(grid), CEvdbToBuffer, grid, &myBuffer);
UTvdbCallPointType(UTvdbGetGridType(grid), CEvdbToBuffer, grid, &myBuffer);
}
Do Not Use
THE PREMIER CONFERENCE & EXHIBITION IN
© 2021 SIGGRAPH. ALL RIGHTS RESERVED. COMPUTER GRAPHICS & INTERACTIVE TECHNIQUES
14
SECURITY CONCERNS
• Accidental Misuse
bool isValid() const { return DataType::mMagic == NANOVDB_MAGIC_NUMBER; }
uint64_t checksum() const { return DataType::mChecksum; }
• Intentional Misuse
template <typename ValueT>
bool isValid(const NanoGrid<ValueT> &grid, bool detailed, bool verbose)
Maximum
Value
Minimum
Value
0 8 2 10
12 4 14 6
3 11 1 9
15 7 13 5
0 8 2 10
12 4 14 6
3 11 1 9
15 7 13 5
0 8 2 10
12 4 14 6
3 11 1 9
15 7 13 5
s e e e e e e e e m m m m m m m m m m m m m m m m m m m m m m m
2 2 2 1 1 1 1 1 1 1 1 1 1 9 8 7 6 5 4 3 2 1 0
2 1 0 9 8 7 6 5 4 3 2 1 0
65535 512/513
s e e e e e e e e m m m m m m m m m m m m m m m m m m m m m m m
2 2 2 1 1 1 1 1 1 1 1 1 1 9 8 7 6 5 4 3 2 1 0
2 1 0 9 8 7 6 5 4 3 2 1 0
• NanoVDB provides a way to move sparse volumes in non-shared memory architectures efficiently
• NanoVDB is a very fast way to gain read-only VDB support
− No dependencies
− C++, C, OpenCL, GLSL, and more platforms provided
− Command line tools to convert .vdb to .nvdb
− Ideally provide direct support for .vdb when possible to simplify people’s pipelines!
• For algorithms manipulating sparse volumes, use OpenVDB
• For storage and compatibility, use .vdb files
format disk
32 bit 1.9 MB
16 bit 1.3 MB
THE PREMIER CONFERENCE & EXHIBITION IN
16 bit blosc 970 KB
COMPUTER GRAPHICS & INTERACTIVE TECHNIQUES 5
ARBITRARY GRIDS
color velocity
Miller, B., Museth, K., Penney, D. and Bin Zafar, N. Cloud modeling and rendering. Siggraph Talk, 2012 THE PREMIER CONFERENCE & EXHIBITION IN
10
COMPUTER GRAPHICS & INTERACTIVE TECHNIQUES
CLOUD MODELING
Lee, D. How to Train for Cloud. Siggraph Asia Course, 2020 THE PREMIER CONFERENCE & EXHIBITION IN
COMPUTER GRAPHICS & INTERACTIVE TECHNIQUES 11
CLOUDS
Lee, D. How to Train for Cloud. Siggraph Asia Course, 2020 THE PREMIER CONFERENCE & EXHIBITION IN
12
COMPUTER GRAPHICS & INTERACTIVE TECHNIQUES
Miller, B., Museth, K., Penney, D. and Bin Zafar, N. Cloud modeling and rendering. Siggraph Talk, 2012
RASTER PRIMITIVES
frustum buffer
LOD pyramid
frustum ortho
Budsberg, J., Losure, M., Museth, K., Baer, M. Liquids in The Croods. DigiPro, 2013
Losure, M. Surreal Night Swimming in Home. Siggraph Dailies, 2015 THE PREMIER CONFERENCE & EXHIBITION IN
COMPUTER GRAPHICS & INTERACTIVE TECHNIQUES 19
Van Opstal, B., Janin, L., Museth, K. Large Scale Simulation of Water and Ice in Dragon 2, Siggraph Talk, 2014
LEVEL SET FILTERING & MORPHOLOGICAL OPS
mask
segmentation
sharpen
THE PREMIER CONFERENCE & EXHIBITION IN
COMPUTER GRAPHICS & INTERACTIVE TECHNIQUES 23
LEVEL SET SURFACING
mask
gradient curvature
velocity vorticity
Losure, M. Surreal Night Swimming in Home. Siggraph Dailies, 2015 THE PREMIER CONFERENCE & EXHIBITION IN
COMPUTER GRAPHICS & INTERACTIVE TECHNIQUES 26
SIMPLIFICATION MASKS
Van Opstal, B., Janin, L., Museth, K. Large Scale Simulation and Surfacing of Water THE PREMIER CONFERENCE & EXHIBITION IN
27
COMPUTER GRAPHICS & INTERACTIVE TECHNIQUES
and Ice in How to Train Your Dragon 2, Siggraph Talk, 2014
OPEN MESH COLLISIONS
open mesh
SDF
UDF
SDF
Stomakhin, A. et al. A material point method for snow simulation. Siggraph, 2013 THE PREMIER CONFERENCE & EXHIBITION IN
31
COMPUTER GRAPHICS & INTERACTIVE TECHNIQUES
Klar, G., Budsberg, J. et al. Production ready MPM simulations. ACM Siggraph Talks, 2017
VDB POINTS
example: 1 M points
VDB from Particles Topology to SDF (6x speed!) VDB Points topology
existing simulation
modified simulation
rasterize
isolate low
density
density points rasterize velocity project non-divergent update velocity
points
RAM >250 Gb
4% CPU usage
• limited resource!
Level sets
Filtering
Morphological ops
Less RAM/CPU
Data Analysis 100% CPU usage
• fits on average
farm blade
• lots of resources
• faster turnaround
Hundreds of waterfalls
points
partitioned points padded read
open mesh
uniform partition
UDF level set + filtering VDB clip seamless distributed level set
iterative
binary partition
SDF
open mesh
UDF
SDF
extrapolated SDF grids
masks
Museth, K. Novel Algorithm for Sparse and Parallel Fast Sweeping: Efficient THE PREMIER CONFERENCE & EXHIBITION IN
45
COMPUTER GRAPHICS & INTERACTIVE TECHNIQUES
Computation of Sparse Signed Distance Fields. Siggraph 2017
MASKED EXTRAPOLATION
source grid
mask
Museth, K. Novel Algorithm for Sparse and Parallel Fast Sweeping: Efficient THE PREMIER CONFERENCE & EXHIBITION IN
46
COMPUTER GRAPHICS & INTERACTIVE TECHNIQUES
Computation of Sparse Signed Distance Fields. Siggraph 2017
FILTER EXTRAPOLATION
Extrap examples
Alden, M., Melich, G. and Museth, K. Efficient and seamless volumetric fracture. THE PREMIER CONFERENCE & EXHIBITION IN
49
COMPUTER GRAPHICS & INTERACTIVE TECHNIQUES
Siggraph Talk, 2012
PROXY GENERATION
Budsberg, J., Bin Zafar, N., Alden, M. Elastic and Plastic Deformations with Rigid THE PREMIER CONFERENCE & EXHIBITION IN
50
COMPUTER GRAPHICS & INTERACTIVE TECHNIQUES
Body Dynamics. Siggraph Talk, 2014
ELASTIC DEFORMATION
Budsberg, J., Bin Zafar, N., Alden, M. Elastic and Plastic Deformations with Rigid THE PREMIER CONFERENCE & EXHIBITION IN
51
COMPUTER GRAPHICS & INTERACTIVE TECHNIQUES
Body Dynamics. Siggraph Talk, 2014
VISUALIZATION
smooth normals
• Lawrence Lee
• Baptiste Van Opstal
• Michael Losure
• Domin Lee
• Alex Timchenko
1 openvdb::tools::foreach(floatgrid->beginValueOn(),
2 [&] (const openvdb::FloatGrid::ValueOnIter& iter) {
3 iter.modifyValue([&](float& surface) { surface -= 1.f; });
4 });
or*
1 openvdb::tree::LeafManager<openvdb::FloatTree> manager(floatgrid->tree());
2 manager.foreach([&](auto& leaf, size_t) {
3 for (auto iter = leaf.beginValueOn(); iter; ++iter) {
4 iter.modifyValue([&](float& surface) { surface -= 1.f; });
5 }
6 });
via
1 openvdb::ax::run("float@surface -= 1.f;", floatgrid);
How AX works
Points and volumes
Closer look:
How AX optimises for sparsity
WHAT IS AX?
AN EXPRESSION LANGUAGE FOR VDB POINTS AND VOLUMES
Flexible
Extend functionality whilst remaining customisable
Performance-driven
JIT compiled to target compiled C++ performance...or better
Simple to write
"Domain specific" with tailored execution pattern
INCLUDED IN OPENVDB 8.X
AX library
Core functionality for language and execution over points and volumes.
Tools
vdb_ax command line tool for quick access to AX
OpenVDB AX Houdini SOP
WHAT DOES AX CODE LOOK LIKE?
C-style language with simple attribute (@) syntax for manipulating VDB data
e.g.
1 if (vec3f@P[0] > 0) {
2 vec3f@Cd = {0,0,1};
3 }
4 else {
5 vec3f@Cd = {1,0,0};
6 }
HOW IT WORKS
Volumes
1 float@a += max(1, float@c);
2 float@b = float@a;
*Note: After last write to float@a, everything else will be optimised out, removing line 2.
**Note: AX will cache grids that are both written-to and read from to avoid execution ordering issues, meaning float@b reads from a cached
version of volume 'a'.
1 #include <openvdb/openvdb.h>
2 #include <openvdb/io/File.h>
3
4 int main()
5 {
6 openvdb::initialize();
7
8 openvdb::io::File in("sphere.vdb");
9 in.open();
10 auto grids = in.getGrids();
11
12 // your code goes here
13
14 openvdb::io::File out("sphere_mod.vdb");
15 out.write(*grids);
16
17 openvdb::uninitialize();
18 return 0;
19 }
Incorporating AX in 4 lines
1 #include <openvdb/openvdb.h>
2 #include <openvdb/io/File.h> <- Include openvdb_ax/ax.h
3
4 #include <openvdb_ax/ax.h>
5
6 int main()
7 {
8 openvdb::initialize();
9
10 openvdb::io::File in("sphere.vdb");
11 in.open();
12 auto grids = in.getGrids();
13 <- Initialize
14
15
openvdb::ax::initialize();
<- Run!
16 openvdb::ax::run("@surface += 0.1;", *grids); <- Uninitialize
17
18 openvdb::ax::uninitialize();
19
20 openvdb::io::File out("sphere_mod.vdb");
21 out.write(*grids); Ta da!
22 Now you are up and running with AX
23 openvdb::uninitialize();
24 return 0;
25 }
CLOSER LOOK
HOW AX OPTIMISES FOR SPARSITY
Problem:
Want to maintain value access paradigm.
Do not want user have to specify mode for tiles etc.
Avoid densify and prune.
Solution: Active tile-streaming.
Let's take a look.
WHAT WILL CAUSE TILE STREAMING?
Using position (or coordinates)
Reading other volumes
Functions that may vary across voxels i.e. rand() (w/o seed)
Only volumes that depend on the above, will be tile streamed, even in the same
snippet. e.g.
1 float@other = 10;
2 if (getcoordx() == 511) float@density = 1.0f;
1 float@density -= 1.0f;
Result: No difference for user between dense and sparse, without resorting to
densify and prune!
OPENVDB AX SUMMARY
New expression language toolkit included in OpenVDB 8.x
Manipulates point attributes and volume values
Extends the utility of VDB and makes it easy to write very fast custom operators
Can be integrated in 4 lines of C++
THANKS FOR LISTENING!
CONTACT
rhj@dneg.com
DOCUMENTATION
https://academysoftwarefoundation.github.io/openvdb/openvdbax.html
densify /
Internal Node 1 voxelize
prune
#children = 32768
Node Tile
Internal Node 2
5.0 5.0 5.0 5.0 5.0
#children = ~infinite
Value Child
Internal Node 1 BitMask BitMask
#children = 4096
0 1 Child Node
Leaf Node
1 1 Invalid
#children = 512
template<typename IterT>
struct IteratorRange
{
IteratorRange(const IterT& iter, size_t grainSize = 8)
{
mSize = 0;
for (IterT it(iter); it.test(); ++mSize, ++it) {} Evaluate the iterator to find out the size
}
...
};
Leaf Node Leaf Node Leaf Node Leaf Node Leaf Node Leaf Node Leaf Node Leaf Node
3 4 6 7 4 5 6 7
template<typename VisitorOp>
void Tree::visit(VisitorOp& op);
Visitor
functor
template<typename VisitorOp>
void Tree::visit(VisitorOp& op);
Generic iterator
struct VisitorOp
{
template <typename IterT>
bool operator()(IterT &iter)
{
typename IterT::NonConstValueType value;
Test what kind of typename IterT::ChildNodeType *child = iter.probeChild(value);
iterator?
if (child)
{
bool isLeaf = child->getLevel() == 0;
Return true ...
}
to continue
return true;
}
Optional
starting index
template <typename TreeT, typename OpT> Called for every node in the tree
size_t tools::visitNodesDepthFirst(TreeT& tree, OpT& op, size_t idx = 0); in depth-first traversal order
struct OffsetOp
{
const float offset;
explicit OffsetOp(float _offset): offset(_offset) { } Node index
(based on
template<typename NodeT> traversal order)
void operator()(NodeT& node, size_t) const
{
for (auto iter = node.beginValueOn(); iter; ++iter) {
iter.setValue(iter.getValue() + mOffset);
}
}
};
Raytracing Sampling
Tree (i, j, k)
Coord ijk(0, 0, 0);
tree.getValue(ijk);
Root Node
Internal Node 1
Internal Node 2
Leaf Node
Raytracing Sampling
Unthreaded 34.8s
1 Thread 96s
Unthreaded 7.51s
1 Thread 7.51s
16 Threads 0.64s
Root Node
Float leaf only Leaf index
struct DoubleOp
{
using LeafT = FloatTree::LeafNodeType; Internal Node Internal Node
void operator()(LeafT& leaf, size_t idx) const {
for (auto iter = leaf.beginValueOn(); iter; ++iter) {
iter.setValue(iter.getValue() * 2);
}
}
};
Leaf Node Leaf Node Leaf Node Leaf Node
tree::LeafManager<FloatTree> leafManager(tree); 1 1 1 1
DoubleOp op;
leafManager.foreach(op);
template<typename LeafOp>
void LeafManager::foreach(const LeafOp& op, bool threaded = true, size_t grainSize=1);
Unthreaded 8.63s
1 Thread 8.13s
tree::LeafManager<FloatTree> leafManager(tree);
32 Threads 0.48s
DoubleOp op;
leafManager.foreach(op);
Bottom Up
Root Node
1
Top Down
Leaf Node Leaf Node Leaf Node Leaf Node
3 3 3 3
Breadth First
template<typename NodeOp>
void NodeManager::foreachTopDown(const NodeOp& op, bool threaded = true, size_t grainSize=1);
Do nothing for
Bottom Up
RootNode or
InternalNode No node index Root Node
1
struct DoubleOp
{
template <typename OtherNodeType>
void operator()(OtherNodeType&) const { }
Internal Node Internal Node
void operator()(FloatTree::LeafNodeType& leaf) const { 2 2
for (auto iter = leaf.beginValueOn(); iter; ++iter) {
iter.setValue(iter.getValue() * 2);
Top Down
}
}
}; Leaf Node Leaf Node Leaf Node Leaf Node
tree::NodeManager<FloatTree> nodeManager(tree);
3 3 3 3
DoubleOp op;
nodeManager.foreachTopDown(op); Breadth First
template<typename NodeOp>
void NodeManager::foreachTopDown(const NodeOp& op, bool threaded = true, size_t grainSize=1);
Unthreaded 9.06s
1 Thread 9.15s
struct DoubleOp
{
template <typename OtherNodeType>
2 Threads 5.45s
void operator()(OtherNodeType&) const { }
4 Threads 2.55s
void operator()(FloatTree::LeafNodeType& leaf) const {
for (auto iter = leaf.beginValueOn(); iter; ++iter) {
iter.setValue(iter.getValue() * 2); 8 Threads 1.22s
}
}
}; 16 Threads 0.59s
One method for all three node types One method for each node type
Tree
struct Op template <typename TreeT>
{ struct Op
Root Node // RootNode, InternalNode, LeafNode {
template <typename NodeT> using RootNodeT = typename TreeT::RootNodeType;
void operator()(NodeT& node) const; using LeafNodeT = typename TreeT::LeafNodeType;
Internal Node 1 };
// RootNode
void operator()(RootNodeT& root) const;
Internal Node 2
// InternalNode
template <typename InternalNodeT>
Leaf Node void operator()(InternalNodeT& internal) const;
// LeafNode
void operator()(LeafNodeT& leaf) const;
};
Node
functor
Root Node
1
Top Down
Leaf Node Leaf Node Leaf Node Leaf Node
3 3 3 3
Breadth First
Return false to
stop processing Node index
sub-nodes (per type)
Root Node
1
struct DoubleOp
{
template <typename OtherNodeType>
bool operator()(OtherNodeType&, size_t) const { return true; }
Internal Node Internal Node
bool operator()(FloatTree::LeafNodeType& leaf, size_t idx) const {
for (auto iter = leaf.beginValueOn(); iter; ++iter) {
2 2
iter.setValue(iter.getValue() * 2);
Top Down
}
return true;
} Leaf Node Leaf Node Leaf Node Leaf Node
};
3 3 3 3
tree::DynamicNodeManager<FloatTree> nodeManager(tree);
DoubleOp op;
nodeManager.foreachTopDown(op); Breadth First
template<typename NodeOp>
void DynamicNodeManager::foreachTopDown(const NodeOp& op, bool threaded = true, size_t grainSize=1);
Unthreaded 8.51s
1 Thread 8.69s
struct DoubleOp
{
template <typename OtherNodeType> 2 Threads 4.72s
bool operator()(OtherNodeType&, size_t) const { return true; }
32 Threads 0.41s
tree::DynamicNodeManager<FloatTree> nodeManager(tree);
DoubleOp op;
nodeManager.foreachTopDown(op);
Unthreaded
depth-first
summation
Internal Node 1
Mark as inactive any active tiles or voxels in the given tree whose values are equal to value (to within tolerance)
tree::DynamicNodeManager<FloatTree> nodeManager(tree);
nodeManager.foreachTopDown(op);
Threaded over leaf nodes
tools::foreach(tree.beginLeaf(), op);
Access Pattern?
Predictable Random
Simplicity Performance
Evaluate Over?
Predictable Random
Simplicity Performance
Evaluate Over?