[Sidefx-houdini-list] Hidden interior shader

Andy Nicholas andy at andynicholas.com
Sun Jan 20 15:39:29 EST 2008


Hi guys,

First off, apologies for such a long and involved post, but this has been
driving me nuts for a couple of days now.

I'm trying to figure out how to create a shader to render numerous
transparent objects made of liquid so that I don't have to worry when
surfaces intersect each other. I.e. that the shader automatically ignores
interior surfaces and only renders the first and last faces (which are the
ones that represent the boundary between air and water). So, for example, if
you have a small sphere that sits totally enclosed within a larger sphere,
then you won't see the smaller sphere at all. It's kind of like a boolean
union operation, but using a shader to achieve the effect.


Ideally, I'd like to be able to query the ray history to figure out whether
I should render the current surface or not, but as far as I know that's not
possible in VEX? Alternatively, if I could store some sort of user data in
the current state that shaders further down the line could access, it would
have the same effect. But again, I don't know if that's possible.


So, having given up on that, I opted for an approach that executes a series
of raycasts after an eye ray hits the surface. Basically it fires a
sequential line of rays (using rayhittest()) until it exits the liquid,
which it figures out by keeping a counter of how many times it has entered
and exited a surface (based on checking the normal). When it finally exits,
it calls trace() that will, in theory, give me the final color of whatever
is behind the surface. I could then combine this with the result of shading
the front and back faces.

The problem is that trace() doesn't seem to give me anything meaningful. In
fact it appears to call the shader again and return a negative result for
the first rayhittest() intersection test. Is there some catch or gotcha with
using trace() inside a surface shader that I don't know about?

Anyway, in case it helps, the VEX code is pasted in below (with comments
that explain what's going on). There's no refraction or reflection built in
yet, as I wanted to get the basics working before I took it to the next
stage.

Any ideas, suggestions (or even RTFM's) are gratefully received.

Thanks a lot :-)

Andy



- - - - 

I've just got this code inside a VEX Inline Code operator with P and I being
supplied to it from a Global Variables node. The outputs to the VEX Inline
Code are just called "out_col" (Vector), "out_opacity" (Vector), and
"out_alpha" (float) and are plugged straight into the output variables. I've
set the output color to various things along the way for debug purposes.



//Set some defaults
$out_col={0,0,0};
$out_opacity={1,1,1};
$out_alpha=1;

vector $phit={0,0,0};
vector $nhit={0,0,0};
vector $curpos={0,0,0};
vector $raydist=$I;
float $maxdist=1000;

//We need to give rayhittest a maximum length as well as direction to ray
cast
$raydist = $maxdist * normalize($raydist);


float $dist=0.0;
float $dotp=0.0;

//We have just hit the first surface so we set this to -1.
//When it gets to zero again, we know we have just left the opposing surface
int $surfcount=-1;

//Keep a count to make sure we do not infinitely loop
int $count=0;

$curpos=$P;

while($surfcount!=0 && $count<50)
{
    $dist = rayhittest($curpos,$raydist,$phit,$nhit,0.0001);
        
    if($dist<0.0)
    {
        //Nothing intersected
        $out_col={1,0,0};
        break;
    }
    else
    {
        //Check if we have just entered or exited a surface
        $dotp = dot($I,$nhit);

        if($dotp>0.0)
        {
            //We have just exited a surface
            $surfcount = $surfcount+1;            
            $out_col={0,0,1};
        }
        else
        {
            //We have just entered a surface
            $surfcount = $surfcount-1;
            $out_col={0,1,0};
        }
    }
    $count = $count+1;

    //Set the new intersection position to be the new ray cast position
    $curpos = $phit;
}

if($surfcount==0)
{
    //This is the final ray trace when we have exited the final surface
    //It always returns red from the "Nothing intersected" block above
    trace($out_col, $out_opacity, $out_alpha, $phit, $I, 0.0001, 1);

/*
    //Note: if you replace the single trace() call above with the following
code:
    //you can see that the position it's ray casting from is correct.

    //Returns if there's anything beyond where we've just exited the surface
    $dist = rayhittest($curpos,$raydist,$phit,$nhit,0.0001);
    if($dist<0.0)
    {
        //Nope, nothing
        $out_col={1,1,0};
    }
    else
    {
        //Yep, we've just hit another surface
        $out_col={0,1,1};
    }
*/
}





















More information about the Sidefx-houdini-list mailing list