Make_ray using optix 7.0.0

Quick question probably dumb, but is there an optix 7.0.0 version of the optix 5.x version of make_ray ?


Not really since optixTrace() takes the ray origin and ray direction as separate parameters. The earlier rtTrace() takes an optix::Ray as input. There’s nothing wrong with using a Ray class if you want to with OptiX 7. It’s just not necessary since the API no longer uses a Ray class.



Thanks again for the information @dhart.
I have been looking over some optix 5.x code examples and was wondering how to convert to Optix 7.x since many things are different between the two. Can you tell me how the following Optix 5.x code (from an example I found online):

rtDeclareVariable(rtObject, sysTopObject, , );
optix::Ray ray = optix::make_Ray(origin, direction, raytype, t_min, t_max);
PerRayData prd;
rtTrace(sysTopObject, ray, prd);

can be converted to OptiX 7.0.0 code? Or is this even possible?

Thanks again.

Since that looks similar to my code from the GTC 2018 OptiX Introduction examples, the answer is already inside the OptiX 7 examples which are ported from those introduction examples for exactly that reason: OptiX_Apps
Just clone that whole repository and look at the files inside the shaders folders.
The explains which example does what and how to build and run these.

rtDeclareVariable(rtObject, sysTopObject, , );
That’s now the OptixTraversable field sysParameter.topObject inside the OptiX launch parameters:
Defined here:
Declared here:

PerRayData prd;
That is a local structure inside the raygeneration program as before.

optix::Ray ray = optix::make_Ray(origin, direction, raytype, t_min, t_max);
The Ray structure and make_Ray() don’t exist anymore. optixTrace() takes these values as individual arguments

rtTrace(sysTopObject, ray, prd);
This gets replaced by optixTrace() which exists as different overloads depending on the number of payload registers (max 8).
This code splits the local 64-bit pointer to the prd structure into two unsigned int payload register values:
which are then used inside the directly following optixTrace() call.
That also contains all arguments of the previous Ray structure, the traversable (topObject) at which the BVH traveral starts, and all other arguments for the visibility flags, ray flags, and indices for the SBT indexing.

For comparison, that’s the same example written against the OptiX 5.1.0 API:

Similar inside the other OptiX 7 examples.
I changed the SystemParameter name to SystemData in later examples.

1 Like

Cool, thanks.

Hi @droettger,

I have been looking over you work at:

The work is great. However I have a question. Maybe it is obvious and I’m overlooking it because it’s a Monday, but how does the payload union apply for a given data value you are saving with an associated Ray? Is the dat portion of the Payload union employed to save the radiance or some other variable with per_ray_data ?

I apologize if my question is confusing or simple but I am just trying to fully understand the purpose of the union with regards to a given Ray.

Thank you for your time and assistance.

The union Payload is just an elegant way to convert the actual local 64-bit pointer to the struct PerRayData (definded inside the ray generation program) to two unsigned int registers and back to a PerRayData pointer by using my splitPointer() and mergePointer() functions defined below it.

This is required because my PerRayData (which was the actual rtPayload sematic variable in the old API) is using more data than fits into the available 8 unsigned int payload registers you can have with optixTrace() in OptiX 7.

This splits the local pointer to the PerRayaData before the optixTrace() call and puts the two unsigned int into the first two payload registers.

When reaching any of the program domains with a ray during traversal, I can then merge the two unsigned int payload registers in the same order to the 64-bit pointer to the local PerRayData and write any results into my actual “payload” structure.

This is the same mechanism as some OptiX SDK 7 examples or this code do with packPointer() and unpackPointer().
Just that my code will only use MOV instructions inside the PTX code already, where the other implementation will use hardcoded shifts and bit operations on 64-bit values which the compiler needs to optimize away.
The final SASS microcode should look the same when everything works as expected, but I prefer helping the compiler as much as possible.

1 Like

Thank you @droettger for the professional and in depth response.

I feel stupid because I realized late last night that you had kind of answered this question in another response. It was a bad Monday, hopefully today will be better.

Thank you again @droettger for all your help. I think I get the splitPointer and mergePointer operations.

Sorry again for bothering so much. I am new to BOTH OptiX 5/6 and 7 so trying to learn all.

Another quick question, if I may.

What’s the best way to pass a single read/write value (stored as float), such as emission calculated in an intersection program, to another program such as closest hit? I was thinking of encoding emission value as part of the PerRayData struct and employing the Split and Merge operations.


For reporting values from an intersection program in OptiX 7, the best method is to use the “attributes” parameters available in the function call optixReportIntersection(). You can pass up to 8 arbitrary 32-bit values, and then use the device functions optixGetAttribute_0 (_1, _2, etc…) in order to read the attribute value in a closest hit or any hit program.

The reason you should not set per ray payload data in your intersection program is because the intersector will be called many times during a ray, and the hits will occur out of depth order, so you would need to be careful to check your ray t values before touching your ray payload struct. It’s tricky to do this right, and it adds unnecessary code into your intersection program that will slow it down. Since OptiX already does this for you, it’s best to use the OptiX attribute mechanism.

The attributes functionality is optimized to keep the data in registers instead of in memory, so using attributes will not only be easier and safer, it will also be faster due to less memory bandwidth. You should only consider writing values from the intersector into memory if you have more than 8 values (32 bytes of data total) that you need to report.

If you are looking up a per-object emission value from a table of materials or from your shader binding table, it will be better to perform this lookup in your closest hit program than in your intersection program. Try to avoid doing operations in your intersector than can be done in your closest hit or anyhit programs. This is similarly because the intersector will be called far more often than the closest hit shader, so it’s much better to do it only once when the value is needed, than to calculate the data in the intersection program only to throw it away most of the time. A good example of this is computing a surface normal. Even if you have the data to compute the normal in the intersector, it’s usually faster to recompute or “rematerialize” all the data you need to compute a normal in your closest hit program instead of passing from the intersector to the hit shader.


1 Like

More info:


Thank you @dhart the response. The information is very useful and much appreciated.