Analytical Foam

comments 9
Featured Video Play Icon

As position based simulation techniques are very fast theses days, it is possible to use them to confrom intersecting spheres to each other, to model foam. Nevertheless there are other possibilities, too.

In this tutorial, Manuel shows you how to come up with an analytical operator that removes the intersections from a dense sphere packing by deforming the intersecting points. This is even faster than PBD and can be used without any simulation at all. The video deals with implementing the setup. The particle effect from the demo is included in the hip file though.

Paul Bourke’s site on the intersection of circles

Download the hip file here

Share on FacebookTweet about this on TwitterPin on PinterestEmail this to someoneShare on LinkedInShare on Google+


  1. harry says

    brilliant, i’ve not got the brains to do this myself but have been looking at sops waays for years! so easy when you are sitting next door! thanks

  2. Maury says

    Very enjoyable tutorial. Thanks very much for sharing your work!

    I think it would be interesting to extend this so that the bubbles’ centers are scattered not only on the ground plane, but in a volume. They could inflate as a function of their height as well, from bottom up. In this case, the intersection plane wouldn’t be perpendicular to the ground plane, so the transform in and out of “local space” would be a little more involved.

    (and also, to cut off the bottoms of all bubbles, with their intersections with the ground plane)

    Again, a great tutorial, a good build on your last one, too. Thanks!

    • Manuel says

      Hi Maury. That is supported. Just try it. The calculations are not restricted to a plane. Itβ€˜ll work in a volume, too. Cheers mnu

      • Maury says

        You’re right, you can fill VDBs with foam!

        I noticed that some intersections were getting small “collars”, if sectdist was negative. This occurs when a smaller sphere’s center is within it’s larger neighbor’s radius. (You have to look pretty closely to see this, it’s usually quite a small thing, though occasionally it makes largeish “disk” shapes)

        Here is one possible way to solve this (very small) issue:

        In the “gather data” wrangle:

        int neighbours[] = nearpoints(0,@P,maxdist*2);
        pop(neighbours,0); //eliminate current point from array
        int intersecting=0;
        float sectdist[];
        matrix xform[];
        //**** new variable:
        float intersection_radius[];
        foreach(int i; int neighbour; neighbours)

        float n_pscale=point(0,”pscale”,neighbour);
        vector n_P=point(0,”P”,neighbour);
        vector between = n_P-@P;
        float dist = length(between);
        float clear = n_pscale+@pscale;

        if (dist<clear)
        float mysectdist = (pow(@pscale,2) – pow(n_pscale,2)+pow(dist,2))/(2*dist);
        append(sectdist, mysectdist);

        // calculate intersection radius, to remove "collars"
        //myintersection_radius corresponds to "h" on the drawing from paul bourke's website:
        //h=sqrt(r^2-a^2) where r is pscale, a is mysectdist
        float myintersection_radius = pow( pow(@pscale,2)-pow(mysectdist,2),0.5);

        vector zaxis = normalize(between);
        vector tmp= normalize(cross(zaxis,{0,1,0}));
        vector yaxis=cross(zaxis,tmp);
        vector xaxis=cross(zaxis,yaxis);

        matrix myxform=set(xaxis, yaxis, zaxis, @P);
        myxform.xa = 0;
        myxform.ya = 0; = 0;

        f[]@sectdist = sectdist;
        4[]@xform = xform;
        //******* new attribute:
        f[]@intersection_radius = intersection_radius;


        And in the "deform" wrangle:


        float sectdists[]=point(1,"sectdist",@id);
        matrix xforms[] = point(1,"xform",@id);
        //************ new var:
        float intersection_radii[]=point(1,"intersection_radius",@id);

        foreach (int i; matrix xform; xforms)
        //*********optionally highlight collar problem instances
        // there’s an intersection
        //local_P=set(local_P.x,local_P.y, sectdists[i]);
        //************************ new code:
        if (sectdists[i]intersection_radii[i]) //this is a “collar” point, as it extends past the intersection radius
        local_P*=(intersection_radii[i]/dist); // scale this point back to the intersection radius
        local_P.z=sectdists[i]; //move this back to the intersection plane.

        //********* if you’d like a flattened bottom, like a ground plane:


        • Manuel says

          thanx for this addition! Now it’s even more flexible!

  3. Kevin says

    Another great one! However, those of us who get an anxious feeling when we see the typo that the instructor has made and the feeling doesn’t go away until they correct it… minute 29 to 30 was a minor form of torture πŸ™‚

    • Manuel says

      I know exactly what you mean. I feel the same when someone does this and I apologize for having so many typos in the code πŸ™‚

      • Kevin says

        Hah, no need to apologize. It was a fun and funny part of the tutorial. To be honest, it might have actually cured me of “typo anxiety” as I believe that minute desensitized me to it :p

  4. Edis says


    Thank you for a incredible yet simple lecture. Can you maybe recommened me some books with which I can start exploring the backgroung behing your process? I have good understanding of basic geometrie, vextors and medium level mathematics.


Leave a Reply

Your email address will not be published. Required fields are marked *