How to get primitive geometry id

In an any_hit_program, I want to determine which geometry the ray is hitting and what material is in the geometry instance.

Is there any way to assign the id while creating geometry and getting this id in hit program?

You can set the value in an attribute variable in the intersection program but if you use it in any_hit it might not be a consistent value but if you use it in a closest_hit program then the primitive ID is definitely the one of the hit primitive.

One intersection program corresponds to one geometry type. What I need is to distinguish all the objects, in which case, two boxes must have two ids. sorry for the ambiguity in my question.

For per object and/or per material identification you could set an ID variable on each GeometryInstance itself or if you have a different Material object, means a different set of material parameters, assigned per GeometryInstance you can also set that ID variable per Material instance.

That won’t work with instancing sub-trees via Transform nodes though. There different instances holding the same GeometryInstance nodes underneath would also have the same IDs.

ID variable on GeometryInstance is exactly what I need.

In my case, each transform holds only one child, which is transformed from one box. Need I worry about the ‘instancing sub-trees via Transform nodes’?

I believe not but not sure. I did create several geometry instances:

for(i=0;i<num;i++){
  rtGeometryInstanceCreate(context, &instance);
  ...
  rtGeometryGroupCreate(context, &geometrygroup);
  ...
  rtGeometryGroupSetChild(geometrygroup,0,instance);
  ...
  rtTransformCreate(context,&transform[i]);
  rtTransformSetChild(transform[i], geometrygroup);
  ...
}
...
for(i=0;i<num;i++)
  rtGroupSetChild(top_group,i,transform[i]);

If each Transform node, resp. the GeometryGroup underneath, holds a unique GeometryInstance you need to identify, you’re just positioning unique objects with Transforms.
Assigning an ID variable to each GeometryInstance should match your use case exactly.

Insert something like this in line 3 above

RTvariable varID;
rtGeometryInstanceDeclareVariable(instance, “geometryInstanceID”, &varID);
rtVariableSet1i(varID, i);

Then add rtDeclareVariable(int, geometryInstanceID, , ); in your programs and you should be able to access the ID you assigned.

(If that is your actual source, you should add error checks around every call. I’d recommend using the C++ wrappers which are much more convenient.)

Thanks a lot! It works.
I wasn’t my actual code. I added error checks around every call by learning from SDK examples.

I have another question now.
I have two boxes transformed from box(-0.5,-0.5,-0.5;0.5,0.5,0.5). One is shifted (-0.75,0,0), the other is shifted (0.75,0,0).
Then I trace 6 rays, all starting from (0,0,0) but toward six directions, +x,-x,+y,-y,+z,-z.
Then the program prints:

i’m thread (0,0) in intersection
i’m thread (1,1) in intersection
i’m thread (2,2) in intersection
i’m thread (3,3) in intersection
i’m thread (4,4) in intersection
i’m thread (5,5) in intersection
i’m thread (0,0), ray from (-0.75,0,0) travels 0.25 along (1,0,0) to where the box normal is (-1,0,0)
i’m thread (1,1) in intersection
i’m thread (2,2) in intersection
i’m thread (3,3) in intersection
i’m thread (4,4) in intersection
i’m thread (5,5) in intersection
i’m thread (1,1), ray from (0.75,0,0) travels 0.25 along (-1,0,0) to where the box normal is (1,0,0)
i’m thread (0,0) in any hit program, hitting instance 1
i’m thread (1,1) in any hit program, hitting instance 0
i’m thread (0,0) in intersection
i’m thread (0,0) in closest hit program, hitting instance 1
i’m thread (1,1) in closest hit program, hitting instance 0

As expected, ray from (0,0,0) travels along (1,0,0) for 0.25 to (0.25,0,0) and ray from (0,0,0) travels along (-1,0,0) for 0.25 to (-0.25,0,0).

But why don’t they invoke AnyHitProgram for the next parallel surface of the box? Like ray from (0,0,0) travels along (1,0,0) for 1.25 to (1.25,0,0)?
Does Optix default to terminate the ray once find closest hit?
Or is that just because my intersection program fail to provide all possible hit points?

The intersection program for my boxes is just copied from SDK sample2.

RT_PROGRAM void box_intersect(int)
{
  float3 t0 = (boxmin - ray.origin)/ray.direction;
  float3 t1 = (boxmax - ray.origin)/ray.direction;
  float3 near = fminf(t0, t1);
  float3 far = fmaxf(t0, t1);
  float tmin = fmaxf( near );
  float tmax = fminf( far );

  //rtPrintf("i'm thread %d, launch id %d in intersection\n", threadIdx.x+blockDim.x*blockIdx.x, launch_index);
  printf("i'm thread (%d,%d) in intersection\n", threadIdx.x+blockDim.x*blockIdx.x,launch_index);
  if(tmin <= tmax) {
    bool check_second = true;
    if( rtPotentialIntersection( tmin ) ) {
       texcoord = make_float3( 0.0f );
       shading_normal = geometric_normal = boxnormal( tmin );
       printf("i'm thread (%d,%d), ray from (%g,%g,%g) travels %g along (%g,%g,%g) to where the box normal is (%g,%g,%g)\n",
              threadIdx.x+blockDim.x*blockIdx.x, launch_index, ray.origin.x, ray.origin.y, ray.origin.z,
               tmin, ray.direction.x, ray.direction.y, ray.direction.z, shading_normal.x, shading_normal.y, shading_normal.z);
       if(rtReportIntersection(0))
         check_second = false;
    }
    if(check_second) {
      if( rtPotentialIntersection( tmax ) ) {
        texcoord = make_float3( 0.0f );
        shading_normal = geometric_normal = boxnormal( tmax );
        printf("i'm thread (%d,%d), ray from (%g,%g,%g) travels %g along (%g,%g,%g) to where the box normal is (%g,%g,%g)\n",
               threadIdx.x+blockDim.x*blockIdx.x, launch_index, ray.origin.x, ray.origin.y, ray.origin.z,
               tmax, ray.direction.x, ray.direction.y, ray.direction.z, shading_normal.x, shading_normal.y, shading_normal.z);

        rtReportIntersection(0);
      }
    }
  }
}

You’re using threadIdx and blockDim and blockIdx inside an OptiX program?
Those are not under your control.
When printing launch index information elsewhere please use the semantic variable rtLaunchIndex.

Basically yes, with no any_hit program there will be no rtIgnoreIntersection() called, so rtReportIntersection() should return true and your check_second block is never entered.

Please read the OptiX Programming Guide chapter 4.8.2. Reporting Intersections

“rtPotentialIntersection takes the intersection’s t-value as an argument.
If the t-value could potentially be the closest intersection of the current traversal
the function narrows the t-interval of the current ray accordingly and returns true.
If the t-value lies outside the t-interval the function returns false, whereupon the
intersection program may trivially return.”

“Should that any hit program invalidate the intersection via the
rtIgnoreIntersection function, then rtReportIntersection will
return false. Otherwise, it will return true.”

At first, I used blockDim and blockIdx and found the different and moved to rtLaunchIndex. I just printed both of them and didn’t remove the uncontrollable one.

If I change

if(rtReportIntersection(0))
  check_second = false;

to

rtReportIntersection(0)

the second block is not entered either. That is consistent with “Optix default to terminate the ray once find closest hit” but makes check_second seem useless.

Anyway, I put rtIgnoreSection() at the end of any hit program. Then I got all expected any hits but closest hit is no more invoked as result of rtIgnoreIntersection().

But I need closest hit. What can I do? Define two ray types? I haven’t figured out how that will work.

It would be easier to provide a solution if you described the exact algorithm you’re trying to solve.

Given a position and direction, I want to get the closest hit geometryInstanceID and hit position and any hit geometryInstanceID.

In my program, it prints “i’m thread %d(id) once entering intersection program”.
For the box, I have two tested t_values. If rtPotentialIntersection(t_value) returns true, the program prints launch_index, t_value, ray.origin, ray.direction and normal of the hit point. No matter what rtReportIntersection() returns for one t_value, the rePotentialIntersection() and likely rtReportIntersection() of the other t_value is designed to be called. (change line 20 and 21 to just rtReportIntersection(0). )
rtReportIntersection(0) invokes any hit program.
The program prints thread id in any hit program.
If there is not rtIgnoreIntersection(), the t_value in the any hit program will be recorded as the new closest intersection.
That invokes closest hit program, where hit_point is computed and printed out as well as ray origin, ray direction and t value.

Is that exact enough?
Thanks!

Now I become clear about the question in post #7. Thanks a lot for your quote of programming guide.

If my program runs as described above, the print is

i’m thread (0,0) in intersection, tmin=0.25, tmax=1.25
i’m thread (1,1) in intersection, tmin=-1.25, tmax=-0.25
i’m thread (2,2) in intersection, tmin=inf, tmax=0.5
i’m thread (3,3) in intersection, tmin=inf, tmax=0.5
i’m thread (4,4) in intersection, tmin=inf, tmax=0.5
i’m thread (5,5) in intersection, tmin=inf, tmax=0.5
tmin passed. i’m thread (0,0), ray from (-0.75,0,0) travels 0.25 along (1,0,0) to where the box normal is (-1,0,0)
i’m thread (1,1) in intersection, tmin=0.25, tmax=1.25
i’m thread (2,2) in intersection, tmin=-0.5, tmax=-inf
i’m thread (3,3) in intersection, tmin=-0.5, tmax=-inf
i’m thread (4,4) in intersection, tmin=-0.5, tmax=-inf
i’m thread (5,5) in intersection, tmin=-0.5, tmax=-inf
tmin passed. i’m thread (1,1), ray from (0.75,0,0) travels 0.25 along (-1,0,0) to where the box normal is (1,0,0)
i’m thread (0,0) in any hit program, hitting instance 1
i’m thread (1,1) in any hit program, hitting instance 0
i’m thread (0,0) in intersection, tmin=-1.25, tmax=-0.25
i’m thread (0,0) in closest hit program, hitting instance 1
i’m thread (1,1) in closest hit program, hitting instance 0
ray from (0,0,0) travels along (1,0,0) for 0.25 to (0.25,0,0)
ray from (0,0,0) travels along (-1,0,0) for 0.25 to (-0.25,0,0)

The program seems to be sure the first t_value (tmin) tested in intersection program is the closest intersection and called any hit program only once. That is because rtPotentialIntersection() is called for the second t_value but the potential interval range was narrowed by first call.

If I tested tmax before tmin, the program narrows the interval to the first t_value (tmax) tested and the second t_value tested still passes rtPotentialIntersection() so any hit program are called twice.

i’m thread (0,0) in intersection, tmin=0.25, tmax=1.25
i’m thread (1,1) in intersection, tmin=-1.25, tmax=-0.25
i’m thread (2,2) in intersection, tmin=inf, tmax=0.5
i’m thread (3,3) in intersection, tmin=inf, tmax=0.5
i’m thread (4,4) in intersection, tmin=inf, tmax=0.5
i’m thread (5,5) in intersection, tmin=inf, tmax=0.5
tmax passed. i’m thread (0,0), ray from (-0.75,0,0) travels 1.25 along (1,0,0) to where the box normal is (1,0,0)
i’m thread (1,1) in intersection, tmin=0.25, tmax=1.25
i’m thread (2,2) in intersection, tmin=-0.5, tmax=-inf
i’m thread (3,3) in intersection, tmin=-0.5, tmax=-inf
i’m thread (4,4) in intersection, tmin=-0.5, tmax=-inf
i’m thread (5,5) in intersection, tmin=-0.5, tmax=-inf
tmax passed. i’m thread (1,1), ray from (0.75,0,0) travels 1.25 along (-1,0,0) to where the box normal is (-1,0,0)
i’m thread (0,0) in any hit program, hitting instance 1
i’m thread (1,1) in any hit program, hitting instance 0
tmin passed. i’m thread (0,0), ray from (-0.75,0,0) travels 0.25 along (1,0,0) to where the box normal is (-1,0,0)
tmin passed. i’m thread (1,1), ray from (0.75,0,0) travels 0.25 along (-1,0,0) to where the box normal is (1,0,0)
i’m thread (0,0) in any hit program, hitting instance 1
i’m thread (1,1) in any hit program, hitting instance 0
i’m thread (0,0) in intersection, tmin=-1.25, tmax=-0.25
i’m thread (0,0) in closest hit program, hitting instance 1
i’m thread (1,1) in closest hit program, hitting instance 0

Now I know why sometimes any hit isn’t invoked but not sure how closest hit program is invoked. Is it called at the end of intersection program with the narrowest t_value?

There are two simple ways to solve this:

1.)
If you have an upper limit on the number of hits which can happen and you know that number beforehand and it’s not too big. => No dynamic allocations required, data could be stored on the per ray payload.
You only need an any_hit program, no closest_hit program for that raytype.

Add these fields to your per ray payload:
int index;
float distance[MAX_INTERSECTIONS];
int id[MAX_INTERSECTIONS];

Initialize the index to -1 in your ray generation program and shoot your ray.
Inside the any hit program increment the index and store the current intersection distance and the geometryInstanceID into the arrays at that new index and call rtIgnoreIntersection to continue testing for intersections. (For error handling you could throw a user exception if index reaches MAX_INTERSECTIONS.)
When the rtTrace returned to the ray generation program search the arrays with index count entries >= 0 for the smallest intersection distance, that is your closest hit ID. With the distance inside the same index you can calculate the closest hit position with the original ray data. All other hits are father away and you were only interested in their IDs.

2.)
Iteratively following the ray in order, basically a simple path tracer with a fixed ray direction.
You only need a closest_hit program, no any_hit program for that raytype.

Add the following fields to your per ray data.
float3 position;
int id;
bool hit;

Inside the closest hit program store the current surface hit position and the geometry instance ID into the per ray data fields, that’s all.
The miss shader sets prd.hit to false.
The ray generation program initializes the float3 position with your ray origin, and does a loop like this pseudo code:

// Results
float3 closestPoint;
int closestId = -1;
int id = -1;

prd.position = your_ray_origin;
prd.hit = true;

while (prd.hit)
{
  optix::Ray ray = optix::make_Ray(prd.position, direction, raytype, epsilon, RT_DEFAULT_MAX);
  rtTrace(top_object, ray, prd);
  if (prd.hit)
  {
    id = prd.id; // Not sure what you want to do with this information for the other hits.
    if (closestId < 0) // First closest hit? Store it.
    {
      closestPoint = prd.position;
      closestId = id;
    }
  }
}

closestPoint is valid when closestId != -1, otherwise the ray missed everything.

Thanks for sharing!
The first method is direct and easy to follow.
The second way seems tricky for me. Where is the closet hit program called from intersection program? What’s the difference between two sequential rtTrace()s?

Mind that the first solution will only work nicely with the Bvh acceleration builder.
(BVH builders using triangle splitting will require filtering of duplicate hits.)

The closest hit program is invoked when a nearest intersection has been found.
Since your box intersection program has been implemented following the OptiX sphere.cu intersection program it should just find all closest intersections in order when stepping along the ray.

The primary ray goes from your origin to the first hit. All following rays go from the current hit to the next hit, stepping along the ray in segments, until the miss shader is reached which will end the loop.
Cons: Slower because tracing a lot more rays.
Pros: Doesn’t need more than one temporary storage on the per ray data in an iterative raygen program; returning the hits in order in case you need that; no searching for the closest hit, it’s always the first; teaches how to program iterative ray tracing; smaller stack size than doing things recursively.

Thanks for explanation. That makes method 2 clear to me.

Due to performance consideration, I’ll take method one. What I really need is the closest t value and geometry ID and ‘closest and second closest’ ID and distance among the hits that are checked once even when rtIgnoreIntersection() is called, namely among the hits that have only one positive t value. So instead of using the fixed size array, I plan to do ray tracing and compare and finding the closest t’s at the same time.

Just found the intersection distance in any hit program is not the real intersection but the distance in the coordinate system where ray is transformed instead of the geometries.
Must I calculate the real intersection by getting transform matrix? Is there any alternative that give the real distance more directly?

True, the coordinate system in the any_hit program is in object space. See OptiX Programming Guide, 4.1.6 Program Variable Transformation.

Means you could do this inside the any_hit program to transform the intersection distance to world coordinates, which would only be required when having scaling inside the transforms:

prd.distance = length(rtTransformVector(RT_OBJECT_TO_WORLD, ray.direction)) * intersectionDistance;

This should arrive at the same result, just slower:
const float3 worldOrigin = rtTransformPoint(RT_OBJECT_TO_WORLD, ray.origin);
const float3 worldHit = rtTransformPoint(RT_OBJECT_TO_WORLD, ray.origin + ray.direction * intersectionDistance);
prd.distance = length(worldHit - worldOrigin);