Sunday, February 14, 2016

New simple ray-box test from Andrew Kensler

Andrew Kensler and Thiago Ize have been helping me tune up my ray-box code trying to find a sweet-spot of fast and simple.   I've updates a few test cases here and added the new method Andrew sent me.   Andrew's new code a super-simple version that on some compilers/machines is very competitive with the Amy Williams method, and you don't have to muck up your ray class.    It's not as fast under clang on my mac (but hard-coding swap helped a little) but it's so simple it will be my go-to method for the time being.

If there are NaN the compare will fail and it returns false.   It handles the inf cases right.    Very clean!


Unknown said...

Maybe that's nit-picking, but I'd argue the "<=" in the exit condition should be replaced with a "<".
The way the code is right now (well, at least the way I read it :-) ) is that it would _reject_ the box test if the ray interval is exactly zero (i.e., t0==t1), but if you do that then that box test would be problematic in a BVH if you have any "planar" triangles, since in that case some of the min() and max() coordinate will be the same, and t0 will end up being equal to t1.

Peter Shirley said...

Sorry I didn't notice this comment before. I am guessing you'd want that because you want to use a box as an actual geometric primitive and get the normal vector and all that? People haven't written much about it but Morgan McGuire has been looking at it lately. You can get the point by just using ray_origin + t0*ray_direction but getting the normal vector is a bit of a pain because you don't get which side is hit. Morgan has been looking at methods where the surface normal comes with the hit. He'll publish that soon I think. But if it's not some huge speed bottleneck in your code, a bunch of ifs to find the face it works (the hitpoint will be on one of the faces).

Anonymous said...

Thanks for sharing. Wouldn't tmax need multiplication by 1.00000024 as described in Thiago's 2013 paper "Robust BVH Ray Traversal"? Also, wouldn't arranging the code this way be more efficient since no swap is involved?

float t0, t1;
if (invD >= 0.0f)
t0 = (min()[a] - r.origin()[a]) * invD;
t1 = (max()[a] - r.origin()[a]) * invD;
t1 = (min()[a] - r.origin()[a]) * invD;
t0 = (max()[a] - r.origin()[a]) * invD;

Peter Shirley said...

Good calls deadlock. But I would want to test your second selection empirically on any compiler/hardware combo I was on.