Optix 7 Spheres

I’ve been playing around with the sphere primitive type within Optix.

I create two spheres with the exact same position - say (0, 0, 0) with radius 0.1. Then I cast a ray from the origin in any direction. The anyhit program only gets executed once. I expect it to run twice. Indeed if I change the position of one of the spheres by a tiny amount, I get two anyhit calls.

My question: Is this the intended behavior. It would be much nicer if two calls were called instead of one.

My second question is regarding the hitkind on spheres. It looks like the spheres’ hitkind is different depending on whether the ray hit from the inside (hitkind=139) or the outside (hitkind=138). I couldn’t find any documentation about this in the api and wanted to confirm here.

Lastly, I imagine the intersection test for spheres (unlike triangles) is happening on the SMs rather than on the RTCores?

Thank you

Is your anyhit program calling optixIgnoreIntersection()?
If not, the observed behavior is reasonable.
The ray intersects with one of the spheres, changed the ray tmax value and that’s it. There is no closer intersection possible. Which AABB it picked would be depending on the BVH traversal.
A single ray cannot distinguish between two coplanar surfaces.

If you want to count something with anyhit programs, please have a look through the threads linked in this post:
https://forums.developer.nvidia.com/t/distinguish-objects-when-closest-hit-program-occured/73988/5
Esp. look for the OPTIX_GEOMETRY_FLAG_REQUIRE_SINGLE_ANYHIT_CALL

The OptiX built-in sphere intersection program can intersect front and back faces.
That is reported via the ray tmax and an additional intersection attribute register (see the unused optixGetAttribute_0 inside the optixSphere example code).
https://raytracing-docs.nvidia.com/optix7/guide/index.html#curves#spheres-and-the-hit-program

My second question is regarding the hitkind on spheres. It looks like the spheres’ hitkind is different depending on whether the ray hit from the inside (hitkind=139) or the outside (hitkind=138). I couldn’t find any documentation about this in the api and wanted to confirm here.

It also sets the hit kind to be able to distinguish which kind of primitive has been hit and on which side.
Use the OptiX device functions listed here to determine the hit primitive type and the face:
https://raytracing-docs.nvidia.com/optix7/guide/index.html#device_side_functions#intersection-information

Lastly, I imagine the intersection test for spheres (unlike triangles) is happening on the SMs rather than on the RTCores?

Yes, the built-in intersection programs you get via optixBuiltinISModuleGet are running on the SMs .

1 Like

Thank you for the quick reply! and for helping me understand

As a follow up:
Can I change the intersection program so that it always returns a hit if it intersects a bounding box. In other words, I want the bounding boxes to be calculated using the sphereinput type (so I don’t have to manually calculate them for each sphere), but I don’t want the default intersection program. I want the intersection test to be only with the bounding box and not with the sphere inside (which should be much simpler and hopefully faster). What is the recommended way of doing this? Should I create a simple __ intersect__ function that always reports an intersection? Would that be unnecessarily going back to the SM every time it traverses a leaf in the BVH?

Can I change the intersection program so that it always returns a hit if it intersects a bounding box. In other words, I want the bounding boxes to be calculated using the sphereinput type (so I don’t have to manually calculate them for each sphere), but I don’t want the default intersection program.

No, the built-in sphere primitives must use the built-in sphere intersection program you get via optixBuiltinISModuleGet.

If you want to use a custom intersection program for whatever primitive type a ray should intersect with, that would only be called on custom primitives and that in turn gets AABBs as build-inputs.

Calculating AABBs from sphere primitives is trivial.

float3 minimum = center - radius; 
float3 maximum = center + radius;

I want the intersection test to be only with the bounding box and not with the sphere inside (which should be much simpler and hopefully faster). What is the recommended way of doing this? Should I create a simple __ intersect__ function that always reports an intersection?

The task of the intersection program is to calculate the closest intersection distance with a geometric primitive.
The intersection program changes the ray tmax program on accepted intersections (ones where no anyhit is calling optixIgnoreIntersection). That shrinks the ray interval [tmin, tmax] each time until there is no closer intersection tmax value.
Meaning even if you’re implementing a custom AABB intersection program, the intersection program needs to be able to calculate the closest intersection distance with that box.
Since that is part of most ray tracer implementations you’ll find tons of examples doing that on the internet.
e.g. in the second of these three books: https://raytracing.github.io/ or here https://pbr-book.org/ in chapter https://pbr-book.org/3ed-2018/Shapes/Basic_Shape_Interface#RayndashBoundsIntersections

Would that be unnecessarily going back to the SM every time it traverses a leaf in the BVH?

All OptiX device code you provide will run on the SMs.
There wouldn’t be much difference between a custom intersection program and one of the built-in ones in principle, except for triangles which use the RT cores on RTX boards for ray-triangle intersections.
(The OptiX built-in intersection programs are tuned for performance and precision a lot though, so they should be preferred when possible.)

That said, if memory isn’t a concern, it would also be possible to build these boxes with 12 built-in triangles and let the RT cores do the intersection in hardware. (When building these boxes, mind the winding, always use counter-clockwise vertex winding in a right-handed coordinate system to let the front and back face result be consistent.
Example from one of my OptiX 7 examples: https://github.com/NVIDIA/OptiX_Apps/blob/master/apps/rtigo10/src/Box.cpp)

If you only need the information which box you selected that would also be simple, and trivial when there is only one GAS with these boxes. Then the triangle primitiveId defines that: boxID = primitiveID / 12;

None of these things will result in getting multiple hits for coplanar faces though, so the behavior of the initial question is still the same for any primitive type with a single ray. Two rays, one intersecting front faces and one intersecting back faces would be able to fully resolve coplanar faces with opposite orientations, e.g. two adjacent boxes with a shared face.
See links in these threads:
https://forums.developer.nvidia.com/t/ray-mesh-intersections/170090/5
https://forums.developer.nvidia.com/t/how-to-adapt-optixtrace-considering-previous-intersections/253961/2

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.