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.
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:
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.
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:
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:
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:
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:
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:
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:
.hdr environment map that is used for image-based lighting and as background. The path can be absolute or relative to the scene file.
The DXR example is a prototype renderer with certain limitations:
::
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.