Hello, I am with a research group implementing physically accurate GPU accelerated simulations for light in biological tissues. I am trying to implement ray tracing of linear curve primitives, and for this I will need to trace intersections of rays originating from inside the linear curve primitives. Is there a feature that can disable back-face culling, and is it present in OptiX 7.6.0?
Currently through OptiX 7.7 as well as the next unreleased version of OptiX, the curve primitives (including linear) are always as ‘backface culled’ as possible meaning that rays originating from inside the curve primitive will try to ‘exit’ without hitting the primitive surface. Only rays originating from outside the surface will register hits when the rays ‘enter’ the surface. This exit-culling isn’t really guaranteed, it’s more of a best-effort, and it happens to help a lot with issues like self-shadowing. Our curve primitives are designed primarily for far-away viewing, for hair rendering or something similar (fur, carpet, fuzzy cloth, etc.), where the curves are typically the width of a few pixels down to sub-pixel size on-screen.
Adding some API to disable the exit culling is something we can consider, but just to be honest, it could take a while to decide, and implement, and release. You are the first person to request it. ;) We would need time to discuss the feasibility, and of course I can’t guarantee we’d agree to it, in part because it might not be feasible for the cubic and quadratic primitive types. One alternative might be to design a custom primitive intersection program that handles the shape & culling behavior you need.
I’m curious to hear more about what you need, if you’re able & willing to share such info. Do you need exit hits for handling refractions? Are the capsule shaped OptiX linear primitive exactly what you need, or will they approximate a curved surface? If you’re modeling tubular structures with them, then presumably you’d chain them end-to-end similar to what we do in our hair sample? In that scenario, rays originating from inside one primitive might hit the endcap of a neighboring primitive before they hit the inside surface of the enclosing capsule. Do you expect to encounter that, and would you need to do something special to handle that situation? Do you need to try to guarantee that all rays will stop at an exit point, or will catching “most” of them be sufficient?
To give a simple summary:
Our program is stepping rays through different 3D volumes of materials and re-launching them in random directions with step sizes based on the underlying properties of the materials. At each step, data on volumetric absorption of the light ray is saved.
We are implementing this by using primitives to bound the different materials, and using closest_hit to quickly search for material changes as the rays move around. The long term idea is to use NVIDIA hardware to accelerate very complex biological geometries like 3D blood vessel networks which would be slow to model and ray trace without primitives and acceleration structures.
Currently we implemented spheres superimposed on a mesh defined by closed surfaces of OptiX triangles. The simulation logic is that the implicitly defined geometries just override the properties of any underlying triangle meshes. This means that intersections from primitive to primitive should not make a difference.
My impression was that we could chain together segments of the linear curves defined by 2 vertices, to create approximations of biological tubular structures. If we can trace back-facing hits on the linear curve the same way as spheres, this should work. Is it feasible to create a custom primitive that works almost identically to linear curves but does not have back-face culling?
Thanks, I will take this info to the team and we’ll talk about the feasibility of exposing exit hit behavior in the API. Yes, it’s possible to do what you want with a custom primitive. Would it work in your case if you had to specify in your optixTrace() call whether you wanted enter or exit hits, and you could only choose one at a time? (I think this implies that your code would need to know whether you were originating inside or outside of a primitive.)
To follow up, I am thinking of modifying our closesthit program to check for intersections with curve primitives, then loop through a custom ray tracing function until the ray exits the curve or enters a new curve.
What would be the most efficient/easiest way to obtain the primitive type and data for a closesthit? Would optixGetLinearCurveVertexData() work for this application?
To follow up, I am thinking of modifying our closesthit program to check for intersections with curve primitives, then loop through a custom ray tracing function until the ray exits the curve or enters a new curve.
That sounds strange and would basically need to calculate the ray intersections with the backfaces of the linear curves, and if you can do that, it would make much more sense to implement that inside a custom intersection program for the geometric primitive behavior you require. Then the rest of your ray tracing algorithms wouldn’t need to change at all.
What would be the most efficient/easiest way to obtain the primitive type and data for a closesthit? Would optixGetLinearCurveVertexData() work for this application?
The other way would be to store the vertex attributes into device buffers (where you needed the vertex positions anyway to build the AS) and then fetch each intersected primitive’s attributes from those device buffers with the proper pointers and the intersected primitive ID.
For example, I’m doing that for cubic B-spline curves here: https://github.com/NVIDIA/OptiX_Apps/blob/master/apps/MDL_renderer/shaders/hit.cu#L1314
That example is using a specific closest hit program for these curve primitives because the MDL shader is different for hair, so it knows what primitive type it intersected.
But if you want to reuse the same closest hit program for different geometric primitives, means the Shader Binding Table has hit records with different intersection programs but the same closest hit program, you use the optixGetHitKind information to determine which primitive type you hit via optixGetPrimtiveType
The same hit kind result also allows checking for the face side when the primitive supports it (built-in triangles and spheres do).
Inside the hit kind there are some values (0 - 127) reserved for custom primitives, which you can use inside optixReportIntersection to communicate what custom primitive type was intersected and on which side if you need that.
See: https://raytracing-docs.nvidia.com/optix7/guide/index.html#device_side_functions#intersection-information