Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[hdEmbree] fix for random number generation (previously: hdEmbree-UsdLux-PR02) #3184

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 43 additions & 17 deletions pxr/imaging/plugin/hdEmbree/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()
{
Expand All @@ -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 = "
Expand All @@ -70,6 +94,8 @@ HdEmbreeConfig::HdEmbreeConfig()
<< useFaceColors << "\n"
<< " cameraLightIntensity = "
<< cameraLightIntensity << "\n"
<< " randomNumberSeed = "
<< randomNumberSeed << "\n"
;
}
}
Expand Down
42 changes: 32 additions & 10 deletions pxr/imaging/plugin/hdEmbree/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -27,45 +37,57 @@ PXR_NAMESPACE_OPEN_SCOPE
///
class HdEmbreeConfig {
public:

/// \brief Return the configuration singleton.
static const HdEmbreeConfig &GetInstance();

/// How many samples do we need before a pixel is considered
/// 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
Expand Down
5 changes: 4 additions & 1 deletion pxr/imaging/plugin/hdEmbree/renderDelegate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) };
Expand All @@ -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).
Expand Down
3 changes: 2 additions & 1 deletion pxr/imaging/plugin/hdEmbree/renderDelegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ class HdEmbreeRenderParam;
#define HDEMBREE_RENDER_SETTINGS_TOKENS \
(enableAmbientOcclusion) \
(enableSceneColors) \
(ambientOcclusionSamples)
(ambientOcclusionSamples) \
(randomNumberSeed)

// Also: HdRenderSettingsTokens->convergedSamplesPerPixel

Expand Down
4 changes: 4 additions & 0 deletions pxr/imaging/plugin/hdEmbree/renderPass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,10 @@ HdEmbreeRenderPass::_Execute(HdRenderPassStateSharedPtr const& renderPassState,
renderDelegate->GetRenderSetting<bool>(
HdEmbreeRenderSettingsTokens->enableSceneColors, true));

_renderer->SetRandomNumberSeed(
renderDelegate->GetRenderSetting<unsigned int>(
HdEmbreeRenderSettingsTokens->randomNumberSeed, (unsigned int)-1));

needStartRender = true;
}

Expand Down
24 changes: 18 additions & 6 deletions pxr/imaging/plugin/hdEmbree/renderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@ HdEmbreeRenderer::SetEnableSceneColors(bool enableSceneColors)
_enableSceneColors = enableSceneColors;
}

void
HdEmbreeRenderer::SetRandomNumberSeed(int randomNumberSeed)
{
_randomNumberSeed = randomNumberSeed;
}

void
HdEmbreeRenderer::SetDataWindow(const GfRect2i &dataWindow)
{
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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();
Expand All @@ -497,13 +503,19 @@ 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<size_t>(_randomNumberSeed);
}
seed = TfHash::Combine(seed, tileStart);
seed = TfHash::Combine(seed, sampleNum);
std::default_random_engine random(seed);

// Create a uniform distribution for jitter calculations.
std::uniform_real_distribution<float> uniform_dist(0.0f, 1.0f);
std::function<float()> uniform_float = std::bind(uniform_dist, random);
auto uniform_float = [&random, &uniform_dist]() { return uniform_dist(random); };

// _RenderTiles gets a range of tiles; iterate through them.
for (unsigned int tile = tileStart; tile < tileEnd; ++tile) {
Expand Down Expand Up @@ -923,7 +935,7 @@ HdEmbreeRenderer::_ComputeAmbientOcclusion(GfVec3f const& position,
{
// Create a uniform random distribution for AO calculations.
std::uniform_real_distribution<float> uniform_dist(0.0f, 1.0f);
std::function<float()> uniform_float = std::bind(uniform_dist, random);
auto uniform_float = [&random, &uniform_dist]() { return uniform_dist(random); };

// 0 ambient occlusion samples means disable the ambient occlusion term.
if (_ambientOcclusionSamples < 1) {
Expand Down
10 changes: 9 additions & 1 deletion pxr/imaging/plugin/hdEmbree/renderer.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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<int> _completedSamples;
Expand Down
Loading