Explicit Instancing [Proposal]

This document proposes changes to how instances are handled in geometry nodes. First, it explains the current state and why it works this way. Then it argues why the original reasoning is flawed nowadays. Lastly, there is a new proposal for how we could move forward.

Current State

Currently, the main rule for how instances in geometry nodes are handled is that they are always automatically made real when necessary. For example, the Set Position node always makes instances real before updating the position.

There were a few reasons for why we decided it should behave this way:

  • Instancing is used only for performance reasons and should be an implementation detail.
  • We thought about having Collection Nodes. Those would probably work on the instance/object/collection level, whereas geometry nodes only works with the geometry data directly.

Issues with the Current Workflow

I think the reasons mentioned above are flawed nowadays or at least out of touch with reality. Below are arguments that speak against our original reasoning.

Performance and Implementation Detail

It is true that one of the main reasons for using instances is that they are typically more time and space efficient. In fact, that is so true that artists working on complex scenes often have to make conscious decisions about what is instanced and what is not. Users have to make the trade-off between the ability to e.g. deform every piece of geometry individually which may result in more visually pleasing results, and limits of hardware and time. In more complex scenes this trade off should not be done automatically but by the artist. Therefore, instancing should not be treated as an implementation detail.

Currently, instancing is not really an implementation detail anyway, since it is possible to see when something is an instance and when not in the spreadsheet. If instances were truly an implementation detail, then the instances category in the spreadsheet shouldn’t exist, and it should always only show the realized geometry data.

However, even if instances were a pure implementation detail, completely abstracted away from the user, it would be a very leaky abstraction, because users (1) still want to know when something is an instance for the reasons mentioned above and (2) it becomes very apparent when something suddenly becomes real, because processing time explodes.

Also, just because we expose the concept of instances to the user, it does not mean that we can’t do more internal instancing (or data sharing) that the user is not necessarily aware of. In fact, we are doing that already and I hope we can do more of that in the future (e.g. sharing attribute data between geometries with a copy-on-write system similar to what we do for geometry components already).

Other Uses of Instancing

Next, I’d like to argue that performance is not the only use case for instancing, especially now that we support geometry instancing (instead of just object and collection instancing).

Having instances allows embedding multiple geometries in a single geometry, while keeping their individual geometry data separate. That gives us a list of geometries that can be used by nodes like the newly proposed Instance on Points node, that supports putting different instances on every point.

Furthermore, being able to access the instances separately allows artists to instance first and then move the instances around afterwards. This may be more user friendly compared to forcing the user to first position points correctly and only instance in the end. Instancing first would also make supporting nodes easy that push instances apart so that they don’t overlap. That’s especially true when different instances have different sizes.

Another thing we don’t support yet but should eventually are attributes on instances. Again it would probably be easier for artists to instance first, and then add attributes to the instances. Those attributes could then be used either by later processing steps or the renderers. Often it is conceptually important whether an attribute is on the instance or on e.g. every vertex. Shaders are build with that in mind.

Rigid body simulations may also benefit from the ability to have multiple instances in a single geometry. A “rigid body solver” node could take in a geometry, simulate all the instances as individual rigid body objects and output a new geometry in which the instances have been moved.

With geometry instancing we got the ability to create nested instances. For example, a forest geometry contains a bunch of tree instances, whereas each tree contains many leave instances. All of that could be built in a single geometry node network. For rendering it does not matter if the list of instances is flattened in the end. However, we could support exporting an entire nested structure for file types that support it. For that we probably want a way to set the name of an instance. The other direction is possible as well. We could import an entire hierarchy into a geometry with nested instances.

These nested instances are also what differentiates the instances component from a simple geometry list socket type that we may want to have at some point.

Yet another use case of instancing besides improving performance is to use geometry nodes for the initial scattering of objects. Then one can use the existing Make Instances Real operator to create separate objects for the individual instances. Their position can then be further fine tuned manually by the artist. Nested instances could even be created with the correct parent relationships.

Collection Nodes

Not much time has been spent figuring out how collection nodes would work exactly. However, as mentioned in a blog post from June, we decided that simulation solvers will be integrated in the pipeline at the geometry nodes level. So at least for that, collection nodes would not be necessary anymore. There may be other reasons to get collection nodes in the future, but for now I wouldn’t bet on it now.

Proposal

The proposal is to treat the instances component as a first-class citizen alongside the other component types: mesh, curve, point cloud, volume and the future hair type. Similar to the point cloud component, the instances component has a single attribute domain: Point. The difference to a point cloud is that every element in the instances component references some other geometry and has a full transformation matrix built-in.

Nodes should generally avoid making instances real implicitly. Instead there will be a separate Realize Instances node that has a geometry input and output that can be used in the general case. Besides that there are a few rules for handling of instances in nodes:

  • Nodes that modify geometry should ignore instances by default (e.g. Extrude).
  • Some nodes may get an option to process each instance separately (e.g. Edge Split, Subdivision Surface). In those cases, the output geometry will still contain instances. Each geometry referenced by the instances is processed separately, independent of the instance attributes (such as position).
  • Nodes that produce completely new geometry based on some input geometry (e.g. Point Distribute, Boolean) should not ignore instances. Instead, one should generally try to make the node work more efficiently by e.g. processing the individual instances in parallel. For those nodes it would usually be less efficient to realize instances before further processing.
  • Nodes that modify attributes should not make instances real, but apply the changes to the instance attributes (e.g. the Set Position node sets the position of instances instead of the position of every vertex).

One open question is whether instances should automatically become real when there are other modifiers after the geometry nodes modifier. Currently, I’d argue with no, because if one wanted to make instances real, one could just add a Realize Instances node.

Implications

The main downside of this proposal is that the user now has to be aware of when something is an instance and may have to insert a Realize Instances node in places where it was not necessary before.

Luckily, there are only a few nodes that actually create new instances (currently Point Instance, Object Info and Collection Info). So the number of places where one had to insert a Realize Instances node is quite small. It is so small, that we could even consider adding an option to output realized geometry from those nodes directly.

We already of multiple ways to show the user when something is an instance: spreadsheet and socket inspection. If some nodes will ignore instances in the future, those may also show a warning mentioning that an explicit Realize Instances node is necessary.

From my perspective, this small disadvantage is well worth it when compared to the possible workflow improvements we get by embracing instances as a first-class citizen in geometry nodes.

35 Likes

Separating instances procedurally if they happen to intersect after scattering sounds super useful. I think it’s fine if we have to be aware of instances. I’m not sure what nested instances mean, but it’s exciting.

1 Like

Yes. Thank you for recognizing all of these downsides of the current approach and the otherwise advantages. The proposal should make it a lot more intuitive in many situations as well, such as using Point Transform after instancing to actually move the instances.

I was also baffled on how the system works until I realized the dedicated Instance entry in the spreadsheet, and was wondering how to add and use attributes with that.

About the downsides, I think it brings as much intuition as it theoretically takes away, so I think it’s a fair trade. You could consider to automatically add the conversion node when using modelling nodes after instancing, but it is arguable if it’s what the user would want.

Like @Hadriscus mentioned, avoiding intersections is one of the most desirable features when scattering, so it’s good to make steps on getting there.

I like this proposal. In the current system the user would always need to be carefull when using some nodes that just automatically realize the instance. I don’t think needing to be aware of instance is a downside, I think once the user understand using instances can have better performance, they should already know what an instance is. Never mention Blender gives a different outline color to instances, I think it is obvious enough.

3 Likes

Great proposal, I like pretty much everything about it. Attributes on instances in particular, been waiting for this one pretty much since day one.
Although other modifiers in the stack should probably display some kind of warning if instances from geo nodes modifier are not realised and cannot be processed. Ideal solution would be to convert whole modifiers system into everything nodes framework, then instances could be transferred between modifiers automatically.

2 Likes

Sounds great! I particularly like the potential to pass instance attributes into Cycles and Eevee, which will make for a very powerful workflow.

I am also happy to see that the instance node will be renamed to instance on points, as I always thought that point instance was a misleading name. On that subject, are there likely to be instance on faces and instance on edges nodes in the future?

Which begs the question of if it is a better idea to have a single node with a domain dropdown for point/edge/face. Though arguably you do need more options for instancing on edges and faces, like a percentage slide between points for edges, so maybe they are better as discrete nodes.

2 Likes

Morning, @jacqueslucke .
Please tell me, will the camera culling feature in Cycles also benefit from it? Currently, when you instance a million objects with GN, the camera sees it as one object, so it can’t cull instances that are outside the view cone, which takes a lot of functionality from the built in culling tool.
Thx, and again: Bravo!

2 Likes

Regarding that, I also have something to ask. One of my friends had made his own camera culling node group in 2.93 and he is looking forward to updating the node group to field once the development is done.

But my question is, is instancing Geometry now already doing culling by default? I downloaded the WIP patch way back and found out the culling behavior because it was super obvious, a bit too obvious to a point that was kind of buggy. And later in the diff page I read this:

Then later on it was fixed and got committed. And in master it is unnoticeable now, it is nice but I am no longer confident that it is actually doing the culling now because I just cannot tell. I want to ask @jacqueslucke is the Geometry instancing mode already doing culling by default? If so then it is super cool I can just go ahead and tell my friend that he no longer needs the node group now

Great Proposal ,

Like others already said, the user Being aware of instancing is probably desired, after all, It’s a very foundamental concept in 3d graphics in general, and having warnings when instances can’t be processed can have also de good side effect of making beginners aware of the concept of instances, and incentivize them to investigate the concept, so… better feature discoverability!

One Idea that It’s probably related but maybe not directly to instances: when COPYING (not instancing) full geometry to points it would be great to transfer non constant attributes from points to the copied geometry and have them affect the original geometry per Copy.

Example: let’s say I have a “leaf” geometry node group that has some “bend” attribute that Deforms the whole geometry before it gets output from the group. If I scatter (copying, not instancing) the “leaf” geometry output on a ground plane, I’d like to have a “bend” attribute on the ground, maybe generated by a noise texture, that get transferred back and input on every copy of the original “leaf” group. So every copy of the leaf can have a different random bending depending on where it’s positioned on the ground geometry.

As I mentioned, I Know that It’s not Instancing, but I think being aware of the possibility to add such feature can somehow influence the decisions taken when designing instancing workflow.

Let me know what you think!

Just for your information, we decided that explicit instancing is the way to go for geometry nodes.


I don’t plan to have a node just for instancing on edges/faces etc. Mainly because there would be many different options indeed. My hope is that we improve the abilities of node groups in the future to make it more feasible to create these more complex nodes as groups.

For geometry nodes itself, I think we can have nodes like Faces to Points or something like that.

No it is not. The thing mentioned in the screenshot is a little bit related but not really. This was just about culling at render time but not when preparing the data.

Sounds like something that could be achieved with a “loop” or “geometry field” mechanism in the future. Maybe in Blender 3.1, we’ll see. We still have to do more design work on that topic.

14 Likes

Yeah, like “for each point:
read bend attr on points
apply bend attr to geo group bend input
copy geo group on point”

I just thought that It could be a directly implemented feature in a possible “copy on points” group. But having general a looping system would be amazing, can’t wait. You devs are rocking!! Nice to read that you already decided for explicit instancing!

2 Likes

Hello
Cool to hear that this proposal got accepted!

Please consider this “mesh to point” proposal :grinning_face_with_smiling_eyes:

Capture d’écran 2021-08-13 155150

as explained in the post, not having the automatic conversion when working with per mesh-element instancing can cause problem down the line, as there will be attribute missing errors

Hey @Eary
i think what we all want is some optimization option at an instance level
for example an new “Instance Viewport Optimization” panel either per scene or per object
The problem is linked with instancing, not with geonode, so that’s a task for devs in charge of the viewport imho

7 Likes

Hmm not so sure about that. If I understand it correctly, in order to support geometry instancing, Jacques already changed the lower level logic of how instancing works in Blender in general, he made changes that was more than just the node system itself, he made sure the new instancing works with render engines and exporters etc., so I think it should still be related to the GN module.

I like this proposal, though it may need a selection field input? Mesh to Curve has a selection input so maybe this one also need it.

1 Like

Just got an idea for circle packing with manipulatable instances. The poisson disk distribution mode was nice but it only made sure the points don’t overlap, not instance.Therefore it is common to see some areas with smaller sized instances having gaps while areas with larger instances being packed.

What about moving the point elimination logic to instances? For example, detect the bounding box or convex hull of individual instances, and don’t put the intance on the point if the bounding boxes overlap. Like instead of eliminating points, eliminate the instances. With explicit instancing this should be possible, right?

Edit: Second thought, perhaps not removing instances since it would still leave a hole there. Maybe just scale the instance down until there is no overlapping.

1 Like

That still sounds like something that can be achieved with a loop mechanism. Like just instance geometry on a poisson distributed set of points, after the geometry has been instanced, the loop starts:

For every distributed point until every intersection check is false:
-Run an intersection check with neighbors, if current intersection is true then:
-Reduce the current point scale by a given step

This is the first brute force method I can think of… i bet it can be more efficient.

But what if you need to keep the scale of instances constant? My guess is that there are already well established methods for close packing objects in 3d space, since it’s an old problem.

Yeah, for sure there are already known packing algorithms, I guess
In the case that you mention, if one wants to keep the scale constant, I guess the point distribution can’t be done prior to the instancing, at least if the criterion is based on intersection. The distribute node has at least to know the bounding box/convex hull or full geometry that is being instanced, to re-distribute/delete the points according to the non-intersection condition.

I may have missed something while reading the proposal, but I see no mention to animation and instances, and automatic handling of animation offset of the same instances, that’s an important matter too, a forest does not move equally and a bunch of ducks don’t fly in sync :slight_smile:

6 Likes

Unless it’s just transforms of the instances which are animated, then they are no longer instances. Especially the flying ducks example, which is certain to involve mesh deformation. At that point, each unique frame of the animation is more or less a unique mesh with its unique memory.