optixTrace: calculate intersections and max distance

I would like to ask some very simple questions that will significantly increase my understanding on some basic concepts of optix ray trace.
Suppose the scenario that I want to send a ray from a 3D point to a GAS and I want to know how many intersections occurred and what’s the distance of the longest intersection.

  1. Which program groups do I actually need for this?
  2. If I choose to use the closest hit group and a ray intersects with the GAS 3 times, how many times is the __closesthit__ch() called? Once and then it stops or 3 times?
  3. Which OPTIX_RAY_FLAG describes best this scenario?
    Thanks!

There are multiple ways to calculate all intersections along a ray.

One way is to use an anyhit shader. If do you that, your anyhit shader will be called at least once for every intersection, until a closest hit is found. Because geometry might be split for performance reasons, it’s possible to get more than one anyhit call corresponding to an intersection. If that could be a problem, you can disable geometry splitting and guarantee a single anyhit call using OPTIX_GEOMETRY_FLAG_REQUIRE_SINGLE_ANYHIT_CALL. To ensure the ray doesn’t stop at the closest hit, and continues to report all the hits, you need to call optixIgnoreIntersection() in your anyhit shader; this way the ray will report all the hits along the ray (and will not call closest hit unless you allow it.)

Another way to calculate all intersections along a ray is to pass a t_min value equal to the previous intersection’s t_hit value, while calling optixTrace() or optixTraverse() in a loop and passing the same ray every time. By advancing t_min to the previous hit point, you skip over that intersection while allowing subsequent intersections. This method can be useful but is even slower than using anyhit.

You can also use a hybrid of both of these methods, and collect a small number of anyhits before casting a new ray. All of these methods can be relatively expensive, but if you need all hits along a ray, then it’s the price to pay. Sometimes there are alternatives to collecting all hit points along a ray, so we recommend considering the problem’s requirements carefully.

2- If you use a closest hit shader program, it will be called at most once per ray. It won’t be called if the ray misses, or if you’ve used an anyhit shader and called optixIgnoreIntersection() for all hits.

3- The ray flags are mostly orthogonal to this discussion; you don’t need any ray flags in order to find all hits along a ray. You can enable or disable anyhit calls using ray flags, and you can disable closest hit calls with a ray flag, and you can also terminate your ray after the first hit is found, which will call closest hit with whatever hit was found (and it might not be the closest one). The terminate-on-first-hit flag’s intent is for shadow rays, for example, where you only need to know if the light source sample is blocked or not, and you don’t care what the t value of the hit point on the occluder is.

Hope that helps. It is worth revisiting the Programming Guide and SDK samples as the concepts fall into place. I know there’s a learning curve and that it’s a lot to absorb, but it is helpful to review things that didn’t make sense the first time. I still review the Programming Guide all the time.


David.

thanks for your reply!
After reading carefully your links and making a lots of small tests I think I am getting closer to understand the optixTrace() basics!
I would like to ask you some simple things just to confirm that I am on the right way.

  1. If I call optixTrace() with enabled:
  • closest hit (set the payload for the closest hit coordinates)
  • anyhit (increase payload hit count)

Somehow (probably splitting the geometry? ) optix locates the closest hit without finding all intersections. Is this true? I saw that the intersections count is not correct in this case.

  1. If I call optixTrace() with enabled:
  • anyhit (increase payload hit count)
  • optixIgnoreIntersection() in anyhit after increasing the payload hit count

Everything is a miss (closest hit is never called) and I have correct calculation of intersections. Is this right?

  1. What combination of flags and functions will provide me the accurate number of intersection and the closest point coordinates? I read in the forum that sometimes anyhit reports duplicate intersection points, if this is true how could I remove these duplicates?

Thanks!

  1. Yes, OptiX will try to find the closest intersection with as little work as possible. To do this it uses data structures (think of binary search trees to give a rough idea) to skip many primitive tests altogether.

  2. Yes, this is the only way to find all intersections in a ray’s path. This will give the correct number assuming you follow the above suggestion and set the OPTIX_GEOMETRY_FLAG_REQUIRE_SINGLE_ANYHIT_CALL flag.

  3. To remove duplicate, use the flag mentioned above. There is no way to invoke any-hit for all intersections and then invoke closest-hit for the closest. You can fire two rays – one with anyhit + ignore-intersection and then another with closest-hit. Or you can use your any-hit ray to keep track of the nearest intersection as it goes and store it in the ray payload.

I am trying to implement the anyhit+optixIgnoreIntersection() approach. When the anyhit function is called for the first time, how can I compare the current hit distance with the optix payload for distance since I have not initialized the payload to a large value? In the raygen function I am not allowed to use the optixSetPayload() to initialize the distance payload. What’s the proper way of doing it?
I have done the following but I am not sure if it is working as it should be, as anyhit could be called parallelly in the device (I am not really sure how this takes place).

extern "C" __global__ void __anyhit__ah() {
	unsigned int hitCount = optixGetPayload_0();

	//reset closest hit distance
	if (hitCount == 0) {
		optixSetPayload_1(__float_as_uint(1e16f));
	}

	//Increment intersection count
	hitCount = hitCount + 1; 
	optixSetPayload_0(hitCount);
	
	//getting current hit distance
	float current_t = optixGetRayTmax();
	//getting the so far closest hit distance from prd
	float anyhitClosestHitDistance = __uint_as_float(optixGetPayload_1());

	if (current_t < anyhitClosestHitDistance)
	{
		optixSetPayload_1(__float_as_uint(current_t));
		float3 closestHitPosition = optixGetWorldRayOrigin() + current_t * optixGetWorldRayDirection();
		optixSetPayload_2(__float_as_uint(closestHitPosition.x)); // Mark scene hit x
		optixSetPayload_3(__float_as_uint(closestHitPosition.y)); // Mark scene hit y 
		optixSetPayload_4(__float_as_uint(closestHitPosition.z)); // Mark scene hit z
	}	
	optixIgnoreIntersection(); // Continue traversal
}

Does the

	//reset closest hit distance
	if (hitCount == 0) {
		optixSetPayload_1(__float_as_uint(1e16f));
	}

initialize the ray payload properly at the first time the anyhit is called?

In raygen, you pass your payload values into optixTrace() or optixTraverse(). There’s no need to use optixSetPayload_x() for initialization in raygen, you can simply initialize the payload value before passing it in. You can see examples of this in the optixPathTracer sample, for example in the traceRadiance() function.


David.

1 Like

Just a last thing, what’s the difference between
unsigned mask_triangle_input_flags[2] = { OPTIX_GEOMETRY_FLAG_NONE, OPTIX_GEOMETRY_FLAG_REQUIRE_SINGLE_ANYHIT_CALL}
and
unsigned mask_triangle_input_flags[1] = { OPTIX_GEOMETRY_FLAG_NONE | OPTIX_GEOMETRY_FLAG_REQUIRE_SINGLE_ANYHIT_CALL}

Before inserting the OPTIX_GEOMETRY_FLAG_REQUIRE_SINGLE_ANYHIT_CALL flag my triangle input flag was
unsigned mask_triangle_input_flags[1] = { OPTIX_GEOMETRY_FLAG_NONE};
Now I want to insert the OPTIX_GEOMETRY_FLAG_REQUIRE_SINGLE_ANYHIT_CALL flag, what is the proper way to do it?

  1. unsigned mask_triangle_input_flags[1] = { OPTIX_GEOMETRY_FLAG_REQUIRE_SINGLE_ANYHIT_CALL};

  2. unsigned mask_triangle_input_flags[1] = { OPTIX_GEOMETRY_FLAG_NONE |OPTIX_GEOMETRY_FLAG_REQUIRE_SINGLE_ANYHIT_CALL};

  3. unsigned mask_triangle_input_flags[2] = { OPTIX_GEOMETRY_FLAG_NONE , OPTIX_GEOMETRY_FLAG_REQUIRE_SINGLE_ANYHIT_CALL};

My case is that I only want to calculate the number of intersections with the scene
Thanks!

Just a last thing, what’s the difference between
unsigned mask_triangle_input_flags[2] = { OPTIX_GEOMETRY_FLAG_NONE, OPTIX_GEOMETRY_FLAG_REQUIRE_SINGLE_ANYHIT_CALL}
and
unsigned mask_triangle_input_flags[1] = { OPTIX_GEOMETRY_FLAG_NONE | OPTIX_GEOMETRY_FLAG_REQUIRE_SINGLE_ANYHIT_CALL}

The first case here has two flags specified – one per SBT. There are 2 SBT entries so it requires a flag for each.
The second case is a single flag (two values ORed into a single mask). OPTIX_GEOMETRY_FLAG_NONE is equal to zero (see docs or optix_types.h). ORing anything with zero yields itself.

Before inserting the OPTIX_GEOMETRY_FLAG_REQUIRE_SINGLE_ANYHIT_CALL flag my triangle input flag was
unsigned mask_triangle_input_flags[1] = { OPTIX_GEOMETRY_FLAG_NONE};
Now I want to insert the OPTIX_GEOMETRY_FLAG_REQUIRE_SINGLE_ANYHIT_CALL flag, what is the proper way to do it?

1. unsigned mask_triangle_input_flags[1] = { OPTIX_GEOMETRY_FLAG_REQUIRE_SINGLE_ANYHIT_CALL};
2. unsigned mask_triangle_input_flags[1] = { OPTIX_GEOMETRY_FLAG_NONE |OPTIX_GEOMETRY_FLAG_REQUIRE_SINGLE_ANYHIT_CALL};
3. unsigned mask_triangle_input_flags[2] = { OPTIX_GEOMETRY_FLAG_NONE , OPTIX_GEOMETRY_FLAG_REQUIRE_SINGLE_ANYHIT_CALL};

1 and 2 are equivalent. 3 would be appropriate if you have 2 SBT entries.

Hope this helps.

1 Like

All is much clearer now! Thanks!