diff --git a/pxr/imaging/plugin/hdEmbree/config.cpp b/pxr/imaging/plugin/hdEmbree/config.cpp index 2501a99d04..f0d2d4d6e8 100644 --- a/pxr/imaging/plugin/hdEmbree/config.cpp +++ b/pxr/imaging/plugin/hdEmbree/config.cpp @@ -20,26 +20,49 @@ TF_INSTANTIATE_SINGLETON(HdEmbreeConfig); // Each configuration variable has an associated environment variable. // The environment variable macro takes the variable name, a default value, // and a description... -TF_DEFINE_ENV_SETTING(HDEMBREE_SAMPLES_TO_CONVERGENCE, 100, - "Samples per pixel before we stop rendering (must be >= 1)"); +TF_DEFINE_ENV_SETTING( + HDEMBREE_SAMPLES_TO_CONVERGENCE, + HdEmbreeDefaultSamplesToConvergence, + "Samples per pixel before we stop rendering (must be >= 1)"); -TF_DEFINE_ENV_SETTING(HDEMBREE_TILE_SIZE, 8, - "Size (per axis) of threading work units (must be >= 1)"); +TF_DEFINE_ENV_SETTING( + HDEMBREE_TILE_SIZE, + HdEmbreeDefaultTileSize, + "Size (per axis) of threading work units (must be >= 1)"); -TF_DEFINE_ENV_SETTING(HDEMBREE_AMBIENT_OCCLUSION_SAMPLES, 16, - "Ambient occlusion samples per camera ray (must be >= 0; a value of 0 disables ambient occlusion)"); +TF_DEFINE_ENV_SETTING( + HDEMBREE_AMBIENT_OCCLUSION_SAMPLES, + HdEmbreeDefaultAmbientOcclusionSamples, + "Ambient occlusion samples per camera ray (must be >= 0;" + " a value of 0 disables ambient occlusion)"); -TF_DEFINE_ENV_SETTING(HDEMBREE_JITTER_CAMERA, 1, - "Should HdEmbree jitter camera rays while rendering? (values >0 are true)"); +TF_DEFINE_ENV_SETTING( + HDEMBREE_JITTER_CAMERA, + HdEmbreeDefaultJitterCamera, + "Should HdEmbree jitter camera rays while rendering?"); -TF_DEFINE_ENV_SETTING(HDEMBREE_USE_FACE_COLORS, 1, - "Should HdEmbree use face colors while rendering? (values > 0 are true)"); +TF_DEFINE_ENV_SETTING( + HDEMBREE_USE_FACE_COLORS, + HdEmbreeDefaultUseFaceColors, + "Should HdEmbree use face colors while rendering?"); -TF_DEFINE_ENV_SETTING(HDEMBREE_CAMERA_LIGHT_INTENSITY, 300, - "Intensity of the camera light, specified as a percentage of <1,1,1>."); +TF_DEFINE_ENV_SETTING( + HDEMBREE_CAMERA_LIGHT_INTENSITY, + HdEmbreeDefaultCameraLightIntensity, + "Intensity of the camera light, specified as a percentage of <1,1,1>."); -TF_DEFINE_ENV_SETTING(HDEMBREE_PRINT_CONFIGURATION, 0, - "Should HdEmbree print configuration on startup? (values > 0 are true)"); +TF_DEFINE_ENV_SETTING( + HDEMBREE_RANDOM_NUMBER_SEED, + HdEmbreeDefaultRandomNumberSeed, + "Seed to give to the random number generator. A value of anything other" + " than -1, combined with setting PXR_WORK_THREAD_LIMIT=1, should" + " give deterministic / repeatable results. A value of -1 (the" + " default) will allow the implementation to set a value that varies" + " from invocation to invocation and thread to thread."); + +TF_DEFINE_ENV_SETTING(HDEMBREE_PRINT_CONFIGURATION, + false, + "Should HdEmbree print configuration on startup?"); HdEmbreeConfig::HdEmbreeConfig() { @@ -50,12 +73,13 @@ HdEmbreeConfig::HdEmbreeConfig() TfGetEnvSetting(HDEMBREE_TILE_SIZE)); ambientOcclusionSamples = std::max(0, TfGetEnvSetting(HDEMBREE_AMBIENT_OCCLUSION_SAMPLES)); - jitterCamera = (TfGetEnvSetting(HDEMBREE_JITTER_CAMERA) > 0); - useFaceColors = (TfGetEnvSetting(HDEMBREE_USE_FACE_COLORS) > 0); + jitterCamera = (TfGetEnvSetting(HDEMBREE_JITTER_CAMERA)); + useFaceColors = (TfGetEnvSetting(HDEMBREE_USE_FACE_COLORS)); cameraLightIntensity = (std::max(100, TfGetEnvSetting(HDEMBREE_CAMERA_LIGHT_INTENSITY)) / 100.0f); + randomNumberSeed = TfGetEnvSetting(HDEMBREE_RANDOM_NUMBER_SEED); - if (TfGetEnvSetting(HDEMBREE_PRINT_CONFIGURATION) > 0) { + if (TfGetEnvSetting(HDEMBREE_PRINT_CONFIGURATION)) { std::cout << "HdEmbree Configuration: \n" << " samplesToConvergence = " @@ -70,6 +94,8 @@ HdEmbreeConfig::HdEmbreeConfig() << useFaceColors << "\n" << " cameraLightIntensity = " << cameraLightIntensity << "\n" + << " randomNumberSeed = " + << randomNumberSeed << "\n" ; } } diff --git a/pxr/imaging/plugin/hdEmbree/config.h b/pxr/imaging/plugin/hdEmbree/config.h index 708c2cd43a..2632cbb35f 100644 --- a/pxr/imaging/plugin/hdEmbree/config.h +++ b/pxr/imaging/plugin/hdEmbree/config.h @@ -12,6 +12,16 @@ PXR_NAMESPACE_OPEN_SCOPE +// NOTE: types here restricted to bool/int/string, as also used for +// TF_DEFINE_ENV_SETTING +constexpr int HdEmbreeDefaultSamplesToConvergence = 100; +constexpr int HdEmbreeDefaultTileSize = 8; +constexpr int HdEmbreeDefaultAmbientOcclusionSamples = 16; +constexpr bool HdEmbreeDefaultJitterCamera = true; +constexpr bool HdEmbreeDefaultUseFaceColors = true; +constexpr int HdEmbreeDefaultCameraLightIntensity = 300; +constexpr int HdEmbreeDefaultRandomNumberSeed = -1; + /// \class HdEmbreeConfig /// /// This class is a singleton, holding configuration parameters for HdEmbree. @@ -27,6 +37,7 @@ PXR_NAMESPACE_OPEN_SCOPE /// class HdEmbreeConfig { public: + /// \brief Return the configuration singleton. static const HdEmbreeConfig &GetInstance(); @@ -34,38 +45,49 @@ class HdEmbreeConfig { /// converged? /// /// Override with *HDEMBREE_SAMPLES_TO_CONVERGENCE*. - unsigned int samplesToConvergence; + unsigned int samplesToConvergence = HdEmbreeDefaultSamplesToConvergence; /// How many pixels are in an atomic unit of parallel work? /// A work item is a square of size [tileSize x tileSize] pixels. /// /// Override with *HDEMBREE_TILE_SIZE*. - unsigned int tileSize; + unsigned int tileSize = HdEmbreeDefaultTileSize; /// How many ambient occlusion rays should we generate per /// camera ray? /// /// Override with *HDEMBREE_AMBIENT_OCCLUSION_SAMPLES*. - unsigned int ambientOcclusionSamples; + unsigned int ambientOcclusionSamples = HdEmbreeDefaultAmbientOcclusionSamples; /// Should the renderpass jitter camera rays for antialiasing? /// - /// Override with *HDEMBREE_JITTER_CAMERA*. Integer values greater than - /// zero are considered "true". - bool jitterCamera; + /// Override with *HDEMBREE_JITTER_CAMERA*. The case-insensitive strings + /// "true", "yes", "on", and "1" are considered true; an empty value uses + /// the default, and all other values are false. + bool jitterCamera = HdEmbreeDefaultJitterCamera; /// Should the renderpass use the color primvar, or flat white colors? /// (Flat white shows off ambient occlusion better). /// - /// Override with *HDEMBREE_USE_FACE_COLORS*. Integer values greater than - /// zero are considered "true". - bool useFaceColors; + /// Override with *HDEMBREE_USE_FACE_COLORS*. The case-insensitive strings + /// "true", "yes", "on", and "1" are considered true; an empty value uses + /// the default, and all other values are false. + bool useFaceColors = HdEmbreeDefaultUseFaceColors; /// What should the intensity of the camera light be, specified as a /// percent of <1, 1, 1>. For example, 300 would be <3, 3, 3>. /// /// Override with *HDEMBREE_CAMERA_LIGHT_INTENSITY*. - float cameraLightIntensity; + float cameraLightIntensity = HdEmbreeDefaultCameraLightIntensity; + + /// Seed to give to the random number generator. A value of anything other + /// than -1, combined with setting PXR_WORK_THREAD_LIMIT=1, should give + /// deterministic / repeatable results. A value of -1 (the default) will + /// allow the implementation to set a value that varies from invocation to + /// invocation and thread to thread. + /// + /// Override with *HDEMBREE_RANDOM_NUMBER_SEED*. + int randomNumberSeed = HdEmbreeDefaultRandomNumberSeed; private: // The constructor initializes the config variables with their diff --git a/pxr/imaging/plugin/hdEmbree/renderDelegate.cpp b/pxr/imaging/plugin/hdEmbree/renderDelegate.cpp index fb77050b40..32ef99dbbb 100644 --- a/pxr/imaging/plugin/hdEmbree/renderDelegate.cpp +++ b/pxr/imaging/plugin/hdEmbree/renderDelegate.cpp @@ -99,7 +99,7 @@ void HdEmbreeRenderDelegate::_Initialize() { // Initialize the settings and settings descriptors. - _settingDescriptors.resize(4); + _settingDescriptors.resize(5); _settingDescriptors[0] = { "Enable Scene Colors", HdEmbreeRenderSettingsTokens->enableSceneColors, VtValue(HdEmbreeConfig::GetInstance().useFaceColors) }; @@ -112,6 +112,9 @@ HdEmbreeRenderDelegate::_Initialize() _settingDescriptors[3] = { "Samples To Convergence", HdRenderSettingsTokens->convergedSamplesPerPixel, VtValue(int(HdEmbreeConfig::GetInstance().samplesToConvergence)) }; + _settingDescriptors[4] = { "Random Number Seed", + HdEmbreeRenderSettingsTokens->randomNumberSeed, + VtValue(HdEmbreeConfig::GetInstance().randomNumberSeed) }; _PopulateDefaultSettings(_settingDescriptors); // Initialize the embree library handle (_rtcDevice). diff --git a/pxr/imaging/plugin/hdEmbree/renderDelegate.h b/pxr/imaging/plugin/hdEmbree/renderDelegate.h index d4f8f78d54..1d8694daa6 100644 --- a/pxr/imaging/plugin/hdEmbree/renderDelegate.h +++ b/pxr/imaging/plugin/hdEmbree/renderDelegate.h @@ -23,7 +23,8 @@ class HdEmbreeRenderParam; #define HDEMBREE_RENDER_SETTINGS_TOKENS \ (enableAmbientOcclusion) \ (enableSceneColors) \ - (ambientOcclusionSamples) + (ambientOcclusionSamples) \ + (randomNumberSeed) // Also: HdRenderSettingsTokens->convergedSamplesPerPixel diff --git a/pxr/imaging/plugin/hdEmbree/renderPass.cpp b/pxr/imaging/plugin/hdEmbree/renderPass.cpp index 9583d8c757..4b28269cd5 100644 --- a/pxr/imaging/plugin/hdEmbree/renderPass.cpp +++ b/pxr/imaging/plugin/hdEmbree/renderPass.cpp @@ -113,6 +113,10 @@ HdEmbreeRenderPass::_Execute(HdRenderPassStateSharedPtr const& renderPassState, renderDelegate->GetRenderSetting( HdEmbreeRenderSettingsTokens->enableSceneColors, true)); + _renderer->SetRandomNumberSeed( + renderDelegate->GetRenderSetting( + HdEmbreeRenderSettingsTokens->randomNumberSeed, (unsigned int)-1)); + needStartRender = true; } diff --git a/pxr/imaging/plugin/hdEmbree/renderer.cpp b/pxr/imaging/plugin/hdEmbree/renderer.cpp index bbce00a8ef..95663f7639 100644 --- a/pxr/imaging/plugin/hdEmbree/renderer.cpp +++ b/pxr/imaging/plugin/hdEmbree/renderer.cpp @@ -69,6 +69,12 @@ HdEmbreeRenderer::SetEnableSceneColors(bool enableSceneColors) _enableSceneColors = enableSceneColors; } +void +HdEmbreeRenderer::SetRandomNumberSeed(int randomNumberSeed) +{ + _randomNumberSeed = randomNumberSeed; +} + void HdEmbreeRenderer::SetDataWindow(const GfRect2i &dataWindow) { @@ -432,8 +438,8 @@ HdEmbreeRenderer::Render(HdRenderThread *renderThread) // Always pass the renderThread to _RenderTiles to allow the first frame // to be interrupted. WorkParallelForN(numTilesX*numTilesY, - std::bind(&HdEmbreeRenderer::_RenderTiles, this, renderThread, - std::placeholders::_1, std::placeholders::_2)); + std::bind(&HdEmbreeRenderer::_RenderTiles, this, + renderThread, i, std::placeholders::_1, std::placeholders::_2)); // After the first pass, mark the single-sampled attachments as // converged and unmap them. If there are no multisampled attachments, @@ -472,7 +478,7 @@ HdEmbreeRenderer::Render(HdRenderThread *renderThread) } void -HdEmbreeRenderer::_RenderTiles(HdRenderThread *renderThread, +HdEmbreeRenderer::_RenderTiles(HdRenderThread *renderThread, int sampleNum, size_t tileStart, size_t tileEnd) { const unsigned int minX = _dataWindow.GetMinX(); @@ -497,8 +503,14 @@ HdEmbreeRenderer::_RenderTiles(HdRenderThread *renderThread, // Initialize the RNG for this tile (each tile creates one as // a lazy way to do thread-local RNGs). - size_t seed = std::chrono::system_clock::now().time_since_epoch().count(); + size_t seed; + if (_randomNumberSeed == -1) { + seed = std::chrono::system_clock::now().time_since_epoch().count(); + } else { + seed = static_cast(_randomNumberSeed); + } seed = TfHash::Combine(seed, tileStart); + seed = TfHash::Combine(seed, sampleNum); std::default_random_engine random(seed); // Create a uniform distribution for jitter calculations. diff --git a/pxr/imaging/plugin/hdEmbree/renderer.h b/pxr/imaging/plugin/hdEmbree/renderer.h index 8e727ecba0..2da9880848 100644 --- a/pxr/imaging/plugin/hdEmbree/renderer.h +++ b/pxr/imaging/plugin/hdEmbree/renderer.h @@ -81,6 +81,12 @@ class HdEmbreeRenderer final /// everything as white. void SetEnableSceneColors(bool enableSceneColors); + /// Sets a number to seed the random number generator with. + /// \param randomNumberSeed If -1, then the random number generator + /// is seeded in a non-deterministic way; + /// otherwise, it is seeded with this value. + void SetRandomNumberSeed(int randomNumberSeed); + /// Rendering entrypoint: add one sample per pixel to the whole sample /// buffer, and then loop until the image is converged. After each pass, /// the image will be resolved into a color buffer. @@ -115,7 +121,7 @@ class HdEmbreeRenderer final // work. For each tile, iterate over pixels in the tile, generating camera // rays, and following them/calculating color with _TraceRay. This function // renders all tiles between tileStart and tileEnd. - void _RenderTiles(HdRenderThread *renderThread, + void _RenderTiles(HdRenderThread *renderThread, int sampleNum, size_t tileStart, size_t tileEnd); // Cast a ray into the scene and if it hits an object, write to the bound @@ -184,6 +190,8 @@ class HdEmbreeRenderer final int _ambientOcclusionSamples; // Should we enable scene colors? bool _enableSceneColors; + // If other than -1, use this to seed the random number generator with. + int _randomNumberSeed; // How many samples have been completed. std::atomic _completedSamples;