Custom GAS data for multiple Mesh Primitives

Hello.

What would be the best way to build a accel structure where a mesh has multiple primitives, and pass a per geometry chunk of data which can then be accessed by optixGetGASPointerFromHandle.

Currently having to create one gas + geometry data per primitive, and then create a OptixInstance with the gas handle, even for a mesh with multiple primitives.

If a gas handle is created by passing multiple build inputs to optixAccelBuild, and their respective geometry chunks copied back to back preceding the gas handle, how can the chunks be indexed into in the optix programs after getting the pointer using optixGetGASPointerFromHandle. Is there a way to know which mesh primitive is hit using optix functions ?

So I feel multiple GAS handles per OptixInstance would be useful, so that each gas handle with custom data can be accessed using optixGetGASPointerFromHandle and also there is no duplication of OptixInstance.

I hope I have been able to bring out the point clearly.

Cheers and Thank You

1 Like

e.g.

The following scene file is a gltf export containing two instances of one cube mesh.

scene.zip (1.4 KB)

the cube mesh has 4 materials applied to its 6 faces. Thus there are 4 mesh primitives.

8 OptixInstance(s) are required to render this completely.

Can the instance count be brought down to 2, one for each world instance. and each of the world OptixInstance would take an array of gas handles, each of which created from each primitive in the mesh.

Cheers.

1 Like

The way such glTF mesh primitives can be combined in a single GAS is via multiple build inputs to the OptiX acceleration structure.
Each build input contains the geometric primitives assigned to a specific material which can be handled with individual shader binding table (SBT) entries, one per glTF mesh primitives.
Means while you would only have one GAS per object (the cube) and one OptiX instance placing it into the scene, multiple materials can be used on one object.

This older post explains it: https://forums.developer.nvidia.com/t/load-scene-with-optix/291957/2

How that is done, is shown inside the OptiX SDK optixMeshViewer example (which is not implementing the glTF spces completely and doesn’t handle all glTF files) and inside the much more sophisticated GLTF_renderer example inside the OptiX Advanced Examples described here:
https://forums.developer.nvidia.com/t/optix-advanced-samples-on-github/48410/16
Link to the github repository in the 4th post of that thread.

You would need to look at the Application::buildDeviceMeshAccel() function inside the Application.cpp file for the build input setup. The application’s DeviceMesh structure contains the glTF data according to OptiX’ needs.

There is no need to use optixGetGASPointerFromHandle with the proper SBT setup, instead optixGetSbtDataPointer is enough to fetch information about the geometric primitives and assigned materials.
as used here inside the closest hit program: https://github.com/NVIDIA/OptiX_Apps/blob/master/apps/GLTF_renderer/cuda/hit.cu#L1406

2 Likes

Hello.

thank you for your inputs.

Currently facing this situation. dealing with closesthit shaders only for now.

scene has 2 meshes with 2 materials attached to each of them.

2 instances each referencing 1 mesh from above.

1st instance has sbtOffset=0;

2nd instance has sbtOffset=2;

Sbt is setup with 4 hit group entries + 1 raygen + 1 miss

if the following is called

	optixTrace(lp.handle, r.org, r.dir, 0.f, 1000.f, 0.f, 0xFF, 0, 0, 0, 0);

the first sbt record attached for each mesh is called for the entire mesh

and if the following is called

	optixTrace(lp.handle, r.org, r.dir, 0.f, 1000.f, 0.f, 0xFF, 0, 1, 0, 0); // rayStride = 1

the second material attached for each mesh i called for the entire mesh.

As if the sbt-geometry-acceleration-structure-index from the following does not exist.

sbt-index = sbt-instance-offset + (sbt-geometry-acceleration-structure-index * sbt-stride-from-trace-call) + sbt-offset-from-trace-call

Any ideas ?

Cheers.

You put the stride into the ray’s sbtOffset argument. There needs to be another argument for the rayFlags before that to have the ray stride of 1 in the right argument position.

See https://raytracing-docs.nvidia.com/optix9/api/group__optix__device__api.html#gaba3c9237619374478bd6efce63af8145

Example: https://github.com/NVIDIA/OptiX_Apps/blob/master/apps/GLTF_renderer/cuda/raygen.cu#L182

(Also no payload arguments? How do you determine which material is assigned when not reporting something back to the optixTrace caller?)

1 Like

haha yes.

i got lost in all the zeros towards the end. Realized it later.

Working as expected now.

I’m storing the material index for each prim in the hit record. and passing the materials array in the launch params, which is then indexed into on every hit.

Similar strategy for textures used by materials. Materials have indices for their textures used with the textures array, passed to launch params.

Minimal duplication.

I was using payloads to get the final color of the rays (ch and miss). rgba floating point. That would let me output just 8 passes per frame which would be limiting in the long run. So abandoned the payloads idea and am writing the passes pixels from the ch shader into the image array. Still early days for the renderer. so the passes data is a basic sampling from the textures.

Will need payloads when the rays start to bounce around, and need to keep track of the damage. haha

Cheers

Not sure what passes you’re storing per launch but if you need more per ray payload than the 32 unsigned int registers the optixTrace supports, you can allocate a local structure inside the raygeneration program and put the 64 bit pointer to that into two of the payload registers.

Look for pack/unpackPointer inside the OptiX SDK examples or for split/mergePointer inside the advanced samples. Comes with a small performance tradeoff due to the indirection and memory accesses though. It’s good practice to keep things in registers if they fit. You can also mix these methods and keep often used payload fields like some flags inside registers.

I would also recommend to write final results to output buffers only inside the raygeneration program to access once to reduce global memory acesses.

Another aproach for multiple passes is to simply do more optixLaunch calls with different behaviors. All the progressive path tracers do that.

1 Like

Apologies… By passes i meant render passes / layers, also called AOV common in offline renderers.

e.g. frame reflections, frame Normals, frame UVs, or any per frame data which could help during the compositing stage, rendered out as separate images or layers in an multilayer file (EXR). which are then used in a compositing software.

currently there are pixels allocated for each of the passes, and passed as an array via launch params. and the ‘layers’ are written to at different times, e.g. normal and uv can be written to inside the ch program. since that is when the data is available. whereas shadow pass would be got after the shadow ray trace is completed, and reflection data would be updates after all the bounces of the ray are computed.

Cheers

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