Question about Voxel mesh and mesh adjustment

Dear all,

I am trying to use Optix 7 for physical simulation. My current model is using a 3D voxel mesh (a.k.a. uniform rectangular mesh) to represent some materials. I have three questions about how to represent the materials efficiently.

Firstly, do I need to make the surface of material as triangles? Or should I write my hit program for voxel? The native voxel mesh has six rectangle surfaces. This seems will increase memory usage by converting to triangle? If I understand correctly, a surface in voxel with 4 vertex will become two triangles with 6 vetex in total?

Also how to deal with rays that can penetrate the surface layer? Do I add internal vertex also to the AS?

Secondly, if I need to remove some mesh cell during simulation, do I need to rebuild the whole acceleration structure? To clarify, I want to add / remove voxel cells to / from the structure frequently.

Finally, the simulation domain has boundaries. How to deal with rays hit the boundary of domain? Is it possible to apply different boundary conditions?

Many thanks!

Please first follow this search for volume rendering on this OptiX sub-forum and read through the found threads.
That should answer most of your questions already.

Firstly, do I need to make the surface of material as triangles? Or should I write my hit program for voxel? The native voxel mesh has six rectangle surfaces. This seems will increase memory usage by converting to triangle? If I understand correctly, a surface in voxel with 4 vertex will become two triangles with 6 vertex in total?

It really depends on your use case which representation of the volume is best suited.
The ray tracing can only speed up intersections with geometry!
For volume rendering of a regular structure of volume bricks it might make sense to apply standard ray marching instead and track empty and filled volumes with some per-brick flag in a 3D spatial structure (3D buffer or texture).
Maybe even the volume data could be held in a 3D texture and you could ray march through that without any geometry involved.

If you want to generate the regular volume bricks as bounding surfaces, using triangles would be fully accelerated by the RT cores in RTX boards, while a custom box primitive intersection would only have acceleration of the BVH traversal.

Also how to deal with rays that can penetrate the surface layer? Do I add internal vertex also to the AS?

If you have surface geometry which can be penetrated by a ray, you normally reached the closest hit program, which then requires to shoot a new continuation ray on the other side of the surface into the volume for transmission events.
You’d need to track which volume the ray is currently in to handle these transmissions.
If surfaces are bounding volumes with different properties on either side, you would normally track the material (volume) properties of either side of the boundary surface.

Not sure what you mean with internal vertex.

Secondly, if I need to remove some mesh cell during simulation, do I need to rebuild the whole acceleration structure? To clarify, I want to add / remove voxel cells to / from the structure frequently.

If you change geometry you need to rebuild the respective AS.
If this is in some hierarchy of AS and the bounding box of any of the children changed, you also need to update all their parent AS.
If the number of volume cells is huge and you update only few cells, it might make sense to split the while volume into multiple AS spatially (e.g. octants) which would reduce the individual AS size.

Finally, the simulation domain has boundaries. How to deal with rays hit the boundary of domain? Is it possible to apply different boundary conditions?

You mean you have an irregular triangle mesh which encloses the volume?
Then using a ray tracer makes sense to hit that.
Not sure what boundary conditions you mean, but in any case OptiX is just a ray casting SDK and knows nothing about that. All behavior of the rays you shoot are fully under your control. You define the behaviour of “materials” and “volumes” with the device programs your develop

Thanks! My problem is a bit complicated. I will try the triangle solution first.

For each ray, does optix first check any_hit, then closest_hit? Is it possible to let the ray pass through some materials in any_hit e.g. skip surface of transparent materials? Will this give performance penalty?

For each ray, does optix first check any_hit, then closest_hit?

Yes, for each intersection it needs to check the anyhit program first if present, and that can happen multiple times along a ray before the closesthit or miss program is reached when present.
Note that the anyhit program invocation is not happening in ray direction order, but depends on the internal BVH traversal order which can be different per ray, and due to splitting BVH builders, the same primitive might get hit multiple times (from different BVH AABBs), which can be controlled via the OptixBuildInput flags (with a possible performance loss), here OPTIX_GEOMETRY_FLAG_REQUIRE_SINGLE_ANYHIT_CALL

Is it possible to let the ray pass through some materials in any_hit e.g. skip surface of transparent materials?

Yes, of course. That is done by calling optixIgnoreIntersection inside the anyhit program.

Example code doing a stochastic cutout opacity evaluation based on a texture map can be found in my OptiX 7 examples anyhit.cu

Will this give performance penalty?

Yes, that will have a very noticeable performance impact!
On RTX boards the hardware BVH traversal done inside the RT cores needs to be interrupted to call into the anyhit program which is executed on the streaming multiprocessors. For optimal performance it is recommended to avoid anyhit programs when possible.

OptiX 7 offers some rayFlags to stop traversal and cull primitives in optixTrace.
There is also the OptixVisibilityMask which allows to ignore intersections for specific geometry which would be faster than anyhit programs when it can be applied.

Follow this forum search for more infomation on anyhit programs:

Is there any documents for the SDK? For example the files in the sutil folder, e.g. Scene, MeshGroup.
Also what the other examples are doing? I only find an article for the optixTriangle example.

Not really. See this post: https://forums.developer.nvidia.com/t/sutil-documentation/158423/2

The OptiX SDK examples exactly show what their executable name implies.
If you need a step-by-step OptiX introduction, look the OptiX 7 SIGGRAPH course material in the sticky posts.
If you understood how the OptiX host and device API functions work in general, stepping through the OptiX SDK examples you’re interested in should clear things up, then have a look at the more advanced examples.

I would recommend having the online OptiX Programming Guide and API reference documentation open at the same time and use the search functionality in them for any OptiX call which happens inside the examples when single stepping through them.

Final question. If I want to update the mesh by adding or removing some triangles and re-built the AS, do I need to repeat the whole process of building the pipeline? Which part I only need to do once?

For example the optixTriangle example, I need to re-run the “accel handling”.
How about the “create module”, “program groups” and “Link pipeline”?

How about if I modified parameters in the Params?

Please read this chapter of the OptiX 7 Programming Guide:
https://raytracing-docs.nvidia.com/optix7/guide/index.html#acceleration_structures#dynamic-updates

When changing triangles in a geometry, you need to rebuild the Geometry Acceleration Structure (GAS) and if that GAS traversable is used inside OptixInstances, you also need to update or rebuild all Instance Acceleration Structures (IAS) which reference that GAS and their parent IAS when there is more than a single level.

An IAS rebuild is required when the child GAS traversable handle changes. It’s recommended when the AABB topology changes a lot for better performance of the AS layout. If not, you can also only update the IAS instead of rebuilding. Similarly when only changing vertex positions and not the mesh topology of a GAS, it’s enough to update the GAS.

GAS updates require that they were build with flag OPTIX_BUILD_FLAG_ALLOW_UPDATE.

If you store the vertex positions and other vertex attributes and the optional triangle indices of that changed geometry somewhere (normally inside the SBT data or for a single GAS maybe inside buffers of the launch parameters), then you would also need to update these buffers, resp. the pointers to them, with the new changed data.

If you only store triangle vertex positions, these can also be fetched inside your OptiX device code from the GAS itself.
For that it’s required to build the GAS with the flag OPTIX_BUILD_FLAG_ALLOW_RANDOM_VERTEX_ACCESS.

There is no need to compile modules or compile and link pipelines when these are not changing. You would only need to update the Shader Binding Table when that is necessary, e.g. if the SBT layout changed when adding whole new meshes with their own SBT entry.

The launch parameters need to contain the top-level traversable handle. If that changed when building an acceleration structure, you would need to set that before the next optixLaunch() call. It’s not changing when only updating an acceleration structure.