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

Add exposure controls to UsdGeomCamera #3085

Open
wants to merge 30 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
22437dc
Add exposure controls to UsdGeomCamera
anderslanglands May 14, 2024
8ae7ff8
feed calculated exposure scale to HdCamera::GetExposure() via UsdImag…
anderslanglands May 14, 2024
0dea7ef
add additional doc comments explaining the exposure attributes and th…
anderslanglands May 16, 2024
dc65e05
docs clarifications
anderslanglands May 28, 2024
c3ad145
rename f-number to f-stop
anderslanglands Jun 10, 2024
cdbe0c5
add definition of nits
anderslanglands Jun 11, 2024
f9a8c8c
add units for imaging ratio
anderslanglands Jun 11, 2024
29f6b34
fix dimensions and further clarify exposure scale
anderslanglands Jun 16, 2024
aa9e5f1
fix spelling on steradian
anderslanglands Jun 24, 2024
c75aea4
fix formatting to backticks and format fStop reference
anderslanglands Jun 24, 2024
50cea0a
convert exposure scale to logarithmic exposure in adapter.
anderslanglands Jul 26, 2024
1d5c60a
replace f-number with f-stop
anderslanglands Sep 6, 2024
4ab55da
add note pointing to physLight doc and explaning the form of the equa…
anderslanglands Sep 6, 2024
425382e
update generated schema
anderslanglands Sep 6, 2024
3749e6b
add initialization for exposure fields
anderslanglands Sep 6, 2024
21d8899
Pixar schema tweaks: rename 'GetExposureScale' to 'ComputeLinearExpos…
Nov 22, 2024
2f668a6
Add exposure parameters to hydra camera schema
Nov 22, 2024
a33a85a
Add UsdImaging 2.0 support for exposure, update UsdImaging usage of U…
Nov 22, 2024
92cd975
Merge pull request #1 from tcauchois/usdGeomCamera-exposure
pmolodo Dec 9, 2024
b5d9f08
add missing camera exposure documentation in usdGeom/generatedSchema.…
pmolodo Dec 9, 2024
f574fd4
rename 'GetExposureScale' to 'ComputeLinearExposureScale' - python wr…
pmolodo Dec 9, 2024
43b89cd
Merge remote-tracking branch 'origin/dev' into usdGeomCamera-exposure
pmolodo Dec 10, 2024
4a929d4
Exposure: simplify hydra-USD mapping
pmolodo Dec 10, 2024
e916b3e
testUsdGeomCamera.test_ComputeLinearExposureScale fixes
pmolodo Dec 10, 2024
0ae21ac
bugfix for UsdImagingCameraAdapter::Get(exposureScale)
pmolodo Dec 11, 2024
7e94c68
avoid copying TfToken in loop
pmolodo Dec 11, 2024
95e2cad
undo accidental cmake version bump
pmolodo Dec 11, 2024
36becba
fix initial value of HdCamera::_exposureScale (1.0, not 0.0)
pmolodo Dec 18, 2024
846dd7f
rename tokens from exposureScale to linearExposureScale
pmolodo Dec 18, 2024
07496b0
change all instances of "exposureScale" to "linearExposureScale"
pmolodo Dec 18, 2024
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
35 changes: 35 additions & 0 deletions pxr/imaging/hd/camera.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,41 @@ HdCamera::Sync(HdSceneDelegate * sceneDelegate,
_exposure = vExposure.Get<float>();
}

const VtValue vExposureCompensation =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should add initialization for all of these member variables in the HdCamera constructor (following the pattern for _exposure).

sceneDelegate->GetCameraParamValue(
id, HdCameraTokens->exposureCompensation);
if (!vExposureCompensation.IsEmpty()) {
_exposureCompensation = vExposureCompensation.Get<float>();
}

const VtValue vExposureTime =
sceneDelegate->GetCameraParamValue(
id, HdCameraTokens->exposureTime);
if (!vExposureTime.IsEmpty()) {
_exposureTime = vExposureTime.Get<float>();
}

const VtValue vExposureIso =
sceneDelegate->GetCameraParamValue(
id, HdCameraTokens->exposureIso);
if (!vExposureIso.IsEmpty()) {
_exposureIso = vExposureIso.Get<float>();
}

const VtValue vExposureFStop =
sceneDelegate->GetCameraParamValue(
id, HdCameraTokens->exposureFStop);
if (!vExposureFStop.IsEmpty()) {
_exposureFStop = vExposureFStop.Get<float>();
}

const VtValue vExposureResponsivity =
sceneDelegate->GetCameraParamValue(
id, HdCameraTokens->exposureResponsivity);
if (!vExposureResponsivity.IsEmpty()) {
_exposureResponsivity = vExposureResponsivity.Get<float>();
}

const VtValue vLensDistortionType =
sceneDelegate->GetCameraParamValue(
id, HdCameraTokens->lensDistortionType);
Expand Down
19 changes: 18 additions & 1 deletion pxr/imaging/hd/camera.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,11 @@ PXR_NAMESPACE_OPEN_SCOPE
(shutterOpen) \
(shutterClose) \
(exposure) \
(exposureTime) \
(exposureIso) \
(exposureFStop) \
(exposureResponsivity) \
(exposureCompensation) \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These new params are missing from the hydra camera schema: https://github.com/PixarAnimationStudios/OpenUSD/blob/release/pxr/imaging/hd/cameraSchema.h ... as well as the two modes of scene index emulation:
https://github.com/PixarAnimationStudios/OpenUSD/blob/release/pxr/imaging/hd/dataSourceLegacyPrim.cpp#L1036
... and ...
https://github.com/PixarAnimationStudios/OpenUSD/blob/release/pxr/imaging/hd/sceneIndexAdapterSceneDelegate.cpp#L1237

... although it looks like for those, since you aren't doing any weird type conversion, the fallbacks might be ok and if you just add the names to dataSourceLegacyPrim.cpp that'll be enough.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @tcauchois - since I see you've already implemented some of the other related stuff, I'm guessing you've already made the changes to hd/hdSchemaDefs.py that would address this? If not, I'm happy to push these fixes myself.

One note - while testing this, I noticed that hdGenSchema.py requires jinja2 >= 3.0 - I'll make a separate PR updating that in VERSIONS.md.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's the separate PR to update the jinja2 req:

#3314

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've made the schemaDefs changes already; I think modulo the discussions downthread about ComputeLinearExposureScale and changing the transport tokens, this is almost ready to go. Thanks for the VERSIONS.md update!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @tcauchois just to be really clear here: are you saying the only update to this PR you require from us is the rework of the exposure scale calculation to be moved to a separate function on HdCamera that gives the (linear) value from UsdGeomCamera::ComputeExposureScale(), and return HdCamera::GetExposure() to its original behaviour?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've pulled this down and added the Hydra 2 functionality and started reconciling issues with closed source code. My plan was to take the PR with my additions, plus the changes mentioned (ComputeExposureScale and the different tokens), if that's ok with you, in which case there's no further action required on your part. If you'd like me to upload my changes as a new PR instead, for you all to look at or iterate on, let me know.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be great if you could just so we can all see what’s actually going in

\
/* how to match window with different aspect */ \
(windowPolicy) \
Expand Down Expand Up @@ -253,6 +258,11 @@ class HdCamera : public HdSprim
return _shutterClose;
}

/// Get the computed exposure scale from the underlying camera.
///
/// Scaling the image brightness by this value will cause the various exposure
/// controls on \ref UsdGeomCamera to behave like those of a real camera to
/// control the exposure of the image.
float GetExposure() const {
return _exposure;
}
Expand Down Expand Up @@ -330,10 +340,17 @@ class HdCamera : public HdSprim
float _splitDiopterWidth2;
float _splitDiopterFocusDistance2;

// shutter/lighting
// shutter
double _shutterOpen;
double _shutterClose;

// exposure
float _exposure;
float _exposureCompensation;
float _exposureTime;
float _exposureIso;
float _exposureFStop;
float _exposureResponsivity;

// lens distortion
TfToken _lensDistortionType;
Expand Down
91 changes: 91 additions & 0 deletions pxr/usd/usdGeom/camera.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,74 @@ UsdGeomCamera::CreateExposureAttr(VtValue const &defaultValue, bool writeSparsel
writeSparsely);
}

UsdAttribute
UsdGeomCamera::GetExposureIsoAttr() const
{
return GetPrim().GetAttribute(UsdGeomTokens->exposureIso);
}

UsdAttribute
UsdGeomCamera::CreateExposureIsoAttr(VtValue const &defaultValue, bool writeSparsely) const
{
return UsdSchemaBase::_CreateAttr(UsdGeomTokens->exposureIso,
SdfValueTypeNames->Float,
/* custom = */ false,
SdfVariabilityVarying,
defaultValue,
writeSparsely);
}

UsdAttribute
UsdGeomCamera::GetExposureTimeAttr() const
{
return GetPrim().GetAttribute(UsdGeomTokens->exposureTime);
}

UsdAttribute
UsdGeomCamera::CreateExposureTimeAttr(VtValue const &defaultValue, bool writeSparsely) const
{
return UsdSchemaBase::_CreateAttr(UsdGeomTokens->exposureTime,
SdfValueTypeNames->Float,
/* custom = */ false,
SdfVariabilityVarying,
defaultValue,
writeSparsely);
}

UsdAttribute
UsdGeomCamera::GetExposureFStopAttr() const
{
return GetPrim().GetAttribute(UsdGeomTokens->exposureFStop);
}

UsdAttribute
UsdGeomCamera::CreateExposureFStopAttr(VtValue const &defaultValue, bool writeSparsely) const
{
return UsdSchemaBase::_CreateAttr(UsdGeomTokens->exposureFStop,
SdfValueTypeNames->Float,
/* custom = */ false,
SdfVariabilityVarying,
defaultValue,
writeSparsely);
}

UsdAttribute
UsdGeomCamera::GetExposureResponsivityAttr() const
{
return GetPrim().GetAttribute(UsdGeomTokens->exposureResponsivity);
}

UsdAttribute
UsdGeomCamera::CreateExposureResponsivityAttr(VtValue const &defaultValue, bool writeSparsely) const
{
return UsdSchemaBase::_CreateAttr(UsdGeomTokens->exposureResponsivity,
SdfValueTypeNames->Float,
/* custom = */ false,
SdfVariabilityVarying,
defaultValue,
writeSparsely);
}

namespace {
static inline TfTokenVector
_ConcatenateAttributeNames(const TfTokenVector& left,const TfTokenVector& right)
Expand Down Expand Up @@ -372,6 +440,10 @@ UsdGeomCamera::GetSchemaAttributeNames(bool includeInherited)
UsdGeomTokens->shutterOpen,
UsdGeomTokens->shutterClose,
UsdGeomTokens->exposure,
UsdGeomTokens->exposureIso,
UsdGeomTokens->exposureTime,
UsdGeomTokens->exposureFStop,
UsdGeomTokens->exposureResponsivity,
};
static TfTokenVector allNames =
_ConcatenateAttributeNames(
Expand Down Expand Up @@ -581,4 +653,23 @@ UsdGeomCamera::SetFromCamera(const GfCamera &camera, const UsdTimeCode &time)
GetFocusDistanceAttr().Set(camera.GetFocusDistance(), time);
}

float
UsdGeomCamera::GetExposureScale(UsdTimeCode time) const
anderslanglands marked this conversation as resolved.
Show resolved Hide resolved
{
float exposureTime = 1.0f;
float exposureIso = 100.0f;
float exposureFStop = 1.0f;
float exposureResponsivity = 1.0f;
float exposureCompensation = 0.0f;

GetExposureTimeAttr().Get(&exposureTime, time);
GetExposureIsoAttr().Get(&exposureIso, time);
GetExposureFStopAttr().Get(&exposureFStop, time);
GetExposureResponsivityAttr().Get(&exposureResponsivity, time);
GetExposureAttr().Get(&exposureCompensation, time);

return (exposureTime * exposureIso * powf(2.0f, exposureCompensation) *
exposureResponsivity) / (100.0f * exposureFStop * exposureFStop);
}

PXR_NAMESPACE_CLOSE_SCOPE
151 changes: 148 additions & 3 deletions pxr/usd/usdGeom/camera.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,36 @@ class SdfAssetPath;
/// However, it follows that if even one property is authored in the correct
/// scene units, then they all must be.
///
/// \section UsdGeom_CameraExposure Camera Exposure Model
///
/// UsdGeomCamera models exposure by a camera in terms of exposure time, ISO,
/// f-number, and exposure compensation, mirroring the controls on a real camera.
/// These parameters are provided by \ref UsdGeomCamera::GetExposureTimeAttr(),
/// \ref UsdGeomCamera::GetExposureIsoAttr(), \ref UsdGeomCamera::GetExposureFNumberAttr(),
/// and \ref UsdGeomCamera::GetExposureAttr(), respectively.
/// \ref UsdGeomCamera::GetExposureResponsivityAttr() provides an additional scaling
/// factor to model the overall responsivity of the system, including response of
/// the sensor and loss by the lens.
///
/// The calculated scaling factor can be obtained from
/// \ref UsdGeomCamera::GetExposureScale(), which is also the value provided by
/// \ref HdCamera::GetExposure(). It is computed as:
/// \code
/// exposureScale = exposureResponsivity *
/// (exposureTime * (exposureIso/100) * pow(2, exposure))
/// / (exposureFStop * exposureFStop)
/// \endcode
///
/// This scaling factor is combined from two parts: The first, known as the __imaging ratio__
/// (in _steradian-second_), converts from incident luminance at the front
/// of the lens system, in _nit_ (_cd/m^2_), to photometric exposure at the sensor
/// in _lux-second_. The second, `exposureResponsivity`
/// (in _inverse lux-second_), converts from photometric exposure at the sensor, in _lux-second_,
/// to a unitless output signal.
///
/// Renderers should simply multiply the brightness of the image by the exposure
/// scale. The default values for the exposure-related attributes combine to give
/// a scale of 1.0.
///
/// \sa \ref UsdGeom_LinAlgBasics
///
Expand Down Expand Up @@ -394,7 +424,7 @@ class UsdGeomCamera : public UsdGeomXformable
// --------------------------------------------------------------------- //
// FSTOP
// --------------------------------------------------------------------- //
/// Lens aperture. Defaults to 0.0, which turns off focusing.
/// Lens aperture. Defaults to 0.0, which turns off depth of field effects.
///
/// | ||
/// | -- | -- |
Expand Down Expand Up @@ -491,7 +521,7 @@ class UsdGeomCamera : public UsdGeomXformable
/// Frame relative shutter close time, analogous comments from
/// shutter:open apply. A value greater or equal to shutter:open
/// should be authored, otherwise there is no exposure and a
/// renderer should produce a black image.
/// renderer should produce a black image. Used for motion blur.
///
/// | ||
/// | -- | -- |
Expand All @@ -513,7 +543,7 @@ class UsdGeomCamera : public UsdGeomXformable
// --------------------------------------------------------------------- //
// EXPOSURE
// --------------------------------------------------------------------- //
/// Exposure adjustment, as a log base-2 value. The default
/// Exposure compensation, as a log base-2 value. The default
/// of 0.0 has no effect. A value of 1.0 will double the
/// image-plane intensities in a rendered image; a value of
/// -1.0 will halve them.
Expand All @@ -534,6 +564,108 @@ class UsdGeomCamera : public UsdGeomXformable
USDGEOM_API
UsdAttribute CreateExposureAttr(VtValue const &defaultValue = VtValue(), bool writeSparsely=false) const;

public:
// --------------------------------------------------------------------- //
// EXPOSUREISO
// --------------------------------------------------------------------- //
/// The speed rating of the sensor or film when calculating exposure.
/// Higher numbers give a brighter image, lower numbers darker.
///
/// | ||
/// | -- | -- |
/// | Declaration | `float exposure:iso = 100` |
/// | C++ Type | float |
/// | \ref Usd_Datatypes "Usd Type" | SdfValueTypeNames->Float |
USDGEOM_API
UsdAttribute GetExposureIsoAttr() const;

/// See GetExposureIsoAttr(), and also
/// \ref Usd_Create_Or_Get_Property for when to use Get vs Create.
/// If specified, author \p defaultValue as the attribute's default,
/// sparsely (when it makes sense to do so) if \p writeSparsely is \c true -
/// the default for \p writeSparsely is \c false.
USDGEOM_API
UsdAttribute CreateExposureIsoAttr(VtValue const &defaultValue = VtValue(), bool writeSparsely=false) const;

public:
// --------------------------------------------------------------------- //
// EXPOSURETIME
// --------------------------------------------------------------------- //
/// Time in seconds that the sensor is exposed to light when calculating exposure.
/// Longer exposure times create a brighter image, shorter times darker.
/// Note that shutter:open and shutter:close model essentially the
/// same property of a physical camera, but are for specifying the
/// size of the motion blur streak which is for practical purposes
/// useful to keep separate.
///
/// | ||
/// | -- | -- |
/// | Declaration | `float exposure:time = 1` |
/// | C++ Type | float |
/// | \ref Usd_Datatypes "Usd Type" | SdfValueTypeNames->Float |
USDGEOM_API
UsdAttribute GetExposureTimeAttr() const;

/// See GetExposureTimeAttr(), and also
/// \ref Usd_Create_Or_Get_Property for when to use Get vs Create.
/// If specified, author \p defaultValue as the attribute's default,
/// sparsely (when it makes sense to do so) if \p writeSparsely is \c true -
/// the default for \p writeSparsely is \c false.
USDGEOM_API
UsdAttribute CreateExposureTimeAttr(VtValue const &defaultValue = VtValue(), bool writeSparsely=false) const;

public:
// --------------------------------------------------------------------- //
// EXPOSUREFSTOP
// --------------------------------------------------------------------- //
/// f-stop of the aperture when calculating exposure. Smaller numbers
/// create a brighter image, larger numbers darker.
/// Note that the `fStop` attribute also models the diameter of the camera
/// aperture, but for specifying depth of field. For practical
/// purposes it is useful to keep the exposure and the depth of field
/// controls separate.
///
///
/// | ||
/// | -- | -- |
/// | Declaration | `float exposure:fStop = 1` |
/// | C++ Type | float |
/// | \ref Usd_Datatypes "Usd Type" | SdfValueTypeNames->Float |
USDGEOM_API
UsdAttribute GetExposureFStopAttr() const;

/// See GetExposureFStopAttr(), and also
/// \ref Usd_Create_Or_Get_Property for when to use Get vs Create.
/// If specified, author \p defaultValue as the attribute's default,
/// sparsely (when it makes sense to do so) if \p writeSparsely is \c true -
/// the default for \p writeSparsely is \c false.
USDGEOM_API
UsdAttribute CreateExposureFStopAttr(VtValue const &defaultValue = VtValue(), bool writeSparsely=false) const;

public:
// --------------------------------------------------------------------- //
// EXPOSURERESPONSIVITY
// --------------------------------------------------------------------- //
/// Scalar multiplier representing overall responsivity of the
/// sensor system to light when calculating exposure. Intended to be
/// used as a per camera/lens system measured scaling value.
///
/// | ||
/// | -- | -- |
/// | Declaration | `float exposure:responsivity = 1` |
/// | C++ Type | float |
/// | \ref Usd_Datatypes "Usd Type" | SdfValueTypeNames->Float |
USDGEOM_API
UsdAttribute GetExposureResponsivityAttr() const;

/// See GetExposureResponsivityAttr(), and also
/// \ref Usd_Create_Or_Get_Property for when to use Get vs Create.
/// If specified, author \p defaultValue as the attribute's default,
/// sparsely (when it makes sense to do so) if \p writeSparsely is \c true -
/// the default for \p writeSparsely is \c false.
USDGEOM_API
UsdAttribute CreateExposureResponsivityAttr(VtValue const &defaultValue = VtValue(), bool writeSparsely=false) const;

public:
// ===================================================================== //
// Feel free to add custom code below this line, it will be preserved by
Expand Down Expand Up @@ -574,6 +706,19 @@ class UsdGeomCamera : public UsdGeomXformable
///
USDGEOM_API
void SetFromCamera(const GfCamera &camera, const UsdTimeCode &time);

/// Computes the ratio between incident luminance and photometric exposure
/// (in lux-seconds), given the <tt>exposure</tt>, <tt>exposure:iso</tt>,
/// <tt>exposure:fnumber</tt>, <tt>exposure:time</tt> and <tt>exposure:responsivity</tt>
/// attributes.
///
/// This is expected to be applied as a multiplier to the brightness of the
/// image generated by the renderer, and given physically meaningful lighting
/// values in the scene, allows the exposure controls on UsdGeomCamera to behave
/// like those of a real camera.
///
USDGEOM_API
float GetExposureScale(UsdTimeCode time=UsdTimeCode::Default()) const;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@anderslanglands I'm going to change this to "Compute". I was wondering a bit about the fact that it's linear, when at least hydra is expecting something log-scale. Is it valuable to have a linear result, or would it be better to put the log inside the function for consistency? (If there's a use for it in linear that would lose precision from the exp2(log2(*)) that's a compelling reason to leave it as is).

If we leave it linear, I might go a step further and call it ComputeLinearExposureScale... Anyway, thoughts?

(Don't worry about updating this, I've pulled it and it's marked up a bunch for compatibility with internal code so it's easier for me to push the last few changes through).

};

PXR_NAMESPACE_CLOSE_SCOPE
Expand Down
Loading