Question about GTC2016 material

Hi, I have a question about the material as in the topic’ title.

In the page 26 (titled Closest Hit Program), BSDF Sampling and BSDF Evaluation are listed as bindless callable program.
These procedures should require different parameters depending on the BSDF type (Lambert, Oren-Nayer, GGX-Microfacet or Disney BRDF …).

Are those parameters fetched once at “Material Hierarchy Traversal”?
And does this closest hit program have enough-sized general purpose buffer to store various parameters?

(Difference BRDF types should requires different number of parameters = different sizes of buffer)
case Lambert: albedo
case Oren-Nayer: albedo, roughness
case GGX-microfacet: IoR, roughness(x, y)
case Disney: baseColor, subsurface, metallic, specular, specularTint, roughness, anisotropic, sheen, sheenTint, clearCoat, clearCoatGloss


Yes, material parameters can be completely different and unfortunately arbitrary many among MDL materials and it’s much more complicated for arbitrarily layered BSDFs.

What I’ve implemented as MaterialParameter structure is an array of 64 floats which was plenty enough to hold all parameters I’ve ever encountered.

All different material parameters structs in a scene were simply held in a single context global buffer (user format with element size sizeof(MaterialParameter)) and a single material index is all I need to combine shaders and material parameter blocks.

The bindless callable programs implementing the material hierarchy traversal (some call that material configuration) knew where which parameter was stored in the MaterialParameter struct, because the description to tell the host where to put them and the macros in the device code where to fetch them from was all auto-generated.

That float array is implemented as a union, basically like this:

#define NUMBER_OF_SLOTS  64

struct MaterialParameter
    int           i1[NUMBER_OF_SLOTS];
    unsigned int  ui1[NUMBER_OF_SLOTS];
    float         f1[NUMBER_OF_SLOTS];

    optix::int2   i2[NUMBER_OF_SLOTS >> 1];
    optix::float2 f2[NUMBER_OF_SLOTS >> 1];

    optix::float4 f4[NUMBER_OF_SLOTS >> 2];
  } u;

Parameters were tightly packed according to their CUDA alignment requirements!
float3 parameters where put into the .xyz slots of a free float4.
The .w component could then be used for any of the int, uint, or float parameters.

Works the same way in my OptiX Introduction examples, just with a smaller hardcoded MaterialParameter struct. See second sticky post in this sub-forum for links to the source code.

“And does this closest hit program have enough-sized general purpose buffer to store various parameters?”

Yes, the single(!) closest hit program used another structure (“Traversal”) to hold the user defined material parameters (and some other results), copied from the global parameter block into that inside the getter functions on initial invocation of the bindless callable program traverser().
That handled different cases controlled by a flag, e.g the anyhit only needed to calculate the cutout opacity.

That array of Traversal structs was limited to 16 nodes in the material hierarchy and each of them could hold one set of whatever MDL bsdf, layer, mixer, modifier node was used. That was also defined as a union.
I’ve not encountered an MDL material which used more nodes at once. It was also just a simple compile time define to size that.

Note that I implemented an own MDL to CUDA translation at that time and that was hitting some MDL SDK limitations not actually providing some of the information I needed.
Newer MDL SDKs actually have a PTX backend implemented which can generate PTX code for OptiX. Have a look into the MDL SDK examples and the OptiX 5.1.0 SDK examples and the threads in the MDL SDK forum for more information.

It should be possible to make that parameter handling more explicit per generated material shader, including lifting the limit of the number of parameters. Though, careful, that has a dependency on the OptiX stack size requirements then.
The init() function in the MDL SDK generated PTX code should do that one-time fetch of the parameters.
I have not used that method myself so far.

Thank you for detailed reply!

I implemented a struct like MaterialParameter you wrote and make a renderer have it in a global(context-scope) buffer.
I also implemented a generic struct which is constructed from a MaterialParameter struct. The renderer uses this struct in BSDF evaluation (for NEE) and sampling (for a next direction and implicit light sampling).

Now all the optix::Material’s used in my renderer system have the same single common closest hit / any hit program.
I feel that there is almost no unique role for each Material in the case for writing path tracer.
The only one difference between optix::Material’s is an assigned material index.

Right, and when you put that material index at the GeometryInstance then the Material node holds only the closesthit and anyhit programs.
That way I could reduce the number of Material nodes in my OptiX Introduction examples to exactly two, for opaque and cutout opacity materials, when not counting the material for the parallelogram area light, because in a more sophisticated material system like MDL, that also becomes part of the material properties.

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