Stoke field density gradient


#1

Hi Bobo,

I’m trying to create faux surface normals based off a granular particle sim by getting an inverse density gradient.

Is there a node in the stoke field magma editor that will sample a particles vicinity Radii and output me a vector of the greatest particle density change in the sphere? Very similar to the box 3 particle sub op.

I see the gradient node but its asking for a position and a field input. Not sure if this is what I need? Maybe you can explain what kind of scalar value it is looking for?

Thank you! Nickolay


#2

Most Stoke category operators require the Position input. The Position Input node provides the voxel position (X,Y,Z) that the Magma iterates through implicitly. (In Krakatoa, the Magma loops over all incoming particles, in Stoke Field Magma, it loops over all voxels).

The reason we require the Position is so that you can provide any modified position if you wanted to. I originally requested to make the Position socket of these nodes default to the voxel position without connecting it, but it just never happened. I should probably ask again :slight_smile: At least I made it so that if you drag from the Position socket, a new Position Input will be made, and if one exists, a connection to it will be created automatically without duplication. So it is still rather fast to build.

In short, for the Gradient node you need to connect the Position (second) socket to a Position input to tell it to sample every voxel in the Stoke Field Magma grid.

The Field (first) socket needs to be connected to the field whose gradient we want. For particle data, you should look at the ParticleSplat operator. It takes a particle source and a Position (same XYZ as the Gradient), and splats the particle data onto a grid that can be then passed as the Field input of the Gradient node. You can select which channels you want, in your case you need Density. You can then negate the Gradient output, multiply by some factor (since we need the Velocity to be in units per second and not units per frame), and output as Velocity.

Naturally, if you have very few particles moving through the grid, there will be some jumping of values around the grid as the particles move from one voxel to the next.

The alternative would be the old ParticleSumRadius operator in the Object category which lets you sample a particle system within a specified radius. This would be called for each voxel again, but you need to do a lot more work there since the output needs to be divided by the Weight, but if the Count of particles in the radius is 0, this could result in a division by zero and NaN values. So it then needs a Switch operator to check the count and pass the result as 0 if the count is 0. On top of that, if you pick a Particle Flow Event, it will not have a default Density (there are two workarounds here, let me know if you want to hear them). In contrast, with the ParticleSplat if a particle does not carry a Density channel, it will assume a 1.0 default when splatting…

Let me know if this helps.


#3

Thank you for the response Bobo! Definitley helpful.

Looks like I’m going to have to generate a density channel from the particleSumRadius. You said the particleSplat defaults ALL voxel values to 1.0 if a desnity channel isnt present? I ran a debug and its giving me values between 0 and 1.8. So is it actually generating density values?

Thank you!


#4

No, the voxels will contain 0 or > 0 depending on what is in them.
What I meant was that if the source of the particles is a live PFlow Event which has no Density channel (since that is a Thinkbox construct and Max does not know about Density), a particle coming into a voxel with no Density will be assumed to have a Density of 1.0 when using ParticleSplat, but will cause an error “Density channel not found” when using particleSumRadius. So in the former case voxels with no particles will have a Density of 0, voxels with particles will have a Density > 0, and in the case of ParticleSumRadius, it will just fail until you provide a valid Density, or sample some other (existing) channel.

To solve the particleSumRadius case, I made a PRT Source from the Particle Flow, added a Magma to the PRT Source and set Density to 1.0. Then I used the PRT Source instead of the Particle Flow as the source in the Stoke Field Magma. I also needed a Switch to check for Count == 0 and set the output to 0.0 when Count is 0, and to Density divided by TotalWeigth when not 0. The results were very similar to ParticleSplat, except that I could increase the Radius and sample much larger regions for particle influence.

That being said, with only a few thousand particles the data was not enough to produce a repelling field to move other particles away. I suspect it would work with a much denser particle system though…