Particle Collision Post Processing  
Houdini Displacement Shader and VEX / March, 2012

When we make particles slide on a object surface, sometimes it’s little hard to make particle position exactly onto a surface because we usually use a low-res model for collision instead of a high-res rendering model. It gets even worse when we use displacement shader for a collision object. There could be several solutions. The simplest way might be just making the collision object little bigger, which works in most cases, but I needed little more accuracy, so I thought about making my own solution.

The object that I got from modeling department had pretty heavy Renderman displacement shader on it and also gets subdivided at render time, so even if I use the final high-res model for particle collision, the particles went through the surface. I was suggested to use the same texture that was used for the displacement shader on the collision object for the particles with transferred UV, which I thought great idea.

However, I wasn’t sure how much amount the displacement shader moved the surface points and could not guaranty there is no procedural touch on top of the displacement texture in the Renderman displacement shader. I thought it might be better to use the final surface position in rendering as reference position, so I don’t need to worry about whether the displacement is from texture or fully procedural, and also I don’t need to worry about using low-res object for collision whose UV coordination could be different from the original high-res object.

I ended up using Renderman point cloud in particle displacement shader for my shot, but here, I’m trying to re-implement it in Houdini and Mantra.

The idea is finding the closest point on normal vector from the displaced surface position and moving the particle point using displacement shader. Thanks to this forum thread. I changed the pseudo code to SL and it worked great. Here is Vex version.

// code from http://www.gamedev.net/topic/444154-closest-point-on-a-line
vector GetClosetPointOnVector (vector A; vector B; vector p; int segmentClamp;)
{
    vector AP = p - A;
    vector AB = B - A;
    float ab2 = AB.x*AB.x + AB.y*AB.y;
    float ap_ab = AP.x*AB.x + AP.y*AB.y;
    float t = ap_ab / ab2;
    if (segmentClamp != 0)
    {
         if (t < 0.0f) t = 0.0f;
         else if (t > 1.0) t = 1.0f;
    }
    vector Closest = A + AB * t;
    return Closest;
}

without displacement shader on particle

with displacement shader on particle

I created point cloud from the collision object with surface position and normal and read them in particle displacement shader as reference points, which worked fine in Renderman but had a problem in Mantra. Point particle in Houdini is rendered as a sphere by default, and displacement calculations are done in micro polygon level which is not only once at a particle point, so the sphere shapes are distorted, and the displacement shader doesn’t work if I render them as a points.

Good thing about Vex is that the same code works not only for shader but also for Sop, which can modify geometry itself. So, Here is the vex code for inline vex sop.

vector pc_push (string pcFile; vector p; int maxPoints; float maxDist; float pcSearchRadius; float offset)
{
        vector dir = 0;
        vector p_data = {0,0,0};
        vector n_data = {0,0,0};
        vector targetP = p;

        int handle = pcopen(pcFile, "P", p, pcSearchRadius, maxPoints);

        while(pciterate(handle))
        {
                vector tmpP;
                pcimport(handle, "P", tmpP);
                p_data += tmpP;

                vector tmpN;
                pcimport(handle, "N", tmpN);
                n_data += tmpN;
        }
        p_data /= maxPoints;
        n_data /= maxPoints;
        vector tmpDir = p_data - p;
        if (length(tmpDir) < maxDist)
        {
                dir = tmpDir;
                targetP = GetClosetPointOnVector(p, tmpDir, p_data, 0);
        }
        addattribute("dir", dir);

        float mixer =  length(dir) / maxDist;
        vector outP = lerp(targetP, p, mixer);
        if ( dot(normalize(n_data), normalize(tmpDir)) > 0)
        {
                outP = p_data;
        }
        outP += n_data * offset;
        return outP;
}

I actually had little hard time to get the Sop to read point cloud generated by Mantra. Mantra stored camera space positions into point cloud, but the Sop was trying to read object space P. It took me pretty much time to find what the problem was, but Houdini “File” sop helped visualize .pc file in viewport. I also checked if the particle is inside of surface by checking surface normal in point cloud and point normal, so I could move only the ones inside of the surface.


Powered by Wordpress, Theme by Hosuk