Skip to content


Repository files navigation

Intel® Open Path Guiding Library

This is release v0.6.0 of Intel® Open PGL. For changes and new features, see the changelog. Visit for more information.


The Intel® Open Path Guiding Library (Intel® Open PGL) implements a set of representations and training algorithms needed to integrate path guiding into a renderer. Open PGL offers implementations of current state-of-the-art path guiding methods, which increase the sampling quality and, therefore, the efficiency of a renderer. The goal of Open PGL is to provide implementations that are well tested and robust enough to be used in a production environment.

The representation of the guiding field is learned during rendering and updated on a per-frame basis using radiance/importance samples generated during rendering. At each vertex of a random path/walk, the guiding field is queried for a local distribution (e.g., incident radiance), guiding local sampling decisions (e.g., directions).

Currently supported path guiding methods include: guiding directional sampling decisions on surfaces and inside volumes based on a learned incident radiance distribution or its product with BSDF components (i.e., cosine lobe) or phase functions (i.e., single lobe HG).

Open PGL offers a C API and a C++ wrapper API for higher-level abstraction. The current implementation is optimized for the latest Intel® processors with support for SSE, AVX, AVX2, and AVX-512 instructions.

Open PGL is part of the Intel® oneAPI Rendering Toolkit and has been released under the permissive Apache 2.0 license.

Example rendering without and with Open PGL
Path traced image of a variation of the Nishita Sky Demo scene from Blender Studio (CC0) without and with using Open PGL to guide directional samples (i.e., on surfaces and inside the water volume).


The current version of Open PGL is still in a pre v1.0 stage and should be used with caution in any production related environment. The API specification is still in flux and might change with upcoming releases.

Version History

Open PGL 0.6.0

  • Api changes:
    • Device added numThread parameter (default = 0) to the constructor to set the number of threads used by Open PGL during training. The default value of 0 uses all threads provided by TBB. If the renderer uses TBB as well and regulates the thread count this count is also used by Open PGL.
    • SurfaceSamplingDistribution and VolumeSamplingDistribution:
      • Added GetId function to return the unique id of the spatial structure used to query the sampling distriubtion.
    • Field and SampleStorage, added Compare function to check if the data stored in different instances (e.g., generated by two separate runs) are similar (i.e., same spatial subdivisions and directional distributions).
    • Field:
      • The constructor of the Field class now takes a FieldConfig instead of a PGLFieldArguments object. (BREAKING API CHANGE)
      • GetSurfaceStatistics and GetVolumeStatistics functions are added to query statistics about the surface and volume guiding field. The functions return a FieldStatistics object. Note, querying the statistics of a Field introduces a small overhead.
    • FieldStatistics:
      • This class store different statistics about a Field, such as, number and size of spatial nodes, statistics about the directional distributions, and the times spend for full and separate steps of the last Update step. The statistics can be queried as a full string (useful for logging) or as CSV strings (useful for analysis and plotting).
      • ToString: Returns a string printing all statistics.
      • HeaderCSVString: Returns the CSV header sting with the names of each statistic.
      • ToCSVString: Returns the CSV value sting of each statistic.
    • FieldConfig:
      • This class is added to replace the PGLFieldArguments struct when using the C++ API. -Init: the function initializes the parameters of the FieldConfig (i.e., similar to pglFieldArgumentsSetDefaults). Additional parameters (deterministic and maxSamplesPerLeaf) are introduced to enable deterministic behavior and to control the spatial subdivision granularity. -SetSpatialStructureArgMaxDepth: this function can be called after Init to the the maximum tree depth of the spatial structure.
    • pglFieldArgumentsSetDefaults: Adding two additional parameters deterministic and maxSamplesPerLeaf. (BREAKING API CHANGE)
  • Tools:
    • Added a set of command line tools which are build when enabling the OPENPGL_BUILD_TOOLS Cmake flag.
      • openpgl_bench: Tool to time different components of Open PGL such as the full training of a Field or the querying (initialization) of SamplingDistributions.
      • openpgl_debug: Tool to validate and compare stored SampleStorage and Field objects or retrain a Field from scratch using multiple stored sets (iterations) of stored samples.
  • Optimizations:
    • Spatial structure (Kd-tree) build is now fully multithreaded. This improves training performance on machines with higher core counts, especially when using deterministic training.
    • Kd-tree switched to use cache-friendlier TreeLets instead of single TreeNode structures.
  • Bugfixes:
    • Field fixed some non-deterministic behavior when spatial cache does not receive any training data during a training iteration due to a large number of training iterations.
    • Removed legacy/broken support for OpenMP threading since there is a dependency to TBB anyway.
    • Fixed build problems on (non-Mac) ARM systems.

Open PGL 0.5.0

  • Api changes:

    • PathSegmentStorage:
      • Removed support for splatting training samples due to the fact that knn-lookups have proven to be better. Therefore, the function attributes splatSamples and sampler have been removed from the PrepareSamples function.

      • Added PropagateSamples method prepare and push samples to the SampleStorage The goal is to replace PrepareSamples, GetSamples and AddSamples.

    • Sampler:
      • Removed since it is not used/needed anymore.
    • SurfaceSamplingDistribution and VolumeSamplingDistribution:
      • The usage of parallax-compensation is now connected to the guiding distribution type. Therefore the explicit useParallaxCompensation parameter is removed from the Init functions of the SamplingDistributions.
      • Added IncomingRadiancePDF function that returns an approximation of the incoming radiance distribution. This PDF does not need to be related to the actual sampling PDF but can be used for Resampled Importance Sampling (RIS).
    • Field:
      • Adding UpdateSurface and UpdateVolume function to update/train the surface and volume field separately.
    • SampleStorage:
      • Adding ClearSurface and ClearVolume function to clear the surface and volume samples separately. This allows to wait until a specific number of samples is collected for the surface or volume cache before updating/fitting the Field.
  • Deactivating/removing OMP threading support since it would still have a dependency on TBB

  • Bugfixes:

    • Fixing bug causing crash during Field::Update when in previous iterations no volume or surface samples were present.

Open PGL 0.4.1

  • Bugfixes:
    • Fixing bug introduced in 0.4.0 when using ApplySingleLobeHenyeyGreensteinProduct() for VMM-based representations

Open PGL 0.4.0

  • Performance:

    • Optimized KNN lookup of guiding caches (x3 speed-up).
    • Optimized Cosine product for VMM based representations.
  • Dependencies:

    • Removed the Embree library dependency for KNN lookups in favour of the header-only library nanoflann.
  • Adding ARM Neon support (e.g., Apple M1).

  • Fixing memory alignment bug for higher SIMD widths.

  • PathSegmentStorage:

    • Fixing bug when multiple refracted/reflected events hit a distant source (i.e., environment map) by clamping to a max distance.
    • Adding GetMaxDistance and SetMaxDistance methods.
    • Adding GetNumSegments and GetNumSamples methods.
  • Field:

    • Stopped tracing a total number of spp statistic since it is not really useful.
      • Removed the GetTotalSPP function.
      • Removed the numPerPixelSamples parameter from the Update function.

Open PGL 0.3.1

  • Field:
    • Added Reset() function to reset a guiding field (e.g., when the lighting or the scene geometry changed)
  • PathSegmentStorage:
    • Fixed bug when using AddSample()

Open PGL 0.3.0

  • Added CMake Superbuild script to build Open PGL, including all its dependencies.
    The dependencies (e.g., TBB and Embree) are downloaded, built, and installed automatically.

  • Added support for different SIMD optimizations (SSE, AVX2, AVX-512). The optimization type can be chosen when initializing the Device.

  • Added support for directional quadtrees for the directional representation.

  • PathSegmentStorage:

    • Added debug function CalculatePixelEstimate to validate if the stored path segment information represents the sampling behavior of the render (i.e., the resulting RGB value should match the pixel value the renderer adds to the framebuffer)
  • SurfaceSamplingDistribution:

    • Added support for guiding based on the product of a normal-oriented cosine lobe and the incident radiance distribution: (ApplyCosineProduct) This feature is only supported for VMM-based directional distributions. Support can be checked with SupportsApplyCosineProduct().
  • VolumeSamplingDistribution:

    • Added support for guiding based on the product of a single lobe HG phase function and the incident radiance distribution: ApplySingleLobeHenyeyGreensteinProduct() This feature is only supported for VMM-based directional distributions. Support can be checked with SupportsApplySingleLobeHenyeyGreensteinProduct().

Open PGL 0.1.0

  • Initial release of Open PGL Features:
    • Incremental learning/updating of a 5D spatio-directional radiance field from radiance samples (see Field).
    • Directional representation based on (parallax-aware) von Mises-Fisher mixtures.
    • PathSegmentStorage is a utility class to help keep track of all path segment information and generate radiance samples when a path/random walk is finished/terminated.
    • Support for guided importance sampling of directions on surfaces (see SurfaceSamplingDistribution) and inside volumes (see VolumeSamplingDistribution)
  • Added C-API and C++-API headers
    • C-API: #include <openpgl/openpgl.h>
    • C++-API: #include <openpgl/cpp/OpenPGL.h> and the namespace openpgl::cpp::

Support and Contact

Open PGL is under active development. Though we do our best to guarantee stable release versions, a certain number of bugs, as-yet-missing features, inconsistencies, or any other issues are still possible. Should you find any such issues, please report them immediately via Open PGL’s GitHub Issue Tracker (or, if you should happen to have a fix for it, you can also send us a pull request).


   Author = {Herholz, Sebastian and Dittebrandt, Addis},
   Year = {2022},
   Note = {},
   Title = {Intel{\textsuperscript{\tiny\textregistered}}
 Open Path Guiding Library}

Building Open PGL from source

The latest Open PGL sources are always available at the Open PGL GitHub repository. The default main branch should always point to the latest tested bugfix release.


Open PGL currently supports Linux and Windows. In addition, before building Open PGL you need the following prerequisites:

  • You can clone the latest Open PGL sources via:

    git clone
  • To build Open PGL you need CMake, any form of C++11 compiler (we recommend using GCC, but also support Clang and MSVC), and standard Linux development tools.

  • Open PGL depends on Embree, which is available at the Embree GitHub repository.

  • Open PGL depends on TBB, which is available at the TBB GitHub repository.

Depending on your Linux distribution, you can install these dependencies using yum or apt-get. Some of these packages might already be installed or might have slightly different names.

CMake Superbuild

For convenience, Open PGL provides a CMake Superbuild script which will pull down Open PGL’s dependencies and build Open PGL itself. The result is an install directory including all dependencies.

Run with:

mkdir build
cd build
cmake ../superbuild
cmake  --build .

The resulting install directory (or the one set with CMAKE_INSTALL_PREFIX) will have everything in it, with one subdirectory per dependency.

CMake options to note (all have sensible defaults):

  • CMAKE_INSTALL_PREFIX will be the root directory where everything gets installed.
  • BUILD_JOBS sets the number given to make -j for parallel builds.
  • INSTALL_IN_SEPARATE_DIRECTORIES toggles installation of all libraries in separate or the same directory.
  • BUILD_TBB_FROM_SOURCE specifies whether TBB should be built from source or the releases on GitHub should be used. This must be ON when compiling for ARM.

For the full set of options, run ccmake [<PGL_ROOT>/superbuild].

Standard CMake build

Assuming the above prerequisites are all fulfilled, building Open PGL through CMake is easy:

Create a build directory, and go into it:

        mkdir build
        cd build

Configure the Open PGL build using:

        cmake -DCMAKE_INSTALL_PREFIX=[openpgl_install] ..
  • CMake options to note (all have sensible defaults):

    • CMAKE_INSTALL_PREFIX will be the root directory where everything gets installed.

    • OPENPGL_BUILD_STATIC if Open PGL should be built as a static or shared library (default OFF).

    • OPENPGL_ISA_AVX512 if Open PGL is compiled with AVX-512 support (default OFF).

    • OPENPGL_ISA_NEON and OPENPGL_ISA_NEON2X if Open PGL is compiled with NEON or double pumped NEON support (default OFF).

    • OPENPGL_LIBRARY_NAME: Specifies the name of the Open PGL library file created. By default the name openpgl is used.

    • OPENPGL_TBB_ROOT location of the TBB installation.

    • OPENPGL_TBB_COMPONENT the name of the TBB component/library (default tbb).

Build and install Open PGL using:

        cmake build
        cmake install

Including Open PGL into a project

Including into CMake build scripts.

To include Open PGL into a project which is using CMake as a build system, one can simply use the CMake configuration files provided by Open PGL.

To make CMake aware of Open PGL’s CMake configuration scripts the openpgl_DIR has to be set to their location during configuration:

cmake -Dopenpgl_DIR=[openpgl_install]/lib/cmake/openpgl-0.6.0 ..

After that, adding OpenPGL to a CMake project/target is done by first finding Open PGL using find_package() and then adding the openpgl:openpgl targets to the project/target:

# locating Open PGL library and headers 
find_package(openpgl REQUIRED)

# setting up project/target
add_executable(myProject ...)

# adding Open PGL to the project/target
target_include_directories(myProject openpgl::openpgl)

target_link_libraries(myProject openpgl::openpgl)

Including Open PGL API headers

Open PGL offers two types of APIs.

The C API is C99 conform and is the basis for interacting with Open PGL. To use the C API of Open PGL, one only needs to include the following header:

#include <openpgl/openpgl.h>

The C++ API is a header-based wrapper of the C API, which offers a more comfortable, object-oriented way of using Open PGL. To use the C++ API of Open PGL, one only needs to include the following header:

#include <openpgl/cpp/OpenPGL.h>


The API specification of Open PGL is currently still in a “work in progress” stage and might change with the next releases - depending on the community feedback and library evolution.

We, therefore, only give here a small overview of the C++ class structures and refer to the individual class header files for detailed information.


#include <openpgl/cpp/Device.h>

The Device class is a key component of OpenPGL. It defines the backend used by Open PGL. OpenPGL supports different CPU backends using SSE, AVX, or AVX-512 optimizations.

Note: support for different GPU backends is planned in future releases.


#include <openpgl/cpp/Field.h>

The Field class is a key component of Open PGL. An instance of this class holds the spatio-directional guiding information (e.g., approximation of the incoming radiance field) for a scene. The Field is responsible for storing, learning, and accessing the guiding information. This information can be the incidence radiance field learned from several training iterations across the whole scene. The Field holds separate approximations for surface and volumetric radiance distributions, which can be accessed separately. The representation of a scene’s radiance distribution is usually separated into a positional and directional representation using a spatial subdivision structure. Each spatial leaf node (a.k.a. Region) contains a directional representation for the local incident radiance distribution.


#include <openpgl/cpp/SurfaceSamplingDistribution.h>

The SurfaceSamplingDistribution class represents the guiding distribution used for sampling directions on surfaces. The sampling distribution is often proportional to the incoming radiance distribution or its product with components of a BSDF model (e.g., cosine term). The class supports functions for sampling and PDF evaluations.


#include <openpgl/cpp/VolumeSamplingDistribution.h>

The VolumeSamplingDistribution class represents the guiding distribution used for sampling directions inside volumes. The sampling distribution is often proportional to the incoming radiance distribution or its product with the phase function (e.g., single lobe HG). The class supports functions for sampling and PDF evaluations.


#include <openpgl/cpp/SampleData.h>

The SampleData struct represents a radiance sample (e.g., position, direction, value). Radiance samples are generated during rendering and are used to train/update the guiding field (e.g., after each rendering progression). A SampleData object is created at each vertex of a random walk/path. To collect the data at a specific vertex, the whole path (from its endpoint to the current vertex) must be considered, and information (e.g., radiance) must be backpropagated.


#include <openpgl/cpp/SampleStorage.h>

The SampleStorage class is a storage container collecting all SampleData generated during rendering. It stores the (radiance/photon) samples generated during rendering. The implementation is thread save and supports concurrent adding of samples from multiple threads. As a result, only one instance of this container is needed per rendering process. The stored samples are later used by the Field class to train/learn the guiding field (i.e., radiance field) for a scene.


#include <openpgl/cpp/PathSegmentStorage.h>

The PathSegmentStorage is a utility class to help generate multiple SampleData objects during the path/random walk generation process. For the construction of a path/walk, each new PathSegment is stored in the PathSegmentStorage. When the walk is finished or terminated, the -radiance- SampleData is generated using a backpropagation process. The resulting samples are then be passed to the global SampleDataStorage.

Note: The PathSegmentStorage is just a utility class meaning its usage is not required. It is possible to for the users to use their own method for generating SampleData objects during rendering.


#include <openpgl/cpp/PathSegment.h>

The PathSegment struct stores all required information for a path segment (e.g., position, direction, PDF, BSDF evaluation). A list of succeeding segments (stored in a PathSegmentStorage) is used to generate SampleData for training the guiding field.

Projects that make use of Open PGL


Projects that are closely related to Open PGL