I want to calculate the velocity of each point on an object's surface in world coordinates and store that in a named attribute so that particles emitted from this surface can inherit the velocity of the surface. This surface velocity needs to be based on world space - ie include the whole object's translational velocity and rotations that contribute to the velocity. I want to use simulation nodes rather than Blender's built in particles as there are other effects I need to add in on top of this.
-
1$\begingroup$ Could you provide a Blender file using blend-exchange.com as a starting point with a GN modifier where "only" the velocity computation is to update ? $\endgroup$– StefLAncienCommented Apr 26 at 10:40
-
$\begingroup$ Done. The file has a basic emitter setup... but I think the solution will require some transfer of velocity data from the base mesh to the emitting particles so will be more complex than this file suggests. $\endgroup$– David MCommented Apr 26 at 11:43
2 Answers
(Using Blender 3.6.8)
Approach
The velocity in the world coordinates of the emitting object is computed and stored as an attribute by a GeometryNodes modifier attached to this object. The achieved field is processed by an other object, using an other GeometryNodes modifier.
This figure illustrates the "Object to track" motion while it is translating along the Y axis and rotating around the Z axis, starting from the world origin. Elongated cones are visualizing the instantaneous velocity vector in world coordinates of each corner of the cube. Tetrahedra are visualizing the successive position of the initial top right corner sampled every 5 frames. The velocity vector must be tangent to such a trajectory, and its magnitude must be proportional to the distance between two adjacent tetrahedra.
GeometryNodes modifiers
The above GN graph, attached to the emitting object, is only storing named attributes. The velocity is computed as the difference between positions at two successive frames using a Simulation Zone
.
1. The previous frame position is stored in Point domain as an attribute named "old". It is initialized with the Position at frame 1, upstream of the Simulation Zone
.
2. Accordingly, the named attribute "velocity" is initialized null.
3. The instantaneous Location and Rotation of the emitting object are recovered in the world coordinates, using an Object Info
node connected to a Self Object
node. To do so, the Transform Space is set to Original.
4. The emitting object, whose geometry is invariant in its own GeometryNodes modifier, is rotated then translated using a Transform Geometry
node.
5. This transformed geometry is not connected to the Group Output
node. It is instead used to transfer, to the simulation zone, the instantaneous Position of the emitting object vertices, using a Sample Index
node.
6. To these current positions are subtracted the "old" ones to compute the "velocity".
7. Eventually, current positions are stored as "old" for the next frame.
The above GN graph is attached to a fixed object acting as an observer, taking advantage of the vertices position and of the "velocity" field of the emitting object.
About the position: (part in green)
1. A tetrahedron (i.e. a Cone
with 3 vertices) is dynamically added to the output geometry using a Simulation Zone
made of a single Join Geometry
node.
2. Sampling is controlled with a Switch
node. A new tetrahedron is added every 5 frames, identified with a Modulo
math node, starting at frame 1.
3. The tetrahedron position is set using an Instance on Points
node.
4. This position is copied from the emitting object vertex of Index 0. This single point is chosen through the Selection socket connected to an Equal
compare node.
5. The Geometry of the emitting object (and so the current position of its vertices) is recovered through an Object Info
node. The Transform Space is set to Relative "to maintain the relative position between the two objects in the scene". Because the observer is fixed while the emitting object is moving, the current position of the seeding point is changing in time.
About the velocity: (part in blue)
6. An elongated cone is positioned, downstream of the Simulation Zone
, at each vertex of the emitting object using as previously an Instance on Points
node connected to an Object Info
node set in Relative Transform Space.
7. Using an Align Euler to Vector
node, the Z axis of instanced cones is aligned to the "velocity" vector recovered from the point they are attached to, through a Named Attribute
node.
8. The same attribute is used to scale instanced cones Z axis, to make these length proportional to the "velocity" magnitude (NB: The factor 30 to scale the length was adjusted to fit the demonstration case ; it has no meaning).
9. Because the observer is fixed in the scene, this illustrates that the "velocity" field stored as attribute in the emitting object is indeed in the world coordinates, and not in the object coordinates. The resulting animation is insensitive to the position and the rotation of the observer.
Resources
Benchmark
Lutzi proposal is based on vector algebra instead of a Transform Geometry
node to apply rotation and translation. The following GeometryNodes graph is timing both approaches when the objective is to store a position field as an attribute. It is attached to a point cloud with 100.000 elements.
1. The rotation is computed with a Vector Rotate
node.
2. The translation is computed with an Add
vector math node, providing the sought field.
1. The rotation, then the translation, are computed by a single Transform Geometry
node.
2. The sought field is recovered through a Sample Index
node set in Point domain and connected to a Position
node.
Conclusion:
Repeated runs show that the "Geometry transformation" approach is 1.5 to 2.0 times faster than the "Vector algebra" one. One explanation is that for a uniform transformation (i.e. with the same parameters for all vertices), the Transform Geometry
node is optimized, counterbalancing the extra Sample Index
node cost. But it can not apply a "per vertex" specific transformation as its Translation, Rotation and Scale sockets are not fields.
My answer is probably equivalent to the one of StefLAncien.
Requirements
The object using these geometry nodes should have a scale of 1. If it is not the case : Ctrl+A> Apply Scale
Global position (rotation included)
First let's build a group node for the global position. The process is to take the object position and add to it the Position which is the local position. This Position won't take into account Transforms (location, rotation, scale). So we can rotate the Position with the object rotation. We get :
Store global velocity
Let's say we are at frame $i$, we have the global position at each frame $\vec{x_i}$ and we want to compute the velocity $\vec{v_i}$. Then the general process to store the velocity is :
- Compute the velocity, which is simply : $\vec{v_i} = \vec{x_i} - \vec{x_{i-1}}$. Note that this means we won't have any velocity at frame 1.
- Store the current position for the next frame $lastPosition:=\vec{x_i}$
And the node tree is :
Notes and blend file
I have added a visualization of the velocity, quite the same as StefLAncien, as a debugging tool. You can enable/disable this in the Modifier tab.