How to deal with multiple volume grids in a geometry #91668
Labels
No Label
Interest
Alembic
Interest
Animation & Rigging
Interest
Asset Browser
Interest
Asset Browser Project Overview
Interest
Audio
Interest
Automated Testing
Interest
Blender Asset Bundle
Interest
BlendFile
Interest
Collada
Interest
Compatibility
Interest
Compositing
Interest
Core
Interest
Cycles
Interest
Dependency Graph
Interest
Development Management
Interest
EEVEE
Interest
EEVEE & Viewport
Interest
Freestyle
Interest
Geometry Nodes
Interest
Grease Pencil
Interest
ID Management
Interest
Images & Movies
Interest
Import Export
Interest
Line Art
Interest
Masking
Interest
Metal
Interest
Modeling
Interest
Modifiers
Interest
Motion Tracking
Interest
Nodes & Physics
Interest
OpenGL
Interest
Overlay
Interest
Overrides
Interest
Performance
Interest
Physics
Interest
Pipeline, Assets & IO
Interest
Platforms, Builds & Tests
Interest
Python API
Interest
Render & Cycles
Interest
Render Pipeline
Interest
Sculpt, Paint & Texture
Interest
Text Editor
Interest
Translations
Interest
Triaging
Interest
Undo
Interest
USD
Interest
User Interface
Interest
UV Editing
Interest
VFX & Video
Interest
Video Sequencer
Interest
Virtual Reality
Interest
Vulkan
Interest
Wayland
Interest
Workbench
Interest: X11
Legacy
Blender 2.8 Project
Legacy
Milestone 1: Basic, Local Asset Browser
Legacy
OpenGL Error
Meta
Good First Issue
Meta
Papercut
Meta
Retrospective
Meta
Security
Module
Animation & Rigging
Module
Core
Module
Development Management
Module
EEVEE & Viewport
Module
Grease Pencil
Module
Modeling
Module
Nodes & Physics
Module
Pipeline, Assets & IO
Module
Platforms, Builds & Tests
Module
Python API
Module
Render & Cycles
Module
Sculpt, Paint & Texture
Module
Triaging
Module
User Interface
Module
VFX & Video
Platform
FreeBSD
Platform
Linux
Platform
macOS
Platform
Windows
Priority
High
Priority
Low
Priority
Normal
Priority
Unbreak Now!
Status
Archived
Status
Confirmed
Status
Duplicate
Status
Needs Info from Developers
Status
Needs Information from User
Status
Needs Triage
Status
Resolved
Type
Bug
Type
Design
Type
Known Issue
Type
Patch
Type
Report
Type
To Do
No Milestone
No project
No Assignees
7 Participants
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: blender/blender#91668
Loading…
Reference in New Issue
No description provided.
Delete Branch "%!s(<nil>)"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
This task is for figuring out whether to map the idea of attribute grids to the attribute concept, and if so, how to do it.
Here's some background I wrote about in chat:
In D12100 I implemented volume to mesh transfer for all grids in a volume, and "Distribute Points in Volume" will have the same decision.
Below are a few different methods that I've been thinking about:
All Grids
{F11707663 size=full}
Pros
Cons
Named Grids
Currently I don't think this option is very compelling, I just included it for the sake of completion.
Pros
Cons
Anonymous Grids
{F11707672 size=full}
Pros
Cons
Changed status from 'Needs Triage' to: 'Confirmed'
Added subscriber: @HooglyBoogly
I think with
8ddfdfd2b2
we have settled on the approach of using all grids in most nodes, and adding something like a "Separate Volume Grids" node to allow only holding a subset of grids in a geometry.Changed status from 'Confirmed' to: 'Resolved'
Changed status from 'Resolved' to: 'Confirmed'
Added subscriber: @JacquesLucke
Added subscriber: @GeorgiaPacific
Added subscriber: @brecht
Attributes
I think integrating with fields / anonymous grids in some way makes sense, to have a consistent user experience with other types of geometry.
The equivalent of a mesh attribute to me seems to be one or more grids with the same name. We could name the attributes in the UI also for consistency, and when an attribute consists of multiple grids also communicate that in the properties editor / spreadsheet.
Looking at the nodes in the screenshots, I would expect Advect Volume or Fracture Volume to work on all grids, no need for field sockets. To me that's like a deforming a mesh or booleans, you do it for the entire mesh including attributes. For level sets I'd consider the signed distance a built-in attribute similar to a mesh position. The number of links is then reduced.
Consistent Attribute Domains
For me the tricky thing is what happens when you mix attributes in fields. Often they might just be a single grid with identical transform, in which case it's efficient. If there are multiple grids or attributes with different transforms, then you need to resample them. That's relatively slow, and it's not well defined which of the transform it should use to resample to. The other issue is that you build a field from e.g. purely a procedural texture, that needs to have a resolution and be bounded somehow.
I'm thinking it may be reasonable to add some constraints on grid transforms within a single volume datablock. At least for them to be valid for use in fields. We could require that all attributes must have matching grid transforms. If there are multiple grids in an attribute, each attribute should have the same number of grids with matching transform (at least conceptually, in practice we may not store an empty grid). This way I think field operations are better defined, as there is a clear domain similar to mesh attributes.
If you then use a VDB file that does not follow this constraint, there would be a warning and it would have to be resampled or split into multiple volumes by the user to be usable for fields (and maybe some other volume nodes).
Added subscriber: @Diogo_Valadares
Here are some more mockups where I've attempted to take Brecht's feedback into account.
Builtin attributes
{F13014945 size=full}
Density and level set distance / signed distance are treated like builtin attributes.
This means that each have their own get and set nodes which can be used to change the values.
However, the "set" nodes do not alter topology, meaning they're only really useful for smaller tweaks.
It's not totally clear that it makes sense to change the distance grids like this at all.
Multiple grids with the same identifier
{F13015009 size=full}
The equivalent of a mesh attribute is one or more grids with the same name. Using multiple grids
to represent a single attribute is very important for performance reasons because merging grids
is slow and should be avoided where possible.
Above is an example of a node that by definition outputs multiple multiple grids. However,
every output grid represents the same "distance" concept that the input grid does. Using the
same name for all of them avoids complexity (e.g. assigning names like "density.001" density.002", etc.).
Accessing specific grids
{F13014996 size=full}
There are two ways to access data from specific grids.
Since creating a grid from scratch is inefficient, volume nodes can detect when a field input
references a grid directly, and avoid the entire field evaluation process.
Regular "tool" nodes
{F13015004 size=full}
Many (most?) nodes don't have to worry about selecting specific grids to operate on,
they can simply modify all grids in the volume data-block. This makes them easy to interact
with as high-level building blocks, because users can think of the volume as one item
rather than as a collection of grids/attributes-- just like a meshes or curves.
Accessing/adding/modifying grids
{F13015000 size=full}
When adding another grid to a volume, the nodes get to choose what topology to use when
creating it. For example, creating a new grid from scratch (no grid dependencies in the input field),
the node would use the topology of an existing grid in the volume. When creating a grid based
on an existing grid (i.e. a named attribute input), the input grid's topology might be unchanged.
If an input grid has a different transform than an existing grid on the volume than it also has to
be resampled to match the transform of existing grids.
Creating volumes from scratch
{F13014954 size=full}
I expect most volumes will be created by simulations or by converting from meshes or points,
but it seems reasonable to allow creating a volume from scratch. Conceptually this node would
create a dense volume and then make it sparse where possible, though there may be various
ways to optimize this to avoid a complete dense evaluation of the input field.
Conclusion
Overall I'm actually quite happy with how this is looking, it seems much more intuitive than
I expected. However, eventually I think we will be forced to add some less elegant aspects to the design
for performance reasons. One example is the efficiently stored "active states " that represent something
like a builtin selection attribute, which we've avoided doing so far. Things like that will be more appetizing
on a UI level once we get things like hidden sockets or subpanels on nodes.
Added subscriber: @Robonnet
I can see that the
level set distance
as built-in attribute makes sense. I'm not so sure aboutdensity
. It seems quite reasonable to have volumetric data without a density (e.g. just a velocity field).Not having a built-in
density
attribute opens up some compatibility questions for existing files of course, but I think that can be solved like so:Points to Volume
node.density
.Do you have an example for how those nodes would be used in practice? Maybe those nodes are just not needed for now.
Do you have an example for when multiple grids would be used for a single attribute?
What do these nodes do exactly?
Do you have an idea for a heuristic already for how to choose the topology? Maybe it shouldn't work when there are different topologies, and the user would have to be explicit by passing in a separate "reference field".
Any thoughts on how implicit or explicit this resampling should be? Sounds like something the user should be informed about in one way or another.
For naming, in devtalk discussion there was an argument that "SDF" is a more common term than "Level Set" in 3D apps. I think it would make some sense to use "SDF" instead of "Level Set" in node names, and to then have "SDF Volume" and "Fog Volume" socket names where a specific type of volume is expected.
I agree density does not need to be a built-in attribute to the same degree as level set distance. To me it seems more on a similar level to temperature and velocity, attributes that physics and rendering will recognize, and that may be used as default values in some attribute name fields.
I think this would happen by default with Join Geometry, so that it's a cheap and well defined operation. And then if you want to resample it all to a single grid, you'd have a node for that (similar to Realize Instances) with settings for how the resampling should happen exactly.
I don't know to what extent this is even a standard OpenVDB feature but it is used by Houdini, see for example #86313 (Open VDB Doesn't Playback Clusters Correctly).
Though thinking about this more, I'm not entirely sure multiple grids are that great a solution, if we look at two distinct use cases:
So another way to look at this would be, OpenVDB files with clusters could be imported as instances, and for volumes generated internally or single grid OpenVDB files we try to make everything use the same transform. I'm not sure what is best. It's a question of how much we try to adapt to OpenVDB, or try to restrict so things fit nicer in the geometry nodes design.
Coming back to this topic after some time, here is an adjusted design based on your feedback:
General
distance
attribute. Others are generic named attributes or anonymous attributesCreating Volume Grids
Volume grids can be created from scratch with primitives. In this case they have a resolution specified manually.
The SDF sphere volume creates a builtin grid called
distance
. The volume cube node creates an anonymous grid.The cube node has a new anonymous attribute output to reference the grid it creates, and a type option to clarify the choice for other nodes and applications.
Volume grids can also be created by converting from other geometry types. In that case the resolution is specified by the user and the topology specified by the geometry.
New grids can be created on existing volumes, but not from scratch, since the resolution and topology must be specified somehow.
The existing nodes for creating attributes would work in this case, but they need an existing grid to use for the topology.
If there are two existing grids, that introduces an arbitrary choice of topology. Eventually there can be a "reference grid" input that must reference a grid.
In the short term we could just require that there only be one other existing grid, or we could use a heuristic to choose the grid. Choosing the first grid or preferring certain grids may work.
Since we control the creation of volume grids, we ensure that all grids on the same geometry have the same transform.
Modifying Volumes
The builtin
distance
grid can be converted to another type, which is outputted as an anonymous attribute.For example, an SDF volume can be converted to a fog volume or a mask/boolean grid
Some nodes modify all grids implicitly in-place. Just like meshes, these builtin nodes are often necessary for topology-changing operations.
The Advect Volume node offsets each voxels in a volume by the given translation.
The SDF Volume Filter node corresponds to the "level set filter" OpenVDB tool and applies some topology changing operation to all SDF grids.
Some nodes deal with multiple volumes, potentially using a subset of the volume grids with some property.
The Volume Boolean node does Union, Intersect, and Difference operations on scalar grids. (This may need anonymous attribute inputs).
The Fracture SDF Volume node uses the builtin SDF distance on a main grid and a "cutter" grid, and creates separate volume instances for each "chunk".
That's a non-obvious API, but it uses the existing geometry nodes instances, and is similar to the existing Curve to Mesh node.
Using Volumes
The Volume to Mesh node is modified to have a choice between processing "All Scalar Grids" and just a single field (possibly an anonymous attribute).
The "all grids" mode is the default, since that's the current behavior of the node.
The Sample Volume node uses a field to choose which grid to reference (or evaluate a new one on the volume).
It samples the volume at specific points in space defined by the field evaluation.
Examples
Here, two SDF volumes are created and the fracture node is used to split one of them in half, forming two unconnected volume instances.
Then the volume to mesh node is used on each instance to convert the volumes to a mesh.
Here, an SDF volume is created from points. Then a new grid is created by multiplying that signed distance with a noise texture.
Setting the values in an SDF grid without changing the topology is potentially error-prone, but it should still be possible and may give a useful effect.
Further Questions
Thoughts
Here is the file I used to create the mockups:
Volume Node Mockups.blend
Added subscriber: @mod_moder
I wrote down the steps I would suggest to implement this design in #103248.
I'm sure this design will have to be tweaked a bit, but generally it seems worth trying, so hopefully some people will have the time to pick it up!
My main concerns are the need to plug in a bunch of extra connections for the grid fields and the unresolved questions about matching up grids for the boolean node.
Those both seems solvable to me though, so I don't think they're blocking.