diff --git a/CMakeLists.txt b/CMakeLists.txt index bfd7337..9bd5887 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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/openpgl/CMakeLists.txt b/openpgl/CMakeLists.txt index e649e9b..fc2582b 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 67ffe6e..5446eb6 100644 --- a/openpgl/api/api.cpp +++ b/openpgl/api/api.cpp @@ -602,6 +602,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; @@ -700,6 +708,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; @@ -867,9 +883,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) @@ -901,12 +917,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 8626304..494fd2f 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; @@ -398,7 +403,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 aadf9d7..d976636 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 aee3a04..66087db 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 6abb684..966946d 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 01bcbc9..a8c75be 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 e1fb19b..9977361 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 f243177..3278599 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 cc4e889..57bbd91 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 e4e8406..390da1e 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 8607b4f..c0cc5bf 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 aaabb1b..98e9ef6 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 a00f949..4c86b53 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() { @@ -1181,6 +1232,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 44c8b0c..bed11c5 100644 --- a/openpgl/directional/vmm/ParallaxAwareVonMisesFisherWeightedEMFactory.h +++ b/openpgl/directional/vmm/ParallaxAwareVonMisesFisherWeightedEMFactory.h @@ -95,7 +95,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}; @@ -114,6 +119,8 @@ struct ParallaxAwareVonMisesFisherWeightedEMFactory void clear(size_t _numComponents); + void clearComponentStats(const size_t &idx); + void clearAll(); virtual void normalize(const float &_numSamples); @@ -201,6 +208,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(); @@ -307,6 +318,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++) @@ -327,6 +356,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); @@ -351,6 +398,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 @@ -364,6 +417,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 @@ -381,6 +440,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; @@ -388,6 +453,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() { @@ -405,6 +489,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; @@ -466,6 +556,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 @@ -473,7 +569,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]; @@ -482,19 +577,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--; } @@ -529,6 +619,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])); @@ -1596,4 +1698,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 b0ba74f..6aa38a9 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 89a2d30..c66021b 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 330ab8c..4297803 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)); @@ -460,6 +471,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; @@ -544,6 +561,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 @@ -564,6 +587,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; } } @@ -720,6 +744,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 468ac91..2f2822f 100644 --- a/openpgl/imagespace/ImageSpaceGuidingBuffer.h +++ b/openpgl/imagespace/ImageSpaceGuidingBuffer.h @@ -25,6 +25,9 @@ #define TINYEXR_IMPLEMENTATION #include +//#define VSP_DENOISE_AFTER_PRODUCT +#define VSP_USE_PVOL_EST + namespace openpgl { @@ -35,14 +38,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 +53,12 @@ struct ImageSpaceGuidingBuffer ~Buffers() { delete[] contribution; - delete[] secondMoment; delete[] albedo; delete[] normal; delete[] spp; delete[] filteredContribution; - delete[] filteredSecondMoment; } void reset() @@ -73,14 +72,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 +85,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 +140,54 @@ 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::EFirstMoment; +#if defined(OPENPGL_VSP_GUIDING) + m_cfg.contributionEstimate = false; + m_cfg.contributionType = PGLContributionTypes::EFirstMoment; +#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::ESecondMoment; + } + } +#if defined(OPENPGL_VSP_GUIDING) + if (layerName == "surfContrib" || layerName == "surfContrib2nd") + { + m_cfg.vspEstimate = true; + if(layerName == "surfContrib2nd") { + m_cfg.vspType = PGLVSPTypes::EContribution; + } + } +#endif + } + + std::cout << "m_cfg.contributionEstimate = " << m_cfg.contributionEstimate << std::endl; + if (m_cfg.contributionEstimate) { + m_contributionEstimateBuffers = new Buffers(m_resolution); + } + + m_cfg.resolution = m_resolution; +#if defined(OPENPGL_VSP_GUIDING) + std::cout << "m_cfg.vspEstimate = " << m_cfg.vspEstimate << std::endl; + 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 +226,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 +246,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 +348,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 +371,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::EFirstMoment) { + 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::EFirstMoment) { + 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::EContribution) { + 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::EContribution) { + 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::EContribution) { + 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::EContribution) { + 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 +631,183 @@ 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; +#ifdef VSP_DENOISE_AFTER_PRODUCT + m_surfaceContributionEstimateBuffers->scaledContribution[pIdx] = m_surfaceContributionEstimateBuffers->contribution[pIdx] * (1 - pVolEst); + m_volumeContributionEstimateBuffers->scaledContribution[pIdx] = m_volumeContributionEstimateBuffers->contribution[pIdx] * pVolEst; +#endif + } + }); + + m_denoiser->denoise(m_pVolBuffer, m_filteredPVolBuffer); + +#ifndef VSP_DENOISE_AFTER_PRODUCT + 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); +#else + m_denoiser->denoise(m_surfaceContributionEstimateBuffers->scaledContribution, m_surfaceContributionEstimateBuffers->normal, m_surfaceContributionEstimateBuffers->albedo, m_surfaceContributionEstimateBuffers->filteredScaledContribution); + m_denoiser->denoise(m_volumeContributionEstimateBuffers->scaledContribution, m_volumeContributionEstimateBuffers->normal, m_volumeContributionEstimateBuffers->albedo, m_volumeContributionEstimateBuffers->filteredScaledContribution); +#endif + +#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; +#ifndef VSP_DENOISE_AFTER_PRODUCT + if (m_cfg.vspType == PGLVSPTypes::EContribution) { + 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; + } +#else + if (m_cfg.vspType == PGLVSPTypes::EContribution) { + surfaceContribution = m_surfaceContributionEstimateBuffers->filteredScaledContribution[pIdx]; + volumeContribution = m_volumeContributionEstimateBuffers->filteredScaledContribution[pIdx]; + } + else { + surfaceContribution.x = std::sqrt(m_surfaceContributionEstimateBuffers->filteredScaledContribution[pIdx].x); + surfaceContribution.y = std::sqrt(m_surfaceContributionEstimateBuffers->filteredScaledContribution[pIdx].y); + surfaceContribution.z = std::sqrt(m_surfaceContributionEstimateBuffers->filteredScaledContribution[pIdx].z); + + volumeContribution.x = std::sqrt(m_volumeContributionEstimateBuffers->filteredScaledContribution[pIdx].x); + volumeContribution.y = std::sqrt(m_volumeContributionEstimateBuffers->filteredScaledContribution[pIdx].y); + volumeContribution.z = std::sqrt(m_volumeContributionEstimateBuffers->filteredScaledContribution[pIdx].z); + } +#endif + +#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::EFirstMoment ? 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 == EVariance ? 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 == EVariance ? 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 +815,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 264f3a1..ab0e74e 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 8cf2509..f48bfe7 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 88608f3..956d5ef 100644 --- a/openpgl/include/openpgl/cpp/FieldConfig.h +++ b/openpgl/include/openpgl/cpp/FieldConfig.h @@ -60,6 +60,8 @@ struct FieldConfig */ void SetUseKnnIsLookup(const bool useKnnIsLookup); + void SetVarianceBasedVSP(const bool varianceBasedVSP); + /** * @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 d468602..6be3485 100644 --- a/openpgl/include/openpgl/cpp/ImageSpaceGuidingBuffer.h +++ b/openpgl/include/openpgl/cpp/ImageSpaceGuidingBuffer.h @@ -25,6 +25,57 @@ namespace util */ 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 bafa72c..89899b5 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 6592cd9..1a19d5f 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 5f55051..d35d602 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 { + EFirstMoment = 0, + ESecondMoment, +}; + +enum PGLVSPTypes +{ + EContribution = 0, + EVariance, +}; + +struct PGLImageSpaceGuidingBufferConfig { + pgl_point2i resolution {0, 0}; + bool contributionEstimate {true}; + PGLContributionTypes contributionType {PGLContributionTypes::EFirstMoment}; + //float maxContributionValue {FLT_MAX}; //TODO +#if defined(OPENPGL_VSP_GUIDING) + bool vspEstimate {false}; + PGLVSPTypes vspType {PGLVSPTypes::EContribution}; +#endif +}; + #endif struct PGLString diff --git a/openpgl/include/openpgl/defines.h.in b/openpgl/include/openpgl/defines.h.in index 8732b4b..cf28e8b 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 0316618..8bf692f 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 ad922cb..abb6daf 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 c4377c5..70d4ba0 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