# Advanced Volume Support in Geometry Nodes Make volumes in geometry nodes more useful and flexible. - [ ] Incorporate feedback from Jacques, Hans, Dalai - [ ] Separate out examples and steps that require work beyond the MVP: Fluid and hair sim need a few specialized nodes. - [ ] Better illustration for "individual grids in volumes" vs. Volume geometry component. left->right train of thought, "there are multiple grids in the volume, but only one volume output" - [ ] Change node design for mesh/point conversion to include volume input+output (discussed in chat) - [ ] Describe meaning of position node in volume context - [ ] Picture comparing different mesh/point conversion resolution modes (Size, Amount, Copy) - [ ] Design a new grid resolution option to facilitate shared transforms between grids. ## Motivation: Use Cases for Volumes ![](https://hackmd.io/_uploads/SJ1Q0q-ea.png) (Generated using [mermaid.live](https://mermaid.live/edit#pako:eNp1VNtq20AQ_ZVlIWBDSh76JkrBjklbqKmx3PRFL5PVSFqyt-yloIb8e0e2Yq-UZF-0mnN25sxl95kLWyMvuJam1uAqw2jdW5U0htPPsPZoavTStBfTsO7szFBq-4gzjvQzyy2kEKXI3G9Jg3rjvdzczQwiKRff8P5ARM9C8g2IWajSQZSgWBAQ4yyBUuqkCLZmJlglWX-Ilk9Jhq5n2yEouQ5TeAeeUlPIfhjCZ-crc3VFn2VVVXzX9WGoAW0Xo52xxWJlpD6eWi7PVsY2vQFN7EFVZh61hpux7Bmwly0lsba1HNqYAd9B-rO_CXKrbOxexWaSXpszUTTKZwdr1Ttu8lbNg3_z1uo5UmYNyvPzIGIaBuhspk_em7PMtTrOKFs5FyZST50QUf7F46BNAtxLHxMNyMEmP83j4GXb4sy4x4ErNbJzozIJ9_sscFEUCnybh9sl5zDiPM3Pm7xcpw4cwZ_WtKyTIVrfZ-aikMKaRQOsgU8P1j4uM3BnHdXGy5DN3hhp7WWk2WXuRGEu9KKzyrY9gxQ766kKpmfr9A8uB_cYELzoMk-_DMOmwWNFDYbw5cHffAVTswZhaFaYclcp2qFUggmPb1X9DpMD4xAdmdSwiKIz8im9wykj3S9sya9TYMz5bmeUlW_pHTOR0cvmcsI4tpeqoWFDBg4c-sy-Ra9B1vya63FX8OcBr3jsUGPFC9rW4B8rXpkX4lElbdkbwYvoE17z5GpSuZHQetC8aOjFwJf_1xSKUw)) Volumes have properties that make it a unique tool in the box: - Spatial lookups are fast, great for processing lots of data. - Finding neighboring cells is very cheap, allows fast filtering and convolution. - Store sparse volumes efficiently. - Improved accuracy for positions using cell coordinates. In short: Volumes can do many things faster than other geometry, and some things that are (almost) impossible otherwise. Some improvements to expect: - Faster simulations without loss of visual detail. - Describe semi-transparent media efficiently. - Costly SDF functions can be computed in real time. - Smoothing/blurring/diffusion is faster and resolution-independent. ### Examples - Construct mesh surface from a SDF volume - Create SDF grid from point cloud - Convert to mesh surface - Output - Remove density from a fog volume - Geometry input containing fog grid - _Named Attribute_ node for "density" grid - Vector math distance node using a utility object location and grid cell position - Map distance to some 0..1 range - Math node multiplying density with the distance factor - _Store Named Attribute_ using modified density and store as "new_density" ![](https://hackmd.io/_uploads/B10PmD2lp.png) - Combining and rendering SDF operations Make holes in cheese: - Cheese wheel mesh - Mesh to SDF Volume - Erode SDF, then distribute points in volume (so bubbles don't poke through the surface) - Points to SDF Volume - Difference of mesh SDF and points SDF - Volume to Mesh makes a cheese with holes Links: - SDF surface rendering in a shader. Shows boolean combination functions. https://www.shadertoy.com/view/lt3BW2 - Detailed tutorial with a 2D case. https://www.ronja-tutorials.com/post/034-2d-sdf-basics/#rectangle - Mesh-Mesh booleans using SDF. http://www.gradientspace.com/tutorials/2017/11/21/signed-distance-fields-tutorial - Basic FLIP fluids (with point cloud sampling) - Particle dynamics: add gravity to velocity attribute - Velocity grid from particles (new node?) - Divergence grid from velocity (new node) - Pressure solve outputs scalar pressure grid - New velocity from pressure gradient - Apply velocity by sampling at particle locations Links: - 10 Minute Physics: How to write a FLIP Water Simulator https://youtu.be/XmzBREkK8kY - Hair self-collision using volumes Links: - Anisotropic Elastoplasticity for Cloth, Knit and Hair Frictional Contact https://dl.acm.org/doi/pdf/10.1145/3072959.3073623 - Volumetric Methods for Simulation and Rendering of Hair https://graphics.pixar.com/library/Hair/paper.pdf ## Volumes in Blender 4.0 Geometry Nodes currently has only very basic volume support for conversions to and from other geometry. With the experimental "New Volume Nodes" enabled there are a few more nodes for sampling and SDF manipulation. Volumes are treated largely as "black boxes": - Converting meshes or point clouds to volumes - Generating a mesh surface from a density grid - Scattering points in a density grid - Sampling a grid at arbitrary points - Basic SDF operations (offset, filter) - Volume "primitives": sphere, cube with variable density ![](https://hackmd.io/_uploads/B1pZitbgT.png) Volumes can already store multiple distinct grids, e.g. when loading from `.vdb` files. These are not accessible individually, nodes have to use the "active" grid index and special names to find which grids to operate on. ![](https://hackmd.io/_uploads/rkD0JoWlT.png) Combining grids is not supported currently. This would be a frequent occurrence e.g. in SDF modeling. Without access to individual grids it's not obvious which grids should be combined. ![](https://hackmd.io/_uploads/Bks6A9-g6.png) Some interaction with grids is possible using sampling and conversion to other geometry, but it's not very efficient. Many useful features of grids can't be utilized this way, e.g. spatial lookups of neighboring points. <!-- Grid operators are mathematical tools that can generate useful spatial information, for example: - Gradient: the direction in which density increases. - Divergence: vector flow into or out of an area. - Laplacian: describes density in physics simulations. We can't generate these currently since there is no way to output a single grid. --> ## Improved Grid Data Access ### Attribute Support Volume geometry gets attribute support. Each attribute represents a named or anonymous grid in the volume. The `Voxel` domain represents grid cells, which are the main elements of grids. ![](https://hackmd.io/_uploads/BkM_J2blT.png) ## MVP Goal is to provide necessary features to unlock volume geometry access. Later tools and simulations can build on top of this initial stage. <!-- - Volume geometry gets attribute support. Each attribute represents a named or anonymous grid in the volume. - Field evaluation with grid inputs produces grid outputs, which can be stored or captured. - Multifunctions, like math or color mixing, work on voxel values. - Rules for combining grids with different topology (spatial extent and/or transform) - Reimplement volume nodes with individual grid outputs instead of a single Volume geometry. Users can apply math functions to voxel values directly (without converting to points and back or similar hacks). Basic SDF operations with grids are now possible by simply adding grid fields and modifying falloff functions. Some grid-based simulations can be implemented (point grids will allow more advanced simulation later). With node tools grids could be used in modeling or sculpting. --> ### Conversion Node Outputs Conversion from mesh and point geometry will output a **density** grid (fog volume) or **distance** grid (SDF volume) in addition to the Volume geometry. This is an anonymous attribute of the volume, rather than a named attribute as is currently the case. ![](https://hackmd.io/_uploads/rJRvGFKxp.png) ### Grid Info Node A new **Grid Info** node provides additional information about a grid: - **Scale**: Size of each voxel in object space. - **Origin**: Translation of the grid voxels relative to object origin. - **Staggered**: For vector grids only: The grid values are split by component and located on face centers (typically used for velocity grids). ### Domain Size The **Domain Size** node in _Voxel_ domain mode returns the number of _active voxels_ of the grid. ### Transform Geometry for Grids The **Transform** node for grids simply changes the voxel scale and origin. The actual grid data is local to grid cells, there is no position attribute that needs to be calculated. ### Resample Grid Change the voxel scale or origin and generate a new grid by interpolating the old grid values. This generally comes with a loss of precision and potential aliasing artifacts, same as when resampling images. [Higher-order sampling methods](https://www.openvdb.org/documentation/doxygen/Interpolation_8h.html) can be used to reduce some of these artifacts. ### Sample Volume **Sample Volume** already has a "Grid" input, but this is a hack: it expects an arbitrary named field and then uses the same name to find a volume grid internally. This will no longer be necessary, the input itself becomes a grid field that can be sampled directly. ### Sample Grid This is a variation of the _Sample Index_ node. The usual element index of points, faces, edges, etc. does not work well with grids, since voxel indices depend very much on grid topology. Instead a grid is typically indexed using _coordinates_ `(i,j,k)`. Unlike the _Sample Volume_ node The _Sample Volume_ node is one way of reading grid data, but its position input includes the grid transform. A new "Evaluate at Coordinate" node provides direct access based on integer coordinates, ignoring the grid scale. ![](https://hackmd.io/_uploads/BkTmEaZga.png) ### Field Evaluation Field evaluation with grid inputs produces grid outputs, which can be stored or captured. Multifunctions, like math or color mixing, work on voxel values. Each output voxel value is computed by evaluating inputs at the voxel location. > **Implementation Note:** When input grids in a field graph have different transforms the evaluator needs to resample the grids, which causes artificial smoothing and reduces performance. Using the same grid transform is preferable because node graphs can be evaluated per leaf buffer as simple multifunctions. ![](https://hackmd.io/_uploads/SkP6Ua-ga.png) ### Voxel Size and Grid Transforms Each volume grid has a _transform_ which determines the size of voxels. For example a grid with a scale of 0.25 has a voxel size of 0.25 units. A volume component has a **default transform**, which is the transform of the active grid. Nodes that construct a grid from a mesh, point cloud, cube or sphere all have one or both of the following options: - _Size_: Voxels have fixed size, amount of voxels computed to cover the extent of the geometry. - _Amount_: Fixed amount of voxels on the longest axis (or all 3 axes for the cube), voxel size is based on geometry bounds. A _Volume Info_ node will provide the transform of an existing volume so it can be copied by other nodes. Using consistent transforms between grids improves performance and avoid loss of precision due to interpolation. It should be the preferred option when creating new grids to reuse an existing transform. ### Grid Topology Behavior For conventional fields in geometry nodes the size of buffers is directly defined by the domain, with the possible use of selection as a filter. For grids the situation is more complicated: Each grid has, by design, its own extent and topology (which voxels are active). When combining multiple grids in a function, say an "Add" math node, the result will usually be a _union_ of the active grid cells. The input grids have a _background value_ that is used when a voxel is inactive. ![](https://hackmd.io/_uploads/SyD6fOYlT.png) Some special cases need to be considered: - > **Empty Input Case:** Some field inputs are not grids themselves but abstract functions, like the "Voxel Center" input. These fields do not have a concrete set of voxels. When all input fields are abstract the resulting grid would be empty. A fallback for this case could be to use the "active grid" topology, or provide a user-defined default grid. --------------------------------------------------------------------- ## Future Steps Not part of the MVP ### Spreadsheet Integration The spreadsheet shows grid attributes for debugging purposes. Each colume contains values in a grid. Each row contains attributes for the same voxel, identified by its grid coordinates `(i,j,k)`. Unlike existing geometry components the Volume attributes are grids, which can have different topology and voxel count. A voxel may be active in some grids but not others, so a spreadsheet row can have gaps when the voxel it represents is not active in the column for a grid. > **Implementation Note:** The index of a voxel depends on the overall voxel count, it should not be stored or used for sampling. ### Volume Viewer Nodes Viewport display for volumes is not very useful for viewer nodes at this point. The viewport can only display float value grids as a density-style fog volume. Viewport rendering of volume should take more grid types into account, as well as other settings such as the staggered grid class for vector grids. Additional techniques for visualizing useful grid information: - Wireframe cell display: useful for gauging voxel size and how it relates to other scene elements. - Slices: Clear picture of interior grid values. There is a feature on volume objects, but its very limited. Where to put a slice exactly requires user control. ### Point Grids OpenVDB has a specialized grid type for storing large point clouds efficiently and with better accuracy than the current Point Cloud geometry ([OpenVDB overview](https://www.openvdb.org/documentation/doxygen/points.html)). This `PointDataGrid` forms the basis of fluid simulations. - PointCloud gets an internal grid representation. - Can switch between both point cloud representations automatically, e.g. a simulation can convert an input point cloud to grid mode. - Point cloud attributes can read/write directly to the `PointDataGrid`. - New set of nodes to enable fluid simulations: - **Divergence** and **Gradient** operators - Pressure solver for incompressible fluids (**Poisson solver**) - Point **advection** with a velocity vector grid - Obstacle grids generated from meshes or other sources (including manual authoring) <!-- FEEDBACK THREAD JacquesLucke17:21 Lukas Tönne I'm looking at your volume nodes document right now. One thing I wonder is how is the transform of the new grid in the Store Named Attribute node determined? Lukas Tönne @LukasTonne17:23 my understanding is that we want all grids in a volume to share the same transform of course that just moves the question to: what is the transform when there isn't any grid in the volume yet ... Hans Goudey @HooglyBoogly17:24 The identity, no? Lukas Tönne @LukasTonne17:25 in my branch right now i have a domain_transform function for components Hans Goudey identity transform means each voxel has a size of 1 unit that's not usually what we want Hans Goudey @HooglyBoogly17:26 Ah right, of course. I think nodes that create volumes from scratch would have to specify the size and resolution combo The "Voxel Center" is just the position input node, right? JacquesLucke17:27 Ok, then my next question is, how do we deal with volumes with grids that are not aligned (as they can be when importing an vdb file afaik) Lukas Tönne @LukasTonne17:28 The "Voxel Center" is just the position input node, right? yeah it would be used pretty much the same way as position. But you said these grid functions shouldn't be treated as attributes, and the position is an AttributeInputField, so have a separate node might be better. not sure Hans Goudey @HooglyBoogly17:29 Field contexts can override specific field inputs, I'd imagine that happening here (like the volume cube field context does now) When combining multiple grids in a function, say an “Add” math node, the result will usually be a union of the active grid cells I'd imagine the result would just be the topology from the grid that defines the field context. For a union, an explicit node should be used probably JacquesLucke17:30 Regarding grid transforms, we also need some answer for what to do when using the join geometry node with multiple volumes with different transforms. Lukas Tönne @LukasTonne17:31 how do we deal with volumes with grids that are not aligned the evaluator can use a generic accessor or sampler for evaluating grids where the transform doesn't match. but it might be better to have explicit sampling nodes for that, which would be consistent with other geometry when combining different volumes I'd imagine the result would just be the topology from the grid that defines the field context. For a union, an explicit node should be used probably yeah it's probably going to need a "domain mask" like that, possibly the active grid or setting it in the nodes Hans Goudey @HooglyBoogly17:35 Not so different from the existing field evaluation mask already I guess, except that it has to handle a separate mask on the other grids too Lukas Tönne @LukasTonne17:37 yes existing field eval gets a array size from the domain (with a round trip converting to a default IndexMask and getting the "min array size" from that) all good points, i'll make sure to clarify things Hans Goudey @HooglyBoogly17:45 Mesh/Point to Volume outputs a grid field instead of a volume Seems like it needs to output both, since there needs to be a geometry that the field refers to. That lets you use further volume nodes on the volume like sampling another attribute or changing densities based on some field evaluation Lukas Tönne @LukasTonne17:47 true, these nodes generate their own grid transform, so they should go into a new volume JacquesLucke17:48 I wonder at which level nodes for inflation/deflation would work. Hans Goudey @HooglyBoogly17:48 I think topology-changing operations have to be geometry nodes (with a volume input and output, and some way to select which grid(s?!) to affect) Lukas Tönne @LukasTonne17:49 if i want to combine these output volumes with other volumes i'll then have to use a sampler (which can be optimized away if transforms match) for example using mesh-to-volume and then put that in a fluid sim as an obstacle I think topology-changing operations have to be geometry nodes It should work without a volume in/out as long as the transform remains the same JacquesLucke17:51 I think topology-changing operations have to be geometry nodes I'm not so sure about that anymore right now. Could also be part of field evaluation Hans Goudey @HooglyBoogly17:51 I don't think fields can "own" grids though JacquesLucke17:52 Fields can own whatever they want, but I'm not surer why that matters here. Lukas Tönne @LukasTonne17:53 such operations are sort of on the edge of what could be called a multifunction, but they filter over neighboring voxels JacquesLucke17:53 well, they are not multi-functions for sure Lukas Tönne @LukasTonne17:53 maybe akin to the accumulate field node? Hans Goudey @HooglyBoogly17:53 Hmm, I guess the distinction of "topology changing" might mean a bit less here? JacquesLucke17:54 maybe akin to the accumulate field node? right, or the blur node the field evaluation implementation still kinda assumes that it just does a multi-function evaluation, but that's only because we haven't refactored it in a long time and moved everything that's not a multi-function evaluation into field inputs. Lukas Tönne @LukasTonne17:57 if you add elements to an array it doesn't match the previous context any more, so it has to become a new GeometrySet. With grids it's possible to have different topologies in the same geometry. JacquesLucke17:58 Yeah, one could also argue that it's still all the same topology in coordinate space. Lukas Tönne @LukasTonne18:02 "same topology" in openvdb terms means that grids have the same active voxels. dilate/erode are changing topology (even if just a little bit by growing/shrinking the boundary). JacquesLucke18:03 Yeah I know, I was using a different definition of topology there. Lukas Tönne @LukasTonne18:04 yeah, just wanted to clarify, there is some overlap with existing blender terms -->