PTX generation for EDFs


I see in the SDK documentation that virtual const ITarget_code* mi::neuraylib::IMdl_backend::translate_material_df does not support EDFs.

For BSDFs, the outputs of this call are the functions "_init", "_sample", "_evaluate" and "_pdf".

I am wondering how I might generate the same set of functions, but for EDFs for use inside of an Optix shader? The optix example that ships with the SDK doesn’t handle EDFs so I’m wondering if this is currently completely unsupported?


Hi jagj,

sorry, the documentation is wrong. EDFs are supported by mi::neuraylib::IMdl_backend::translate_material_df as well as mi::neuraylib::ILink_unit::add_material_df and mi::neuraylib::ILink_unit::add_material.

In the “df_cuda” example, you can see how the generated functions are used.

Best regards

Hi Moritz,

Thank you, I was able to get it working. A followup question I have is that it looks like the backend will output those functions even if the material does not have an EDF; it looks like they are essentially no-ops. When doing my shading inside of my Optix shaders, is the expected use-case just that you call the functions regardless and if they are a no-op then there’s no performance hit?

The df_cuda example checks if the function ptr is not null, but I’m not sure if there’s a similar mechanism for an optix __direct_callable__ function.

Also, is the documentation wrong with respect to VDFs as well?

Hi jagj,


Well, if you request functions to be generated for an EDF, the backend will do so.
You could check, whether ICompiled_material::lookup_sub_expression() returns an IExpression_constant for surface.emission.emission. If so, the MDL just had edf() for this field.
Then you could store the info, that no emission is needed, in the corresponding HitGroupData object.

Calling a no-op function is not for free. When using direct callables, this cannot be optimized away.

optixDirectCall() receives an index into the callablesRecordBase array of your OptixShaderBindingTable. You could use an obviously invalid index here, like MAX_INT, and check for that before calling optixDirectCall(). It depends on your use case, whether this may be a better solution than a bool in the hit group data.

Btw, please make sure, you use the single-init mode when generating, as used the df_cuda example. Then only one init function is generated for a set of expressions for one material and the precalculated values can be used by all those expressions. Otherwise you have to call one init function for the bsdf part and another for the edf part and some calculations may be done multiple times.
You can enable the single-init mode by using the special expression path "init" as first element in the mi::neuraylib::Target_function_description list, which you can provide to the mi::neuraylib::ILink_unit::add_material() function.

No, the documentation about the VDFs is correct, we don’t generate code for them.

Best regards

For the volume, the reason is that the actual vdf is rather simple but usage highly depends on your volume integrator. Most renderers would likely have a ready to use volume integrator with a fixed vdf and would just use the volume coefficients from MDL.

Thanks for the info! I think I could probably store a -1 for the call index for materials with no EDF.

One followup question regarding the use of EDFs (this gets a little bit into the path tracing side of things): When doing light sampling of a triangle mesh, you would normally compute a CDF, use that to randomly sample a point on the mesh, and then your PDF is 1/area of the triangles. If you attach an EDF material to this mesh, would you only want to use the EDF for evaluation of the emission intensity and still use your own PDF? How exactly does the Edf_sample function compute its PDF?

Ah, that’s what the init does. Is that documented anywhere?

re: VDFs: That makes sense.

Thanks for the help