diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 4c0f312b..f8e0eb78 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -193,7 +193,7 @@ jobs: with: image: rockylinux:8.7 cmd: | - export OPENPGL_RELEASE_PACKAGE_VERSION=0.7.1 + export OPENPGL_RELEASE_PACKAGE_VERSION=0.8.0 scripts/release/linux.sh artifact-out: release-linux artifact-path: ./openpgl*.gz @@ -207,8 +207,8 @@ jobs: artifact-out: release-windows artifact-path: ./openpgl*.zip cmd: | - $env:OPENPGL_RELEASE_PACKAGE_VERSION="0.7.1" - $OPENPGL_RELEASE_PACKAGE_VERSION="0.7.1" + $env:OPENPGL_RELEASE_PACKAGE_VERSION="0.8.0" + $OPENPGL_RELEASE_PACKAGE_VERSION="0.8.0" scripts/release/windows.ps1 "Visual Studio 15 2017 Win64" "v141" release-macos: @@ -220,7 +220,7 @@ jobs: artifact-out: release-macos artifact-path: ./*.zip cmd: | - export OPENPGL_RELEASE_PACKAGE_VERSION="0.7.1" + export OPENPGL_RELEASE_PACKAGE_VERSION="0.8.0" scripts/release/macos.sh #release-macos-arm: @@ -232,7 +232,7 @@ jobs: # artifact-out: release-macos-arm # artifact-path: ./*.zip # cmd: | - # export OPENPGL_RELEASE_PACKAGE_VERSION="0.7.1" + # export OPENPGL_RELEASE_PACKAGE_VERSION="0.8.0" # scripts/release/macos.sh -DBUILD_TBB_FROM_SOURCE=ON ## Binary Scan Jobs ## diff --git a/CHANGELOG.md b/CHANGELOG.md index eba3799c..a71d6b55 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,80 @@ # Version History +## Open PGL 0.8.0 + +- New (**Experimental**) Feature: + - Volume Scatter Probability Guiding (VSPG): + - This feature allows guiding the optimal volume scattering + probability (VSP) and is based on Xu et al. work “Volume Scatter + Probability Guiding”. This **experimental** feature can be enabled + by setting the CMake variable `OPENPGL_EF_VSP_GUIDING=ON`. The + volume scattering probability for a given direction can be queried + using the `VolumeScatterProbability` function of the + `SurfaceSamplingDistribution` and `VolumeSamplingDistribution` + classes. +- API changes: + - `SampleData`: + - New enum `ENextEventVolume` flag that identifies if the radiance + stored in this sample comes from a volume or surface scatting event + (e.g., if the next event is inside a volume or on a surface). +- API changes (`OPENPGL_EF_VSP_GUIDING=ON`): + - `FieldConfig`: + - `SetVarianceBasedVSP` when set to `true` the VSP value is + calculated based on the `variance` and not the `contribution` of + the nested volume and surface estimators. The default is `false` + (i.e., `contribution`). + - `VolumeScatterProbability` and `SurfaceSamplingDistribution`: + - `VolumeScatterProbability` this function returns the optimal VSP + probability for a given direction. Based on the type the VSP value + is either calculated based on the `contribution` or the `variance` + of the nested (surface and volume) estimators. +- API changes (`OPENPGL_EF_IMAGE_SPACE_GUIDING_BUFFER=ON`): + - `ImageSpaceGuidingBuffer`: Moving to a config-based initialization + system and adding support to query the VSP for each pixel (i.e., + primary ray) of the image-space guiding buffer. The + `ImageSpaceGuidingBuffer` constructor now takes a + `ImageSpaceGuidingBuffer::Config` instead of a `Point2i` parameter. + + - The function to query the estimate of a pixel’s contribution got + renamed from `ContributionEstimate` to `GetContributionEstimate`. + + - `ImageSpaceGuidingBuffer::Config`: Class for setting up the + `ImageSpaceGuidingBuffer` (e.g., resolution, enabling contribution, + or VSP buffers). + + - The `Config` constructor initializes the config class. It takes a + `Point2i` defining the resolution of the desired + `ImageSpaceGuidingBuffer`. + - The resolution of the desired `ImageSpaceGuidingBuffer` can be + queried using `GetResolution`. + - Estimating the image contribution can be enabled or disables using + `EnableContributionEstimate`. + - If the estimation of the image contribution is enabled can be + checked using `ContributionEstimate`. + - The type of the estimated image contribution is defined via + `SetContributionType`. The type is defined via the + `PGLContributionTypes` enum and can be based on the contribution + (`EContribContribution`) or variance (`EContribVariance`). + - The type of the image contribution can be queried using + `GetContributionType`. +- API changes (`OPENPGL_EF_IMAGE_SPACE_GUIDING_BUFFER=ON` and + `OPENPGL_EF_VSP_GUIDING=ON`): + - `ImageSpaceGuidingBuffer`: Adding the possibility to query the VSP + value. + - The VSP for a given pixel (i.e., primary ray) can be queried using + the `GetVolumeScatterProbabilityEstimate` function. + - `ImageSpaceGuidingBuffer::Config`: + - Estimating the image space VSP values can be activated and + deactivated using `EnableVolumeScatterProbabilityEstimate)`. + - If estimating of the image-space VSP values is enabled can be + checked using `VolumeScatterProbabilityEstimate`. + - The type of the estimated image-space VSP values is defined via + `SetVolumeScatterProbabilityType`. The type is defined via the + `PGLVSPTypes` enum and can be based on the contribution + (`EVSPContribution`) or variance (`EVSPVariance`). + - The type of the image-space VSP values can be queried using + `GetVolumeScatterProbabilityType`. + ## Open PGL 0.7.1 - Bugfixes: @@ -8,7 +83,7 @@ [\#23](https://github.com/RenderKit/openpgl/issues/23). - Fixing noisy stdout printouts [\#19](https://github.com/RenderKit/openpgl/issues/19). - - Improving robustness of the integer arithmetric used during the + - Improving robustness of the integer arithmetic used during the deterministic multi-threaded building of the spatial subdivision structure. - Improving numerical stability of fitting process of the VMM-based @@ -55,7 +130,7 @@ - `pgl_direction`: A new **wrapper** type for directional data. When using C++ `pgl_direction` can directly be assigned by and to `pgl_vec3f`. - - `pgl_spectrum`: A new **wrapper** type for spetral (i.e., linear + - `pgl_spectrum`: A new **wrapper** type for spectral (i.e., linear RGB) data. When using C++ `pgl_spectrum` can directly be assigned by and to `pgl_vec3f`. - `SampleData`: @@ -130,7 +205,7 @@ at progression `2^0`,`2^1`,…,`2^N`). - `IsReady`: If the ISGB is ready (i.e., at least one `Update` step was performed). - - `GetPixelContributionEstimate`: Returns the pixel contibution + - `GetPixelContributionEstimate`: Returns the pixel contribution estimate for a given pixel, which can be used, for example, for guided RR. - `Reset`: Resets the ISGB. @@ -141,7 +216,7 @@ - `albedo`: The albedo of the surface or the volume at the first scattering event (type `pgl_vec3f`). - `normal`: The normal at the first surface scattering event or the - ray dairection towards the camers if the first event is a volume + ray direction towards the cameras if the first event is a volume event (type `pgl_vec3f`). - `flags`: Bit encoded information about the sample (e.g., if the first scattering event is a volume event `Sample::EVolumeEvent`). @@ -150,7 +225,7 @@ - Compression for spectral and directional: To reduce the size of the `SampleData` and `ZeroValueSampleData` data types it is possible to - enable 32-Bit compression, which is mainly adviced when enabling the + enable 32-Bit compression, which is mainly advised when enabling the RC feature via `OPENPGL_EF_RADIANCE_CACHES=ON`. - `OPENPGL_DIRECTION_COMPRESSION`: Enables 32-Bit compression for `pgl_direction`. @@ -179,7 +254,7 @@ 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. + structure used to query the sampling distribution. - `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 diff --git a/CMakeLists.txt b/CMakeLists.txt index 5f683960..f7062441 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,7 +22,7 @@ include(GNUInstallDirs) ## Establish project ## -project(openpgl VERSION 0.7.1 LANGUAGES C CXX) +project(openpgl VERSION 0.8.0 LANGUAGES C CXX) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/cmake) include(openpgl_macros) @@ -52,6 +52,7 @@ option(OPENPGL_BUILD_CHECK_TOOL "Build check tool application." OFF) try_compile(COMPILER_SUPPORTS_ARM_NEON "${CMAKE_BINARY_DIR}" "${PROJECT_SOURCE_DIR}/cmake/check_arm_neon.cpp") OPTION(OPENPGL_EF_RADIANCE_CACHES "Enables experimental feature (ir)radiance caches." OFF) +OPTION(OPENPGL_EF_VSP_GUIDING "Enables experimental feature volume scatter probability guiding." OFF) OPTION(OPENPGL_EF_IMAGE_SPACE_GUIDING_BUFFER "Enables experimental feature ImageSpaceGuidignBuffer." OFF) OPTION(OPENPGL_DIRECTION_COMPRESSION "Using 32-Bit compression to represent directions." OFF) diff --git a/README.md b/README.md index ca330aea..c56b7a69 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Intel® Open Path Guiding Library -This is release v0.7.1 of Intel® Open PGL. For changes and new features, +This is release v0.8.0 of Intel® Open PGL. For changes and new features, see the [changelog](CHANGELOG.md). Visit http://www.openpgl.org for more information. @@ -50,19 +50,80 @@ specification is still in flux and might change with upcoming releases. The full version history can be found [here](./CHANGELOG.md) -## Open PGL 0.7.1 - -- Bugfixes: - - Fixing invalidation of the guiding field on initial creation if a - cell contains no samples - [\#23](https://github.com/RenderKit/openpgl/issues/23). - - Fixing noisy stdout printouts - [\#19](https://github.com/RenderKit/openpgl/issues/19). - - Improving robustness of the integer arithmetric used during the - deterministic multi-threaded building of the spatial subdivision - structure. - - Improving numerical stability of fitting process of the VMM-based - guiding models. +## Open PGL 0.8.0 + +- New (**Experimental**) Feature: + - Volume Scatter Probability Guiding (VSPG): + - This feature allows guiding the optimal volume scattering + probability (VSP) and is based on Xu et al. work “Volume Scatter + Probability Guiding”. This **experimental** feature can be enabled + by setting the CMake variable `OPENPGL_EF_VSP_GUIDING=ON`. The + volume scattering probability for a given direction can be queried + using the `VolumeScatterProbability` function of the + `SurfaceSamplingDistribution` and `VolumeSamplingDistribution` + classes. +- API changes: + - `SampleData`: + - New enum `ENextEventVolume` flag that identifies if the radiance + stored in this sample comes from a volume or surface scatting event + (e.g., if the next event is inside a volume or on a surface). +- API changes (`OPENPGL_EF_VSP_GUIDING=ON`): + - `FieldConfig`: + - `SetVarianceBasedVSP` when set to `true` the VSP value is + calculated based on the `variance` and not the `contribution` of + the nested volume and surface estimators. The default is `false` + (i.e., `contribution`). + - `VolumeScatterProbability` and `SurfaceSamplingDistribution`: + - `VolumeScatterProbability` this function returns the optimal VSP + probability for a given direction. Based on the type the VSP value + is either calculated based on the `contribution` or the `variance` + of the nested (surface and volume) estimators. +- API changes (`OPENPGL_EF_IMAGE_SPACE_GUIDING_BUFFER=ON`): + - `ImageSpaceGuidingBuffer`: Moving to a config-based initialization + system and adding support to query the VSP for each pixel (i.e., + primary ray) of the image-space guiding buffer. The + `ImageSpaceGuidingBuffer` constructor now takes a + `ImageSpaceGuidingBuffer::Config` instead of a `Point2i` parameter. + + - The function to query the estimate of a pixel’s contribution got + renamed from `ContributionEstimate` to `GetContributionEstimate`. + + - `ImageSpaceGuidingBuffer::Config`: Class for setting up the + `ImageSpaceGuidingBuffer` (e.g., resolution, enabling contribution, + or VSP buffers). + + - The `Config` constructor initializes the config class. It takes a + `Point2i` defining the resolution of the desired + `ImageSpaceGuidingBuffer`. + - The resolution of the desired `ImageSpaceGuidingBuffer` can be + queried using `GetResolution`. + - Estimating the image contribution can be enabled or disables using + `EnableContributionEstimate`. + - If the estimation of the image contribution is enabled can be + checked using `ContributionEstimate`. + - The type of the estimated image contribution is defined via + `SetContributionType`. The type is defined via the + `PGLContributionTypes` enum and can be based on the contribution + (`EContribContribution`) or variance (`EContribVariance`). + - The type of the image contribution can be queried using + `GetContributionType`. +- API changes (`OPENPGL_EF_IMAGE_SPACE_GUIDING_BUFFER=ON` and + `OPENPGL_EF_VSP_GUIDING=ON`): + - `ImageSpaceGuidingBuffer`: Adding the possibility to query the VSP + value. + - The VSP for a given pixel (i.e., primary ray) can be queried using + the `GetVolumeScatterProbabilityEstimate` function. + - `ImageSpaceGuidingBuffer::Config`: + - Estimating the image space VSP values can be activated and + deactivated using `EnableVolumeScatterProbabilityEstimate)`. + - If estimating of the image-space VSP values is enabled can be + checked using `VolumeScatterProbabilityEstimate`. + - The type of the estimated image-space VSP values is defined via + `SetVolumeScatterProbabilityType`. The type is defined via the + `PGLVSPTypes` enum and can be based on the contribution + (`EVSPContribution`) or variance (`EVSPVariance`). + - The type of the image-space VSP values can be queried using + `GetVolumeScatterProbabilityType`. # Support and Contact @@ -206,6 +267,9 @@ Configure the Open PGL build using: - `OPENPGL_EF_IMAGE_SPACE_GUIDING_BUFFER`: Enables the **experimental** image-space guiding buffer feature (default `OFF`). + - `OPENPGL_EF_VSP_GUIDING`: Enables the **experimental** volume + scatter probability guiding feature (default `OFF`). + - `OPENPGL_DIRECTION_COMPRESSION`: Enables the 32Bit compression for directional data stored in `pgl_direction` (default `OFF`). @@ -236,7 +300,7 @@ To make CMake aware of Open PGL’s CMake configuration scripts the `openpgl_DIR` has to be set to their location during configuration: ``` bash -cmake -Dopenpgl_DIR=[openpgl_install]/lib/cmake/openpgl-0.7.1 .. +cmake -Dopenpgl_DIR=[openpgl_install]/lib/cmake/openpgl-0.8.0 .. ``` After that, adding OpenPGL to a CMake project/target is done by first diff --git a/doc/changelog_latest.md b/doc/changelog_latest.md index 1718fafb..94b12510 100644 --- a/doc/changelog_latest.md +++ b/doc/changelog_latest.md @@ -1,7 +1,37 @@ -## Open PGL 0.7.1 +## Open PGL 0.8.0 -- Bugfixes: - - Fixing invalidation of the guiding field on initial creation if a cell contains no samples [#23](https://github.com/RenderKit/openpgl/issues/23). - - Fixing noisy stdout printouts [#19](https://github.com/RenderKit/openpgl/issues/19). - - Improving robustness of the integer arithmetric used during the deterministic multi-threaded building of the spatial subdivision structure. - - Improving numerical stability of fitting process of the VMM-based guiding models. +- New (**Experimental**) Feature: + - Volume Scatter Probability Guiding (VSPG): + - This feature allows guiding the optimal volume scattering probability (VSP) and is based on Xu et al. work "Volume Scatter Probability Guiding". + This **experimental** feature can be enabled by setting the CMake variable `OPENPGL_EF_VSP_GUIDING=ON`. + The volume scattering probability for a given direction can be queried using the `VolumeScatterProbability` function of the `SurfaceSamplingDistribution` and `VolumeSamplingDistribution` classes. +- API changes: + - `SampleData`: + - New enum `ENextEventVolume` flag that identifies if the radiance stored in this sample comes from a volume or surface scatting event (e.g., if the next event is inside a volume or on a surface). +- API changes (`OPENPGL_EF_VSP_GUIDING=ON`): + - `FieldConfig`: + - `SetVarianceBasedVSP` when set to `true` the VSP value is calculated based on the `variance` and not the `contribution` of the nested volume and surface estimators. The default is `false` (i.e., `contribution`). + - `VolumeScatterProbability` and `SurfaceSamplingDistribution`: + - `VolumeScatterProbability` this function returns the optimal VSP probability for a given direction. Based on the type the VSP value is either calculated based on the `contribution` or the `variance` of the nested (surface and volume) estimators. +- API changes (`OPENPGL_EF_IMAGE_SPACE_GUIDING_BUFFER=ON`): + - `ImageSpaceGuidingBuffer`: + Moving to a config-based initialization system and adding support to query the VSP for each pixel (i.e., primary ray) of the image-space guiding buffer. + The `ImageSpaceGuidingBuffer` constructor now takes a `ImageSpaceGuidingBuffer::Config` instead of a `Point2i` parameter. + - The function to query the estimate of a pixel's contribution got renamed from `ContributionEstimate` to `GetContributionEstimate`. + + - `ImageSpaceGuidingBuffer::Config`: Class for setting up the `ImageSpaceGuidingBuffer` (e.g., resolution, enabling contribution, or VSP buffers). + - The `Config` constructor initializes the config class. It takes a `Point2i` defining the resolution of the desired `ImageSpaceGuidingBuffer`. + - The resolution of the desired `ImageSpaceGuidingBuffer` can be queried using `GetResolution`. + - Estimating the image contribution can be enabled or disables using `EnableContributionEstimate`. + - If the estimation of the image contribution is enabled can be checked using `ContributionEstimate`. + - The type of the estimated image contribution is defined via `SetContributionType`. The type is defined via the `PGLContributionTypes` enum and can be based on the contribution (`EContribContribution`) or variance (`EContribVariance`). + - The type of the image contribution can be queried using `GetContributionType`. + +- API changes (`OPENPGL_EF_IMAGE_SPACE_GUIDING_BUFFER=ON` and `OPENPGL_EF_VSP_GUIDING=ON`): + - `ImageSpaceGuidingBuffer`: Adding the possibility to query the VSP value. + - The VSP for a given pixel (i.e., primary ray) can be queried using the `GetVolumeScatterProbabilityEstimate` function. + - `ImageSpaceGuidingBuffer::Config`: + - Estimating the image space VSP values can be activated and deactivated using `EnableVolumeScatterProbabilityEstimate)`. + - If estimating of the image-space VSP values is enabled can be checked using `VolumeScatterProbabilityEstimate`. + - The type of the estimated image-space VSP values is defined via `SetVolumeScatterProbabilityType`. The type is defined via the `PGLVSPTypes` enum and can be based on the contribution (`EVSPContribution`) or variance (`EVSPVariance`). + - The type of the image-space VSP values can be queried using `GetVolumeScatterProbabilityType`. \ No newline at end of file diff --git a/doc/changelog_previous.md b/doc/changelog_previous.md index c0996193..5d7403a1 100644 --- a/doc/changelog_previous.md +++ b/doc/changelog_previous.md @@ -1,3 +1,11 @@ +## Open PGL 0.7.1 + +- Bugfixes: + - Fixing invalidation of the guiding field on initial creation if a cell contains no samples [#23](https://github.com/RenderKit/openpgl/issues/23). + - Fixing noisy stdout printouts [#19](https://github.com/RenderKit/openpgl/issues/19). + - Improving robustness of the integer arithmetic used during the deterministic multi-threaded building of the spatial subdivision structure. + - Improving numerical stability of fitting process of the VMM-based guiding models. + ## Open PGL 0.7.0 - New (**Experimental**) Features: @@ -14,7 +22,7 @@ - API changes: - `pgl_direction`: A new **wrapper** type for directional data. When using C++ `pgl_direction` can directly be assigned by and to `pgl_vec3f`. - - `pgl_spectrum`: A new **wrapper** type for spetral (i.e., linear RGB) data. When using C++ `pgl_spectrum` can directly be assigned by and to `pgl_vec3f`. + - `pgl_spectrum`: A new **wrapper** type for spectral (i.e., linear RGB) data. When using C++ `pgl_spectrum` can directly be assigned by and to `pgl_vec3f`. - `SampleData`: - New enum `EDirectLight` flag that identifies if the radiance stored in this sample comes directly from an emitter (e.g., emissive surface, volume, or light source). - `direction`: Changes the type `pgl_vec3f` to `pgl_direction`. @@ -55,18 +63,18 @@ -`AddSample`: Add a pixel sample of type `ImageSpaceGuidingBuffer::Sample` to the buffer. - `Update`: Updates the image-space guiding information/approximations from the previously collected samples (e.g., denoises the pixel contribution estimates using OIDN). For efficiency reasons, it makes sense not to update the buffer after every rendering progression but in an exponential fashion (e.g., at progression `2^0`,`2^1`,...,`2^N`). - `IsReady`: If the ISGB is ready (i.e., at least one `Update` step was performed). - - `GetPixelContributionEstimate`: Returns the pixel contibution estimate for a given pixel, which can be used, for example, for guided RR. + - `GetPixelContributionEstimate`: Returns the pixel contribution estimate for a given pixel, which can be used, for example, for guided RR. - `Reset`: Resets the ISGB. - `ImageSpaceGuidingBuffer::Sample`: This structure is used to store information about a per-pixel sample that is passed to the ISGB. - `contribution`: The contribution estimate of the pixel value of a given sample (type `pgl_vec3f`). - `albedo`: The albedo of the surface or the volume at the first scattering event (type `pgl_vec3f`). - - `normal`: The normal at the first surface scattering event or the ray dairection towards the camers if the first event is a volume event (type `pgl_vec3f`). + - `normal`: The normal at the first surface scattering event or the ray direction towards the cameras if the first event is a volume event (type `pgl_vec3f`). - `flags`: Bit encoded information about the sample (e.g., if the first scattering event is a volume event `Sample::EVolumeEvent`). - Optimizations: - Compression for spectral and directional: - To reduce the size of the `SampleData` and `ZeroValueSampleData` data types it is possible to enable 32-Bit compression, which is mainly adviced when enabling the RC feature via `OPENPGL_EF_RADIANCE_CACHES=ON`. + To reduce the size of the `SampleData` and `ZeroValueSampleData` data types it is possible to enable 32-Bit compression, which is mainly advised when enabling the RC feature via `OPENPGL_EF_RADIANCE_CACHES=ON`. - `OPENPGL_DIRECTION_COMPRESSION`: Enables 32-Bit compression for `pgl_direction`. - `OPENPGL_RADIANCE_COMPRESSION`: Enables 32-Bit compression for `pgl_spectrum`. - Bugfixes: @@ -80,7 +88,7 @@ - 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. + - Added `GetId` function to return the unique id of the spatial structure used to query the sampling distribution. - `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)** diff --git a/doc/compilation.md b/doc/compilation.md index bd3ca6f9..59cf2945 100644 --- a/doc/compilation.md +++ b/doc/compilation.md @@ -101,6 +101,8 @@ Configure the Open PGL build using: - `OPENPGL_EF_IMAGE_SPACE_GUIDING_BUFFER`: Enables the **experimental** image-space guiding buffer feature (default `OFF`). + - `OPENPGL_EF_VSP_GUIDING`: Enables the **experimental** volume scatter probability guiding feature (default `OFF`). + - `OPENPGL_DIRECTION_COMPRESSION`: Enables the 32Bit compression for directional data stored in `pgl_direction` (default `OFF`). - `OPENPGL_RADIANCE_COMPRESSION`: Enables the 32Bit compression for RGB data stored in `pgl_spectrum` (default `OFF`). diff --git a/openpgl/CMakeLists.txt b/openpgl/CMakeLists.txt index e649e9ba..fc2582b0 100644 --- a/openpgl/CMakeLists.txt +++ b/openpgl/CMakeLists.txt @@ -46,6 +46,11 @@ if(OPENPGL_EF_RADIANCE_CACHES) target_compile_definitions(${PROJECT_NAME} PRIVATE OPENPGL_RADIANCE_CACHES) endif() +if(OPENPGL_EF_VSP_GUIDING) +target_compile_definitions(${PROJECT_NAME} PRIVATE OPENPGL_VSP_GUIDING) +endif() + + if(OPENPGL_EF_IMAGE_SPACE_GUIDING_BUFFER) target_compile_definitions(${PROJECT_NAME} PRIVATE OPENPGL_IMAGE_SPACE_GUIDING_BUFFER) endif() @@ -235,6 +240,11 @@ if(OPENPGL_EF_RADIANCE_CACHES) set(OPENPGL_RADIANCE_CACHES ON) endif() +if(OPENPGL_EF_VSP_GUIDING) + set(OPENPGL_VSP_GUIDING ON) +endif() + + if(OPENPGL_EF_IMAGE_SPACE_GUIDING_BUFFER) set(OPENPGL_IMAGE_SPACE_GUIDING_BUFFER ON) endif() diff --git a/openpgl/api/api.cpp b/openpgl/api/api.cpp index 9e40343c..1ed85112 100644 --- a/openpgl/api/api.cpp +++ b/openpgl/api/api.cpp @@ -609,6 +609,14 @@ extern "C" OPENPGL_DLLEXPORT uint32_t pglSurfaceSamplingDistributionGetId(PGLSur return gSurfaceSamplingDistribution->getId(); } +#ifdef OPENPGL_VSP_GUIDING +extern "C" OPENPGL_DLLEXPORT float pglSurfaceSamplingDistributionVolumeScatterProbability(PGLSurfaceSamplingDistribution surfaceSamplingDistribution, pgl_vec3f direction) +{ + ISurfaceSamplingDistribution *gSurfaceSamplingDistribution = (ISurfaceSamplingDistribution *)surfaceSamplingDistribution; + return gSurfaceSamplingDistribution->volumeScatterProbability(openpgl::Vector3(direction.x, direction.y, direction.z)); +} +#endif + extern "C" OPENPGL_DLLEXPORT bool pglSurfaceSamplingDistributionValidate(PGLSurfaceSamplingDistribution surfaceSamplingDistribution) { ISurfaceSamplingDistribution *gSurfaceSamplingDistribution = (ISurfaceSamplingDistribution *)surfaceSamplingDistribution; @@ -707,6 +715,14 @@ extern "C" OPENPGL_DLLEXPORT uint32_t pglVolumeSamplingDistributionGetId(PGLVolu return gVolumeSamplingDistribution->getId(); } +#ifdef OPENPGL_VSP_GUIDING +extern "C" OPENPGL_DLLEXPORT float pglVolumeSamplingDistributionVolumeScatterProbability(PGLVolumeSamplingDistribution volumeSamplingDistribution, pgl_vec3f direction) +{ + IVolumeSamplingDistribution *gVolumeSamplingDistribution = (IVolumeSamplingDistribution *)volumeSamplingDistribution; + return gVolumeSamplingDistribution->volumeScatterProbability(openpgl::Vector3(direction.x, direction.y, direction.z)); +} +#endif + extern "C" OPENPGL_DLLEXPORT bool pglVolumeSamplingDistributionValidate(PGLVolumeSamplingDistribution volumeSamplingDistribution) { IVolumeSamplingDistribution *gVolumeSamplingDistribution = (IVolumeSamplingDistribution *)volumeSamplingDistribution; @@ -874,9 +890,9 @@ extern "C" OPENPGL_DLLEXPORT void pglReleaseString(PGLString str) // ImageSpaceGuidingBuffer /////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// -extern "C" OPENPGL_DLLEXPORT PGLImageSpaceGuidingBuffer pglFieldNewImageSpaceGuidingBuffer(const pgl_point2i resolution) +extern "C" OPENPGL_DLLEXPORT PGLImageSpaceGuidingBuffer pglFieldNewImageSpaceGuidingBuffer(const PGLImageSpaceGuidingBufferConfig cfg) { - return (PGLImageSpaceGuidingBuffer) new openpgl::ImageSpaceGuidingBuffer(resolution, false); + return (PGLImageSpaceGuidingBuffer) new openpgl::ImageSpaceGuidingBuffer(cfg); } extern "C" OPENPGL_DLLEXPORT PGLImageSpaceGuidingBuffer pglFieldNewImageSpaceGuidingBufferFromFile(const char *fileName) @@ -908,12 +924,20 @@ extern "C" OPENPGL_DLLEXPORT void pglImageSpaceGuidingBufferStore(PGLImageSpaceG gImageSpaceGuidingBuffer->store(fileName); } -extern "C" OPENPGL_DLLEXPORT pgl_vec3f pglImageSpaceGuidingBufferGetPixelContributionEstimate(PGLImageSpaceGuidingBuffer imageSpaceGuidingBuffer, const pgl_point2i pixel) +extern "C" OPENPGL_DLLEXPORT pgl_vec3f pglImageSpaceGuidingBufferGetContributionEstimate(PGLImageSpaceGuidingBuffer imageSpaceGuidingBuffer, const pgl_point2i pixel) { auto *gImageSpaceGuidingBuffer = (openpgl::ImageSpaceGuidingBuffer *)imageSpaceGuidingBuffer; return gImageSpaceGuidingBuffer->getContributionEstimate(pixel); } +#if defined(OPENPGL_VSP_GUIDING) +extern "C" OPENPGL_DLLEXPORT float pglImageSpaceGuidingBufferGetVolumeScatterProbabilityEstimate(PGLImageSpaceGuidingBuffer imageSpaceGuidingBuffer, const pgl_point2i pixel) +{ + auto *gImageSpaceGuidingBuffer = (openpgl::ImageSpaceGuidingBuffer *)imageSpaceGuidingBuffer; + return gImageSpaceGuidingBuffer->getVolumeScatterProbabilityEstimate(pixel); +} +#endif + extern "C" OPENPGL_DLLEXPORT bool pglImageSpaceGuidingBufferIsReady(PGLImageSpaceGuidingBuffer imageSpaceGuidingBuffer) { auto *gImageSpaceGuidingBuffer = (openpgl::ImageSpaceGuidingBuffer *)imageSpaceGuidingBuffer; diff --git a/openpgl/data/PathSegmentDataStorage.h b/openpgl/data/PathSegmentDataStorage.h index 0f714232..8f1bfb49 100644 --- a/openpgl/data/PathSegmentDataStorage.h +++ b/openpgl/data/PathSegmentDataStorage.h @@ -260,6 +260,11 @@ struct PathSegmentDataStorage flags |= SampleData::EInsideVolume; } + if (m_segmentStorage[i + 1].volumeScatter) + { + flags |= SampleData::ENextEventVolume; + } + bool directLightSample = false; #ifdef OPENPGL_RADIANCE_CACHES float misWeight = 1.f; @@ -400,7 +405,7 @@ struct PathSegmentDataStorage pglDirection = {dirOut[0], dirOut[1], dirOut[2]}; isd.directionOut = pglDirection; #endif - isd.volume = insideVolume; + isd.flags = flags; #if defined(OPENPGL_PATHSEGMENT_STORAGE_USE_ARRAY) if (m_zero_value_sample_idx + 1 <= m_max_zero_value_sample_size) { diff --git a/openpgl/data/Range.h b/openpgl/data/Range.h index aadf9d70..d976636e 100644 --- a/openpgl/data/Range.h +++ b/openpgl/data/Range.h @@ -12,7 +12,7 @@ struct Range size_t m_begin{0}; size_t m_end{0}; -#ifdef OPENPGL_RADIANCE_CACHES +#if defined(OPENPGL_RADIANCE_CACHES) || defined(OPENPGL_VSP_GUIDING) size_t m_is_begin{0}; size_t m_is_end{0}; #endif @@ -25,7 +25,7 @@ struct Range OPENPGL_ASSERT(int(m_end) - int(m_begin) >= 0); return m_end - m_begin; } -#ifdef OPENPGL_RADIANCE_CACHES +#if defined(OPENPGL_RADIANCE_CACHES) || defined(OPENPGL_VSP_GUIDING) inline size_t sizeZeroValueSamples() const { OPENPGL_ASSERT(int(m_is_end) - int(m_is_begin) >= 0); @@ -36,7 +36,7 @@ struct Range { m_begin = 0; m_end = 0; -#ifdef OPENPGL_RADIANCE_CACHES +#if defined(OPENPGL_RADIANCE_CACHES) || defined(OPENPGL_VSP_GUIDING) m_is_begin = 0; m_is_end = 0; #endif @@ -46,7 +46,7 @@ struct Range { os.write(reinterpret_cast(&m_begin), sizeof(m_begin)); os.write(reinterpret_cast(&m_end), sizeof(m_end)); -#ifdef OPENPGL_RADIANCE_CACHES +#if defined(OPENPGL_RADIANCE_CACHES) || defined(OPENPGL_VSP_GUIDING) os.write(reinterpret_cast(&m_is_begin), sizeof(m_is_begin)); os.write(reinterpret_cast(&m_is_end), sizeof(m_is_end)); #endif @@ -56,7 +56,7 @@ struct Range { is.read(reinterpret_cast(&m_begin), sizeof(m_begin)); is.read(reinterpret_cast(&m_end), sizeof(m_end)); -#ifdef OPENPGL_RADIANCE_CACHES +#if defined(OPENPGL_RADIANCE_CACHES) || defined(OPENPGL_VSP_GUIDING) is.read(reinterpret_cast(&m_is_begin), sizeof(m_is_begin)); is.read(reinterpret_cast(&m_is_end), sizeof(m_is_end)); #endif @@ -66,7 +66,7 @@ struct Range { bool equal = true; if (m_begin != b.m_begin || m_end != b.m_end -#ifdef OPENPGL_RADIANCE_CACHES +#if defined(OPENPGL_RADIANCE_CACHES) || defined(OPENPGL_VSP_GUIDING) || m_is_begin != b.m_is_begin || m_is_end != b.m_is_end #endif ) diff --git a/openpgl/data/SampleData.h b/openpgl/data/SampleData.h index aee3a049..66087db7 100644 --- a/openpgl/data/SampleData.h +++ b/openpgl/data/SampleData.h @@ -22,7 +22,8 @@ typedef PGLSampleData SampleData; enum SampleData_Flags { EInsideVolume = 1 << 0, // point does not represent any real scene intersection point - EDirectLight = 1 << 1 // if the samples represents direct light from a light source + EDirectLight = 1 << 1, // if the samples represents direct light from a light source + ENextEventVolume = 1 << 2 }; inline bool isValid(const SampleData &dsd) @@ -86,6 +87,11 @@ inline bool isDirectLight(const SampleData &sd) return (sd.flags & EDirectLight); } +inline bool isNextEventVolume(const SampleData &sd) +{ + return (sd.flags & ENextEventVolume); +} + inline std::string toString(const SampleData &sd) { std::stringstream ss; @@ -186,6 +192,16 @@ inline bool ZeroValueSampleDataLess(const PGLZeroValueSampleData &compA, const P ))))); } +inline bool isInsideVolume(const PGLZeroValueSampleData &zvsd) +{ + return (zvsd.flags & EInsideVolume); +} + +inline bool isNextEventVolume(const PGLZeroValueSampleData &zvsd) +{ + return (zvsd.flags & ENextEventVolume); +} + inline SampleData *LoadSampleData(const std::string fileName, size_t &numData) { std::ifstream file; diff --git a/openpgl/data/SampleDataStorage.h b/openpgl/data/SampleDataStorage.h index d0688f40..4285cf4a 100644 --- a/openpgl/data/SampleDataStorage.h +++ b/openpgl/data/SampleDataStorage.h @@ -104,7 +104,7 @@ struct SampleDataStorage inline void addZeroValueSample(const ZeroValueSampleData &sample) { - if (sample.volume) + if (isInsideVolume(sample)) { m_volumeContainer.zeroValueSamples.push_back(sample); } diff --git a/openpgl/device/Device.h b/openpgl/device/Device.h index 01bcbc96..a8c75bee 100644 --- a/openpgl/device/Device.h +++ b/openpgl/device/Device.h @@ -99,6 +99,9 @@ struct Device : public IDevice typename GuidingField::Settings gFieldSettings; gFieldSettings.settings.decayOnSpatialSplit = 0.25f; gFieldSettings.settings.deterministic = args.deterministic; +#ifdef OPENPGL_VSP_GUIDING + gFieldSettings.settings.varianceBasedVSP = args.varianceBasedVSP; +#endif gFieldSettings.debugSettings.fitRegions = args.debugArguments.fitRegions; PGLKDTreeArguments *spatialSturctureArguments = (PGLKDTreeArguments *)args.spatialSturctureArguments; @@ -148,6 +151,9 @@ struct Device : public IDevice typename GuidingField::Settings gFieldSettings; gFieldSettings.settings.decayOnSpatialSplit = 0.25f; gFieldSettings.settings.deterministic = args.deterministic; +#ifdef OPENPGL_VSP_GUIDING + gFieldSettings.settings.varianceBasedVSP = args.varianceBasedVSP; +#endif gFieldSettings.debugSettings.fitRegions = args.debugArguments.fitRegions; PGLKDTreeArguments *spatialSturctureArguments = (PGLKDTreeArguments *)args.spatialSturctureArguments; @@ -197,6 +203,9 @@ struct Device : public IDevice typename GuidingField::Settings gFieldSettings; gFieldSettings.settings.decayOnSpatialSplit = 0.25f; gFieldSettings.settings.deterministic = args.deterministic; +#ifdef OPENPGL_VSP_GUIDING + gFieldSettings.settings.varianceBasedVSP = args.varianceBasedVSP; +#endif gFieldSettings.debugSettings.fitRegions = args.debugArguments.fitRegions; PGLKDTreeArguments *spatialSturctureArguments = (PGLKDTreeArguments *)args.spatialSturctureArguments; diff --git a/openpgl/directional/ISurfaceSamplingDistribution.h b/openpgl/directional/ISurfaceSamplingDistribution.h index e1fb19b1..99773619 100644 --- a/openpgl/directional/ISurfaceSamplingDistribution.h +++ b/openpgl/directional/ISurfaceSamplingDistribution.h @@ -57,6 +57,10 @@ struct ISurfaceSamplingDistribution virtual const IRegion *getRegion() const = 0; +#ifdef OPENPGL_VSP_GUIDING + virtual float volumeScatterProbability(Vector3 dir) const = 0; +#endif + protected: // const IRegion* m_region {nullptr}; uint32_t m_id{0}; diff --git a/openpgl/directional/IVolumeSamplingDistribution.h b/openpgl/directional/IVolumeSamplingDistribution.h index f2431771..32785996 100644 --- a/openpgl/directional/IVolumeSamplingDistribution.h +++ b/openpgl/directional/IVolumeSamplingDistribution.h @@ -59,6 +59,10 @@ struct IVolumeSamplingDistribution virtual const IRegion *getRegion() const = 0; +#ifdef OPENPGL_VSP_GUIDING + virtual float volumeScatterProbability(Vector3 dir) const = 0; +#endif + protected: // const IRegion* m_region {nullptr}; uint32_t m_id{0}; diff --git a/openpgl/directional/dqt/DQTFactory.h b/openpgl/directional/dqt/DQTFactory.h index cc4e889d..57bbd917 100644 --- a/openpgl/directional/dqt/DQTFactory.h +++ b/openpgl/directional/dqt/DQTFactory.h @@ -154,6 +154,11 @@ class DirectionalQuadtreeFactory is.read(reinterpret_cast(nodes.data()), size * sizeof(nodes[0])); }; + float getNumSamples() const + { + return numSamples; + }; + // TODO: Needs to be implmented bool operator==(const Statistics &b) const { @@ -175,6 +180,10 @@ class DirectionalQuadtreeFactory const SampleStatistics &sampleStatistics) const {} + void updateVolumeScatterProbability(Distribution &dist, Statistics &stats, const SampleData *samples, const size_t numSamples, const ZeroValueSampleData *zeroValueSamples, + const size_t numZeroValueSamples, const bool varianceBased) const + {} + void fit(Distribution &dist, Statistics &stats, const SampleData *samples, const size_t numSamples, const Configuration &cfg, FittingStatistics &fitStats) { for (uint32_t i = 0; i < 5; i++) diff --git a/openpgl/directional/dqt/DQTSurfaceSamplingDistribution.h b/openpgl/directional/dqt/DQTSurfaceSamplingDistribution.h index e4e8406a..390da1ea 100644 --- a/openpgl/directional/dqt/DQTSurfaceSamplingDistribution.h +++ b/openpgl/directional/dqt/DQTSurfaceSamplingDistribution.h @@ -87,6 +87,13 @@ struct DQTSurfaceSamplingDistribution : public ISurfaceSamplingDistribution m_region = region; } +#ifdef OPENPGL_VSP_GUIDING + float volumeScatterProbability(Vector3 dir) const override + { + return 0.f; + } +#endif + private: TDirectionalQuadtree distribution; const IRegion *m_region{nullptr}; diff --git a/openpgl/directional/dqt/DQTVolumeSamplingDistribution.h b/openpgl/directional/dqt/DQTVolumeSamplingDistribution.h index 8607b4f0..c0cc5bf8 100644 --- a/openpgl/directional/dqt/DQTVolumeSamplingDistribution.h +++ b/openpgl/directional/dqt/DQTVolumeSamplingDistribution.h @@ -93,6 +93,13 @@ struct DQTVolumeSamplingDistribution : public IVolumeSamplingDistribution m_region = region; } +#ifdef OPENPGL_VSP_GUIDING + float volumeScatterProbability(Vector3 dir) const override + { + return 0.f; + } +#endif + private: TDirectionalQuadtree distribution; const IRegion *m_region{nullptr}; diff --git a/openpgl/directional/vmm/AdaptiveSplitandMergeFactory.h b/openpgl/directional/vmm/AdaptiveSplitandMergeFactory.h index aaabb1b0..98e9ef6b 100644 --- a/openpgl/directional/vmm/AdaptiveSplitandMergeFactory.h +++ b/openpgl/directional/vmm/AdaptiveSplitandMergeFactory.h @@ -95,6 +95,10 @@ struct AdaptiveSplitAndMergeFactory return sufficientStatistics.getNumComponents(); } + inline float getNumSamples() const + { + return sufficientStatistics.getNumSamples(); + }; std::string toString() const; bool operator==(const Statistics &b) const; @@ -122,6 +126,9 @@ struct AdaptiveSplitAndMergeFactory void updateFluenceEstimate(VMM &vmm, const SampleData *samples, const size_t numSamples, const size_t numZeroValueSamples, const SampleStatistics &sampleStatistics) const; + void updateVolumeScatterProbability(VMM &vmm, Statistics &stats, const SampleData *samples, const size_t numSamples, const ZeroValueSampleData *zeroValueSamples, + const size_t numZeroValueSamples, const bool varianceBased) const; + std::string toString() const { std::ostringstream oss; @@ -463,4 +470,13 @@ void AdaptiveSplitAndMergeFactory::updateFluenceEstimate(VMM & factory.updateFluenceEstimate(vmm, samples, numSamples, numZeroValueSamples, sampleStatistics); } +template +void AdaptiveSplitAndMergeFactory::updateVolumeScatterProbability(VMM &vmm, Statistics &stats, const SampleData *samples, const size_t numSamples, + const ZeroValueSampleData *zeroValueSamples, const size_t numZeroValueSamples, + const bool varianceBased) const +{ + WeightedEMFactory factory = WeightedEMFactory(); + factory.updateVolumeScatterProbability(vmm, stats.sufficientStatistics, samples, numSamples, zeroValueSamples, numZeroValueSamples, varianceBased); +} + } // namespace openpgl diff --git a/openpgl/directional/vmm/ParallaxAwareVonMisesFisherMixture.h b/openpgl/directional/vmm/ParallaxAwareVonMisesFisherMixture.h index 70b0d00a..3c781ea8 100644 --- a/openpgl/directional/vmm/ParallaxAwareVonMisesFisherMixture.h +++ b/openpgl/directional/vmm/ParallaxAwareVonMisesFisherMixture.h @@ -76,6 +76,10 @@ struct ParallaxAwareVonMisesFisherMixture embree::vfloat _distances[NumVectors]; Point3 _pivotPosition{0.0f, 0.0f, 0.0f}; +#ifdef OPENPGL_VSP_GUIDING + embree::vfloat _volumeScatterProbabilityWeights[NumVectors]; +#endif + #ifdef OPENPGL_RADIANCE_CACHES // fluence attributes // float _fluence {0.0f}; @@ -152,15 +156,14 @@ struct ParallaxAwareVonMisesFisherMixture void setComponentDistance(const size_t &idx, const float &distance); - void decay(const float alpha) - { -#ifdef OPENPGL_RADIANCE_CACHES - _numFluenceSamples *= alpha; -#endif - } + void decay(const float alpha); bool isValid() const; +#ifdef OPENPGL_VSP_GUIDING + float volumeScatterProbability(const Vector3 &direction) const; +#endif + std::string toString() const; void _calculateNormalization(); @@ -256,6 +259,9 @@ std::string ParallaxAwareVonMisesFisherMixture_eMinus2Kappa[tmp.quot][tmp.rem]; ss << "\t meanCosine: " << this->_meanCosines[tmp.quot][tmp.rem]; ss << "\t distance: " << _distances[tmp.quot][tmp.rem]; +#ifdef OPENPGL_VSP_GUIDING + ss << "\t volumeScatterProbabilityWeight: " << _volumeScatterProbabilityWeights[tmp.quot][tmp.rem]; +#endif #ifdef OPENPGL_RADIANCE_CACHES ss << "\t fluenceRGBWeightWithMIS: " << _fluenceRGBWeightsWithMIS[tmp.quot].x[tmp.rem] << "\t" << _fluenceRGBWeightsWithMIS[tmp.quot].y[tmp.rem] << "\t" << _fluenceRGBWeightsWithMIS[tmp.quot].z[tmp.rem]; @@ -304,6 +310,10 @@ void ParallaxAwareVonMisesFisherMixture(stream, _fluenceRGBWeightsWithMIS); serializeVec3Vectors(stream, _fluenceRGBWeights); +#endif +#ifdef OPENPGL_VSP_GUIDING + serializeFloatVectors(stream, _volumeScatterProbabilityWeights); #endif stream.write(reinterpret_cast(&_numComponents), sizeof(_numComponents)); stream.write(reinterpret_cast(&_pivotPosition), sizeof(Point3)); @@ -529,6 +558,9 @@ void ParallaxAwareVonMisesFisherMixture(stream, _fluenceRGBWeightsWithMIS); deserializeVec3Vectors(stream, _fluenceRGBWeights); +#endif +#ifdef OPENPGL_VSP_GUIDING + deserializeFloatVectors(stream, _volumeScatterProbabilityWeights); #endif stream.read(reinterpret_cast(&_numComponents), sizeof(_numComponents)); stream.read(reinterpret_cast(&_pivotPosition), sizeof(Point3)); @@ -591,6 +623,12 @@ bool ParallaxAwareVonMisesFisherMixture= 0.0f; OPENPGL_ASSERT(valid); +#ifdef OPENPGL_VSP_GUIDING + valid = valid && embree::isvalid(_volumeScatterProbabilityWeights[tmpK.quot][tmpK.rem]); + valid = valid && _volumeScatterProbabilityWeights[tmpK.quot][tmpK.rem] >= 0.0f; + valid = valid && _volumeScatterProbabilityWeights[tmpK.quot][tmpK.rem] <= 1.0f; + OPENPGL_ASSERT(valid); +#endif } // check unused componets @@ -632,6 +670,11 @@ bool ParallaxAwareVonMisesFisherMixture +void ParallaxAwareVonMisesFisherMixture::decay(float alpha) +{ +#ifdef OPENPGL_RADIANCE_CACHES + _numFluenceSamples *= alpha; +#endif +} + template void ParallaxAwareVonMisesFisherMixture::_calculateNormalization() { @@ -1189,6 +1240,31 @@ bool ParallaxAwareVonMisesFisherMixture +float ParallaxAwareVonMisesFisherMixture::volumeScatterProbability(const Vector3 &direction) const +{ + const int cnt = (_numComponents + VecSize - 1) / VecSize; + + embree::vfloat volumeScatterProbability = {0.0f}; + embree::vfloat pdf = {0.0f}; + embree::Vec3> vec3Direction(direction[0], direction[1], direction[2]); + + const embree::vfloat ones(1.0f); + const embree::vfloat zeros(0.0f); + + for (int k = 0; k < cnt; k++) + { + const embree::vfloat cosTheta = embree::dot(vec3Direction, _meanDirections[k]); + const embree::vfloat cosThetaMinusOne = embree::min(cosTheta - ones, zeros); + const embree::vfloat eval = _weights[k] * _normalizations[k] * embree::fastapprox::exp>(_kappas[k] * cosThetaMinusOne); + pdf += eval; + volumeScatterProbability += _volumeScatterProbabilityWeights[k] * eval; + } + + return reduce_add(volumeScatterProbability) / reduce_add(pdf); +} +#endif #ifdef OPENPGL_RADIANCE_CACHES template Vector3 ParallaxAwareVonMisesFisherMixture::incomingRadiance(const Vector3 &direction, const bool directLightMIS) const diff --git a/openpgl/directional/vmm/ParallaxAwareVonMisesFisherWeightedEMFactory.h b/openpgl/directional/vmm/ParallaxAwareVonMisesFisherWeightedEMFactory.h index 22cf1651..0e282a0a 100644 --- a/openpgl/directional/vmm/ParallaxAwareVonMisesFisherWeightedEMFactory.h +++ b/openpgl/directional/vmm/ParallaxAwareVonMisesFisherWeightedEMFactory.h @@ -96,7 +96,12 @@ struct ParallaxAwareVonMisesFisherWeightedEMFactory public: embree::Vec3 > sumOfWeightedDirections[VMM::NumVectors]; embree::vfloat sumOfWeightedStats[VMM::NumVectors]; - +#ifdef OPENPGL_VSP_GUIDING + embree::vfloat volumeContributionWeights[VMM::NumVectors]; + embree::vfloat surfaceContributionWeights[VMM::NumVectors]; + embree::vfloat volumeSampleNumberWeights[VMM::NumVectors]; + embree::vfloat surfaceSampleNumberWeights[VMM::NumVectors]; +#endif float sumWeights{0.f}; float numSamples{0.f}; float overallNumSamples{0.f}; @@ -115,6 +120,8 @@ struct ParallaxAwareVonMisesFisherWeightedEMFactory void clear(size_t _numComponents); + void clearComponentStats(const size_t &idx); + void clearAll(); virtual void normalize(const float &_numSamples); @@ -202,6 +209,10 @@ struct ParallaxAwareVonMisesFisherWeightedEMFactory void updateComponentDistances(VMM &vmm, SufficientStatistics &sufficientStats, const SampleData *samples, const size_t numSamples) const; +#ifdef OPENPGL_VSP_GUIDING + void updateVolumeScatterProbability(VMM &vmm, SufficientStatistics &sufficientStats, const SampleData *samples, const size_t numSamples, + const ZeroValueSampleData *zeroValueSamples, const size_t numZeroValueSamples, const bool varianceBased) const; +#endif private: void _initUniformDirections(); @@ -312,6 +323,24 @@ bool ParallaxAwareVonMisesFisherWeightedEMFactory::SufficientS valid = valid && embree::isvalid(sumOfDistanceWeightes[tmpK.quot][tmpK.rem]); valid = valid && sumOfDistanceWeightes[tmpK.quot][tmpK.rem] >= 0.0f; OPENPGL_ASSERT(valid); + +#ifdef OPENPGL_VSP_GUIDING + valid = valid && embree::isvalid(volumeContributionWeights[tmpK.quot][tmpK.rem]); + valid = valid && volumeContributionWeights[tmpK.quot][tmpK.rem] >= 0.0f; + OPENPGL_ASSERT(valid); + + valid = valid && embree::isvalid(surfaceContributionWeights[tmpK.quot][tmpK.rem]); + valid = valid && surfaceContributionWeights[tmpK.quot][tmpK.rem] >= 0.0f; + OPENPGL_ASSERT(valid); + + valid = valid && embree::isvalid(volumeSampleNumberWeights[tmpK.quot][tmpK.rem]); + valid = valid && volumeSampleNumberWeights[tmpK.quot][tmpK.rem] >= 0.0f; + OPENPGL_ASSERT(valid); + + valid = valid && embree::isvalid(surfaceSampleNumberWeights[tmpK.quot][tmpK.rem]); + valid = valid && surfaceSampleNumberWeights[tmpK.quot][tmpK.rem] >= 0.0f; + OPENPGL_ASSERT(valid); +#endif } for (size_t k = numComponents; k < VMM::MaxComponents; k++) @@ -332,6 +361,24 @@ bool ParallaxAwareVonMisesFisherWeightedEMFactory::SufficientS valid = valid && embree::isvalid(sumOfDistanceWeightes[tmpK.quot][tmpK.rem]); valid = valid && sumOfDistanceWeightes[tmpK.quot][tmpK.rem] == 0.0f; OPENPGL_ASSERT(valid); + +#ifdef OPENPGL_VSP_GUIDING + valid = valid && embree::isvalid(volumeContributionWeights[tmpK.quot][tmpK.rem]); + valid = valid && volumeContributionWeights[tmpK.quot][tmpK.rem] == 0.0f; + OPENPGL_ASSERT(valid); + + valid = valid && embree::isvalid(surfaceContributionWeights[tmpK.quot][tmpK.rem]); + valid = valid && surfaceContributionWeights[tmpK.quot][tmpK.rem] == 0.0f; + OPENPGL_ASSERT(valid); + + valid = valid && embree::isvalid(volumeSampleNumberWeights[tmpK.quot][tmpK.rem]); + valid = valid && volumeSampleNumberWeights[tmpK.quot][tmpK.rem] == 0.0f; + OPENPGL_ASSERT(valid); + + valid = valid && embree::isvalid(surfaceSampleNumberWeights[tmpK.quot][tmpK.rem]); + valid = valid && surfaceSampleNumberWeights[tmpK.quot][tmpK.rem] == 0.0f; + OPENPGL_ASSERT(valid); +#endif } valid = valid && embree::isvalid(numSamples); @@ -356,6 +403,12 @@ void ParallaxAwareVonMisesFisherWeightedEMFactory::SufficientS stream.write(reinterpret_cast(&overallNumSamples), sizeof(float)); stream.write(reinterpret_cast(&numComponents), sizeof(size_t)); stream.write(reinterpret_cast(&normalized), sizeof(bool)); +#ifdef OPENPGL_VSP_GUIDING + serializeFloatVectors(stream, volumeContributionWeights); + serializeFloatVectors(stream, surfaceContributionWeights); + serializeFloatVectors(stream, volumeSampleNumberWeights); + serializeFloatVectors(stream, surfaceSampleNumberWeights); +#endif } template @@ -369,6 +422,12 @@ void ParallaxAwareVonMisesFisherWeightedEMFactory::SufficientS stream.read(reinterpret_cast(&overallNumSamples), sizeof(float)); stream.read(reinterpret_cast(&numComponents), sizeof(size_t)); stream.read(reinterpret_cast(&normalized), sizeof(bool)); +#ifdef OPENPGL_VSP_GUIDING + deserializeFloatVectors(stream, volumeContributionWeights); + deserializeFloatVectors(stream, surfaceContributionWeights); + deserializeFloatVectors(stream, volumeSampleNumberWeights); + deserializeFloatVectors(stream, surfaceSampleNumberWeights); +#endif } template @@ -386,6 +445,12 @@ void ParallaxAwareVonMisesFisherWeightedEMFactory::SufficientS sumOfWeightedStats[k] = zeros; sumOfDistanceWeightes[k] = zeros; +#ifdef OPENPGL_VSP_GUIDING + volumeContributionWeights[k] = 0.0f; + surfaceContributionWeights[k] = 0.0f; + volumeSampleNumberWeights[k] = 0.0f; + surfaceSampleNumberWeights[k] = 0.0f; +#endif } sumWeights = 0.0f; @@ -393,6 +458,25 @@ void ParallaxAwareVonMisesFisherWeightedEMFactory::SufficientS normalized = false; } +template +void ParallaxAwareVonMisesFisherWeightedEMFactory::SufficientStatistics::clearComponentStats(const size_t &idx) +{ + const div_t tmpIdx = div(idx, VMM::VectorSize); + + sumOfWeightedDirections[tmpIdx.quot].x[tmpIdx.rem] = 0.f; + sumOfWeightedDirections[tmpIdx.quot].y[tmpIdx.rem] = 0.f; + sumOfWeightedDirections[tmpIdx.quot].z[tmpIdx.rem] = 0.f; + sumOfWeightedStats[tmpIdx.quot][tmpIdx.rem] = 0.f; + + sumOfDistanceWeightes[tmpIdx.quot][tmpIdx.rem] = 0.f; +#ifdef OPENPGL_VSP_GUIDING + volumeContributionWeights[tmpIdx.quot][tmpIdx.rem] = 0.f; + surfaceContributionWeights[tmpIdx.quot][tmpIdx.rem] = 0.f; + volumeSampleNumberWeights[tmpIdx.quot][tmpIdx.rem] = 0.f; + surfaceSampleNumberWeights[tmpIdx.quot][tmpIdx.rem] = 0.f; +#endif +} + template void ParallaxAwareVonMisesFisherWeightedEMFactory::SufficientStatistics::clearAll() { @@ -410,6 +494,12 @@ void ParallaxAwareVonMisesFisherWeightedEMFactory::SufficientS sumOfWeightedStats[k] *= alpha; sumOfDistanceWeightes[k] *= alpha; +#ifdef OPENPGL_VSP_GUIDING + volumeContributionWeights[k] *= alpha; + surfaceContributionWeights[k] *= alpha; + volumeSampleNumberWeights[k] *= alpha; + surfaceSampleNumberWeights[k] *= alpha; +#endif } numSamples *= alpha; @@ -471,6 +561,12 @@ void ParallaxAwareVonMisesFisherWeightedEMFactory::SufficientS std::swap(sumOfWeightedDirections[tmpIdx0.quot].z[tmpIdx0.rem], sumOfWeightedDirections[tmpIdx1.quot].z[tmpIdx1.rem]); std::swap(sumOfWeightedStats[tmpIdx0.quot][tmpIdx0.rem], sumOfWeightedStats[tmpIdx1.quot][tmpIdx1.rem]); std::swap(sumOfDistanceWeightes[tmpIdx0.quot][tmpIdx0.rem], sumOfDistanceWeightes[tmpIdx1.quot][tmpIdx1.rem]); +#ifdef OPENPGL_VSP_GUIDING + std::swap(volumeContributionWeights[tmpIdx0.quot][tmpIdx0.rem], volumeContributionWeights[tmpIdx1.quot][tmpIdx1.rem]); + std::swap(surfaceContributionWeights[tmpIdx0.quot][tmpIdx0.rem], surfaceContributionWeights[tmpIdx1.quot][tmpIdx1.rem]); + std::swap(volumeSampleNumberWeights[tmpIdx0.quot][tmpIdx0.rem], volumeSampleNumberWeights[tmpIdx1.quot][tmpIdx1.rem]); + std::swap(surfaceSampleNumberWeights[tmpIdx0.quot][tmpIdx0.rem], surfaceSampleNumberWeights[tmpIdx1.quot][tmpIdx1.rem]); +#endif } template @@ -478,7 +574,6 @@ void ParallaxAwareVonMisesFisherWeightedEMFactory::SufficientS { const div_t tmpIdx0 = div(idx0, VMM::VectorSize); const div_t tmpIdx1 = div(idx1, VMM::VectorSize); - const div_t tmpIdx2 = div(numComponents - 1, VMM::VectorSize); // merging the statistics of the component 0 and 1 sumOfWeightedDirections[tmpIdx0.quot].x[tmpIdx0.rem] += sumOfWeightedDirections[tmpIdx1.quot].x[tmpIdx1.rem]; @@ -487,19 +582,14 @@ void ParallaxAwareVonMisesFisherWeightedEMFactory::SufficientS sumOfWeightedStats[tmpIdx0.quot][tmpIdx0.rem] += sumOfWeightedStats[tmpIdx1.quot][tmpIdx1.rem]; sumOfDistanceWeightes[tmpIdx0.quot][tmpIdx0.rem] += sumOfDistanceWeightes[tmpIdx1.quot][tmpIdx1.rem]; - // copying the statistics of the last component to the position of component 1 - sumOfWeightedDirections[tmpIdx1.quot].x[tmpIdx1.rem] = sumOfWeightedDirections[tmpIdx2.quot].x[tmpIdx2.rem]; - sumOfWeightedDirections[tmpIdx1.quot].y[tmpIdx1.rem] = sumOfWeightedDirections[tmpIdx2.quot].y[tmpIdx2.rem]; - sumOfWeightedDirections[tmpIdx1.quot].z[tmpIdx1.rem] = sumOfWeightedDirections[tmpIdx2.quot].z[tmpIdx2.rem]; - sumOfWeightedStats[tmpIdx1.quot][tmpIdx1.rem] = sumOfWeightedStats[tmpIdx2.quot][tmpIdx2.rem]; - sumOfDistanceWeightes[tmpIdx1.quot][tmpIdx1.rem] = sumOfDistanceWeightes[tmpIdx2.quot][tmpIdx2.rem]; - - // reseting the statistics of the last component - sumOfWeightedDirections[tmpIdx2.quot].x[tmpIdx2.rem] = 0.0f; - sumOfWeightedDirections[tmpIdx2.quot].y[tmpIdx2.rem] = 0.0f; - sumOfWeightedDirections[tmpIdx2.quot].z[tmpIdx2.rem] = 0.0f; - sumOfWeightedStats[tmpIdx2.quot][tmpIdx2.rem] = 0.0f; - sumOfDistanceWeightes[tmpIdx2.quot][tmpIdx2.rem] = 0.0f; +#ifdef OPENPGL_VSP_GUIDING + volumeContributionWeights[tmpIdx0.quot][tmpIdx0.rem] += volumeContributionWeights[tmpIdx1.quot][tmpIdx1.rem]; + surfaceContributionWeights[tmpIdx0.quot][tmpIdx0.rem] += surfaceContributionWeights[tmpIdx1.quot][tmpIdx1.rem]; + volumeSampleNumberWeights[tmpIdx0.quot][tmpIdx0.rem] += volumeSampleNumberWeights[tmpIdx1.quot][tmpIdx1.rem]; + surfaceSampleNumberWeights[tmpIdx0.quot][tmpIdx0.rem] += surfaceSampleNumberWeights[tmpIdx1.quot][tmpIdx1.rem]; +#endif + swapComponentStats(idx1, numComponents - 1); + clearComponentStats(numComponents - 1); numComponents--; } @@ -534,6 +624,18 @@ void ParallaxAwareVonMisesFisherWeightedEMFactory::SufficientS sumOfDistanceWeightes[tmpI.quot][tmpI.rem] = tmp; sumOfDistanceWeightes[tmpJ.quot][tmpJ.rem] = tmp; +#ifdef OPENPGL_VSP_GUIDING + float weight0 = 0.5f; + float weight1 = 0.5f; + volumeContributionWeights[tmpJ.quot][tmpJ.rem] = weight1 * volumeContributionWeights[tmpI.quot][tmpI.rem]; + volumeContributionWeights[tmpI.quot][tmpI.rem] *= weight0; + surfaceContributionWeights[tmpJ.quot][tmpJ.rem] = weight1 * surfaceContributionWeights[tmpI.quot][tmpI.rem]; + surfaceContributionWeights[tmpI.quot][tmpI.rem] *= weight0; + volumeSampleNumberWeights[tmpJ.quot][tmpJ.rem] = weight1 * volumeSampleNumberWeights[tmpI.quot][tmpI.rem]; + volumeSampleNumberWeights[tmpI.quot][tmpI.rem] *= weight0; + surfaceSampleNumberWeights[tmpJ.quot][tmpJ.rem] = weight1 * surfaceSampleNumberWeights[tmpI.quot][tmpI.rem]; + surfaceSampleNumberWeights[tmpI.quot][tmpI.rem] *= weight0; +#endif numComponents += 1; OPENPGL_ASSERT(!std::isnan(sumOfWeightedDirections[tmpI.quot].x[tmpI.rem]) && std::isfinite(sumOfWeightedDirections[tmpI.quot].x[tmpI.rem])); @@ -1615,4 +1717,149 @@ std::string ParallaxAwareVonMisesFisherWeightedEMFactory::Part return ss.str(); } +#ifdef OPENPGL_VSP_GUIDING +template +void ParallaxAwareVonMisesFisherWeightedEMFactory::updateVolumeScatterProbability(VMM &vmm, SufficientStatistics &sufficientStats, const SampleData *samples, + const size_t numSamples, const ZeroValueSampleData *zeroValueSamples, + const size_t numZeroValueSamples, const bool varianceBased) const +{ + // OPENPGL_ASSERT(vmm.isValid()); + + if (numSamples + numZeroValueSamples == 0) + { + return; + } + + const int cnt = (vmm._numComponents + VMM::VectorSize - 1) / VMM::VectorSize; + const int rem = vmm._numComponents % VMM::VectorSize; + + const embree::vfloat zeros(0.0f); + const embree::vfloat ones(1.0f); + + typename VMM::SoftAssignment softAssign; + + for (size_t n = 0; n < numSamples; n++) + { + const float weight = samples[n].weight; + pgl_vec3f direction = samples[n].direction; + const Vector3 sampleDirection(direction.x, direction.y, direction.z); + + if (vmm.softAssignment(sampleDirection, softAssign)) + { + for (size_t k = 0; k < cnt; k++) + { + if (isNextEventVolume(samples[n])) + { + if (!varianceBased) + { + sufficientStats.volumeContributionWeights[k] += weight * softAssign.assignments[k]; + OPENPGL_ASSERT(embree::isvalid(sufficientStats.volumeContributionWeights[k])); + } + else + { + sufficientStats.volumeContributionWeights[k] += weight * weight * softAssign.assignments[k]; + OPENPGL_ASSERT(embree::isvalid(sufficientStats.volumeContributionWeights[k])); + } + sufficientStats.volumeSampleNumberWeights[k] += softAssign.assignments[k]; + OPENPGL_ASSERT(embree::isvalid(sufficientStats.volumeSampleNumberWeights[k])); + } + else + { + if (!varianceBased) + { + sufficientStats.surfaceContributionWeights[k] += weight * softAssign.assignments[k]; + OPENPGL_ASSERT(embree::isvalid(sufficientStats.surfaceContributionWeights[k])); + } + else + { + sufficientStats.surfaceContributionWeights[k] += weight * weight * softAssign.assignments[k]; + OPENPGL_ASSERT(embree::isvalid(sufficientStats.surfaceContributionWeights[k])); + } + sufficientStats.surfaceSampleNumberWeights[k] += softAssign.assignments[k]; + OPENPGL_ASSERT(embree::isvalid(sufficientStats.surfaceSampleNumberWeights[k])); + } + } + } + } + + for (size_t n = 0; n < numZeroValueSamples; n++) + { + pgl_vec3f direction = zeroValueSamples[n].direction; + const Vector3 sampleDirection(direction.x, direction.y, direction.z); + + if (vmm.softAssignment(sampleDirection, softAssign)) + { + for (size_t k = 0; k < cnt; k++) + { + if (isNextEventVolume(zeroValueSamples[n])) + { + sufficientStats.volumeSampleNumberWeights[k] += softAssign.assignments[k]; + OPENPGL_ASSERT(embree::isvalid(sufficientStats.volumeSampleNumberWeights[k])); + } + else + { + sufficientStats.surfaceSampleNumberWeights[k] += softAssign.assignments[k]; + OPENPGL_ASSERT(embree::isvalid(sufficientStats.surfaceSampleNumberWeights[k])); + } + } + } + } + + if (rem > 0) + { + for (size_t i = rem; i < VMM::VectorSize; i++) + { + vmm._volumeScatterProbabilityWeights[cnt - 1][i] = 0.0f; + sufficientStats.volumeContributionWeights[cnt - 1][i] = 0.0f; + sufficientStats.surfaceContributionWeights[cnt - 1][i] = 0.0f; + sufficientStats.volumeSampleNumberWeights[cnt - 1][i] = 0.0f; + sufficientStats.surfaceSampleNumberWeights[cnt - 1][i] = 0.0f; + } + } + + for (size_t k = 0; k < cnt; k++) + { + embree::vfloat numVolSamples = sufficientStats.volumeSampleNumberWeights[k]; + embree::vfloat numSurfSamples = sufficientStats.surfaceSampleNumberWeights[k]; + embree::vfloat numSurfVolSamples = numVolSamples + numSurfSamples; + embree::vfloat estPVol = select(numSurfVolSamples > FLT_EPSILON, numVolSamples / numSurfVolSamples, zeros); + + if (!varianceBased) + { + embree::vfloat volumeContributionWeight = select(numVolSamples > FLT_EPSILON, sufficientStats.volumeContributionWeights[k] / numVolSamples, zeros); + embree::vfloat surfaceContributionWeight = select(numSurfSamples > FLT_EPSILON, sufficientStats.surfaceContributionWeights[k] / numSurfSamples, zeros); + embree::vfloat sumContributionWeight = (surfaceContributionWeight * (ones - estPVol)) + (volumeContributionWeight * estPVol); + vmm._volumeScatterProbabilityWeights[k] = select(sumContributionWeight > FLT_EPSILON, (volumeContributionWeight * estPVol) / sumContributionWeight, zeros); + } + else + { + embree::vfloat volumeContributionSecondMomentWeight = + select(numVolSamples > FLT_EPSILON, sufficientStats.volumeContributionWeights[k] / numVolSamples, zeros); + volumeContributionSecondMomentWeight *= (estPVol * estPVol); + volumeContributionSecondMomentWeight = select(volumeContributionSecondMomentWeight > FLT_EPSILON, embree::sqrt(volumeContributionSecondMomentWeight), zeros); + + embree::vfloat surfaceContributionSecondMomentWeight = + select(numSurfSamples > FLT_EPSILON, sufficientStats.surfaceContributionWeights[k] / numSurfSamples, zeros); + surfaceContributionSecondMomentWeight *= (ones - estPVol) * (ones - estPVol); + surfaceContributionSecondMomentWeight = select(surfaceContributionSecondMomentWeight > FLT_EPSILON, embree::sqrt(surfaceContributionSecondMomentWeight), zeros); + + embree::vfloat sumContributionSecondMomentWeight = surfaceContributionSecondMomentWeight + volumeContributionSecondMomentWeight; + vmm._volumeScatterProbabilityWeights[k] = + select(sumContributionSecondMomentWeight > FLT_EPSILON, volumeContributionSecondMomentWeight / sumContributionSecondMomentWeight, zeros); + } + } + + if (rem > 0) + { + for (size_t i = rem; i < VMM::VectorSize; i++) + { + vmm._volumeScatterProbabilityWeights[cnt - 1][i] = 0.0f; + sufficientStats.volumeContributionWeights[cnt - 1][i] = 0.0f; + sufficientStats.surfaceContributionWeights[cnt - 1][i] = 0.0f; + sufficientStats.volumeSampleNumberWeights[cnt - 1][i] = 0.0f; + sufficientStats.surfaceSampleNumberWeights[cnt - 1][i] = 0.0f; + } + } +} +#endif } // namespace openpgl diff --git a/openpgl/directional/vmm/VMMSurfaceSamplingDistribution.h b/openpgl/directional/vmm/VMMSurfaceSamplingDistribution.h index b0ba74f2..6aa38a9d 100644 --- a/openpgl/directional/vmm/VMMSurfaceSamplingDistribution.h +++ b/openpgl/directional/vmm/VMMSurfaceSamplingDistribution.h @@ -155,6 +155,13 @@ struct __aligned(TVMMDistribution::VectorSize * 4) VMMSurfaceSamplingDistributio { m_region = region; } + +#ifdef OPENPGL_VSP_GUIDING + float volumeScatterProbability(Vector3 dir) const override + { + return m_liDistribution.volumeScatterProbability(dir); + } +#endif }; } // namespace openpgl \ No newline at end of file diff --git a/openpgl/directional/vmm/VMMVolumeSamplingDistribution.h b/openpgl/directional/vmm/VMMVolumeSamplingDistribution.h index 89a2d30e..c66021b1 100644 --- a/openpgl/directional/vmm/VMMVolumeSamplingDistribution.h +++ b/openpgl/directional/vmm/VMMVolumeSamplingDistribution.h @@ -187,6 +187,13 @@ struct __aligned(TVMMDistribution::VectorSize * 4) VMMVolumeSamplingDistribution { m_region = region; } + +#ifdef OPENPGL_VSP_GUIDING + float volumeScatterProbability(Vector3 dir) const override + { + return m_liDistribution.volumeScatterProbability(dir); + } +#endif }; } // namespace openpgl \ No newline at end of file diff --git a/openpgl/field/Field.h b/openpgl/field/Field.h index ff4afe82..117241f8 100644 --- a/openpgl/field/Field.h +++ b/openpgl/field/Field.h @@ -54,6 +54,9 @@ struct Field bool useStochasticNNLookUp{false}; bool useISNNLookUp{false}; bool deterministic{false}; +#ifdef OPENPGL_VSP_GUIDING + bool varianceBasedVSP{false}; +#endif float decayOnSpatialSplit{0.25f}; std::string toString() const; @@ -80,7 +83,9 @@ struct Field m_useStochasticNNLookUp = settings.settings.useStochasticNNLookUp; m_useISNNLookUp = settings.settings.useISNNLookUp; m_spatialSubdivBuilderSettings = settings.settings.spatialSubdivBuilderSettings; - +#ifdef OPENPGL_VSP_GUIDING + m_vspVarianceBased = settings.settings.varianceBasedVSP; +#endif m_distributionFactorySettings = settings.distributionFactorySettings; samples_.reserve(1e6); } @@ -272,6 +277,9 @@ struct Field os.write(reinterpret_cast(&m_iteration), sizeof(m_iteration)); os.write(reinterpret_cast(&m_totalSPP), sizeof(m_totalSPP)); os.write(reinterpret_cast(&m_deterministic), sizeof(m_deterministic)); +#ifdef OPENPGL_VSP_GUIDING + os.write(reinterpret_cast(&m_vspVarianceBased), sizeof(m_vspVarianceBased)); +#endif os.write(reinterpret_cast(&m_fitRegions), sizeof(m_fitRegions)); os.write(reinterpret_cast(&m_isSceneBoundsSet), sizeof(m_isSceneBoundsSet)); os.write(reinterpret_cast(&m_sceneBounds), sizeof(m_sceneBounds)); @@ -304,6 +312,9 @@ struct Field is.read(reinterpret_cast(&m_iteration), sizeof(m_iteration)); is.read(reinterpret_cast(&m_totalSPP), sizeof(m_totalSPP)); is.read(reinterpret_cast(&m_deterministic), sizeof(m_deterministic)); +#ifdef OPENPGL_VSP_GUIDING + is.read(reinterpret_cast(&m_vspVarianceBased), sizeof(m_vspVarianceBased)); +#endif is.read(reinterpret_cast(&m_fitRegions), sizeof(m_fitRegions)); is.read(reinterpret_cast(&m_isSceneBoundsSet), sizeof(m_isSceneBoundsSet)); is.read(reinterpret_cast(&m_sceneBounds), sizeof(m_sceneBounds)); @@ -461,6 +472,12 @@ struct Field regionStorage.first.outRadianceHist.update(samples.data() + regionStorage.second.m_begin, regionStorage.second.m_end - regionStorage.second.m_begin, zeroValueSamples.data() + regionStorage.second.m_is_begin, regionStorage.second.m_is_end - regionStorage.second.m_is_begin); +#endif +#ifdef OPENPGL_VSP_GUIDING + m_distributionFactory.updateVolumeScatterProbability( + regionStorage.first.distribution, regionStorage.first.trainingStatistics, samples.data() + regionStorage.second.m_begin, + regionStorage.second.m_end - regionStorage.second.m_begin, zeroValueSamples.data() + regionStorage.second.m_is_begin, + regionStorage.second.m_is_end - regionStorage.second.m_is_begin, m_vspVarianceBased); #endif // TODO: we should move setting the pivot to the factory regionStorage.first.distribution._pivotPosition = sampleMean; @@ -557,6 +574,12 @@ struct Field regionStorage.first.outRadianceHist.update(samples.data() + regionStorage.second.m_begin, regionStorage.second.m_end - regionStorage.second.m_begin, zeroValueSamples.data() + regionStorage.second.m_is_begin, regionStorage.second.m_is_end - regionStorage.second.m_is_begin); +#endif +#ifdef OPENPGL_VSP_GUIDING + m_distributionFactory.updateVolumeScatterProbability( + regionStorage.first.distribution, regionStorage.first.trainingStatistics, samples.data() + regionStorage.second.m_begin, + regionStorage.second.m_end - regionStorage.second.m_begin, zeroValueSamples.data() + regionStorage.second.m_is_begin, + regionStorage.second.m_is_end - regionStorage.second.m_is_begin, m_vspVarianceBased); #endif regionStorage.first.valid = regionStorage.first.isValid(); #ifdef OPENPGL_DEBUG_MODE @@ -577,6 +600,7 @@ struct Field if (regionStorage.first.splitFlag) { regionStorage.first.trainingStatistics.decay(this->m_decayOnSpatialSplit); + regionStorage.first.distribution.decay(this->m_decayOnSpatialSplit); regionStorage.first.splitFlag = false; } } @@ -733,6 +757,10 @@ struct Field // if the fitting process should be deterministic (i.e, samples are sorted before training) bool m_deterministic{false}; +#ifdef OPENPGL_VSP_GUIDING + bool m_vspVarianceBased{false}; +#endif + bool m_isSceneBoundsSet{false}; BBox m_sceneBounds; diff --git a/openpgl/imagespace/ImageSpaceGuidingBuffer.h b/openpgl/imagespace/ImageSpaceGuidingBuffer.h index 468ac915..58a4c86f 100644 --- a/openpgl/imagespace/ImageSpaceGuidingBuffer.h +++ b/openpgl/imagespace/ImageSpaceGuidingBuffer.h @@ -25,6 +25,8 @@ #define TINYEXR_IMPLEMENTATION #include +#define VSP_USE_PVOL_EST + namespace openpgl { @@ -35,14 +37,12 @@ struct ImageSpaceGuidingBuffer Buffers(pgl_point2i resolution) : numPixels(resolution.x * resolution.y) { contribution = new pgl_vec3f[numPixels]; - secondMoment = new pgl_vec3f[numPixels]; albedo = new pgl_vec3f[numPixels]; normal = new pgl_vec3f[numPixels]; spp = new float[numPixels]; filteredContribution = new pgl_vec3f[numPixels]; - filteredSecondMoment = new pgl_vec3f[numPixels]; } Buffers(const Buffers &buffer) = delete; @@ -52,14 +52,12 @@ struct ImageSpaceGuidingBuffer ~Buffers() { delete[] contribution; - delete[] secondMoment; delete[] albedo; delete[] normal; delete[] spp; delete[] filteredContribution; - delete[] filteredSecondMoment; } void reset() @@ -73,14 +71,12 @@ struct ImageSpaceGuidingBuffer #endif { contribution[pIdx] = {0.f, 0.f, 0.f}; - secondMoment[pIdx] = {0.f, 0.f, 0.f}; albedo[pIdx] = {0.f, 0.f, 0.f}; normal[pIdx] = {0.f, 0.f, 0.f}; spp[pIdx] = 0.f; filteredContribution[pIdx] = {0.f, 0.f, 0.f}; - filteredSecondMoment[pIdx] = {0.f, 0.f, 0.f}; } }); } @@ -88,20 +84,31 @@ struct ImageSpaceGuidingBuffer int numPixels{0}; pgl_vec3f *contribution{nullptr}; - pgl_vec3f *secondMoment{nullptr}; pgl_vec3f *albedo{nullptr}; pgl_vec3f *normal{nullptr}; float *spp{nullptr}; pgl_vec3f *filteredContribution{nullptr}; - pgl_vec3f *filteredSecondMoment{nullptr}; }; - ImageSpaceGuidingBuffer(pgl_point2i resolution, bool useSecondMoment) : m_useSecondMoment(useSecondMoment), m_resolution(resolution) + ImageSpaceGuidingBuffer(PGLImageSpaceGuidingBufferConfig cfg) : m_cfg(cfg) { - m_denoiser = new Denoiser(m_resolution, false); - m_contributionEstimateBuffers = new Buffers(m_resolution); + m_resolution = cfg.resolution; + m_denoiser = new Denoiser(cfg.resolution, false); + if(m_cfg.contributionEstimate) { + m_contributionEstimateBuffers = new Buffers(cfg.resolution); + } +#if defined(OPENPGL_VSP_GUIDING) + if(m_cfg.vspEstimate) { + m_surfaceContributionEstimateBuffers = new Buffers(cfg.resolution); + m_volumeContributionEstimateBuffers = new Buffers(cfg.resolution); + m_pVolBuffer = new float[m_resolution.x * m_resolution.y]; + m_filteredPVolBuffer = new float[m_resolution.x * m_resolution.y]; + m_vspContributionBuffer = new float[m_resolution.x * m_resolution.y]; + } +#endif + this->reset(); m_ready = false; } @@ -132,11 +139,52 @@ struct ImageSpaceGuidingBuffer const size_t numPixels = m_resolution.x * m_resolution.y; m_denoiser = new Denoiser(m_resolution, false); - m_contributionEstimateBuffers = new Buffers(m_resolution); std::vector layer_names; tinyexr::GetLayers(exrHeader, layer_names); + // identify config based on the stored layers + m_cfg.contributionEstimate = false; + m_cfg.contributionType = PGLContributionTypes::EContribContribution; +#if defined(OPENPGL_VSP_GUIDING) + m_cfg.vspEstimate = false; + m_cfg.vspType = PGLVSPTypes::EVSPContribution; +#endif + for (int i = 0; i < layer_names.size(); i++) { + std::string layerName = layer_names[i]; + if (layerName == "Contrib" || layerName == "Contrib2nd") + { + m_cfg.contributionEstimate = true; + if(layerName == "Contrib2nd") { + m_cfg.contributionType = PGLContributionTypes::EContribVariance; + } + } +#if defined(OPENPGL_VSP_GUIDING) + if (layerName == "surfContrib" || layerName == "surfContrib2nd") + { + m_cfg.vspEstimate = true; + if(layerName == "surfContrib2nd") { + m_cfg.vspType = PGLVSPTypes::EVSPVariance; + } + } +#endif + } + + if (m_cfg.contributionEstimate) { + m_contributionEstimateBuffers = new Buffers(m_resolution); + } + + m_cfg.resolution = m_resolution; +#if defined(OPENPGL_VSP_GUIDING) + if(m_cfg.vspEstimate) { + m_surfaceContributionEstimateBuffers = new Buffers(m_resolution); + m_volumeContributionEstimateBuffers = new Buffers(m_resolution); + m_pVolBuffer = new float[m_resolution.x * m_resolution.y]; + m_filteredPVolBuffer = new float[m_resolution.x * m_resolution.y]; + m_vspContributionBuffer = new float[m_resolution.x * m_resolution.y]; + } +#endif + for (int i = 0; i < layer_names.size(); i++) { std::string layerName = layer_names[i]; @@ -175,26 +223,18 @@ struct ImageSpaceGuidingBuffer } float *bufferPtr = nullptr; - if (layerName == "Contrib") + if (layerName == "Contrib" || layerName == "Contrib2nd") { bufferPtr = (float *)m_contributionEstimateBuffers->filteredContribution; } - else if (layerName == "Contribt2nd") - { - bufferPtr = (float *)m_contributionEstimateBuffers->filteredSecondMoment; - } else if (layerName == "Spp") { bufferPtr = (float *)m_contributionEstimateBuffers->spp; } - else if (layerName == "ContribRaw") + else if (layerName == "ContribRaw" || layerName == "Contrib2ndRaw") { bufferPtr = (float *)m_contributionEstimateBuffers->contribution; } - else if (layerName == "Contribt2ndRaw") - { - bufferPtr = (float *)m_contributionEstimateBuffers->secondMoment; - } else if (layerName == "Albedo") { bufferPtr = (float *)m_contributionEstimateBuffers->albedo; @@ -203,6 +243,60 @@ struct ImageSpaceGuidingBuffer { bufferPtr = (float *)m_contributionEstimateBuffers->normal; } +#if defined(OPENPGL_VSP_GUIDING) + if (layerName == "surfContrib" || layerName == "surfContrib2nd") + { + bufferPtr = (float *)m_surfaceContributionEstimateBuffers->filteredContribution; + } + else if (layerName == "surfSpp") + { + bufferPtr = (float *)m_surfaceContributionEstimateBuffers->spp; + } + else if (layerName == "surfContribRaw" || layerName == "surfContrib2ndRaw") + { + bufferPtr = (float *)m_surfaceContributionEstimateBuffers->contribution; + } + else if (layerName == "surfAlbedo") + { + bufferPtr = (float *)m_surfaceContributionEstimateBuffers->albedo; + } + else if (layerName == "surfN") + { + bufferPtr = (float *)m_surfaceContributionEstimateBuffers->normal; + } + else if (layerName == "volContrib" || layerName == "volContrib2nd") + { + bufferPtr = (float *)m_volumeContributionEstimateBuffers->filteredContribution; + } + else if (layerName == "volSpp") + { + bufferPtr = (float *)m_volumeContributionEstimateBuffers->spp; + } + else if (layerName == "volContribRaw" || layerName == "volContrib2ndRaw") + { + bufferPtr = (float *)m_volumeContributionEstimateBuffers->contribution; + } + else if (layerName == "volAlbedo") + { + bufferPtr = (float *)m_volumeContributionEstimateBuffers->albedo; + } + else if (layerName == "volN") + { + bufferPtr = (float *)m_volumeContributionEstimateBuffers->normal; + } + else if (layerName == "VSP") + { + bufferPtr = (float *)m_vspContributionBuffer; + } + else if (layerName == "PVolRAW") + { + bufferPtr = (float *)m_pVolBuffer; + } + else if (layerName == "PVol") + { + bufferPtr = (float *)m_filteredPVolBuffer; + } +#endif else { } @@ -251,6 +345,13 @@ struct ImageSpaceGuidingBuffer { delete m_denoiser; delete m_contributionEstimateBuffers; +#if defined(OPENPGL_VSP_GUIDING) + delete m_surfaceContributionEstimateBuffers; + delete m_volumeContributionEstimateBuffers; + delete m_pVolBuffer; + delete m_filteredPVolBuffer; + delete m_vspContributionBuffer; +#endif } void store(const std::string &fileName) const @@ -267,40 +368,125 @@ struct ImageSpaceGuidingBuffer std::vector numChannels; std::vector layerChannels; std::vector channelValues; - int cIdx = layerChannels.size(); - layerChannels.emplace_back(cIdx, "Contrib"); - numChannels.emplace_back(3); - channelValues.emplace_back((const float *)m_contributionEstimateBuffers->filteredContribution); - - cIdx = layerChannels.size(); - layerChannels.emplace_back(cIdx, "Contribt2nd"); - numChannels.emplace_back(3); - channelValues.emplace_back((const float *)m_contributionEstimateBuffers->filteredSecondMoment); - - cIdx = layerChannels.size(); - layerChannels.emplace_back(cIdx, "Spp"); - numChannels.emplace_back(1); - channelValues.emplace_back((const float *)m_contributionEstimateBuffers->spp); - - cIdx = layerChannels.size(); - layerChannels.emplace_back(cIdx, "ContribRaw"); - numChannels.emplace_back(3); - channelValues.emplace_back((const float *)m_contributionEstimateBuffers->contribution); - - cIdx = layerChannels.size(); - layerChannels.emplace_back(cIdx, "Contribt2ndRaw"); - numChannels.emplace_back(3); - channelValues.emplace_back((const float *)m_contributionEstimateBuffers->secondMoment); - - cIdx = layerChannels.size(); - layerChannels.emplace_back(cIdx, "Albedo"); - numChannels.emplace_back(3); - channelValues.emplace_back((const float *)m_contributionEstimateBuffers->albedo); - - cIdx = layerChannels.size(); - layerChannels.emplace_back(cIdx, "N"); - numChannels.emplace_back(3); - channelValues.emplace_back((const float *)m_contributionEstimateBuffers->normal); + int cIdx = 0; + if(m_cfg.contributionEstimate) { + cIdx = layerChannels.size(); + if (m_cfg.contributionType == PGLContributionTypes::EContribContribution) { + layerChannels.emplace_back(cIdx, "Contrib"); + } else { + layerChannels.emplace_back(cIdx, "Contrib2nd"); + } + numChannels.emplace_back(3); + channelValues.emplace_back((const float *)m_contributionEstimateBuffers->filteredContribution); + + cIdx = layerChannels.size(); + layerChannels.emplace_back(cIdx, "Spp"); + numChannels.emplace_back(1); + channelValues.emplace_back((const float *)m_contributionEstimateBuffers->spp); + + cIdx = layerChannels.size(); + if (m_cfg.contributionType == PGLContributionTypes::EContribContribution) { + layerChannels.emplace_back(cIdx, "ContribRaw"); + } else { + layerChannels.emplace_back(cIdx, "Contrib2ndRaw"); + } + numChannels.emplace_back(3); + channelValues.emplace_back((const float *)m_contributionEstimateBuffers->contribution); + + cIdx = layerChannels.size(); + layerChannels.emplace_back(cIdx, "Albedo"); + numChannels.emplace_back(3); + channelValues.emplace_back((const float *)m_contributionEstimateBuffers->albedo); + + cIdx = layerChannels.size(); + layerChannels.emplace_back(cIdx, "N"); + numChannels.emplace_back(3); + channelValues.emplace_back((const float *)m_contributionEstimateBuffers->normal); + } +#if defined(OPENPGL_VSP_GUIDING) + if(m_cfg.vspEstimate) { + cIdx = layerChannels.size(); + if (m_cfg.vspType == PGLVSPTypes::EVSPContribution) { + layerChannels.emplace_back(cIdx, "surfContrib"); + } else { + layerChannels.emplace_back(cIdx, "surfContrib2nd"); + } + numChannels.emplace_back(3); + channelValues.emplace_back((const float *)m_surfaceContributionEstimateBuffers->filteredContribution); + + cIdx = layerChannels.size(); + layerChannels.emplace_back(cIdx, "surfSpp"); + numChannels.emplace_back(1); + channelValues.emplace_back((const float *)m_surfaceContributionEstimateBuffers->spp); + + cIdx = layerChannels.size(); + if (m_cfg.vspType == PGLVSPTypes::EVSPContribution) { + layerChannels.emplace_back(cIdx, "surfContribRaw"); + } else { + layerChannels.emplace_back(cIdx, "surfContrib2ndRaw"); + } + numChannels.emplace_back(3); + channelValues.emplace_back((const float *)m_surfaceContributionEstimateBuffers->contribution); + + cIdx = layerChannels.size(); + layerChannels.emplace_back(cIdx, "surfAlbedo"); + numChannels.emplace_back(3); + channelValues.emplace_back((const float *)m_surfaceContributionEstimateBuffers->albedo); + + cIdx = layerChannels.size(); + layerChannels.emplace_back(cIdx, "surfN"); + numChannels.emplace_back(3); + channelValues.emplace_back((const float *)m_surfaceContributionEstimateBuffers->normal); + + cIdx = layerChannels.size(); + if (m_cfg.vspType == PGLVSPTypes::EVSPContribution) { + layerChannels.emplace_back(cIdx, "volContrib"); + } else { + layerChannels.emplace_back(cIdx, "volContrib2nd"); + } + numChannels.emplace_back(3); + channelValues.emplace_back((const float *)m_volumeContributionEstimateBuffers->filteredContribution); + + cIdx = layerChannels.size(); + layerChannels.emplace_back(cIdx, "volSpp"); + numChannels.emplace_back(1); + channelValues.emplace_back((const float *)m_volumeContributionEstimateBuffers->spp); + + cIdx = layerChannels.size(); + if (m_cfg.vspType == PGLVSPTypes::EVSPContribution) { + layerChannels.emplace_back(cIdx, "volContribRaw"); + } else { + layerChannels.emplace_back(cIdx, "volContrib2ndRaw"); + } + numChannels.emplace_back(3); + channelValues.emplace_back((const float *)m_volumeContributionEstimateBuffers->contribution); + + cIdx = layerChannels.size(); + layerChannels.emplace_back(cIdx, "volAlbedo"); + numChannels.emplace_back(3); + channelValues.emplace_back((const float *)m_volumeContributionEstimateBuffers->albedo); + + cIdx = layerChannels.size(); + layerChannels.emplace_back(cIdx, "volN"); + numChannels.emplace_back(3); + channelValues.emplace_back((const float *)m_volumeContributionEstimateBuffers->normal); + + cIdx = layerChannels.size(); + layerChannels.emplace_back(cIdx, "VSP"); + numChannels.emplace_back(1); + channelValues.emplace_back((const float *)m_vspContributionBuffer); + + cIdx = layerChannels.size(); + layerChannels.emplace_back(cIdx, "PVolRAW"); + numChannels.emplace_back(1); + channelValues.emplace_back((const float *)m_pVolBuffer); + + cIdx = layerChannels.size(); + layerChannels.emplace_back(cIdx, "PVol"); + numChannels.emplace_back(1); + channelValues.emplace_back((const float *)m_filteredPVolBuffer); + } +#endif int totalNumLayers = layerChannels.size(); int totalNumChannels = 0; @@ -442,39 +628,157 @@ struct ImageSpaceGuidingBuffer void update() { - if (m_useSecondMoment) - { - m_denoiser->denoise(m_contributionEstimateBuffers->contribution, m_contributionEstimateBuffers->secondMoment, m_contributionEstimateBuffers->normal, - m_contributionEstimateBuffers->albedo, m_contributionEstimateBuffers->filteredContribution, m_contributionEstimateBuffers->filteredSecondMoment); - } - else - { + if(m_cfg.contributionEstimate) { m_denoiser->denoise(m_contributionEstimateBuffers->contribution, m_contributionEstimateBuffers->normal, m_contributionEstimateBuffers->albedo, m_contributionEstimateBuffers->filteredContribution); } +#if defined(OPENPGL_VSP_GUIDING) + if(m_cfg.vspEstimate) { + const int numPixels = m_resolution.x * m_resolution.y; + +#ifdef USE_EMBREE_PARALLEL + embree::parallel_for(0, (int)numPixels, 1, [&](const embree::range &r) { + for (size_t pIdx = r.begin(); pIdx < r.end(); pIdx++) +#else + tbb::parallel_for(tbb::blocked_range(0, numPixels), [&](tbb::blocked_range r) { + for (int pIdx = r.begin(); pIdx < r.end(); ++pIdx) +#endif + { + const float surfaceSampleCount = m_surfaceContributionEstimateBuffers->spp[pIdx]; + const float volumeSampleCount = m_volumeContributionEstimateBuffers->spp[pIdx]; + const float pVolEst = volumeSampleCount / (surfaceSampleCount + volumeSampleCount); + m_pVolBuffer[pIdx] = pVolEst; + } + }); + + m_denoiser->denoise(m_pVolBuffer, m_filteredPVolBuffer); + m_denoiser->denoise(m_surfaceContributionEstimateBuffers->contribution, m_surfaceContributionEstimateBuffers->normal, m_surfaceContributionEstimateBuffers->albedo, m_surfaceContributionEstimateBuffers->filteredContribution); + m_denoiser->denoise(m_volumeContributionEstimateBuffers->contribution, m_volumeContributionEstimateBuffers->normal, m_volumeContributionEstimateBuffers->albedo, m_volumeContributionEstimateBuffers->filteredContribution); + +#ifdef USE_EMBREE_PARALLEL + embree::parallel_for(0, (int)numPixels, 1, [&](const embree::range &r) { + for (size_t pIdx = r.begin(); pIdx < r.end(); pIdx++) { +#else + tbb::parallel_for(tbb::blocked_range(0, numPixels), [&](tbb::blocked_range r) { + for (int pIdx = r.begin(); pIdx < r.end(); ++pIdx) { +#endif + + float pVolEst = m_filteredPVolBuffer[pIdx]; + pVolEst = std::max(0.f, std::min(1.f, pVolEst)); +#ifndef VSP_USE_PVOL_EST + // If we add zero samples to the other buffers (e.g., to the volume buffer when we have a surface sample) + // Then we do not need to multiply with (1.f -pVolEst) or pVolEst. + // Note for the second moment look for the USE_PVOL_CORRECTION earlier whne calcualting the sqrt of the second moment. + pgl_vec3f surfaceContribution = m_surfaceContributionEstimateBuffers->filteredContribution[pIdx]; + pgl_vec3f volumeContribution = m_volumeContributionEstimateBuffers->filteredContribution[pIdx]; +#else + // If the surface/volume buffers only include volume or surface samples we have + // to correct with (1.f -pVolEst) and pVolEst. + // Not since we already use the sqrt of the second moment we only need to multiply + // with pVolEst and not pVolEst°2 + + pgl_vec3f surfaceContribution, volumeContribution; + if (m_cfg.vspType == PGLVSPTypes::EVSPContribution) { + surfaceContribution = (1.f - pVolEst) * m_surfaceContributionEstimateBuffers->filteredContribution[pIdx]; + volumeContribution = pVolEst * m_volumeContributionEstimateBuffers->filteredContribution[pIdx]; + } + else { + surfaceContribution.x = m_surfaceContributionEstimateBuffers->filteredContribution[pIdx].x > 0.f ? (1.f - pVolEst) * std::sqrt(m_surfaceContributionEstimateBuffers->filteredContribution[pIdx].x) : 0.f; + surfaceContribution.y = m_surfaceContributionEstimateBuffers->filteredContribution[pIdx].y > 0.f ? (1.f - pVolEst) * std::sqrt(m_surfaceContributionEstimateBuffers->filteredContribution[pIdx].y) : 0.f; + surfaceContribution.z = m_surfaceContributionEstimateBuffers->filteredContribution[pIdx].z > 0.f ? (1.f - pVolEst) * std::sqrt(m_surfaceContributionEstimateBuffers->filteredContribution[pIdx].z) : 0.f; + + volumeContribution.x = m_volumeContributionEstimateBuffers->filteredContribution[pIdx].x > 0.f ? pVolEst * std::sqrt(m_volumeContributionEstimateBuffers->filteredContribution[pIdx].x) : 0.f; + volumeContribution.y = m_volumeContributionEstimateBuffers->filteredContribution[pIdx].y > 0.f ? pVolEst * std::sqrt(m_volumeContributionEstimateBuffers->filteredContribution[pIdx].y) : 0.f; + volumeContribution.z = m_volumeContributionEstimateBuffers->filteredContribution[pIdx].z > 0.f ? pVolEst * std::sqrt(m_volumeContributionEstimateBuffers->filteredContribution[pIdx].z) : 0.f; + } + +#endif + pgl_vec3f contribution = surfaceContribution + volumeContribution; + float contributionScalar = pglVec3fMax(contribution); + float volumeContributionScalar = pglVec3fMax(volumeContribution); + + m_vspContributionBuffer[pIdx] = contributionScalar > 0.f ? volumeContributionScalar / (contributionScalar) : -1.f; + } + }); + } +#endif m_ready = true; } void addSample(const pgl_point2i pixel, const PGLImageSpaceSample &sample) { std::size_t pixelIdx = pixel.y * m_resolution.x + pixel.x; - m_contributionEstimateBuffers->spp[pixelIdx] += 1; - float alpha = 1.f / m_contributionEstimateBuffers->spp[pixelIdx]; - - m_contributionEstimateBuffers->contribution[pixelIdx] = (1.f - alpha) * m_contributionEstimateBuffers->contribution[pixelIdx] + alpha * sample.contribution; - m_contributionEstimateBuffers->albedo[pixelIdx] = (1.f - alpha) * m_contributionEstimateBuffers->albedo[pixelIdx] + alpha * sample.albedo; - m_contributionEstimateBuffers->normal[pixelIdx] = (1.f - alpha) * m_contributionEstimateBuffers->normal[pixelIdx] + alpha * sample.normal; - m_contributionEstimateBuffers->secondMoment[pixelIdx] = - (1.f - alpha) * m_contributionEstimateBuffers->secondMoment[pixelIdx] + alpha * (sample.contribution * sample.contribution); + if(m_cfg.contributionEstimate) { + m_contributionEstimateBuffers->spp[pixelIdx] += 1; + float alpha = 1.f / m_contributionEstimateBuffers->spp[pixelIdx]; + + const pgl_vec3f quantity = m_cfg.contributionType == PGLContributionTypes::EContribContribution ? sample.contribution : sample.contribution*sample.contribution; + m_contributionEstimateBuffers->contribution[pixelIdx] = (1.f - alpha) * m_contributionEstimateBuffers->contribution[pixelIdx] + alpha * sample.contribution; + m_contributionEstimateBuffers->albedo[pixelIdx] = (1.f - alpha) * m_contributionEstimateBuffers->albedo[pixelIdx] + alpha * sample.albedo; + m_contributionEstimateBuffers->normal[pixelIdx] = (1.f - alpha) * m_contributionEstimateBuffers->normal[pixelIdx] + alpha * sample.normal; + } +#if defined(OPENPGL_VSP_GUIDING) + if(m_cfg.vspEstimate) { + if (sample.IsSurfaceEvent()) { + m_surfaceContributionEstimateBuffers->spp[pixelIdx] += 1; +#ifdef VSP_USE_PVOL_EST + // calculating the alpha using only the number of surface samples + float alpha = 1.f / m_surfaceContributionEstimateBuffers->spp[pixelIdx]; +#else + // calculating the alpha simulating we added zero samples for each volume sample as well + float alpha = 1.f / (m_surfaceContributionEstimateBuffers->spp[pixelIdx] + m_volumeContributionEstimateBuffers->spp[pixelIdx]); +#endif + pgl_vec3f quantity = m_cfg.vspType == EVSPVariance ? sample.contribution * sample.contribution : sample.contribution; + m_surfaceContributionEstimateBuffers->contribution[pixelIdx] = (1.f - alpha) * m_surfaceContributionEstimateBuffers->contribution[pixelIdx] + alpha * quantity; + m_surfaceContributionEstimateBuffers->albedo[pixelIdx] = (1.f - alpha) * m_surfaceContributionEstimateBuffers->albedo[pixelIdx] + alpha * sample.albedo; + m_surfaceContributionEstimateBuffers->normal[pixelIdx] = (1.f - alpha) * m_surfaceContributionEstimateBuffers->normal[pixelIdx] + alpha * sample.normal; +#ifndef VSP_USE_PVOL_EST + // adding zero value samples to the volume buffer + m_volumeContributionEstimateBuffers->contribution[pixelIdx] = (1.f - alpha) * m_volumeContributionEstimateBuffers->contribution[pixelIdx]; +#endif + } else { + m_volumeContributionEstimateBuffers->spp[pixelIdx] += 1; +#ifdef VSP_USE_PVOL_EST + // calculating the alpha using only the number of volume samples + float alpha = 1.f / m_volumeContributionEstimateBuffers->spp[pixelIdx]; +#else + // calculating the alpha simulating we added zero samples for each surface sample as well + float alpha = 1.f / (m_surfaceContributionEstimateBuffers->spp[pixelIdx] + m_volumeContributionEstimateBuffers->spp[pixelIdx]); +#endif + pgl_vec3f quantity = m_cfg.vspType == EVSPVariance ? sample.contribution * sample.contribution : sample.contribution; + m_volumeContributionEstimateBuffers->contribution[pixelIdx] = (1.f - alpha) * m_volumeContributionEstimateBuffers->contribution[pixelIdx] + alpha * quantity; + m_volumeContributionEstimateBuffers->albedo[pixelIdx] = (1.f - alpha) * m_volumeContributionEstimateBuffers->albedo[pixelIdx] + alpha * sample.albedo; + m_volumeContributionEstimateBuffers->normal[pixelIdx] = (1.f - alpha) * m_volumeContributionEstimateBuffers->normal[pixelIdx] + alpha * sample.normal; +#ifndef VSP_USE_PVOL_EST + // adding zero value samples to the surface buffer + m_surfaceContributionEstimateBuffers->contribution[pixelIdx] = (1.f - alpha) * m_surfaceContributionEstimateBuffers->contribution[pixelIdx]; +#endif + } + } +#endif } - pgl_vec3f getContributionEstimate(const pgl_point2i pixel, const bool secondMoment = false) const + pgl_vec3f getContributionEstimate(const pgl_point2i pixel) const { + if(!m_ready || !m_cfg.contributionEstimate) { + return {0.f, 0.f, 0.f}; + } + std::size_t pixelIdx = pixel.y * m_resolution.x + pixel.x; - const pgl_vec3f c = !secondMoment ? m_contributionEstimateBuffers->filteredContribution[pixelIdx] : m_contributionEstimateBuffers->filteredSecondMoment[pixelIdx]; + const pgl_vec3f c = m_contributionEstimateBuffers->filteredContribution[pixelIdx]; return c; } +#if defined(OPENPGL_VSP_GUIDING) + float getVolumeScatterProbabilityEstimate(const pgl_point2i pixel) const + { + if (!m_ready || !m_cfg.vspEstimate) { + return 0.5f; + } + std::size_t pixelIdx = pixel.y * m_resolution.x + pixel.x; + return m_vspContributionBuffer[pixelIdx]; + } +#endif bool isReady() const { return m_ready; @@ -482,20 +786,56 @@ struct ImageSpaceGuidingBuffer void reset() { - if (m_contributionEstimateBuffers) + if (m_cfg.contributionEstimate && m_contributionEstimateBuffers) { m_contributionEstimateBuffers->reset(); } +#if defined(OPENPGL_VSP_GUIDING) + if(m_cfg.vspEstimate) { + if (m_surfaceContributionEstimateBuffers) + { + m_surfaceContributionEstimateBuffers->reset(); + } + if (m_volumeContributionEstimateBuffers) + { + m_volumeContributionEstimateBuffers->reset(); + } + const int numPixels = m_resolution.x * m_resolution.y; + +#ifdef USE_EMBREE_PARALLEL + embree::parallel_for(0, (int)numPixels, 1, [&](const embree::range &r) { + for (size_t pIdx = r.begin(); pIdx < r.end(); pIdx++) +#else + tbb::parallel_for(tbb::blocked_range(0, numPixels), [&](tbb::blocked_range r) { + for (int pIdx = r.begin(); pIdx < r.end(); ++pIdx) +#endif + { + m_pVolBuffer[pIdx] = 0.f; + m_filteredPVolBuffer[pIdx] = 0.f; + m_vspContributionBuffer[pIdx] = 0.f; + } + }); + } +#endif m_ready = false; } private: bool m_ready{false}; - bool m_useSecondMoment{false}; + PGLImageSpaceGuidingBufferConfig m_cfg; pgl_point2i m_resolution{0, 0}; Denoiser *m_denoiser{nullptr}; Buffers *m_contributionEstimateBuffers{nullptr}; + +#if defined(OPENPGL_VSP_GUIDING) + Buffers *m_surfaceContributionEstimateBuffers{nullptr}; + Buffers *m_volumeContributionEstimateBuffers{nullptr}; + + float *m_pVolBuffer {nullptr}; + float *m_filteredPVolBuffer {nullptr}; + float *m_vspContributionBuffer {nullptr}; +#endif }; } // namespace openpgl \ No newline at end of file diff --git a/openpgl/include/openpgl/common.h b/openpgl/include/openpgl/common.h index 264f3a1f..ab0e74ec 100644 --- a/openpgl/include/openpgl/common.h +++ b/openpgl/include/openpgl/common.h @@ -171,6 +171,23 @@ inline void pglVec3fAdd(pgl_vec3f &veca, const pgl_vec3f &vecb) veca.z += vecb.z; } +inline void pglVec3fMultiply(pgl_vec3f &veca, const pgl_vec3f &vecb) +{ + veca.x *= vecb.x; + veca.y *= vecb.y; + veca.z *= vecb.z; +} + +inline float pglVec3fMax(const pgl_vec3f &vec) +{ + return std::max(vec.x, std::max(vec.y, vec.z)); +} + +inline float pglVec3fMin(const pgl_vec3f &vec) +{ + return std::min(vec.x, std::min(vec.y, vec.z)); +} + inline void pglVec2f(pgl_vec2f &vec, const float x, const float y) { vec.x = x; diff --git a/openpgl/include/openpgl/config.h b/openpgl/include/openpgl/config.h index 8cf2509a..f48bfe70 100644 --- a/openpgl/include/openpgl/config.h +++ b/openpgl/include/openpgl/config.h @@ -124,6 +124,9 @@ extern "C" void *directionalDistributionArguments; // for debugging bool deterministic{false}; +#ifdef OPENPGL_VSP_GUIDING + bool varianceBasedVSP{false}; +#endif PGLDebugArguments debugArguments; }; diff --git a/openpgl/include/openpgl/cpp/FieldConfig.h b/openpgl/include/openpgl/cpp/FieldConfig.h index 88608f3d..7264aa92 100644 --- a/openpgl/include/openpgl/cpp/FieldConfig.h +++ b/openpgl/include/openpgl/cpp/FieldConfig.h @@ -59,7 +59,9 @@ struct FieldConfig * @param useKnnIsLookup if distance based importance sampling of the neighbors should be used */ void SetUseKnnIsLookup(const bool useKnnIsLookup); - +#ifdef OPENPGL_VSP_GUIDING + void SetVarianceBasedVSP(const bool varianceBasedVSP); +#endif /** * @brief For debugging and benchmarking the update of the spatial structure this function can disable * the training of the directional distribution during the update iterations. @@ -104,5 +106,12 @@ OPENPGL_INLINE void FieldConfig::SetUseKnnIsLookup(const bool useKnnIsLookup) reinterpret_cast(m_args.spatialSturctureArguments)->isKnnLookup = useKnnIsLookup; } +#ifdef OPENPGL_VSP_GUIDING +OPENPGL_INLINE void FieldConfig::SetVarianceBasedVSP(const bool varianceBasedVSP) +{ + m_args.varianceBasedVSP = varianceBasedVSP; +} +#endif + } // namespace cpp } // namespace openpgl \ No newline at end of file diff --git a/openpgl/include/openpgl/cpp/ImageSpaceGuidingBuffer.h b/openpgl/include/openpgl/cpp/ImageSpaceGuidingBuffer.h index d468602a..1c90d05b 100644 --- a/openpgl/include/openpgl/cpp/ImageSpaceGuidingBuffer.h +++ b/openpgl/include/openpgl/cpp/ImageSpaceGuidingBuffer.h @@ -19,12 +19,63 @@ namespace util * @brief The ImageSpaceGuidingBuffer class calculates image-space guiding information from pixel samples. * * The class collects and stores the Monte-Carlo random work pixels samples generated during rendering. - * The information gathered by these samples is then used, duting the @ref Update step to calculate/estimate - * image-space guiding information (e.g., pixel contribtuion estimates for guided/adjoint-driven RR). + * The information gathered by these samples is then used, during the @ref Update step to calculate/estimate + * image-space guiding information (e.g., pixel contribution estimates for guided/adjoint-driven RR). * */ struct ImageSpaceGuidingBuffer { + struct Config { + + Config(Point2i resolution) { + data.resolution = {resolution.x, resolution.y}; + } + + void EnableContributionEstimate(const bool contributionEstimate) + { + data.contributionEstimate = contributionEstimate; + } + + bool ContributionEstimate() const { + return data.contributionEstimate; + } + + Point2i GetResolution() const { + return {data.resolution.x, data.resolution.y}; + } + + void SetContributionType(PGLContributionTypes type) { + data.contributionType = type; + } + + PGLContributionTypes GetContributionType() { + return data.contributionType; + } + +#if defined(OPENPGL_VSP_GUIDING) + void EnableVolumeScatterProbabilityEstimate(const bool vspEstimate) + { + data.vspEstimate = vspEstimate; + } + + bool VolumeScatterProbabilityEstimate() const { + return data.vspEstimate; + } + + void SetVolumeScatterProbabilityType(PGLVSPTypes type) { + data.vspType = type; + } + + PGLVSPTypes GetVolumeScatterProbabilityType() { + return data.vspType; + } +#endif + friend struct ImageSpaceGuidingBuffer; + private: + PGLImageSpaceGuidingBufferConfig data; + }; + + typedef PGLImageSpaceSample Sample; /** @@ -32,7 +83,7 @@ struct ImageSpaceGuidingBuffer * * @param resolution The size/reslution of the image buffer */ - ImageSpaceGuidingBuffer(const Point2i resolution); + ImageSpaceGuidingBuffer(const Config cfg); /** * Creates/Loads an ImageSpaceGuidingBuffer from multi-channel EXR file. @@ -71,7 +122,11 @@ struct ImageSpaceGuidingBuffer * * This quantity is usefull guided/adjoint-driven Russina roulette decisions. */ - Vector3f GetPixelContributionEstimate(const Point2i pixel) const; + Vector3f GetContributionEstimate(const Point2i pixel) const; + +#if defined(OPENPGL_VSP_GUIDING) + float GetVolumeScatterProbabilityEstimate(const Point2i pixel) const; +#endif /** * @brief If the image-space guiding buffer is ready and can be used. @@ -92,9 +147,9 @@ struct ImageSpaceGuidingBuffer /// Implementation //////////////////////////////////////////////////////////// -OPENPGL_INLINE ImageSpaceGuidingBuffer::ImageSpaceGuidingBuffer(const Point2i resolution) +OPENPGL_INLINE ImageSpaceGuidingBuffer::ImageSpaceGuidingBuffer(const Config cfg) { - m_imageSpaceGuidingBufferHandle = pglFieldNewImageSpaceGuidingBuffer(resolution); + m_imageSpaceGuidingBufferHandle = pglFieldNewImageSpaceGuidingBuffer(cfg.data); } OPENPGL_INLINE ImageSpaceGuidingBuffer::ImageSpaceGuidingBuffer(const std::string &fileName) @@ -128,11 +183,19 @@ OPENPGL_INLINE void ImageSpaceGuidingBuffer::Store(const std::string &fileName) pglImageSpaceGuidingBufferStore(m_imageSpaceGuidingBufferHandle, fileName.c_str()); } -OPENPGL_INLINE Vector3f ImageSpaceGuidingBuffer::GetPixelContributionEstimate(const Point2i pixel) const +OPENPGL_INLINE Vector3f ImageSpaceGuidingBuffer::GetContributionEstimate(const Point2i pixel) const +{ + OPENPGL_ASSERT(m_imageSpaceGuidingBufferHandle); + return pglImageSpaceGuidingBufferGetContributionEstimate(m_imageSpaceGuidingBufferHandle, pixel); +} + +#if defined(OPENPGL_VSP_GUIDING) +OPENPGL_INLINE float ImageSpaceGuidingBuffer::GetVolumeScatterProbabilityEstimate(const Point2i pixel) const { OPENPGL_ASSERT(m_imageSpaceGuidingBufferHandle); - return pglImageSpaceGuidingBufferGetPixelContributionEstimate(m_imageSpaceGuidingBufferHandle, pixel); + return pglImageSpaceGuidingBufferGetVolumeScatterProbabilityEstimate(m_imageSpaceGuidingBufferHandle, pixel); } +#endif OPENPGL_INLINE bool ImageSpaceGuidingBuffer::IsReady() const { diff --git a/openpgl/include/openpgl/cpp/SurfaceSamplingDistribution.h b/openpgl/include/openpgl/cpp/SurfaceSamplingDistribution.h index bafa72cf..89899b5a 100644 --- a/openpgl/include/openpgl/cpp/SurfaceSamplingDistribution.h +++ b/openpgl/include/openpgl/cpp/SurfaceSamplingDistribution.h @@ -146,6 +146,10 @@ struct SurfaceSamplingDistribution */ pgl_vec3f Irradiance(pgl_vec3f &normal, const bool directLightMIS) const; #endif + +#ifdef OPENPGL_VSP_GUIDING + float VolumeScatterProbability(pgl_vec3f &direction) const; +#endif /////////////////////////////////////// /// Future plans /////////////////////////////////////// @@ -286,5 +290,12 @@ OPENPGL_INLINE pgl_vec3f SurfaceSamplingDistribution::Irradiance(pgl_vec3f &norm } #endif +#ifdef OPENPGL_VSP_GUIDING +OPENPGL_INLINE float SurfaceSamplingDistribution::VolumeScatterProbability(pgl_vec3f &direction) const +{ + OPENPGL_ASSERT(m_surfaceSamplingDistributionHandle); + return pglSurfaceSamplingDistributionVolumeScatterProbability(m_surfaceSamplingDistributionHandle, direction); +} +#endif } // namespace cpp } // namespace openpgl \ No newline at end of file diff --git a/openpgl/include/openpgl/cpp/VolumeSamplingDistribution.h b/openpgl/include/openpgl/cpp/VolumeSamplingDistribution.h index 6592cd94..1a19d5fa 100644 --- a/openpgl/include/openpgl/cpp/VolumeSamplingDistribution.h +++ b/openpgl/include/openpgl/cpp/VolumeSamplingDistribution.h @@ -164,6 +164,9 @@ struct VolumeSamplingDistribution pgl_vec3f Fluence(const bool directLightMIS) const; #endif +#ifdef OPENPGL_VSP_GUIDING + float VolumeScatterProbability(pgl_vec3f &direction) const; +#endif /////////////////////////////////////// /// Future plans /////////////////////////////////////// @@ -308,5 +311,13 @@ OPENPGL_INLINE pgl_vec3f VolumeSamplingDistribution::Fluence(const bool directLi } #endif +#ifdef OPENPGL_VSP_GUIDING +OPENPGL_INLINE float VolumeSamplingDistribution::VolumeScatterProbability(pgl_vec3f &direction) const +{ + OPENPGL_ASSERT(m_volumeSamplingDistributionHandle); + return pglVolumeSamplingDistributionVolumeScatterProbability(m_volumeSamplingDistributionHandle, direction); +} +#endif + } // namespace cpp } // namespace openpgl \ No newline at end of file diff --git a/openpgl/include/openpgl/data.h b/openpgl/include/openpgl/data.h index 5f55051c..14bddd04 100644 --- a/openpgl/include/openpgl/data.h +++ b/openpgl/include/openpgl/data.h @@ -25,7 +25,9 @@ struct PGLSampleData /// point does not represent any real scene intersection point EInsideVolume = 1 << 0, /// if the samples represents direct light from a light source - EDirectLight = 1 << 1 + EDirectLight = 1 << 1, + + ENextEventVolume = 1 << 2 }; /// the position of the sample (i.e., at which energy arrives) @@ -75,7 +77,7 @@ struct PGLZeroValueSampleData pgl_direction directionOut; #endif /// if the position is inside a volume - bool volume; + uint32_t flags; }; /** @@ -163,7 +165,7 @@ struct PGLImageSpaceSample void SetSurfaceEvent(bool isSurfaceEvent) { if (isSurfaceEvent) - flags ^= EVolumeEvent; + flags &= ~EVolumeEvent; else flags |= EVolumeEvent; } @@ -171,11 +173,33 @@ struct PGLImageSpaceSample /// @brief If the samples originates from a surface event. bool IsSurfaceEvent() const { - return flags ^ EVolumeEvent; + return !(flags & EVolumeEvent); } #endif }; +enum PGLContributionTypes { + EContribContribution = 0, + EContribVariance, +}; + +enum PGLVSPTypes +{ + EVSPContribution = 0, + EVSPVariance, +}; + +struct PGLImageSpaceGuidingBufferConfig { + pgl_point2i resolution {0, 0}; + bool contributionEstimate {true}; + PGLContributionTypes contributionType {PGLContributionTypes::EContribContribution}; + //float maxContributionValue {FLT_MAX}; //TODO +#if defined(OPENPGL_VSP_GUIDING) + bool vspEstimate {false}; + PGLVSPTypes vspType {PGLVSPTypes::EVSPContribution}; +#endif +}; + #endif struct PGLString diff --git a/openpgl/include/openpgl/defines.h.in b/openpgl/include/openpgl/defines.h.in index 8732b4b9..cf28e8b5 100644 --- a/openpgl/include/openpgl/defines.h.in +++ b/openpgl/include/openpgl/defines.h.in @@ -5,6 +5,7 @@ #cmakedefine OPENPGL_SUPPORT_DEVICE_TYPE_CPU_16 #cmakedefine OPENPGL_RADIANCE_CACHES +#cmakedefine OPENPGL_VSP_GUIDING #cmakedefine OPENPGL_IMAGE_SPACE_GUIDING_BUFFER #cmakedefine OPENPGL_DIRECTION_COMPRESSION #cmakedefine OPENPGL_RADIANCE_COMPRESSION \ No newline at end of file diff --git a/openpgl/include/openpgl/imagespaceguidingbuffer.h b/openpgl/include/openpgl/imagespaceguidingbuffer.h index 0316618d..8bf692f2 100644 --- a/openpgl/include/openpgl/imagespaceguidingbuffer.h +++ b/openpgl/include/openpgl/imagespaceguidingbuffer.h @@ -19,7 +19,7 @@ typedef ManagedObject ImageSpaceGuidingBuffer; typedef ImageSpaceGuidingBuffer *PGLImageSpaceGuidingBuffer; - OPENPGL_CORE_INTERFACE PGLImageSpaceGuidingBuffer pglFieldNewImageSpaceGuidingBuffer(const pgl_point2i resolution); + OPENPGL_CORE_INTERFACE PGLImageSpaceGuidingBuffer pglFieldNewImageSpaceGuidingBuffer(const PGLImageSpaceGuidingBufferConfig cfg); OPENPGL_CORE_INTERFACE PGLImageSpaceGuidingBuffer pglFieldNewImageSpaceGuidingBufferFromFile(const char *fileName); @@ -31,8 +31,10 @@ typedef ManagedObject ImageSpaceGuidingBuffer; OPENPGL_CORE_INTERFACE void pglImageSpaceGuidingBufferStore(PGLImageSpaceGuidingBuffer imageSpaceGuidingBuffer, const char *fileName); - OPENPGL_CORE_INTERFACE pgl_vec3f pglImageSpaceGuidingBufferGetPixelContributionEstimate(PGLImageSpaceGuidingBuffer imageSpaceGuidingBuffer, const pgl_point2i pixel); - + OPENPGL_CORE_INTERFACE pgl_vec3f pglImageSpaceGuidingBufferGetContributionEstimate(PGLImageSpaceGuidingBuffer imageSpaceGuidingBuffer, const pgl_point2i pixel); +#if defined(OPENPGL_VSP_GUIDING) + OPENPGL_CORE_INTERFACE float pglImageSpaceGuidingBufferGetVolumeScatterProbabilityEstimate(PGLImageSpaceGuidingBuffer imageSpaceGuidingBuffer, const pgl_point2i pixel); +#endif OPENPGL_CORE_INTERFACE bool pglImageSpaceGuidingBufferIsReady(PGLImageSpaceGuidingBuffer imageSpaceGuidingBuffer); OPENPGL_CORE_INTERFACE void pglImageSpaceGuidingBufferReset(PGLImageSpaceGuidingBuffer imageSpaceGuidingBuffer); diff --git a/openpgl/include/openpgl/surfacesamplingdistribution.h b/openpgl/include/openpgl/surfacesamplingdistribution.h index ad922cbb..abb6daf7 100644 --- a/openpgl/include/openpgl/surfacesamplingdistribution.h +++ b/openpgl/include/openpgl/surfacesamplingdistribution.h @@ -51,6 +51,10 @@ typedef ManagedObject SurfaceSamplingDistribution; OPENPGL_CORE_INTERFACE pgl_vec3f pglSurfaceSamplingDistributionOutgoingRadiance(PGLSurfaceSamplingDistribution surfaceSamplingDistribution, pgl_vec3f direction); #endif +#ifdef OPENPGL_VSP_GUIDING + OPENPGL_CORE_INTERFACE float pglSurfaceSamplingDistributionVolumeScatterProbability(PGLSurfaceSamplingDistribution surfaceSamplingDistribution, pgl_vec3f direction); +#endif + #ifdef __cplusplus } // extern "C" #endif \ No newline at end of file diff --git a/openpgl/include/openpgl/volumesamplingdistribution.h b/openpgl/include/openpgl/volumesamplingdistribution.h index c4377c5c..70d4ba05 100644 --- a/openpgl/include/openpgl/volumesamplingdistribution.h +++ b/openpgl/include/openpgl/volumesamplingdistribution.h @@ -54,6 +54,9 @@ typedef ManagedObject VolumeSamplingDistribution; OPENPGL_CORE_INTERFACE pgl_vec3f pglVolumeSamplingDistributionFluence(PGLVolumeSamplingDistribution volumeSamplingDistribution, const bool directLightMIS); #endif +#ifdef OPENPGL_VSP_GUIDING + OPENPGL_CORE_INTERFACE float pglVolumeSamplingDistributionVolumeScatterProbability(PGLVolumeSamplingDistribution VolumeSamplingDistribution, pgl_vec3f direction); +#endif #ifdef __cplusplus } // extern "C" #endif \ No newline at end of file