Ray-triangles intersection when ray origin is inside multiple triangles

Hi, I’m trying to launch a ray whose origin is contained inside multiple triangles and get the primitive IDs of all the triangles within which it is contained. My scene only contains triangles. Since the ray origin is inside the triangles, I get tmax= 0 on ray-triangle intersection and I can’t just generate another ray with new origin as the hit point(hit_t = origin + tmax * direction) of the previous intersection as suggested by some other posts on this forum.

This is an example of my scene:
Triangle-1: (-1,2,0) (1,-2,0) (2,0,0)
Triangle-2: (-0.5,2,0) (0.5,-2,0) (2.5,0,0)
Ray Origin: (0,0,0)
Ray Direction: arbitrary
image

I am new to Optix and from what I’ve read so far, it seems like the any_hit program might be useful for this but result in slowdowns. Is there a way in which I can launch a single ray, with its origin inside multiple triangle primitives and arbitrary direction, and get back the ID of all such triangles? Thanks!

Hi @n16, welcome!

Our warnings against any-hit programs are really only intended to make people aware of the costs of any-hit in cases where any-hit is not actually needed. This is mostly because any-hit programs are turned on by default and you have to opt-out. Sometimes, any-hit programs are needed, and in that case I don’t want people to feel bad about using the feature as it was intended.

There are three ways I can think of to collect multiple hits along a ray: (1) any-hit, (2) use side-effect code in an intersection program, and (3) iteratively query for the closest hit and then re-cast a new ray from the previous hit point.

OptiX currently will not return intersections for hardware triangles at t<=0, and the closest-hit order for co-incident geometry is undefined. This means the iterative closest-hit approach will not work for you because you won’t be able to catch both hits of two or more co-incident triangles. Unfortunately, this also means the any-hit won’t work either when the origin is co-incident with your co-incident triangles. If your triangles aren’t lying in an axial plane like z=0, then you might catch some of the hits sometimes due to floating point rounding discrepancies between the triangle plane and your ray origin, but that’s uncontrollable and I’m sure you don’t want your case to depend on floating point rounding.

So I guess the solution would be to use any-hit or a custom intersection. With any-hit you’d have to move the ray origin off the co-incident plane (perhaps scaled by the triangle’s normal or some other handy direction that doesn’t lie in the triangles’ plane) and then narrow your t-min and t-max values to a reasonable and safe window, and filter out any unwanted hits as part of your any-hit processing. Or, you can write a custom primitive intersection program, and then you will be able to handle t=0 if you want, without having to move the ray origin. Using a custom primitive intersection program will have a slightly higher cost than any-hit, since the intersection itself will be software.

This is an interesting use case, can you elaborate on what you’re doing with multiple intersections at the ray origin? The use case that seems to get questions more often is trying to do the opposite and avoid getting any ray hits at the ray origin. ;) (For example, when casting shadow rays away from a surface, you don’t want the surface to count as a hit, otherwise it will shadow itself.)


David.

Thank you for your detailed response @dhart! I’m trying to use the Optix Wrapper Library(OWL) to solve a non-rendering problem similar to: http://www.sci.utah.edu/~wald/Publications/2020/springs/springs.pdf

I got this to work by using a custom intersection program; however, I’m trying to leverage the speedup from hardware ray-triangle intersection.

With any-hit you’d have to move the ray origin off the co-incident plane (perhaps scaled by the triangle’s normal or some other handy direction that doesn’t lie in the triangles’ plane)

If I’m not wrong, since z=0 for all the triangles, the normals of the triangle would be (0,0,1) when normalized. Would you mind elaborating on how to do the scaling for the ray origin?

If all the triangles are z=0, can you set your ray origin’s z value to -1, and set your ray direction to (0,0,1)? My previous reply was written under the assumption that you didn’t want to move the ray origin because you might hit other unwanted triangles that are not at z=0. But if your triangles are always at z=0, there’s no strong need to limit your t-max, and you can easily move the ray origin out of the z=0 plane, use a ray direction perpendicular to the z-plane, and get the hits you want. In this case, I’d suspect to collect multiple hit points, using an any-hit program and the built-in hardware triangle intersector would probably be the fastest approach.


David.

Yes, all my triangles are z=0. Unfortunately, setting the ray origin’s z value to -1, and ray direction to (0,0,1) did not work. I’ve been trying other methods but I’m not getting the answer I expect. I’m including details about my setup:
Vertex buffer:
Triangle-0:
-1, 3, 0
3, 1, 0
-1, -1, 0

Triangle-1:
-0.25, 3, 0
3.75, 1, 0
-0.25, -1, 0

Triangle-2:
-0.26, 3, 0
3.74, 1, 0
-0.26, -1, 0

Triangle-3:
-0.27, 3, 0
3.73, 1, 0
-0.27, -1, 0

Triangle-4:
-0.28, 3, 0
3.72, 1, 0
-0.28, -1, 0

Triangle-5:
-0.47, 3, 0
3.53, 1, 0
-0.47, -1, 0

Triangle-6:
-0.5, 3, 0
3.5, 1, 0
-0.5, -1, 0

Triangle-7:
0, 3, 0
4, 1, 0
0, -1, 0

Triangle-8:
0.1, 3, 0
4.1, 1, 0
0.1, -1, 0

Triangle-9:
0.12, 3, 0
4.12, 1, 0
0.12, -1, 0

Ray origin: (0.75,1,-1)
Ray direction: (0,0,1)
Tmin = 0
Tmax = 5
FrameBufferSize = (1,1)

My expected answer is all triangles are hit since the ray origin is contained by all 10 triangles. However, the AnyHit program only returns Triangle-8 and I’m not sure why that is happening. Please let me know if you need more information about my setup. Thank you.

I tried it on your data, and I can get a per-pixel count of the triangles:

Did you call optixIgnoreIntersection() in your any-hit shader? That’s probably the easiest thing to forget.

Also make sure you haven’t used OPTIX_RAY_FLAG_TERMINATE_ON_FIRST_HIT, and that you haven’t disabled any-hit shaders anywhere.

If it’s not any of those, then maybe you could send a reproducing code sample?


David.

You were right. I was missing the call to optixIgnoreIntersection(). It works well now. Thank you for all your help @dhart !

1 Like

In this case, I’d suspect to collect multiple hit points, using an any-hit program and the built-in hardware triangle intersector would probably be the fastest approach.

Hi, I’ve been working on this problem since our last discussion and I had a question about my findings. I find that performance of an any-hit program with the built-in hardware triangle intersector is worse compared to using a custom intersection program (where I can report hits from the intersection program itself and not call any-hit/closest-hit programs) as the size of data increases.

I wanted to know if this is due to bypassing the call to any-hit/closest-hit program when using a custom intersection program. Thanks!

I’m not sure I understand your comparison. In your custom intersector case without any-hit, how are you collecting multiple hits along the ray? The reason to use any-hit is to collect multiple hits, right?

The overhead cost of an any-hit program is going to be similar to the cost of a custom intersector; both of those will interrupt the hardware traversal in order to use the CUDA cores for some computation. So it’s not too surprising if you compare the hardware intersector plus the any-hit, that it’s slightly slower than a custom intersector without any-hit. It might also be useful to characterize how much computation and how much memory access happens in the any-hit program compared to the intersection program.

If I understand your question, then I think the answer is yes, bypassing the any-hit is what makes your custom intersector faster than the any-hit program combined with hardware intersection. But isn’t it the case that bypassing the any-hit program will ultimately not produce the results you need?

It sounds like multiple things are changing in your experiment? We should try to isolate one thing at a time and compare them carefully to understand the difference. What have you found is the cost of hardware intersection plus any-hit compared to custom intersection plus any-hit? Similarly it would be good to test both without the any-hit program. With all four cases (hardware IS with and without AH, and software IS with and without AH), you will be able to better isolate the cost of the intersector separately from the cost of the any-hit program, since the question of whether to use a custom intersector, and the question of whether to use an any-hit program, are mostly two different questions.


David.

Thanks a lot for your response, @dhart !

Since I only need to know the primitive ID of the hit, I can accumulate that information in the custom intersection program itself.

Compared to my original problem, my problem now becomes “launch a ray whose origin is contained inside multiple spheres and get the primitive IDs of all the spheres within which it is contained”.
I think Sections 3.1 and 3.2 of http://www.sci.utah.edu/~wald/Publications/2020/springs/springs.pdf may explain what I’m trying to say a lot better.

Both approaches give me the same results.

It might also be useful to characterize how much computation and how much memory access happens in the any-hit program compared to the intersection program.

I will do this. Is there any in-built support for this in Optix 7.1?

I haven’t tried this yet since I didn’t need the any-hit with the custom intersection. Without the any-hit, the custom intersection is 25% - 40% faster on an average.

Is there a way to directly access the hardware triangle IS results without an AH/CH program? This would be ideal but I wasn’t able to find a way to do it. Thanks!

So in order to collect multiple hits, I would guess you are testing whether the ray hits the primitive in your IS program, and if there is a hit, storing the prim ID to an array, and then intentionally not reporting an intersection so that traversal continues tracing your ray? Is that correct? Or do you have another way to find all the hits along the ray without using a hit program? Is the storing of the prim ID exactly the same in both cases? Do you sort to end up with the hits in depth order? Are you using an atomic to accumulate the prim ID? Are you able to simplify your custom intersection test to avoid computing the hit point because you don’t need it?

Is there any in-built support for this [track compute & memory] in OptiX 7.1?

Not really, I was more thinking of estimating it manually and/or using Nsight Compute, to check if the any-hit needed to do more computation than the intersector for some reason. There are some instruction count stats reported during OptiX compilation, but that doesn’t really do what I was suggesting.

There is overhead just for calling any-hit, and that might be the entire explanation here, but it’s hard to say without profiling and examining all the details. I maybe wouldn’t have expected a discrepancy as large as what you see, but I don’t have a baseline to compare against either. In any case, if you found a fast method that does everything you want, you should run with it!

Is there a way to directly access the hardware triangle IS results without an AH/CH program?

No, the hit programs are the official way to directly access IS results. In some cases, like shadows/AO, you can assume a hit, and use a miss program to know you guessed wrong. Otherwise, the scope of the attributes that IS programs compute is constrained to the hit programs in order to reduce the live state and register usage to a minimum.


David.

Yes, that is right.

Yes, the primitives are stored in the same order in both cases.

No, I don’t sort since the order of hits does not matter to me.

No, I am not using an atomic. Since I’m launching only one ray, I didn’t think it was necessary.

Yes, I don’t calculate the hit point. As long as i know the prim ID, that’s enough for me.

Got it. I just wanted to make sure that I wasn’t seeing unexplained anomalous behavior. Thanks for your help!

1 Like