Chaos in Houdini! Modeling Strange Attractors with Particles
For those of you who are familiar with the Lorenz Attractor (Google ''strange attractors'') this fun dive into rendering chaos will be interesting.
Join the DZone community and get the full member experience.Join For Free
I recently posted Chaos in Swift! Visualizing the Lorenz Attractor with Metal, which was inspired by a Houdini tweet from Erwin Santacruz. It appears that Erwin's solution creates geometry, so I thought I'd look at this from a different perspective to see if I could create a particle based visualization of strange attractors in Houdini.
So, it turns out to be a lot simpler than I expected!
The secret sauce is the POP Wrangle node. This node runs a VEX expression against every particle and allows Houdini control over each particle's properties such as size, position, and velocity. The number of solves per-frame is controlled by the POP Solver's subsets attributes.
To give my attractors some variance, my projects use very small spheres as the particle source. Once Houdini had created a DOP Network, I've removed gravity, added the POP Wrangle and an instance geometry node for fine control over particle size:
The VEX inside the POP Wrangle nodes isn't a million miles away from my Metal code. The main difference is that for each invocation, it's getting and setting the x, y and z attributes of each particle's position (@P). So, the Hadley attractor POP Wrangle expression is:
float alpha = 0.2; float beta = 4.0; float zeta = 8; float d = 1.0; float deltaX = -@P.y * @P.y - @P.z * @P.z - alpha * @P.x + alpha * zeta; float deltaY = @P.x * @P.y - beta * @P.x * @P.z - @P.y * d; float deltaZ = beta * @P.x * @P.y + @P.x * @P.z - @P.z; @P.x += deltaX / 300.0; @P.y += deltaY / 300.0; @P.z += deltaZ / 300.0; ...and the Lorenz Mod 2 attractor POP Wrangle expression is: float a = 0.9; float b = 5; float c = 9.9; float d = 1; float deltaX = -a * @P.x + @P.y * @P.y - @P.z * @P.z + a * c; float deltaY = @P.x * (@P.y - b * @P.z) + d; float deltaZ = -@P.z + @P.x * (b * @P.y + @P.z); @P.x += deltaX / 1000.0; @P.y += deltaY / 1000.0; @P.z += deltaZ / 1000.0;
The final renders have some depth-of-field and motion-blur added with some nausea inducing camera moves (I'm no cinematographer!). Enjoy!
Published at DZone with permission of Simon Gladman, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.