About two functions: rtPotentialIntersection and rtReportIntersection

I have thought about the two function for a couple of days,but there still are some aspects I don’t understand.Now I will write down my confusion,I hope someone can correct my misunderstandings.

bool rtPotentialIntersection(float t)

rtPotentialIntersection will determine whether the reported hit distance is within the valid interval associated with the ray, and return true if the intersection is valid.

q1:In a Intersection Program, first the hit distance, t, will be computed. What does the t-value mean? How can it be computed? I didn’t found any information about it.

q2: What does the valid interval mean? It is decided by programmer or default? How can I known the value of interval?

q3: If the function return false, the intersection program will trivially return. Does it imply the ray hit nothing, and the miss program will be invoked?

q4: Now we assume rtPotentialIntersection returned true. it imply the ray hit someting, but at the moment, we don’t know what kind of material it hit, the material can be either opaque or transparent, so we call it Potential Intersection. Is it right? I hope someone can explain what any_hit mean in detail for me, I appreciate it.

Subsequently, the geometry program will compute the attributes (normal, texture coordinates, etc.) associated with the intersection before calling rtReportIntersection. Now I want to talk about the second function.
bool rtReportIntersection(unsigned int material)
If rtReportIntersection is invoked, at the same time any_hit program will be invoked, whether rtReportIntersection return true or false is decided by any_hit program.
If rtIgnoreIntersection is called, rtReportIntersection will return false, which means the intersection not exist, the object is probably transparent. Otherwise rtReportIntersection returns true, and then closest_hit Program will be invoked.
This is my understanding about rtReportIntersection.

These are my questions and understanding, I expect you can solve my questions and point out my misunderstanding. Thank you.

A1: t is the distance of the ray origin to the hit point. It is based on the formula of a arbitrary point on a ray:
x=ray.origin + t*ray.direction

A2: The initial valid interval is the interval associated with the ray parameter: tmax = ray.tmax and tmin = ray.tmin(shd be a value close to zero initially)
where, ray.tmax is defined by the last parameter of your optix ray constructor, in the following case, it is RT_DEFAULT_MAX:

optix::Ray ray = optix::make_Ray(ray_origin, ray_direction, radiance_ray_type, scene_epsilon,     RT_DEFAULT_MAX);

Every time you call rtPotentialIntersection(t), it will perform a comparison between the interval (tmin to tmax) and (tmin to t). If the interval (tmin to t) lies within the interval (tmin to tmax), then the new tmax will be tmax = t. This means that the current intersection could be potentially the closest intersection, and hence the t-interval is being narrowed.

A3: If rtPotentialIntersection returns false, it just means the intersection is ignored because it is outside the t-interval. It can mean either the hit point is further than the current closest intersection(t > tmax), or the hit point is in the opposite direction of the ray (where t is negative). The intersection program may be trivially returned, because there is no more intersection that is closer than tmax.

A4: if rtPotentialIntersection returns true, the intersection program has hit something within the valid interval t. You use rtReportIntersection to specify the material associated with the geometry that has been intersected.

if rtReportIntersection() returns false, it does not mean the intersection does not exist, but it means the intersection is ignored. In this case, the t-interval that was previously updated by rtPotentialIntersection() will be reset to the previous t-interval.

In ray tracing context, any_hit is a ray program used to checked for visibility. It reports an intersection whenever the ray hits a geometry(triangle, plane, sphere), however, the intersection might not be the closest intersection with respect to the ray origin. But in OptiX, any_hit does have a high potential to return the closest intersection point due to the way its acceleration structure is designed.

You are partially correct on this. rtReportIntersection() will invoke the any_hit program first, which determines if the current intersection should be accepted or ignored as the closest intersection. If it returns true, it means it has found its nearest intersection point, and it will automatically invoke the closest hit program.

Do correct me if I’m wrong.

Thank you, mdkoal, Your answers are really helpful, I have known much from you. But there still is something I want to consult with you.

First, I still want to talk about the any_hit Program. As you know, most of the time, rtIgnoreIntersection and rtTerminateRay will be invoked in the any_hit Program, rtIgnoreIntersection is used to ignore the intersection, which means the object is transparent probably. And rtTerminateRay is called to terminate the ray traversal,then the closest_hit will invoked. So if I define a any_hit function without rtIgnoreIntersection or rtTerminateRay, what will happen? Will the rtReportIntersection function still returns true,and then closest_hit Program runs automatically?

Then I want to talk about another problem. As you can see in the picture, the Ray goes through 3 triangles, which are all opaque with the same material, the material contains any_hit and closest_hit function, and rtTerminateRay reside in any_hit Program. Now I will describle my question.

Let’s assume that we would compute the distance between the origin and the triangle_3 first, so we can get t-value,which is between tmin and tmax, rtPotentialIntersection will return true. Then rtReportIntersecion will be invoked to check whether it is a closest intersection, since rtTerminateRay exist in any_hit Program, rtReportIntersection returns true, then closest_hit Program will be called. In the end the triangle_3 become the closest intersection, in fact it’s not, the ray is blocked by triangle_1 and triangle_2. So what’s wrong with it? I can’t figure out.

You can call rtTerminateRay to speed things up if triggering just one any_hit is sufficient (e.g. you want to see whether there’s something that blocks a ray).

The order in which your geometries’ any_hit programs will be called is theoretically in parallel so there’s no chance of only triangle 3’s any_hit program being called (if OptiX is properly set).

If any_hit programs are called for triangle 1, 2 and 3, OptiX will determine the closest hit by comparing their ‘t’ values and call the actual closest_hit program (in your example, triangle1’s closest_hit).

Thank you for your answer,marknv!

I want to know the difference between any_hit with rtTerminateRay and without rtTerminateRay, in both situation the rtReportIntersection will return true, and then the closest_hit is invoked.

The geometries’s any_hit program are called parallelly? But for a ray, it can only compute one t-value, and then invoke rtPotentialIntersection and rtReportIntersection. How can a ray parallelly compute multiple t-value, and invoke multiple rtPotentialIntersection and rtReportIntersection?

Thank you for your explanation, I’m a new learner, Perhaps there is someting I misunderstand.

I made a mistake in my response, the closest hit program is not automatically invoked if the (user_defined) any_hit program decides to accept the current intersection. My interpretation of how the closest hit program works is as followed:

You have to treat the (user_defined) closest_hit program(associated with the material) as having a default no-op(no rtTerminateRay) any_hit program associated with it. Hence technically, as mark mentioned, the no-op any_hit program will be called for every intersection along the ray, and then comparing the minimum tmax value for each interval to find the closest intersection. Your closest_hit program is not related to any user-defined any_hit program associated with the material.

Well, I might be wrong in my interpretation since the documentation related to rtReportIntersection is rather short. It is also new to me that the any_hit program for multiple intersection in the same ray is done in parallel. (initially I thought that could only be done on hardware with dynamic parallelism)


Remember that RT_PROGRAMs are actually CUDA kernels. Depending on the acceleration structure any_hit programs may be hit in any order (even in parallel). OptiX will take care of determining the real closest hit intersection based on the information provided.

Let me clarify a few things.

Primitive intersections against a single ray are not performed in parallel at this time. Basically a single ray is processed serially through the acceleration structure. We do process multiple rays in parallel.

While traversing the following things can happen.

foreach primitive in primitives {
  if (ray.intersectsBoundingBox(primitive))
  if (terminateRayCalled) break;
if (closest_object)

During intersection

intersect(ray, primitive)
  t = compute_t(ray, primitive)
  if (rtPotentialIntersection(t)) {
    material_attributes = stuff;
    if (rtReportIntersection(materialIds[primitive]))
      // Intersection reported
      // Intersection didn't stick...if there is more than one t value (such as a sphere) you can try another rtPotentialIntersection/rtReportIntersection pair.

rtPotentialIntersection checks the interval to t like mdkoa1 said.

if t in [t_min, t_max]
  potential_closest_t = t;
  potential_closest_object = current_primitive;
  return true;
  return false;

Then rtReportIntersection looks like this:

  if (!ignore_intersection_called) {
    t_max = potential_closest_t
    closest_object = potential_closest_object;
    current_material = materials[matID];
    return true;
  } else {
    return false;

The pseudo code for rtTerminateRay is a bit more complex and doesn’t quite fit in here with this pseudo code, but basically it takes the current potential intersection data makes it the current and then stops traversal. When traversal stops it either calls the closest_hit from the material associated from the last successful any_hit call or the miss shader if no any_hit calls were successful.

Thank you very much, @mdkoa1, @marknv and @JBigler. Thanks to your helps, I have known Optix much better. I like our discussion!
Now I still talk about a few questions.

q1: How does a ray traversing for a geometry?
A ray begins traversing from rtTrace, How can it find the geometry which will be hitted? Is it done automatically with the help of acceleration structure and we programmer need do nothing? If so it is, why is the visit_program created? And When I lenrn the example2 project of SDK example, I didn’t find a visit_program, but the project still works normally.

I’m trying to interpret the code. If there is someting wrong, please point out.
The code starts with the assumption the ray hitted a geometry, I belive “primitves” in the code represent a geometry, and the geometry consists of many primitives. In fact, we know the ray hit a geometry, but which primitive associated with the geomertry will intersect with the ray is unknown. So a loop is needed, in which all the primitives are checked to make sure whether they are intersected. Fortunately, we need not handle the loop, it is constructed by the Optix system. We just need to code a intersection program.

the code is uploaded again

If you are referring to sample2, “box.cu” contains your visit program.

The primitive scanning loop is done internally by optix through the use of its generated acceleration structure. As the programmer, your job only requires you to determine whether the current intersection point is a valid intersection point (through the use of rtPotentialIntersection). And you also have to decide what kind of attributes(e.g surface normals, texture coordinates) you wish to pass into your material closest_hit or any_hit program.