Smartest setup to ensure a ray hits multiple objects? (Optix 6)

Hi!

I am shooting rays in (more or less) random directions. The ones that interest me are the ones that hit an objectA THEN an objectB and then either objectC or nothing.

I can give each of these different materials and programs. What is the “best” setup for that, in terms of ray types and materials and programs to use?

One approach I tried was using a ray type for objectA (0) and one for objectB (1), each with their own closest hit program, 0 and 1.

That works for A, but when I then recast a ray in that direction for ray type 1 it doesn’t seem to hit.

That can be implemented with a single ray type and a small state machine on your per ray payload.
The idea is to be able to track what the last recent hit was and advance the state machine when it matches exactly your desired patterns:
A->B->C or A->B->Miss.
For that you would only need to be able to identify your objects which can be done by storing some identifier variable at the GeometryInstance scope (my preferred choice) or Material scope.

So all 3 with anyhit? Somehow I was under the impression that the order in which they are called isn’t guaranteed.

Thank you, I give this a shot!

You didn’t mention any_hit programs. I read your description as if you’re tracing rays through the scene and hit objects with the closest_hit invocations and then continue the ray path, potentially hitting other objects.

The simplest approach would be to do that with an iterative path tracing method, irrespective of how you define the next path segment, by scattering on surfaces or along a straight line. This captures the hits in order automatically.

That would not work the same way using only any_hit invocations.
If you would want to handle that inside any_hit and miss programs only, then you would need to store all hits on the per ray payload with their current intersection distance and then sort by distance afterwards, filter out potential duplicates and see if the object IDs match your regular expression.
Note that when the any_hit program always ignores the intersections to capture more, that will always invoke the miss program at the end. This wouldn’t be required when you hit object A, B, C in order anyway.

Handling the state machine inside the closest_hit invocations along a ray path can determine if the order of desired closest hits happens much simpler, including early exits when the state machine reaches a state not matching your regular expression of hit events. (For example, stop the ray path when the first object was not A.)

If your objects do not have holes defined by the materials (cutout opacity requiring any_hit programs) and are built-in triangles, that path tracing method with closest_hit programs only would be pretty optimal on RTX boards as well.

Related threads linked here: https://devtalk.nvidia.com/default/topic/1052022/optix/processing-intersections-in-order/post/5340199

Ok, actually more confused now :)

You mentioned this with a single ray type. Wouldn’t closest_hit only be called once then, at hitting A? Would I then rtIgnoreIntersection to get to b and c?

Did you read all of the other posts linked in the related topics recursively?

When reaching the closest hit program, right, that ray ends there.
At that point your closest hit program would need to handle the state machine.

In an iterative path tracer you would return back to the ray generation program afterwards, which then shoots another ray starting from that surface hit point into a direction you calculated with a method to not hit the same primitive again, normally by adding a scene related epsilon to the ray t_min interval start.

Similarly for a recursive program which would shoot the next ray from within the closest hit program.
(Don’t do that if you can work iteratively!)

Depending on what you’re actually trying to achieve (exact algorithmic problem description?) you might need to filter out back face hits.

Something like this for the iterative case.
(Pseudo code! There would need to be some more end conditions for the path termination.)

There is no any_hit program needed at all for this (unless primitives have coutouts done with anyhit tests).

// Inside the closest hit program:
// Setup the next ray segment origin, direction, distance for the iterative path tracer.
//...
// Handle the state machine checking the expected order of hits.
switch (prd.state)
{
  case STATE_START:
    prd.state = (objectID == A) ? STATE_HIT_A : STATE_NO_MATCH;
    break;

  case STATE_HIT_A:
    prd.state = (objectID == B) ? STATE_HIT_AB : STATE_NO_MATCH;
    break;

  case STATE_HIT_AB:
    prd.state = (objectID == C) ? STATE_MATCH_ABC : STATE_NO_MATCH; // End state.
    break;

  //default:
    // Do nothing, keep the last state for analysis in raygen program.
}

// Inside the miss program.
RT_PROGRAM miss()
{
  prd.state = (prd.state == STATE_HIT_AB) ? STATE_MATCH_ABMiss : prd.state; // Keep the previous state in case that was STATE_MATCH_ABC.
  prd.flags |= FLAG_TERMINATE;
}

// Inside the ray generation program:
RT_PROGRAM raygen()
{
  // Setup primary ray and rtPayload for an iterative path tracer.
  // ...
  prd.state = STATE_START;
  // ...
  while (depth < MAX_PATH_LENGTH) // Limit this to the minimum number required to match the state expression.
  {
    // Potentially setup some more data per ray segment here.
    // ...
    rtTrace(root, ray, prd);
    
    if (prd.state == STATE_MATCH_ABC || prd.state == STATE_MATCH_ABMiss)
    {
      // Found path which matches the order of A->B->C or A->B->Miss and can distinguish either.
      break;
    }
    else if (prd.state == STATE_NO_MATCH)
    {
      // The path did not hit the objects in the desired order.
      break;
    }
    // else continue the path.
    
    ++depth;
  }  
}

A brute force path tracer which could easily be changed to do that can be found in optixIntro_04 here:
https://github.com/nvpro-samples/optix_advanced_samples/tree/master/src/optixIntroduction

In an existing path tracer this wouldn’t need any additional ray type either. This can be implemented by tagging a ray as a special case inside some rtPayload field which controls the behaviour of the individual program domains, including early exits.

This could be handled with a special ray type and the minimal required program code if that is easier, but I normally try to reduce the amount of OptiX objects to the minimum necessary.

Awesome, ok. Thank you, I got it now. But will read up on the additional material. Just thought I had a big misunderstanding regarding the closest_hit