Anonymous Attributes, Attribute Sockets and Node Group Processors [Proposal]

This proposal is inspired by multiple proposals that have come before:

Quick note, all the images are just mockups. They have been created using node groups, the Toggle Node Options operator in the context menu of nodes and a little patch that makes it easy to change the display shape of sockets in the sidebar (patch).

Anonymous Attribute

Definition

To recap, currently a geometry attribute is a container for data that is attached to geometry.

  • It belongs to a specific domain of the geometry (such as vertices, edges, faces, …).
  • It has a specific data type (such as float, integer, …).
  • It has a name that identifies the attribute on a geometry.

Anonymous attributes extend this concept a little bit. Now attributes fall into two categories:

  • Persistent Attributes: Those are the same as the attributes we have already.
  • Anonymous Attributes: Those differ from persistent attributes in the following ways:
    • They are not identified by name. Instead they are identified by some yet-to-be-defined thing that the user should never see (might be a random name, or some runtime data structure).
    • Their identifier is reference counted. Once the reference count drops to zero, the anonymous attribute can be removed automatically. The geometry itself only has a weak (non-owning) reference of the identifier.
    • They might have a debug-name, which is not an identifier. This name might be shown to users, e.g. in the spreadsheet or in socket inspection.

Attribute Socket

Definition

  • An attribute socket is a new kind of socket. For the purpose of this proposal, they will have a square socket.
  • There is an attribute socket type for every attribute data type (e.g. Float Attribute socket, Vector Attribute socket, …).
  • The data flowing through an attribute socket is one of those:
    • A single value of the base data type.
    • A name of a persistent attribute.
    • A strong (owning) reference of the identifier of an anonymous attribute.

Usage

The Attribute node can be used to get a new reference to a persistent attribute. In theory we could also have an implicit conversion from strings to attribute sockets, but I’m not sure if that is desirable.

Anonymous attribute references cannot be created from a string. Instead, they are created by nodes that add new attributes.

It is not possible to use math nodes directly on the attribute sockets.

Since attribute sockets contain references to attributes, they can be used after topology changes.

Attribute sockets can be exposed to the modifier as input or output.

  • Input: In the modifier the user can choose between providing a constant value or an attribute name (with attribute search).
  • Output: In the modifier the user can give a persistent name to the attribute. If the name is left empty, the attribute is discarded.


image

The existing attribute math nodes would still be valid nodes in the sense that they would not have to be updated. They would just only work on persistent attributes. It is possible to update them to make use of attribute sockets though. However, in practice operations on attributes should be done with the attribute processor. This is explained in more detail in the next section.

image

An Expand Selection node could output selections for different domains that can be used by subsequent nodes. Attribute sockets can be converted between types implicitly. The actual conversion of the referenced attribute will happen in the node using the attribute.

image

It is possible to create persistent attributes using a Set Attribute node. It’s not in the mockup, but the node would have a data type and maybe domain dropdown menu.

image

Creating persistent attributes with nodes can be useful when preparing geometry for rendering or export. One could build node groups that encapsulate hardcoded names that match the names used by the material or other software. Material assets could come with a (possibly automatically generated) geometry node group that makes setting up the right attributes easy.

The same concept can also be applied to getting data into geometry nodes.
For example, a node group like this one could be build for an object (possibly automatically).
This way, the hardcoded attribute names are abstracted away from the main node group.
What works for the Object Info node here, also works for loading geometry from files.

Node Group Processors

Definition

A Node Group Processor is any node that references another node group and invokes/calls/executes it in some way.

Group Node

The Group node exists for a long time already. When it executes the referenced node group it works as if it was copy-pasted into the parent node group. The behavior will not change with this proposal.

Attribute Processor

The Attribute Processor is a new kind of node group processor. Instead of executing the referenced node group only once, it executes it for every element on some geometry domain. The inputs and outputs are exposed as attribute sockets. Inside the referenced node group, it is possible to use some “special” nodes that only make sense in the context of the attribute processor. E.g. one can access the index of the current iteration. Furthermore, builtin attributes like position can be used directly without having to expose them to the outside (although that is possible as well of course).

It’s not shown in the mockup, but the attribute processor node needs a domain dropdown. In this current design, it does not need a selection input because it does not change existing attributes. Providing a selection nevertheless could be useful to improve performance. Maybe it is also possible to somehow detect the selection automatically by looking at what the output is used for. Not sure if that can work well.

Inside the attribute processor, normal math nodes can be used to perform math operations on attributes in a non-linear way. In the current design, the node group referenced by the attribute processor would just be another geometry node group, just that some nodes might be disabled and others become available.

Others

I won’t go into much detail here, but at least wanted to mention a few other possible node group processors.

  • Submesh Processor: The referenced node group would have a geometry input and output and is executed for every connected component in a mesh separately.
  • Spline Processor: Similar as above, but it will be executed for every spline. The output can be a spline but also something else that replaces the spline.
  • Curve Generator: References a node group that turns a float into vector that can be used to generate a spline.
  • Volume Generator: Has a position as input and outputs volume grid values for the position. On the outside one would have to specify a bounding box and resolution.
  • Recursive Geometry: Executes the same node group multiple times on the same geometry.

Comparison

All statements in this section are from the perspective of this proposal.

Original Attribute Socket Types

  • The concept of the Attribute Socket is essentially the same, just that this proposal fills in some more details and integrates it into a larger context.

Original Attribute Processor

  • Inputs and outputs inside the attribute processor group correspond to inputs and outputs in the attribute processor node respectively.
  • Less need for various drop downs to configure the attribute processor.
  • The attribute processor does not overwrite existing attributes.

Fields and Anonymous Attributes

  • Cannot use math nodes on attributes directly in the main node tree.
  • No callbacks in the main node tree, there is only one “execution context”.
  • Attribute Sockets correspond to specific attributes (whereas fields do not), so nodes using attribute sockets as input can lookup the domain of the attribute. This is especially useful for selections.
  • It’s more clear what node inputs may be attributes just by looking at socket types.
  • One has to work with more node trees to achieve the same effect. With fields, everything could be done in a single node tree and is more plug-and-play.
  • Knowledge gained in shader nodes does not apply here directly, e.g. one could not just use a standalone noise node to control some displacement. The concepts do apply to some degree within the attribute processor though.

Expandable Geometry Socket

  • Hardcoded attributes names can still be used inside the node tree when appropriate.
  • The modifier ui can look the same. It’s very explicit when inputs and outputs of the geometry node group are attributes, and the modifier ui can be drawn accordingly.
  • It’s not necessary to flatten all data into lists to benefit from non-linear math nodes. Flattening data into lists is easy when there is a single mesh, but much less so when there are e.g. curves or instances. Also, I’m not sure how one would flatten a (sparse) volume grid into a list in a good way. It also has to be possible to un-flatten the list when it is used.
  • Attribute sockets can be linked across topology changes on the geomery.
  • Anonymous attributes that are not referenced anymore can be used automatically (so a “pop” operation is not necessary).
  • It’s not necessary to give anonymous attributes meaningful names, because they can just be connected to where they are used, even when there are topology changes.
  • There is no need for domain dropdowns in attribute outputs, because domain interpolation happens at the node using the attribute.

Compatibility

It is not required to change any existing nodes. They are all still compatible with this proposal.
We might want to change some nodes to improve the workflow, but versioning should be relatively straight forward. That makes it easy to gradually transition from the current workflow to the new workflow.

Possible Future Extensions

Lists

New list data types don’t conflict with this proposal and extend it well. It might make sense to add attribute lists as well. Those could be used in some nodes to determine which attributes should be transferred and which should not. Generally, all attributes are transferred everywhere, but that can be very expensive at times, so providing some more control might be useful.

Reference Sockets

Maybe referencing attributes is just a special case of some more generic concept: referencing data that is stored somewhere else. I don’t have anything in particular in mind currently.

Passing Callbacks

One major limitation of this proposal currently is that one cannot pass e.g. a procedural texture (generated with nodes) around. It is always used in the same place where it is defined. This can be restrictive in some use cases.

A possible solution is to pass around geometry node groups as callbacks. Obviously, there are some issues to overcome. Mainly, what happens if different node groups have different sets of inputs and outputs. I think that can be overcome be attaching some meaning to group inputs and outputs that is understood by the nodes executing them. This is similar to what was done in the fields proposal implicitly.

While it is more cumbersome to work with callbacks in this way compared to working with fields, it is much more explicit and opt-in.

10 Likes

I’m missing a rationale for moving back from attribute processing inside the base nodetree? I thought fields were an improvement on the attribute processor’s “capsulation”. Is there some unforeseen design challenge to implementing fields, or did you decide it wasn’t that great to gather geometry and attribute processing in the same nodetree after all?

Cheers,

Hadrien

The main rational is that using callbacks in the main node tree interferes with strict data flow. While that is not strictly a bad thing, it’s worth thinking about other approaches which do not have this property.

Note that the Attribute Processor in this proposal should be quite a bit easier to use compared to original prototype due to anonymous attributes and attribute sockets.

1 Like

Ok. I have to say this flies over my head heheh but thanks for the explanation still !
So what would this imply for generative modeling, and sharing attributes between different geometries? would it be as straightforward as “getting” an attribute and “setting” it to another geometry?

You’d have to be quite a bit more explicit about what you mean. What does sharing attributes between different geometries mean here? Also there are many ways to transfer attributes from one geometry to another. Can be done in one node, or with get/set nodes once we have lists.

Exactly, I meant transferring an attribute to a different geometry! would the attribute output socket simply be connected to the other geometry, or would this process rely on supplemental nodes, such as attribute transfer by id?
I was mainly referring to those examples other users have made using Sverchok and component list manipulation to generate shapes…

@jacqueslucke I realize these questions might be very basic, but they’re legitimate coming from me. So I can always leave the analysis and critique to the more advanced users and come back when the dust has settled. (not wanting to create unnecessary noise)
In any case thanks for the explanations!

You cannot just link an attribute socket to a completely different geometry. The data in the attribute socket is just a name/reference to an attribute. You’d need supplemental nodes for attribute transfer.

Sad that the idea of making GN works more like shader nodes has been abandoned. This was one of the main reasons I liked the field proposal.
Also how is this new proposal gonna help with the expression implementation situation? In the field proposal Hans mentioned the field design is going to make expression pretty easy by breaking them down into several math nodes. How is expression handled by this design? Will we be able to do expression In a more straightforward way without the need to type it in a text box?

1 Like

Sad that the idea of making GN works more like shader nodes has been abandoned.

Nothing has been abandoned yet. This is just a different proposal that makes different trade-offs. It’s competing with the fields proposal and does not just replace it.

How is expression handled by this design? Will we be able to do expression In a more straightforward way without the need to type it in a text box?

One way to do it is to use an attribute processor that contains the “expression(s)”. This attribute processor should be placed directly in front of the node that is supposed to use the expressions. So you can still do everything with nodes, the expression will just be in a different node group.

3 Likes

All of that works well here too! The nodes themselves (which could include expressions that you type in too) are just in a specific context instead of being a field.

I’ll skip the volume example since it’s similar and you probably get the point.

2 Likes

I could be mistaken but I think what’s usually meant by expression is a one-liner sitting for example in a value field. The same can be done with nodes but the point of using expressions for simple operations is their compacity isn’t it?

Attributes being displayed by square sockets make a lot of sense. The fact you cannot use math nodes on attributes in the main node area I’m ok with. Loading geometry from files and having things abstracted nicely in a object geometry with uv attribute setup sounds great. It would be nice if the abstraction when creating persistent attributes was changed for different object types so if you say dragged a grease pencil object in it would make fills/strokes as outputs automatically in nodegroup. I know the Compact Nodes design is currently being worked on the developer website which is exciting, here is how I would try to compact the nodes keeping the square sockets of your proposal. (my example is on the right) If this proposal of strict data flow keeps things simpler in the main node tree why not read the nodes downwards as well.

What if the attributes are numbers? What would the process be to change those? Reason I mention it is because it reminds of the current GN design, where because of the way attributes are handled, there’s a redundancy in nodes - math/vector operations for attributes, and math/vector operations for non-attributes.

The redundancy extends to color operations for attributes and color operations for non-attribute data.

It would be much simpler if there is no need for attribute-specific math/vec/color operators, but they can instead be used with all kinds of data.

If the attributes cannot directly use math/vec/color ops, then instead of replicating all the nodes, there could be one type of node that switches an attribute to a data type that can work with the math/vec/color ops and then switches it back.

This is very exciting! Knowing the power of using node scripting with list capabilities from elsewhere, the ability to get list items and do specific operations like instancing, transforming, etc on specific list items could significantly reduce the amount of nodes needed to achieve something specific. As far as I understand, attributes are lists, but in the current design, since they are bound to the geometry object it makes sense that they can’t exist. With anonymous nodes, the decoupling of attributes from objects could certainly provide a gateway for manipulating list data.

Sure thing, this is not what @Eary asked about though.

I don’t see much of a benefit of vertical nodes in this example. While mockups takes less space, it’s significantly less readable imo. Also, this is outside of the scope of this proposal.

Well, that is exactly one of the issues this and other proposals address. In this proposal, you could use normal math nodes on attributes within the Attribute Processor. In the expandable geometry socket proposal you’d use the math nodes on lists and in the fields proposal you’d use the math nodes on fields to process attributes.

3 Likes

Hi @jacqueslucke ,
I’m investing a lot of time reading all proposals that you compared this one with and trying to wrap my head around it.

This is probably my favorite so far. I’m basically fine if the attributes are handled in a group of its own with the processor. It reminds me of Houdini’s workflow with Attribute Vop/Attribute wrangler, but with the advantage of anonymous attributes (I don’t know if there is a similar concept in Houdini).

The attribute processor does not overwrite existing attributes.

I’m not sure I get this. What for example, if I want to change the position attribute of a geometry in the processor, say, with a noise texture. How do I displace the points if I cannot overwrite the existing position attribute? Maybe using the temporary attribute outputted by the processor and then replace the position attribute with a specific node externally to the processor? Is there a specific reason I’m overseeing to not allow the attribute processor to directly overwrite existing persistent attributes?

I think that the “set attribute” could be called “create attribute”, It makes more sense to a UX point of view in my opinion, since you define it as a node that “creates” persistent attributes. Using the current GN system, I constantly find myself trying to search for a “create attribute” node, while as far as i know (correct me if i’m wrong), there is only the “attribute fill” node to explicitly create an attribute from scratch.

Funnily enough, I thought about this exact same use case. For this specific proposal I opted into keeping the Attribute Processor simple and make it not overwrite existing attributes. Adding support for overwriting existing attributes is definitely possible, but might increase the number of options in the node. In this proposal the solution is indeed to use a separate Set Attribute node to set the position attribute.

Yeah, I don’t mind either way really. The thing with “create” is that it suggests that you are always adding a new attribute. That might not be the case though, because you can overwrite existing attributes as well.

There are other ways to “trick” attribute math nodes into creating a new attribute from scratch, but the Attribute Fill node is indeed to best approach currently.

1 Like

For this specific proposal I opted into keeping the Attribute Processor simple and make it not overwrite existing attributes.

So whatever socket is shown on the group input and on the group output nodes are either values or anonymous attributes right?

when you say “keeping the attribute processor simple” do you mean in terms of node setup/appearance from the exterior, or in terms of behavior/capabilities?

To be honest, while I’m ok to process attributes inside a different node group , having to do that and then having to think how to use them outside sound like unnecessary extra step for me. I’d rather edit the attribute directly inside the processor if needed.

Plus (bear with me if I always talk about nodes name, but I think It’s really important in terms of usability), the name “attribute processor” sounds like something that processes, so edits attributes.

In the mockup you show a node (kind of “import persistent/builtin attribute” node) to access the position in a read only mode, what if you could add “export attribute” node to allow the processor to overwrite persistent attributes when needed, inside the processor group itself ,without complicating the external interface of the nodegroup.

If the attribute processor had the capability of editing attributes directly, It would be more suitable for creating custom nodes that just work. In the example of the noise displacement attribute processor node, one could reuse the nodegroup as “terrain” mesh modifier node, you just plug the geometry and tweak the inputs parameters.
Right now , as far as I undestand, it can just generate an anonymous “displace” attribute that needs to be used outside.

Edit: Here is an example of an “Inflate” node processor, but It’s able to “export” or “write” existing attributes on demand, with specific nodes if needed (here the “export” name may not be the best choice tbh).
I hope it makes sense, in the example the normal is “imported” or “read” from the geometry, but never overwritten by the Scale node, the position attribute instead, is first read (just like @jacqueslucke 's example), processed, and then written by the export attribute

2 Likes