MDL SDK API nvidia_logo_transpbg.gif Up
Example for a Microsoft DXR-based MDL-enabled Path Tracer
[Previous] [Up] [Next]

This example demonstrates how to integrate MDL into a real-time path tracer that is able to render scenes provided in the glTF format. The rendering framework is based on Microsoft DirectX Raytracing (DXR) and utilizes the MDL HLSL backend to generate HLSL code for evaluating expressions and distribution functions directly inside shaders.

New Topics

  • Integration of MDL in a real-time renderer
  • Using the glTF metallic-roughness support material, as well as other MDL materials and MDLE
  • Importing the NV_materials_mdl glTF vendor extension

Detailed Description

The Example's Rendering Framework


In order to illustrate the use of MDL in the context of real-time rendering and in scenes consisting of multiple objects with different materials, at least a small rendering framework that handles scene loading and the main application loop is required. To concentrate on the MDL features, most parts of the low-level Direct3D API have been hidden behind classes that provide a more abstract view, which in turn makes it easier to communicate how MDL fits into the bigger picture. To get started with DXR and for more detailed information on the Direct3D API it is recommended to look into other resources such as the NVIDIA DX12 Raytracing tutorial and the Microsoft DirectX-Graphics-Samples.

The parts of the framework that are important for the MDL integration can be found in a few source files: mdl_d3d12/mdl_material.h, mdl_d3d12/mdl_material.cpp, and example_dxr.cpp. While the latter contains the main application that triggers the scene loading, the pipeline setup as well as the update and rendering loop, the first two files define classes for the MDL SDK interface, the MDL target code with a link unit and most importantly the Mdl_material class which handles the loading and parameterization of the scenes materials.

The essential parts of the renderer are implemented in HLSL. In the hit shaders (content/mdl_hit_programs.hlsl), the material is evaluated for the current intersection of the ray with the surface. Afterwards, the direction for the next segment of the ray path is computed. The interface for the evaluation corresponds to the one in the [df_cuda example]. The required glue-code and the runtime functions for HLSL can be found in the content/mdl_target_code_types.hlsl and content/mdl_renderer_runtime.hlsl.

The actual renderer will not have to call the contained functions directly, they are referenced by the generated code. Instead, the entry points into the generated code have to be invoked by the renderer. For instance:

mdl_bsdf_evaluate( // defined in the generated target code
scattering_function_index, // returned by link_unit::add_material(...)
// and passed to shader in constant buffer
eval_data, // local in/output struct prepared by the renderer
mdl_state); // contains information about the hit point and the material

The generated code is written to the binary directory for debugging and inspection purposes: link_unit_source.hlsl

Material parameters are mapped to a dear imgui user interface, just like in the [df_cuda example], so they can be changed at runtime. See mdl_d3d12/mdl_material_info.h, gui.h, and gui.cpp for more details.

Specifying MDL Materials in glTF scenes


The example supports importing the NV_materials_mdl glTF vendor extension which adds support for MDL to the glTF scene format. The extension allows the definition of function call graphs in the glTF file, which can be referenced by the glTF material nodes. Please refer to mdl_d3d12/mdl_material_description.cpp and mdl_d3d12/gltf_extensions.h for details on how to integrate the extension.

Here is an example of a glTF material node with the MDL extension that references a MDL material function call defined in the glTF file:

{
"name": "ShaderBall",
"extensions": {
"NV_materials_mdl": {
"functionCall": 0
}
}
}

glTF provides a set of parameters that are intended to be used in physically-based rendering (PBR) and is therefore compatible with MDL. This PBR material also acts as a fallback material for the case that the NV_materials_mdl extension is not used. Here is an example for a glTF material node with the PBR model:

{
"name": "ShaderBall",
"doubleSided": true,
"emissiveFactor": [ 0.0, 0.0, 0.0 ],
"normalTexture": {
"index": 1,
"scale": 1,
"texCoord": 0
},
"pbrMetallicRoughness": {
"baseColorFactor": [ 1.0, 1.0, 1.0, 1.0 ],
"baseColorTexture": {
"index": 2,
"texCoord": 0
},
"metallicFactor": 1.0,
"roughnessFactor": 1.0,
"metallicRoughnessTexture": {
"index": 0,
"texCoord": 0
}
}
}

Note, that textures are defined separately. They are referenced by the index property. While loading the scene, these parameters are parsed, the texture file paths are resolved and the support material defined in the provided gltf_support.mdl is parameterized accordingly.

To avoid the creation of redundant HLSL code class-compilation is used, which allows to change parameters at runtime. For each scene material that uses the glTF_support material the same HLSL code is executed, but the individual parameter sets, the argument blocks, are bound as buffers to the graphics pipeline.

Lastly, an arbitrary MDL material can be assigned by setting the name property of the glTF material node to a fully qualified absolute MDL material identifier like this:

{
"name": "::nvidia::sdk_examples::tutorials::dxr_sphere_mat"
}

All other material properties are ignored in that case. This means that the selected MDL material must provide default values for all its parameters.

The Example also supports the MDL encapsulated format. In this case, the file path of the MDLE has to be specified in the name property of the glTF material node. This file path can either be absolute or relative to the .gltf scene file:

{
"name": "glass.mdle"
}

Running the example


When running the DXR example, a simple sphere with an MDL material is rendered. Moving the mouse while pressing the left or the middle mouse button allows to rotate or pan the camera, while scrolling allows to move forward and back. Pressing space reveals the user interface that allows to change parameters of the material, the camera and the renderer. Pressing print writes a screenshot into the current working directory.

To load a different scene, the DXR example is started from the command line with an glTF file to load:

dxr.exe some_folder/my_scene.gltf

Various interesting scenes can be found online, for instance on sketchfab.com, so give it a try.

There are further options that can be used with the DXR example. Here are a few commonly used ones:

  • -h | --help
    Shows all options with a short explanation.
  • --mdl_path <value>
    By default, the example uses three default search paths. On the Windows platform this is the admin-space search path, e.g., C:\ProgramData\NVIDIA Corporation\mdl and the user-space search path %USERPROFILE%\Documents\mdl as well as scene folder that contains the loaded glTF. By passing the command line option --mdl_path <value>, this default behavior can be changed. The option can appear multiple times.
  • --hdr <filename>
    Path of an .hdr environment map that is used for image-based lighting and as background. The path can be absolute or relative to the scene file.
  • --mat <qualified_name>
    Override all materials in the scene using a qualified material name or an MDLE path.

Restrictions


The DXR example is a prototype renderer with certain limitations:

  • The encoding of MDL material qualifiers into the name property has obviously limitations. All names starting with :: literal are interpreted as MDL names and all names ending with .mdle are interpreted as file paths, which both are unlikely but not impossible to appear by accident.
  • The example is based on a very simple path tracer. Multiple importance sampling (MIS) is only used to sample the BSDFs and the environment light. Emissive surfaces however are only hit by chance and the progressive rendering requires more time to converge.
[Previous] [Up] [Next]