8
$\begingroup$

I'm animating a Mix-RGB position change in a geonode setup that shifts an instanced plane from 2400 grid vertices to a different series of grids that add up to an equal 2400.

The mix animation didn't look good until I connected a Random Value Node to the Index field.

The problem now is that the Random Value calculation for the index doesn't care about unique values. It ends up assigning many planes to the same (index) location and therefore the final grids have empty spots.

I understand "Random Value" within a given range naturally duplicates numbers, but is there a way to prevent that?

Maybe there is another node or fancy math trick to redistribute the index numbers in a "random" way?

Thank you!

Picture 1 below: Node setup
Picture 2: Left side: final result (desired) but location mix animation was very ugly.
Picture 2: Right side: final result (not desired) but location mix animation looked great.


enter image description here

enter image description here

$\endgroup$
2

4 Answers 4

8
$\begingroup$

Result:

enter image description here

Geometry Nodes:

enter image description here

enter image description here

enter image description here

Note: In most seeds, most points are random, those who aren't are perceptually random.

What the Point Index Randomizer node group does is first generates a random index for every point, and if a point got and index that is used by an previous point, it gets an index that was not generated from a list containing all not generated indexes.

$$\text{Explanation}$$

First, for every point, a random index is generated with limits equal to: $\left(\text{min}=0,\text{max}=C-1\right)$, where $C$ is equal to the point count. I will call these rIndex.

Now points with an rIndex that is used by previous points are identified by accumulating $1$ with Group Input equal to rIndex. The trailing value is compared verified if it is equal to $0$, if it is, then the point have an unique $rIndex$ compared to preceding points. I will call the resulting boolean field notRepeated.

Example result of trailing:

enter image description here

Now it accumulates notRepeated, the trailing result is the same as the index of a repeating point if it were on a list with only the repeating points. I will call these indexRepeat

Here's an example:

enter image description here

indexRepeat is what will be used in repeating points to get from the unused index list an index that wasn't generated by the Random Value node. But first it needs to calculate that list.

To calculate the list, it first branch the points geometry, set the position of the points to $\left(0,0,\text{rIndex}\right)$ and then separates points where notRepeat is $true$ (will call these usedIndexes). After that it creates a Mesh line with Count equal to the point count of the main points and vertex position equal to $\left(0,0,\text{Index}\right)$, then it deletes all vertices with a distance to points of usedIndexes equal to $0$, resulting in a mesh where all vertices have at their $Z$ coordinate an unused index. I will call this mesh unusedIndexes.

Now, for a point in the base points, if notRepeat is $true$, the Randomized Index is equal to rIndex. If not, a value for Randomized Index is transferred from unusedIndexes using indexRepeat as index. It could transfer sequentially, but, to look more random, for even indexRepeat values, it gets a value starting from index $0$, and for odd values it starts from the last unused index.

$\endgroup$
3
  • 2
    $\begingroup$ wow...+1 i am glad to have another GN expert here...! $\endgroup$
    – Chris
    Jun 1, 2022 at 9:57
  • 1
    $\begingroup$ Your "point index randomizer" node did the trick perfectly. This was a lot of work on your part and greatly appreciated. I wonder why Blender doesn't have a built in way of managing this. Thank you for your explanation as well - I'll be studying it for the next year or so trying to understand it properly! $\endgroup$ Jun 1, 2022 at 18:01
  • $\begingroup$ Tough job, nicely done. I'm almost ashamed to post my version :) $\endgroup$
    – Robin Betts
    Jun 1, 2022 at 19:50
6
$\begingroup$

For lazy people, there's a quick hack to shuffle indices (or, by extension, any other attribute) in GN, dependent on Blender's implementation of Separate Geometry and Join Geometry.

If you simply take geometry apart, and put it back together again, Blender stacks all the indices of the 'bottom half' on the indices of the 'top half'. So this operation:

enter image description here

.. is like randomly selecting roughly half of a deck of cards and putting them, still in order, on the bottom of the deck.

Do that 2 or 3 times, and you get a very reasonable shuffle.. here, as in your example:

enter image description here

.. with this sort of result:

enter image description here

$\endgroup$
7
  • 1
    $\begingroup$ Oh, fine, I find that very inspiring! $\endgroup$
    – quellenform
    Jun 1, 2022 at 19:56
  • $\begingroup$ @quellenform I hope you're not claiming to be as lazy as me ;) $\endgroup$
    – Robin Betts
    Jun 1, 2022 at 20:01
  • $\begingroup$ lazy people always look for the easiest solution, but the easiest solutions are usually the hardest work ;-) $\endgroup$
    – quellenform
    Jun 1, 2022 at 20:11
  • 2
    $\begingroup$ Thank you Robin for the alternative solution! I really like this one. Particularly so because it's simple and I understand it immediately. Good creative thinking! $\endgroup$ Jun 1, 2022 at 21:44
  • 1
    $\begingroup$ If I couldn't see the author of this answer, I would guess @quellenform... $\endgroup$ Jun 4, 2022 at 21:28
3
$\begingroup$

I believe my method to generate a new shuffled index is faster and simpler than everything proposed so far, it could also work with millions of points easily, not sure the other algo can handle a large amount of points

Here's the simple logic behind it:

  1. generate a random field integer ranging from 0 to 3, these will represent 4 distinct "groups" indices, we could work with more groups if needed but I believe 4 is a sweet spot
Index | GroupIndex 
      |
0     | 2
1     | 2
2     | 3
3     | 1
4     | 0
5     | 1
6     | 3
7     | 0
8     | 2
9     | 2
  1. With the accumulate field nodes set to a value of 1 int, we can distinctly enumerate the elements in each groups thanks to the trailing output, and get each group length thanks to the total output. PS: If you have trouble understanding this logic, please read the documentation about the accumulate field node
Idx |GrIdx| GrEnum          | GrLen  
    |     | #==Acc.Trailing | #==Acc.Total
0   | 2   | 0               | 4
1   | 2   | 1               | 4
2   | 3   | 0               | 2
3   | 1   | 0               | 2
4   | 0   | 0               | 2
5   | 1   | 1               | 2
6   | 3   | 1               | 2
7   | 0   | 1               | 2
8   | 2   | 2               | 4
9   | 2   | 3               | 4
  1. To each group enumeration, we add all previous groups lengths in order to obtain our non-repeating shuffled index
Idx |GrIdx|GrEnum|GrLen| Shuffled Index        
    |     |      |     | #adding the previous groups len to the trailing enum 
0   | 2   | 0    | 4   | 4 # == 0+[2+2]   
1   | 2   | 1    | 4   | 5 # == 1+[2+2]   
2   | 3   | 0    | 2   | 8 # == 0+[2+2+4] 
3   | 1   | 0    | 2   | 2 # == 0+[2]     
4   | 0   | 0    | 2   | 0 # == 0         
5   | 1   | 1    | 2   | 3 # == 1+[2]     
6   | 3   | 1    | 2   | 9 # == 1+[2+2+4] 
7   | 0   | 1    | 2   | 1 # == 1         
8   | 2   | 2    | 4   | 6 # == 2+[2+2]   
9   | 2   | 3    | 4   | 7 # == 3+[2+2]   

enter image description here

It is also important to know, in the context of this question, that indices can be offsetted and mirrored, i tried to use these functions to shuffle my indices in this thread below (it didn't work at the end but I'm sure it would've been another potential solution after more trials)

How to shuffle an attribute in geometry node?

i tried various techniques already, see shuffle_by_offsetting_index.blend consisting of "offsetting" or "mirroring" indices depending on odd/even numbers or random ranges.

$\endgroup$
3
  • $\begingroup$ Poorly explained and illustrated, as well as difficult to understand structured, but a brilliant solution! $\endgroup$
    – quellenform
    Jun 23, 2022 at 8:32
  • $\begingroup$ you're right thanks for your suggestion $\endgroup$
    – Fox
    Jun 23, 2022 at 12:06
  • 1
    $\begingroup$ @quellenform ok added a tablature, i hope that's enough :) $\endgroup$
    – Fox
    Jun 23, 2022 at 12:26
2
$\begingroup$

Random coordinate -> sort

You can randomize X coordinate (or just any attribute if you're willing to modify the algorithms), and then sort by it. The resolution-sampling algorithm probably will be enough:

How can I re-sort the points/indexes of an object in geometry nodes?

Precomputed mapping

If you know beforehand how many vertices there will be to rearrange, and you don't want to have a dynamic seed, or there's a limited number of seeds, you can create a mesh that maps an index to another index expressed as a custom attribute or something else... Here I used X coordinate:

Now in order to automatically create that mesh map, you need to run a Python script:

import bpy, numpy as np
from bpy import context as C, data as D

_len = 100
a = np.arange(_len, dtype=np.float32)

np.random.shuffle(a)
a.resize(_len*3)
a = np.reshape(a, (_len, 3), order='F')
a = np.reshape(a, _len*3)

me = D.meshes.new('random map')
me.vertices.add(_len)
me.vertices.foreach_set("co", a)

ob = D.objects.new('random map', me)
C.scene.collection.objects.link(ob)

This is obviously the fastest solution.

Raycasting algorithm

Set random Z coordinate, spawn triangles on points (raycast only works with faces), offset the position slightly down so that you don't hit self, and ray-cast down to hit the next face below. If you don't hit anything, you're the very bottom face, so raycast again, from the top, down on the top-most face. This way you map each point to a random face - and since each triangle has 3 consecutive verts, dividing by 3 and flooring the index gives you the index of the point that spawned that triangle.

Beware! There's no guarantee you won't position multiple points on the same coordinate. You can improve this answer with Hulifier's approach of dealing with duplicates.

This answer is slower than Hulifier's, but you're extremely unlikely to get a duplicate.

$\endgroup$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .