Volumes in Geometry Nodes [Proposal]

Geometry nodes supports volumes for some time already with Volume to Mesh, Mesh to Volume and Volume Cube nodes. However, many basic features as simple as modifying volumes are not supported yet. This document proposes a new way to integrate volumes into geometry nodes that removes many existing limitations.

Grid Socket

At the core of this proposal is a new kind of socket for grids. It has a new socket shape and exists in multiple variants for the different base types.

image

The data in a grid socket corresponds to an unnamed OpenVDB grid. That means it contains:

  • The full topology information (active and inactive voxels and tiles).
  • A transformation (also determines voxel size).
  • The data that is stored in each active voxel and tile.
  • A background value that used used wherever the grid is not active.

Tiles are storing grids that have the same value in many neighboring voxels more efficiently. The concept of tiles is exposed to the user, because it’s important to be aware of them to be able to optimize node setups in some cases.

Volumes in Geometry Sockets

The Volume component in geometry can contain multiple grids. Each grid either has an explicit name or is anonymous. Grids are similar to attributes in many ways, but working with them is a bit different than with other attributes. Grids within the same volume do not have to be aligned. It’s often recommended that they are though because that can lead to better performance because less resampling is required.

An important difference between grids and e.g. mesh attributes is that grids can exist on their own, because each grid contains its own spacial information. Mesh attributes are always attached to some mesh.

Grids can be extracted and inserted into a geometry with specialized nodes. The Store Named Grid adds a new grid, potentially replacing an existing grid with the same name. The Get Named Grid node gives access to a named grid in a volume. The Remove input mainly exists to allow for better performance by avoiding accidentally having the same grid twice in memory.

For common cases, we can optionally add special nodes (or node groups) to make it easier to work with certain naming conventions.

Anonymous Grids

Supporting anonymous grids in a volume is a little less straight forward and requires a solution to the problem of referencing anonymous attributes. Note, that the Grid Reference input requires a field that is an grid reference, it can not be an arbitrary field. Because grids are usually processed separately from geometry and also more expensive, anonymous grids are likely less common than anonymous attributes.

Converting between Grids and other Geometry Types

We already have some nodes to do this, but they’ll change with this proposal. Instead of working on a geometry directly, they work with grids instead. The nodes only have a “Voxel Size” input to make using the same transform simpler and to avoid using arbitrary heuristics within each node. A Bounds to Voxel Size node group replaces the old “Resolution” behavior from existing volume nodes.

image

Fog Volumes and SDFs

Both, fog volumes and SDFs (signed distance fields) are represented as grids. Special nodes can be used to convert between them.

image

Building Grids from Scratch

Multiple different nodes can be used to build grids from scratch:

  • SDF Sphere: Creates a new SDF grid for a sphere with the given size and center.
  • Cube Grid: Similar to the existing Volume Cube node, but a bit more general since it can create grids of arbitrary types.
  • Grid: Initializes a new grid based on the topology of an existing grid. Each value is initialized with a field. Note that the data type of the topology grid does not matter since only its topology is used.

Changing Topology without Changing Values

These nodes change which voxels and tiles are used while representing approximately the same data. They can be used to prepare grids for later operations.

Value Processing

Function nodes like the math node allow the inputs to be grids. If at least one input is a grid, the output is a grid as well. Non-grid inputs can be single values or fields. All grid inputs have to be aligned, i.e. have the same transform. The output grid will have the same transform as well. The active voxels and tiles in the output will generally be the union of the input grids.

image

If an input is a field, it is evaluated in the context of each active voxel and tile in the output grid.

This will probably require a new socket shape to tell the user which inputs allow single values, fields and grids. This patch proposes a new asterisk shape for that purpose.

Note, while every math node creates a new grid on the user-level, internally it probably a good idea to group a bunch of math nodes together and avoid intermediate grids as long as the final result is not affected.

Voxel Field Inputs

A new kind of field context for voxels is added. It supports new field inputs. Those field inputs just output default values when they are used in non-voxel contexts. The Position input gives the voxel center. Using the same node makes node groups more reusable. The Voxel Indices node gives the position of the voxel in the index space of the grid. It has to give the min and max because tiles can span multiple voxels.

image

Accessing Grid Values

Individual values in a grid can either be accessed based on their position in object space using the Sample Grid node, or with index values in grid index space using the Sample Grid Index node. The former may also interpolate between different voxel values. Its position input defaults to the Position node.

image

Aligning Grids

All grids passed into a a function node have to have the same transform. If they don’t, there is an error and the user has to make sure that they align. This is important because there are different ways to align grids and which one is used can have a big impact on performance and final result.

There are two main ways to align grids: with the Sample Grid or with the Resample Grid node. The main difference is that when the Sample Grid node is used, the result it a field, so the topology does not affect the topology of the output of the function node.

Debugging

Grids contain all information necesssary to display them, so they can just be connected to a viewer (which has to be changed to support grids) without the need for a geometry socket.

Modifying SDFs

The image below shows two nodes that can be used to work with SDFs. Many more nodes are possible of course.

Advection

This section serves as an example for how to differentiate nodes that deal with individual grids and those that work on all grids in a volume.

image

FAQ

  1. Why a new socket type? A separate socket type maps more naturally to how people use grids. The definition of fields would have to be extended to contain data from the context like the active voxels. Then the field itself would affect the context it’s evaluated in. Also, because grids can be standalone data, the design constraints are different than for processing attributes on other geometry types.
  2. Is there other data that would be processed similarly? Yes. Lists could be processed separately from geometry in a similar way, at the cost of more manual domain handling. Making geometry data flow more explicit would often be helpful too. It’s also possible to imagine sockets for acceleration structures like 2D grids or KD-Trees in the future.
Revision History
  • Renamed SDF sockets to SDF Grid
  • Added Voxel Coordinate node
  • Changed Voxel Center node to Position
  • Changed advect velocity inputs to fields
  • Added units to some inputs
  • Mesh to Density Grid, Density Grid to Mesh
    • Partially filled boundary voxels mostly only make sense for density and not so much for e.g. temperature.
  • Mesh to SDF Grid
    • “SDF” is just a math function, while a grid is a rasterized function.
      • Also applies to Offset SDF and Union SDF.
    • Exposing the boundary thickness (“bandwidth”) might be a good teaching moment.
  • Density to SDF Grid, SDF to Density Grid
    • Whatever grid is passed into the former node is interpreted as density (0 is “empty”).
    • Needs a threshold input.
  • “Voxel Size” input requires logarithmic input slider to avoid crashing things too easily.
    • This way we don’t really need the resolution input anymore which mainly exists to solve that problem.
  • Could provide a node group that computes a “good” voxel size from a geometry (bounding box) and a resolution.
    • Makes it easier to not have to implement the resolution behavior in all volume nodes.
  • Even if we replace the “Voxel Size” input with a transform matrix later, it would still be good to have a slider to control the size in the node.
    • On the other hand, not having a slider would make it more obvious that the same transform should be used in all volume nodes.
  • Resample Grid only takes the transform of the reference grid into account.
    • The reference grid should not be called Topology Grid then.
  • Simplify Grid, threshold vs. epsilon
    • it’s a delta for comparisons
    • Check what name openvdb uses, same for Densify
  • Might need a special resampling node for SDFs.
    • It has to make sure that the bandwidth is a few voxels large.
  • A possible alternative to supporting grids in every function node could be to always use a Grid node when creating a new field.
    • Keeps the behavior of the function nodes simpler.
    • Has the benefit that the user can know for sure that there are no intermediate grids (which can use a lot of memory).
    • The downside is that it makes simple math operations harder on grids because one always needs the extra Grid and Sample Grid node.
    • We want function nodes to work similarly on grids and lists in the future, and for lists it seems more obvious that function nodes should be able to modify them directly.
  • Voxel Coordinates should be called Voxel Indices because coordinates tends to imply “positions”.
  • Sample Grid + Sample Grid Index
  • Advect Grid/Volume node input should be Offset instead of Velocity.
    • “Velocity” indicates something with time.
33 Likes

Cool ideas :slight_smile: I don’t love the new socket icon; it is quite busy and looks very out of place in Blender

10 Likes

Practically speaking, if I understand correctly, the choice of having grids as a different socket type allows using regular math/vector math nodes to modify grid values rather than relying on specialized nodes such as “boolean volume” or “displace volume” ?

I don’t like this icon either (it would be the only figurative socket shape) but I suppose it’s a placeholder.

Yeah I think that icon is quite ugly tbh :slight_smile: I think something like either these two would be better (grid input and output is one suggestion each)
volumegrip
I also tried that cube to the right subdivided into four but it looked a bit too busy.

5 Likes

Is this an alternate proposal to all the work @LukasTonne has done with volumes? It seems he’s invested a lot of time and resources into the matter

1 Like

I love the deep and simple expansion that seems to arise from this low-level - looking implementation. Im struggling to understand how this isn’t simply another domain, like curves or instances.

This isn’t to say much against the proposal itself, but rather because as an educator I wouldn’t know how to explain how this 8s different from a domain, and I think documenting it will will be key, just like when fields were proposed.

Thank you for your work! Amazing, and a ton of work with the mockups to show us the use cases and possible end results

1 Like

We’ve been discussing different options for high-level concepts, such as whether grid sockets should be fields (part of volumes) or standalone “data” sockets. My development work atm focuses more on the implementation side to figure out how to actually use the OpenVDB API. I’m building a set of prototype nodes to achieve intermediate goals, like basic grid manipulation, combining SDF grids, computing divergence-free velocity fields, etc. Apart from teaching myself how to work with grids it also highlights some issues which need to be addressed by design and help us to (hopefully) make the right decisions. This work is not in vain, most of these prototypes should be convertible into actual production-ready nodes.

12 Likes

To be clear, the specific icon used here isn’t proposed as final, it’s just a mockup to get the point across quickly. Try to imagine something nicer there :slight_smile:

12 Likes

While “grid can exist on its own”, can a “grid” replace “geometry output” in the group output?
When talking about grid not being a field, it often reminds me about “noise texture” node.
If I want to use noise texture to displace a volume, will the socket icon of noise texture node change?
what if I use the same noise texture in both field socket and grid socket?
Or does it mean we need totally different noise texture node for field and Grid in order to make socket difference clear?

I have many other questions, but I don’t want to ask with assumptions before my aforementioned questions are addressed.

2 Likes

I am not understanding the difference between Fog and volumes as I am currently aware they are the same thing?

Keeping it as volume seems to me that it would be easier to work with especially is alot more volume nodes are developed down the line (e.g. a displace volume node) .

for SDFs in the case of SDF modelling they way I understand this from the post is : Mesh > Mesh to volume > Volume to SDF function > SDF manipulation > SDF to Volume> Volume To mesh . is this correct?

1 Like

That’s fog as opposed to SDF, fog is “regular” volume grid and SDF is meant to output an isosurface. Both are volumes

It may be more clear to name the sockets “SDF Grid” instead of just “SDF”.

We could also use “Density” instead of “Fog” in the names, to avoid introducing another term? So for example “Mesh to Density” and “Density to SDF” nodes. We already use “SDF” instead of “Level Set”, so we are not following the OpenVDB names already.

1 Like

This kind of word soup is starting to get confusing. It may make sense to take a step back and look at the bigger picture:

We have a problem that up until now, volume was always synonymous with fog, but now we will also gain ability to work with surface volumes. Both are volumes, but one type is meant to store fog density while other one is supposed to store distance to nearest surface. In these circumstances, the word density would make logical sense, but if new or unfamiliar user saw these node names, they’d be confused. Like for example Mesh to Density fails to communicate that the density would be stored in volumetric grid voxels.

So I think it’s better to come up with consistent name duo, that communicates the difference properly, like for example:
Fog Volume & Surface Volume
Fog Grid & Surface Grid
SDF Volume & Density Volume

The issue here is that the nodes are pretty narrow so they don’t fit many words. :confused:

2 Likes

The node would be in a Volume sub-menu, which the search also takes into account. So that makes it more discoverable even if the the node name is shorter. But it could be longer too and the node wider.

Where and how do you specifically propose to use these names, what are the resulting node and socket names?

Note the proposal makes a distinction between Volume and Grid, and between Density/Fog and SDF. What I suggest is to use a single word for Density/Fog, and make that Density. That way we have 4 instead of 5 terms, and to me it seems consistent.

The term for SDFs we already discussed in another topic, and to me “Surface Volume” is still confusing and non-standard terminology.

I wouldn’t say this replaces the specialized nodes. Displacement (advection) is an operation that changes the topology and can not be done efficiently with just math nodes.

Booleans could be done with math nodes. But if the design was based on editing grids in fields rather than through grid sockets, that would be true there as well.

In the simple case you would have an Advect Grid node, with a field input for the noise texture and a grid input for the density or SDF grid.

If you link a grid into the one of the noise texture node inputs, then it would get a grid output as well.

It would be a single node, see the “Value Processing” section for how the socket types would dynamically change.

Thanks for the feedback! We updated the proposal with some of the suggestions (mentioned in the Revision History section).

Regarding renaming of Fog to Density, I don’t think it’s that simple, because the word density implies the density naming convention, which has a specific meaning. To me it’s weird to use the word “Density” for a temperature, color, or brightness grid. Especially since the volume could also contain a grid named “density” at the same time.

2 Likes

While it has been mentioned that the grid socket type should support various data types such as float, vector, boolean, etc., it appears that, following the update outlined in this proposal, only the float grid is available as the final output across all nodes.

The proposal raises the idea of displaying grids without geometry, but the distinction between grids and geometry volumes remains somewhat unclear to me. It’s stated that a volume can contain multiple grids, but it’s not evident why a volume cannot contain multiple volumes, just like how geometry can hold multiple geometries.

This issue extends beyond socket types; it also affects the overall modifier workflow. For instance, if the default output upon creating a modifier is geometry, I find it necessary to switch it to a grid socket to output and visualize the volume.

(On a side note, while the socket icon is not explicitly addressed in the proposal, it strongly influences my perception of the need for this new socket type.)

1 Like

I agree it’s not an ideal general name for this type of grid as opposed to SDFs. But for the specific nodes that have Fog in the name currently, the sockets are already named Density Grid and are arguably dealing with density. It’s unclear to me if there will be future nodes which need the more general name.

1 Like

Right next to the sockets. My main issue was with the idea of names like “Mesh to Density”. From a POV of inexperienced user, that would be very confusing even if the user knew the concept of volumetric/voxel grid geometry and how it relates/differs from polygonal geometries.

It’s hard to infer that density means density stored in volumetric grid, where as with “Fog”, any user at least roughly familiar with geometric domains will quickly realize the Fog will most likely be in a form of volumetric grid.

But since this is tricky, and @HooglyBoogly made really good points about density being misleading because Fog volumes can carry diverse attribute set, not just density, I would simply vote for following naming:
Fog and SDF:

  • Both are understandable for user familiar with common types of data stored in volumetric domains.
  • Both are 3 letter words that will fit well in constrained node UI space.

And the names would become easy to understand, such as:
“Mesh to Fog”, “Fog to SDF”, “Mesh to SDF”, “SDF to Fog”, etc…

2 Likes