Quantcast
Channel: FlexMonkey
Viewing all 257 articles
Browse latest View live

Chaotic Magnetic Pendulum with Custom Radial Forces in Houdini

$
0
0

Following on from my recent post about creating a double pendulum in SideFX Houdini, here's another project that roughly simulates the effect of a magnetic pendulum and visualizes the resulting attractor.

The system consists of a single pendulum and four fields visualized as small spheres. The blue spheres indicate a repulsive radial force and the red spheres indicate an attractive radial force.

Perhaps surprisingly, Houdini doesn't come with a radial force "out-of-the-box", so I turned to the VOP Force dynamics node to create my own. This node allows the creation of custom forces based on a VOP network and sits inside my DOP network. My radial force looks like this:


It's not quite a gravitational force because it it ignores mass, but my node does apply an impulse in the direction between the affected object and the input position based on the inverse square of the distance between them.

The one gotcha was that, by default, Houdini dynamics "sleeps" stationary objects. Luckily, my pendulum RBD Object has an initial velocity and remains active over the 500 frames in the video above.

The final DOP Network has four of these custom radial force nodes - two with negative strengths and two with positive strengths yielding the final result. 


Simulating Accretion with Houdini's Grains Solver

$
0
0
When I started thinking about using SideFX Houdini to simulate accretion, I was expecting to have my VEX skills (or lack of) pushed to their limits. However, after looking at Houdini's grains solver, I was able to come up with quite a nice looking solution with only a handful of lines of VEX.

My first grains experiment (Experimenting with Impacts) created a solid block of sand from a box. But, there's no reason why the grain source needs to be a single piece of geometry. For this project, I created a disc (a flattened tube), created points from the disc's volume and copied small spheres to each point:



That disc shaped array of spheres became my grain source:



Houdini grains can be manipulated with POP forces such as drag, vortex (axis) and attract. So my DOP network has a handful of these to give the orbiting effect with a single radial gravity at the center of the system:



The combination of upping the clumping values in the POP Grains node and adding an attractive POP Interact node pulls the individual grains towards each other. The POP Wrangle contains a few lines of VEX to squeeze the grains into the disc shape:
if (@P.y < -0.1) {
    @force.y += 50;
}
else if (@P.y > 0.1)  {
    @force.y -= 50;
}
The final step was to swap out the default sprite rendering used by the grains shelf tool for actual geometry. I found the end result from sprites was too flat: sprites don't cast shadows over each other. An icosahedron gave a nice look, so my grain particles geometry node ended up like this:



The particles were assigned the out-of-the box clay material and voilà, a few hours later, I was procedurally building proto-planets!

Because everything looks better with depth-of-field, here's another render:




The music (if you can call it that) was created by me using Garage Band.

Animating Planet Engulfment in Houdini

$
0
0


Here's a nice-ish looking and easy to set up project that animates a hapless planet caught in the gravitational field of a small dense star. As the planet approaches, it begins to melt and gets sucked into star's inescapable pull.

The unlucky planet is actually a sphere with Mountain displacement node. I used the Lava From Object shelf tool to convert that to a viscous fluid with temperature dependent viscosity and set its temperature to 0 in the Flip Fluid node.

To get the planet orbiting the central "star", I removed the default gravity from the DOP network and added a POP Axis Force. Rather that using the axis force's suction, I added my own POP Wrangle to add radial gravity with the following VEX:
float reversalDistance = 1; float mass = 410; vector origin = {0, 0, 0};
float dist = max(distance(v@P, origin), 0.001); 
float gravityStrength = mass / pow(dist, 2.0);
vector gravity = (origin - v@P) * gravityStrength; 
@force += gravity;
To get the planet melting, I added a spherical heating volume, but rather than using the default "target" merge method, I used "add" to increase the heat incrementally.

Finally, for the first time, I used Houdini's compositor to add some post-effects: a glow and an additive comp of a Sobel edge detection version of the original:



Reaction Diffusion in SideFX Houdini

$
0
0

This may sound a tad geeky, but reaction diffusion is one of my favorite things. I've been tinkering with different implementations for four years - almost to the day - so, it felt like high-time to have a go at a SideFX Houdini version.

My entire reaction diffusion solver lives inside a Geometry node:




  1. The system starts off with a circle - 7m in radius.
  2. The circle is converted to points...
  3. ...and each point given a random color.
  4. The reaction diffusion model uses an 64-bit vector named chemical for its components.            
  5. This wrangle node contains VEX to make the point cloud to be mainly red (the x chemical component) surrounding a blue (the z chemical component) square.
  6. The solver contains the reaction diffusion VEX code.
  7. I scale the points according to their concentration of the z chemical component with some simple VEX: @pscale = smooth(0.4, 1, v@chemical.z);
  8. The colored points will be converted to metaballs...
  9. ...with a copy.
  10. Finally, an attribute copy node copies the point colors to the metaballs. 
The VEX for drawing the central square (5) is:
float radius = 1;
if(@P.x > -radius && @P.x < radius && @P.z > -radius && @P.z < radius) {
    @Cd.z *= 2.0;
    @Cd.x *= 0.5;
}
else {
    @Cd.x *= 5;
    @Cd.z = 0;
}
v@chemical.x = @Cd.x;
v@chemical.z = @Cd.z; 
The reaction diffusion code inside the solver (6) is a tad more complex. It's based on the Gray-Scott model:


I went a little leftfield with the Laplacian calculation - rather than using the standard five point stencil Laplace operator, I sample and sum the twelve nearest points (including the current) and subtract the current value multiplied by twelve. This seemed to work better with the tetrahedral point configuration I used in the Points from Volume.

I also tweaked the second component delta - rather than (F + k)v, my code is just kv.

After watching this video from Fifty50, I exchanged pcimport for nearpoints. I'm not sure if there's any advantage to either approach, but the latter seemed a little simpler.

Anyway, after all of that, my Gray Scott VEX is:

vector laplacian = {0, 0, 0}; 
int count = 12;  
int neighbours[] = nearpoints(geoself(), v@P, 1, count); 
int point; 
foreach(point; neighbours) {
    laplacian += point(geoself(), "chemical", point);  
} 
laplacian -= v@chemical * count; 

float dU = 0.25; 
float dV = 0.06; 
float k = 0.18; 
float f = 0.07;
float timeStep = 0.25; 
float max = 1; 

float u = clamp(v@chemical.x, 0, max);
float v = clamp(v@chemical.z, 0, max);

float reactionRate = u * v * v;
float deltaU = (dU * clamp(laplacian.x, 0, 1)) - reactionRate + f *(max - u); 
float deltaV = (dV * clamp(laplacian.y, 0, 1)) + reactionRate - k * v;

float newU = clamp(u + deltaU * timeStep, 0, max); 
float newV = clamp(v + deltaV * timeStep, 0, max);

@chemical = set(newU, newV, newV); 
@Cd = set(@chemical.x, 0, @chemical.z);  
@density = @Cd.b; 

There is a huge universe of amazing reaction diffusion patterns. If you'd like to explore more, I'd suggest taking a look at Ready. If you look carefully, you may even find a contribution from me in there!

Houdini FLIP Fluid & Radial Gravity

$
0
0

Here's an animation of a rocky planetoid with a liquid core that ejects a stream of water in an aquatic volcano. The planetoid's gravity pulls the water back which settles into streams and pools.

The animation was pretty simple to set up and relies on my radial gravity VEX inside a POP Wrangle node:

float mass = 430;  
vector origin = {0, 0, 0}; 
float dist = max(distance(v@P, origin), 0.001);  
float gravityStrength = mass / pow(dist, 2.0); 
vector gravity = (origin - v@P) * gravityStrength;  
@force += gravity;

The planet is simply a sphere with a Mountain deform, converted to a rigid body. The watery volcano is a squashed sphere with an Emit Particle Fluid shelf tool. I gave the Fluid Source an initial velocity to shoot it upwards and changed the activation in the Source Volume to the expression $F > 3 && $F < 225.

The only tricky thing was waiting for the render - with a particle separation of 0.05, the video above took about twelve hours!

Animating Gravitational Tides with Houdini FLIP Fluids

$
0
0

This video contains three treatments of a gravitational tides project using Houdini FLIP fluids.

The project uses two radial gravity POP Wrangle nodes using identical VEX (which I discussed in my last blog post, Houdini FLIP Fluid and Radial Gravity) apart from the definition of the origin attribute. The orbiting satellite geometry, unsurprisingly named satellite, drives the origin for one of the radial gravity nodes:
float x = ch("../../satellite/tx"); 
float y = ch("../../satellite/ty"); 
float z = ch("../../satellite/tz");
The source for the fluid surrounding the main planet is simply a small sphere copied and scattered over a larger sphere that's approximately the same size as the Mountain deformed sphere that forms the main planet's geometry. 

Both the main planet and the satellite are rigid bodies so that the fluid flows around them. So, with the two gravity nodes, the fluid, and the rigid bodies, my DOP Network looks like:


The satellite's orbit decays in the second and third clips. This is done with some simple trigonometry with the radius of the orbit based on the current frame number:


Easy! My main planet only has a radius of 2.7 meters, so I've dialed down the scale time on the DOP Network to 0.1. Giving it a start frame of -50 ensures the fluid has nicely settled around the little mountains on the main planet.

Mixing Fluids in Houdini

$
0
0


I suspect that mixing two or more disparate fluids is one of the first challenges Houdini newbies, like myself, set themselves. I blogged about an approach recently in Stripy Viscous Fluid Impacts in Houdini- I created a single point cloud to act as the fluid's initial location, with different colors depending on the position of each point.

The slight problem with this approach is that because the fluid has a single surface, Houdini doesn't render any interior detail showing how different fluids interact. To simulate the effect in the video above, where drops of a blue liquid fall into a body of a red liquid, my new approach creates two separate fluid surfaces and that missing interior detail is visible.

As well as looking better, this technique allows for totally different materials on each fluid. You could, for example, animate pouring mercury or oil into water.

Creating the liquid geometry source

The first step is to create the point cloud that is used to define the initial location of the fluid - the body of the liquid and the three drops. This is a geometry node with a tube for the liquid body and three spheres for the drops:



There are some important extra steps:


  • The two wrangle nodes set the density: my drips are more dense than the main liquid body, so I explicitly set the density of each in VEX with @density = 4; for the drips and 1 for the body.
  • The two group nodes assigns groups to the different liquids, DRIP and BODY, these will be used later when creating the surfaces.
  • The Point Separation in both Scatter nodes is set to 0.2 which will match the Particle Separation in the FLIP Object.
With the geometry in place, I used the FLIP Fluid from Object shelf tool to create a fluid from the geometry.

Building the glass

The drinking glass was created from two tubes with slightly different radii subtracted from each other with a Cookie node. A third, shorter tube acts as the base:




Containing the fluid

After building the glass, I went ahead and converted it to a rigid body hoping it would contain the fluid. However, no amount of fiddling would make its concave shape work nicely. 

The solution was to create a rigid body from another tube (with end caps) and check "invert sign" in the static object node inside the DOP Network. 

Creating Multiple Fluid Surfaces

Now for the magic. The FLIP Fluid from Object shelf too creates a network to render the fluid surface. Using a Blast node (added just before the Fluid Compress), I delete all the points from one group (i.e. check Delete Non Selected and set the Group Type to points), then copy the entire network and change the group name in the Blast node to the other group. 

These two fluid surface networks can be assigned their own materials. In my project, I simply copied the basic liquid and changed the colors so that I have red and blue liquids.

The Object Merge node in the fluid interior network allows for multiple objects and I simply add the copied render node to that.

Gotchas

All well and good. However, since I slowed time down quite a lot, my fluid (especially the drips) was quite jittery. My first attempt to resolve this was to add smoothing to the Particle Fluid Surface. This went some way to helping, but fluid blobs would magically appear and vanish depending on smoothing.

My solution is to increase the fluid substeps, up the Velocity Smoothing in FLIP Solver to 1, and check Limit Refinement in the Particle Fluid Nodes.

@cgcris_com has also suggested turning off Rebuild SDF in the Particle Fluid Surface node. The video above hasn't implemented this suggestion, but I'll try that soon.

Faking Toroidal Eddies in Side FX Houdini

$
0
0

Following on from my recent blog post, Mixing Fluids in Houdini, I wanted to simulate a toroidal eddy effect where the incoming drip takes the form of a torus and the fluid flows around the circumference of its minor radius. 

My first thought was to use a POP Axis Force, but that rotates particles around the circumference of the major radius. So, I took another approach: create lots of curves placed around a circle and use those as the geometry source for a POP Curve Force.

To create the geometry, I simply created two circles and copied the smaller one to the points of the larger one:



The PolyFrame nodes ensures the smaller circles radially align by setting the normal and tangent names:



Making the smaller circle an arc rather than a full circle and setting both circle's type to open arc, the resulting geometry looks like a fruit bowl:




I used the same technique I discussed in Mixing Fluids in Houdini to split the fluid into two groups. Over in the DOP Network, the POP Curve Force was set to use the bowl shape of curves and assigned to the drip group. This means that only the drip is affected by the curves and the body of the fluid remains static.

Here's the final render, along with some other fluid mixing experiments:







Animating Rayleigh-Taylor Instability in SideFX Houdini

$
0
0

The Rayleigh-Taylor instability is the instability between two fluids of different densities. It can appear as "fingers" of a denser liquid dropping into a less dense liquid or as a mushroom cloud in an explosion.

The phenomenon "comes for free" in SideFX Houdini FLIP Fluids. By simply creating a box, converting that to a FLIP fluid object and using groups to assign a lower density to the particles in the top half, it's easy to create an effect like this:



However, I wondered what would happen if rather than using the default linear gravity, I created concentric spheres of fluids of different densities and made the fluids subject to a radial gravity force (see Houdini FLIP Fluid and Radial Gravity).

Set up was pretty simple: for the fluid source, I created two spheres (an inner and outer). A Cookie node subtracts the inner from the outer so that I have a proper core and shell. Both of those geometries are converted to points and assigned to groups. Attribute Wrangle nodes define the densities - in my case the inner points have a density of 1 and the outer points have a density of 1.25.


I added a Blast node to the automatically generated fluid surface geometry node (so that only the inner fluid is rendered) and my radial gravity code in a POP Wrangle in the DOP Network. I tweaked the VEX a little so that it would consider density:
float mass = 100;
vector origin = {0, 0, 0};
float dist = max(distance(v@P, origin), 0.001);
float gravityStrength = (mass + @density) / pow(dist, 2.0);
vector gravity = (origin - v@P) * gravityStrength;
@force += gravity;
The final result, shown in the video above, is a fluid expanding into an invisible surrounding liquid of a greater density. Quite a nice effect with very little work! 



Houdini Grain Solver with Custom VEX Forces

$
0
0

This video contains five clips using SideFX Houdini's Grain Solver with an attached POP Wrangle that uses VEX to generate custom forces. Here's a quick rundown of the VEX I used for each clip (please forgive the use of a variable named oomph).

Clip One "Twin Peaks"

Here, I compare each grain's current angle to the scene's origin to the current time. If the grain's angle is within a certain distance of the current time, I calculate a vertical force based on the Gaussian function of the distance of the particle from the origin The sigma of the Gaussian function is based on the sine of the time to give a rotating and pulsating force.
float e = 2.7182818284590452353602874713527;  
float tau = $PI * 2; float width = 0.15; 
vector force = v@force;  
float particleAngle = atan2(v@P.x, v@P.z); 
float timeAngle = ($PI + @Time); 

float angleDist = abs(particleAngle - timeAngle) % tau;  

float oomph = 30;        
float angleDist2 = abs(particleAngle - timeAngle - $PI) % tau; 
if (angleDist < width || angleDist2 < width) {
    float dist =  clamp(length(@P) - 1.0, -1.0, 1.0);     
    float variance = pow(0.125 + abs(sin(@Time * 2.25)) * 0.35, 2.0);     
    float gaus = (1.0 / sqrt(tau * variance)) * pow(e, -pow(dist, 2) / (2 * variance));        
    oomph *= gaus;         
    @force = set(force.x, force.y + oomph, force.z); 
}

Clip Two: "Three Peaks"

This VEX is not dissimilar to the previous one, but rather than calculating the difference between each grain's angle and the current time, the force is based on the sine of the angle added to the current time. 

The final render is a top down view with a little depth-of-field added.
float e = 2.7182818284590452353602874713527;  
float tau = $PI * 2;  
vector force = v@force;  
float particleAngle = (atan2(v@P.x, v@P.z) * 3) + (@Time * 10); 
float oomph = 20 * max(0, sin(particleAngle));  
float dist =  clamp(length(@P) - 1.0, -1.0, 1.0);  
float variance = pow(0.125 + abs(sin(@Time * 1.5)) * 0.35, 2.0);  
float gaus = (1.0 / sqrt(tau * variance)) * pow(e, -pow(dist, 2) / (2 * variance));  
oomph *= gaus;  
@force = set(force.x, force.y + oomph, force.z);

Clip Three: "Rotating Ridges"

The upwards force is a function of each grain's angle to the scene's origin multiplied by the sine of its distance to the origin:


float tau = $PI * 2; 
float width = 0.3;  
vector force = v@force;     
float particleAngle = atan2(v@P.x, v@P.z); 
float timeAngle = ($PI + @Time);   
float dist = abs(particleAngle - timeAngle) % tau;   
if (dist < width) { 
    float oomph = 25;     
    oomph *= abs(sin((@Time * -2) + length(@P * 6)));
    @force = set(force.x, force.y + oomph, force.z); 
}

Clip Four: "Ripples 1"

The upwards force is based on the difference between each grain's distance from the scene's origin and the current time multiplied by some curlnoise. The force is also tempered by each grain's vertical position so that it decreases with height.
vector force = v@force;    
float oomph = 0;     
int dist = int(length(@P) * 20); 
int time = int(@Frame % 40);      
if (dist == time) {    
    oomph = 90; 
}     
oomph *= (1 - clamp(v@P.y, 0.0, 1.0));     
vector4 noiseSource = set(v@P.x * 0.5, v@P.y * 0.5, 
v@P.z * 0.5, @Time * 5);  
vector noise = curlnoise(noiseSource);  
oomph *= 1.0 + (0.25 * noise.x);      
@force = set(force.x, force.y + oomph, force.z);

Clip Five: "Ripples 1"

The upwards force is based on the sine of the distance of each grain from the scene's origin added to the current time.


float phase = length(@P)  * 5.0; 
phase = max(0.0, sin(phase + (@Time * -3.0)));    
float amplitude = 15 * (1 - clamp(v@P.y, 0.0, 1.0)) * (2.5 - clamp(length(@P), 0.0, 2.5));  
vector force = v@force;       
@force = set(force.x, force.y + (amplitude * phase), force.z);


Parametric Fibonacci Spheres in Houdini

$
0
0

Fibonacci spheres are created from a point set that follows a spiral path to form a sphere (you can see an example, with code at OpenProcessing).

For the video above, I used the approach described in the Entagma Strange Attractors tutorial, but changed the VEX in my solver to:
if (inpointgroup(geoself(), "active", @ptnum)) { 
    float samples = 500; 
    float offset = 2.0 / samples; 
    float increment = $PI * (3.0 - sqrt(2.0)); 
    float i = @ptnum; 
    float y = ((i * offset) - 1) + (offset / 5); 
    float r = sqrt(1 - pow(y,2)); 
    float phi = (i % samples) * increment; 
    float x = cos(phi) * r * 1.5; 
    float z = sin(phi) * r * 1.5; 
     
    vector p = set(x, y, z);  
    int newPoint = addpoint(geoself(), p);  
    if (newPoint <= samples) { 
        setpointgroup(geoself(), "active", newPoint, 1); 
    } 
    setpointgroup(geoself(), "active", @ptnum, 0);  
    int previous = @ptnum - 4; // second clip is simply `@ptnum`  
    if(previous > 0) { 
        int polyline = addprim(geoself(), "polyline"); 
        addvertex(geoself(), polyline, previous); 
        addvertex(geoself(), polyline, newPoint);  
    }     
} 
This code (the core algorithm is borrowed from this StackOverflow answer) yields a complete sphere over 500 frames.

To jazz things up a little, the parametric geometry lives inside a spherical volume with a geometry area light in its centre - this gives a nice fog that receives shadows.

The first clip was raytraced, which gives lovely results but takes an age. The second clip is micropolygon rendered, which is much faster but not half as pretty.

Faux Grain / Fluid Interaction in Houdini

$
0
0

This post describes a simple way to create a system comprising of a regularly surfaced fluid and a faux grain system. The video above contains three clips using the same basic technique: creating a single point source for the FLIP SOP initial data but using groups to render some as a fluid and some as individual tiny spheres - the grains. 

The first clip shows a granular sphere dropping into a fluid tank. The initial fluid tank geometry network looks like this:



I added a blast node to the fluid surface node created by the fluid tank shelf tool to remove all the particles from the DRIP group. Then created another node to render the DRIP group particles as faux grains:



Not bad for a first attempt - the granular sphere splashes into the fluid tank and its grains are advected by the fluid. 

The second clip flips the effect round: a fluid sphere falls into a torus built from hundreds-and-thousands (or nonpareils for those across the pond). The fundaments are the same, but I use an attribute wrangle in the source geometry node to give the sugary donut a high viscosity to retain its shape.

To get the grains to dissolve (maybe disintegrate is a better word), I diffuse the viscosity. This is done with a POP wrangle node attached to the FLIP solver with the following VEX:


int pointCloud = pcopen(geoself(), 'P', @P, 0.3, 48); 
float newViscosity = pcfilter(pointCloud, 'viscosity'); 
if (newViscosity < @viscosity) {
    @viscosity =  newViscosity;
}
The VEX averages the viscosity of 48 nearby particles and, if less than the current particle's viscosity, updates the current viscosity with that average. Only updating the viscosity with a smaller value ensure the fluid doesn't congeal.

The third and final clip is another experiment dropping a granular body into a liquid. Here, I wanted the granular geometry to not only disintegrate but its component grains to separate.  As part of my debugging process, I was updating the point color based on viscosity - since it seemed like a nice effect, I kept that in too.

The final VEX for my POP wrangle is:
if (inpointgroup(geoself(), "PILL", @ptnum)) {

    // Diffuse viscosity...
   
    int pointCloud = pcopen(geoself(), 'P', @P, 0.4, 36);
   
    float newViscosity = pcfilter(pointCloud, 'viscosity');
   
    if (newViscosity < @viscosity) {
        @viscosity =  newViscosity;
    }
   
    @Cd = set((@viscosity / 3.0), 
              1 - (@viscosity / 3.0), 
              1 - (@viscosity / 3.0));
   
    // Separate low viscosity particles...
   
    float viscosityLimit = 2.0;
   
    if (@viscosity < viscosityLimit) {
       
        float maxdist = 0.1;
        int maxpts = 36;
        float seperationStrength = (viscosityLimit - @viscosity) * -50;
   
        int neighbours[] = nearpoints(geoself(), v@P, maxdist, maxpts);
        int point;
       
        foreach(point; neighbours) {
            if (inpointgroup(geoself(), "PILL", point)) {
                @force += (point(geoself(), "P", point) - @P) * seperationStrength;
            }
        }
    }
   
}
As I'd been tinkering with this technique for a while, I decided to speed things up in the third clip by not rendering the fluid - but actually, I think the final result is quite nice just with the spheres.

A few caveats: the prevent mysteriously appearing grains, the birth threshold under particle motion / reseeding in my FLIP solver is zero. Also, the POP wrangle nodes attached to the FLIP solver have their first input set to myself


Revisiting Mitosis in SideFX Houdini

$
0
0

I played with animating mitosis in Houdini last year (see Simulating Mitosis in Houdini), but the math wasn't quite right, so I thought I'd revisit my VEX to see if it could be improved. After some tinkering, the video above shows my latest (hopefully improved) results.

The basic project remains the same: 




  • A point generate geometry node creates a single source point
  • An attribute wrangle creates three new attributes for the point used for the mitosis animation:
    • float @age
    • float @numChildren
    • vector @velocity
  • A jitter node which is useful if the generate node is used to create more than one point
  • A solver node to iterate over the geometry and is where the magic happens
The VEX inside the solver is where the point separation and generation happens:


int pointCloud = pcopen(0, "P", @P, 3 , 50);
vector localCentre = pcfilter(pointCloud, "P");
 
vector direction = normalize(v@P - localCentre); 
float r = distance(v@P, localCentre);
if(r < 1) {
    r = 2 - (r * 2);
}
 
v@velocity += (direction / (r * r)) * 0.1; 
if (@age > 1 && @numChildren < 3) {
    vector randOffset;

    randOffset.x = -0.01 + (noise(localCentre.x + @Time + @age) * 0.02);
    randOffset.y = -0.01 + (noise(localCentre.y + @Time + @age) * 0.02);
    randOffset.z = -0.01 + (noise(localCentre.z + @Time + @age) * 0.02);

    @age = 0;
    @numChildren += 1;
 
    int newPoint = addpoint(geoself(), @P + randOffset);
}
else {
    @age += abs((random($FF * @ptnum)) * 0.05);
}

@P += @velocity;
v@velocity *= 0.5;

As in my previous version, I find the average position of each point's neighbors. However, this time, I create a direction vector that is normalized (i.e. always has a length of 1) and a separate float, r, which is the distance of each point to the local average position. This approach allows me to fake the distance for points close to the local center (so they don't fly off into infinity) and get a bit Newtonian with the math used to calculate the velocity (i.e. by dividing by r²).

The mitosing point cloud is used to generate two VDB based polygon surfaces, the larger of which is smoothed to give the nice metaball effect as the points move apart:


Finally, I gave the outer surface a thin film refracting material, added some depth-of-field, and, voilà, little mitosing blobs! 





Particle Advection by Reaction Diffusion in SideFX Houdini

$
0
0

After watching this excellent tutorial that discusses advecting particles by magnetic fields to create an animation of the sun, I was inspired to use the same technique to advect particles by fields that are a function of reaction diffusion systems

The source for my reaction diffusion vector field is a geometry node. This field this geometry node outputs defines the concentrations of the component chemical-species that will later be used to advect the particles:



The VDB node in this network defines the name of the field, I've used rdField, and the type: a Vector Float. The Volume Wrangle defines the initial state: I want a central cube where the  field's z is high surrounded by an area where the x is high:
if (@P.x > -0.1 && @P.x < 0.1 &&
    @P.y > -0.1 && @P.y < 0.1 &&
    @P.z > -0.1 && @P.z < 0.1) {
 
        v@foo.x *= 0.1;
        v@foo.z += rand(@P * 11) * 5;
}
else {
        v@foo.z *= 0;
        v@foo.x += rand(@P * 11) * 5;
}
Inside the solver, I iterate over the volume, applying the VEX to create the reaction diffusion simulation. I've done this many times in two dimensions and adding a third dimension is easy for VDBs in Houdini:
float delta = volumevoxeldiameter(0, "rdField") / sqrt(3);  
vector laplacian = set(0, 0, 0);
laplacian += volumesamplev(0, "
rdField", v@P + set( delta,  0,  0));
laplacian += volumesamplev(0, "
rdField", v@P + set( -delta, 0,  0));
laplacian += volumesamplev(0, "
rdField", v@P + set( 0,  delta,  0));
laplacian += volumesamplev(0, "
rdField", v@P + set( 0, -delta,  0));
laplacian += volumesamplev(0, "
rdField", v@P + set( 0,  0,  delta));
laplacian += volumesamplev(0, "
rdField", v@P + set( 0,  0, -delta));
laplacian -= (@
rdField * 6.0);  
float dU = 0.189;
float dV = 0.03;
float k = 0.082;
float f = 0.0425;
float timeStep = 0.75; 
 
float u = v@foo.x;
float v = v@foo.z;
 
float reactionRate = u * v * v;
float deltaU = (dU * laplacian.x) - reactionRate + f * (1 - u);
float deltaV = (dV * laplacian.z) + reactionRate - (f + k) * v;
 
float newU = clamp(u + deltaU * timeStep, 0, 1);
float newV = clamp(v + deltaV * timeStep, 0, 1);
 
v@rdField = set(newU, newU, newV); 
The Laplacian is generated by sampling the neighboring voxels at each side of the current voxel. The VEX functions volumevoxeldiameter and volumesamplev make this pretty simple. Dividing the former by the square root of 3 gives the voxel side length.

To assist during development, I added a volume slice. Sure enough, after a handful of iterations, my little box in the centre of the volume has now "evolved" based on the Gray-Scott model:



The next step is to use the shelf tools to create a particle system and a DOP Network:



Houdini does have a POP Advect by Volumes node, but that expects volumes where the vector fields define velocity. My vector field defines values to which I want particles advected towards. So, the advecting happens inside a POP Wrangle node with the Input 2 set to the output of the geometry node discussed above. The VEX in this node looks at the surrounding voxels and sets the force based on the difference of opposite neighboring voxels:
float mult = 10; 
float delta = volumevoxeldiameter(1, "rdField") / sqrt(3); 

v@force.x += volumesamplev(1, "rdField", v@P + set( delta,  0,  0)).b * mult;
v@force.x -= volumesamplev(1, "rdField", v@P + set( -delta, 0,  0)).b * mult;

v@force.y += volumesamplev(1, "rdField", v@P + set( 0,  delta,  0)).b * mult;
v@force.y -= volumesamplev(1, "rdField", v@P + set( 0, -delta,  0)).b * mult;

v@force.z += volumesamplev(1, "rdField", v@P + set( 0,  0,  delta)).b * mult;
v@force.z -= volumesamplev(1, "rdField", v@P + set( 0,  0, -delta)).b * mult;
A few additional nodes keep the particles within the bounding box and add a speed limit and some drag.

The second clip takes things a little further. First of all, the Gray-Scott parameters are slightly tweaked to yield a solitons rather than a coral effect:
float dU = 0.2097;
float dV = 0.080;
float k = 0.062;
float f = 0.025;
float timeStep = 0.5; 
I then extended the POP Wrangle in the DOP Network to add an additional force: not only are the particles advected by the relative nearby chemical strengths in the volume, they are also advected towards other nearby particles. The Attributes from Volume node copies the rdField into the particle's Cd and the VEX uses that:
float mult = 10;
float delta = volumevoxeldiameter(1, "
rdField") / sqrt(3);
v@force.x += volumesamplev(1, "
rdField", v@P + set( delta,  0,  0)).b * mult;
v@force.x -= volumesamplev(1, "
rdField", v@P + set( -delta, 0,  0)).b * mult;
v@force.y += volumesamplev(1, "
rdField", v@P + set( 0,  delta,  0)).b * mult;
v@force.y -= volumesamplev(1, "
rdField", v@P + set( 0, -delta,  0)).b * mult;
v@force.z += volumesamplev(1, "
rdField", v@P + set( 0,  0,  delta)).b * mult;
v@force.z -= volumesamplev(1, "
rdField", v@P + set( 0,  0, -delta)).b * mult; 
int point;
int count = 48;
int neighbours[] = nearpoints(0, v@P, 2, count); 
 
foreach(point; neighbours) {
    vector otherColor = point(0, "Cd", point);
    vector otherPosition = point(0, "P", point);
    float dist = distance(@P, otherPosition);  
    vector direction = normalize(otherPosition - @P);
 
    if (dist < 0.1) {
        dist = 0.5;
        direction = -direction;
    }
 
    v@force += 0.04 * direction * (1 / pow(dist,2)) * otherColor.b;
The particles are rendered as little strands by giving them trails that feed into a Wireframe node:

The final clip in the video above is based on the Belousov-Zhabotinsky Reaction, so the solve VEX is rather different:
float alpha = 1.3;
float beta = 1.55; //1.55;
float gamma = 1.65;
float delta = volumevoxeldiameter(0, "rdField") / sqrt(3);
vector accumulator = set(0, 0, 0); 
 
// sides...
accumulator += volumesamplev(0, "rdField", v@P + set( delta,  0,  0));
accumulator += volumesamplev(0, "rdField", v@P + set( -delta, 0,  0));
accumulator += volumesamplev(0, "rdField", v@P + set( 0,  delta,  0));
accumulator += volumesamplev(0, "rdField", v@P + set( 0, -delta,  0));
accumulator += volumesamplev(0, "rdField", v@P + set( 0,  0,  delta));
accumulator += volumesamplev(0, "rdField", v@P + set( 0,  0, -delta));
// top corners...
accumulator += volumesamplev(0, "rdField", v@P + set(  delta, -delta,  delta));
accumulator += volumesamplev(0, "rdField", v@P + set( -delta, -delta,  delta));
accumulator += volumesamplev(0, "rdField", v@P + set(  delta, -delta, -delta));
accumulator += volumesamplev(0, "rdField", v@P + set( -delta, -delta, -delta));
// bottom corners...
accumulator += volumesamplev(0, "rdField", v@P + set(  delta, delta,  delta));
accumulator += volumesamplev(0, "rdField", v@P + set( -delta, delta,  delta));
accumulator += volumesamplev(0, "rdField", v@P + set(  delta, delta, -delta));
accumulator += volumesamplev(0, "rdField", v@P + set( -delta, delta, -delta));
accumulator += @rdField;
vector average = ((@rdField * 2) + (accumulator / 15)) / 3;
 
float a = average.x + average.x * (alpha * gamma * average.y) - average.z;
float b = average.y + average.y * ((beta * average.z) - (alpha * average.x));
float c = average.z + average.z * ((gamma * average.x) - (beta * average.y)); 
 
a = clamp(a, 0, 1);
b = clamp(b, 0, 1);
c = clamp(c, 0, 1);
 
v@rdField = set(a, b, c);
v@rdField.r += (rand(@P * @Time) * 0.05) - 0.025;
v@rdField.g += (rand(13 * @P * @Time) * 0.05) - 0.025;
v@rdField.b += (rand(17 * @P * @Time) * 0.05) - 0.025; 
In this example, the positions of the trails are smoothed out to get rid of any sharp angles:











Particle Advection by Gray Scott Reaction Diffusion Revisited

$
0
0

This post continues from my recent blog entry, Particle Advection by Reaction Diffusion in SideFX Houdini. In this project, I've done away with the VEX in the DOP network, and replaced it with a VDB Analysis node to create a vector field that represents the gradient in the reaction diffusion volume. This allows me to use a POP Advect by Volumes node in the DOP network rather than hand coding by own force wrangle.

The network that creates the advecting force is:



The middle network box, Reaction Diffusion Field, is pretty much identical to the Gray Scott reaction diffusion solver from my previous article. However, this time the v chemical species is used to define the density of another VDB in the Reaction Diffusion Geometry network box, on the right. After converting that VDB to polygons, I get a nice geometry representation of my reaction diffusion system:



This solver isn't super fast, so rather than recalculating it every time I run the simulation, it's saved to a file using a File Cache node. 

The Gradient Field network box creates a vector field from the cached geometry. Here, the geometry is converted to a fog VDB and that, in turn, is converted to a vector field using a VDB Analysis gradient operator. I played around by adding intermediate nodes to resample and convert the VDB topology to SDF to get an effective field. 

I used the POP shelf tool to create a particle system and simply added a POP Advect by Volumes to advect the particles. 

Easy 🙂 




"Sparse Vermiform" Rendering of Fluids with Pressure Based Color

$
0
0

This blog post discusses a technique for rendering SideFX Houdini FLIP fluids as sparse fields of wormlike particles (hence my slightly over-the-top Sparse Vermiform moniker) with their color based on the fluid system's gas pressure field.

The project begins with an oblate spheroid that is converted to a FLIP Fluid using the FLIP Fluid from Object shelf tool. The fluid object sits within a box that's converted to a static body with its volume inverted to act as a container.

To introduce a wave-like motion to the fluid, I decided to animate gravity by adding an expression to the z channel of the default gravity node: sin($F * 2) * 3.333.

Now for the magic (that probably everybody knew apart from me!): delving into FLIP fluid object's FLIP Configure Object, you can find a series of vector and scalar field nodes for different fields such as velocity, mass density, divergence and pressure. Passing the gas pressure field to each particle is easy as adding a Gas Field to Particle node to the DOP network and plugging it into the FLIP Solver:


I set both the attribute and source field to pressure - now, each particle now has an @pressure attribute that corresponds to the local gas pressure in the FLIP fluid. Although I'm using this attribute to set the particle color, it could be used for many other effects, such as making viscosity or temperature a function of pressure.

Moving onto rendering, I trashed the automatically created nodes for creating a fluid surface and created my own geometry node containing:


The sparsity is introduced with a Delete node that culls points based on their @ptnum attribute using "delete by expression": (@ptnum % 75) != 0.

Now that each point has a @pressure, the Attribute Wrangle node uses that to generate an RGB color from a hue based on pressure and populate the @Cd attribute:

    float hue = smooth(0, 1, abs(sin(@pressure * 0.1)));
    @Cd = hsvtorgb(hue, 1, 1);

The Trail node with a "connect as polygons" result type plugged into a Wireframe node yields the little worms. 

Voila! 



Primordial Particle System in SideFX Houdini

$
0
0

It's been a fairly busy few months at my "proper" job, so my recreational Houdini tinkering has taken a bit of a back seat. However, when I saw my Swarm Chemistry hero, Hiroki Sayama tweeting a link to How a life-like system emerges from a simple particle motion law, I thought I'd dust off Houdini to see if I could implement this model in VEX.

The paper discusses a simple particle system, named Primordial Particle Systems (PPS), that leads to life-like structures through morphogenesis. Each particle in the system is defined by its position and heading and, with each step in the simulation, alters its heading based on the PPS rule and moves forward at a defined speed. The heading is updated based on the number of neighbors to the particle's left and right. 

The project set up is super simple: 



Inside a geometry node, I create a grid, and randomly scatter 19,000 points across it. An attribute wrangle node assigns a random value to @angle:


@angle = $PI * 2 * rand(@ptnum); 

The real magic happens inside another attribute wrangle inside the solver.

In a nutshell, my VEX code iterates over each point's neighbors and sums the neighbor count to its left and right. To figure out the chirality, I use some simple trigonometry to rotate the vector defined by the current particle and the neighbor by the current particle's angle, then calculate the angle of the rotated vector. 


while(pciterate(pointCloud)) {

    vector otherPosition;
    pcimport(pointCloud, "P", otherPosition);

    vector2 offsetPosition = set(otherPosition.x - @P.x, otherPosition.z - @P.z);
    float xx = offsetPosition.x * cos(-@angle) - offsetPosition.y * sin(-@angle);
    float yy = offsetPosition.x * sin(-@angle) + offsetPosition.y * cos(-@angle);
    
    float otherAngle = atan2(yy, xx); 

    if (otherAngle >= 0) {
        L++;
    } 
    else {
        R++;
    }   
}
After iterating over the nearby particles, I update the angle based on the PPS rule:
float N = float(L + R);
@angle += alpha + beta * N * sign(R - L);
...and, finally, I can update the particle's position based on its angle and speed:
vector velocity = set(cos(@angle) * @speed, 0.0, sin(@angle) * @speed);  
@P += velocity ;
Not quite finally, because to make things pretty, I update the color using the number of neighbors to control hue:
@Cd = hsvtorgb(N / maxParticles, 1.0, 1.0); 
Easy!

References

Schmickl, T. et al. How a life-like system emerges from a simple particle motion law. Sci. Rep. 6, 37969; doi: 10.1038/srep37969 (2016).


Viewing all 257 articles
Browse latest View live