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

Conversation

anderslanglands
Copy link

@anderslanglands anderslanglands commented May 14, 2024

This PR adds exposure controls to UsdGeomCamera, allowing to specify brightness as with a physical camera. The calculated exposure value is fed to HdCamera::GetExposure() via UsdImagingCameraAdapter. This means that:

  1. Existing scenes that use the existing exposure attribute continue to work as before
  2. Renderers that already use HdCamera::GetExposure() get the exposure calculation automatically if the new exposure attibutes are changed from their defaults.

This is currently draft to start discussion as per @meshula

Description of Change(s)

Adds exposure:time, exposure:iso, exposure:fNumber, exposure:responsivity attributes and repurposes exposure attribute to be interpreted as exposure compensation. Adds UsdGeomCamera::ComputeLinearExposureScale() method which allows users to calculate the imaging ratio (i.e. given a certain luminance on the sensor, what is the photometric exposure output) from these attributes.

Also adds a unit test to test the above.

Adds tokens exposureTime, exposureIso, exposureFNumber, exposureResponsivity, exposureCompensation to HdCamera. Adds HdCamera::GetLinearExposureScale() for delegates to be able to use the new controls with a single function call.

Adds exposure:time, exposure:iso, exposure:fNumber, exposure:responsivity attributes and repurposes exposure attribute to be interpreted as exposure compensation. Adds UsdGeomCamera::GetExposureScale() method which allows users to calculate the imaging ratio (i.e. given a certain luminance on the sensor, what is the photometric exposure output) from these attributes.

Also adds a unit test to test the above.
@jesschimein
Copy link
Collaborator

Filed as internal issue #USD-9673

@jesschimein
Copy link
Collaborator

/AzurePipelines run

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

- move factor of 100 in exposure equation to tie it to ISO
- clarify that the exposure equation also takes into account the lens system
- clarify how shutter:open/close, exposure:time, fStop and exposure:fNumber are related
The combination of these parameters gives an exposure scaling factor (also
referred to as the __imaging ratio__) that converts from incident luminance
passing through the lens system and reaching the sensor, in __nits__, to an
output signal based on photometric exposure in __lux-seconds__.
Copy link
Member

Choose a reason for hiding this comment

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

For clarity, is the "imaging ratio" the factor that converts the luminance in nits=cd/m^2 (=lux/radians^2, I think) into exposure in lux-seconds (so [per-wavelength perceptually adjusted] energy per area) of the film? -So "imaging ratio" has unit radians^2 second(??)? Or is this the factor that converts it all the way from luminance to actual the unit-less floating point value that I want to write into some image file, e.g., OpenEXR? So it's unit is simply 1/nits?

Since the factor also includes the iso, it seems to me that it is the latter. In that case, I guess exposureResponsivity should have unit "1/(nits * 100ISO)" - if ISO were an actual unit.

Minor nitpick: "passing through" sounds like you only look at the light that makes it through, say, the coating of a lens. Would "Enter the lens system" be more correct? And where would you author what fraction of light doesn't make it through the lens system?

Note: It might be nice to remind the reader that nits = cd/m^2 in SI units. Bonus: lux and nits should actually be related by radians^2. Or even better, maybe say that ignoring units, the "imaging ratio" is the value of the output signal you get if you had nits luminance entering the lens or equivalently a large Lambertian radiator far from the lens radiating with f lux (where f should be 1/area of sphere, I think, I might be missing some factor here).

Copy link
Author

@anderslanglands anderslanglands May 29, 2024

Choose a reason for hiding this comment

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

Minor nitpick: "passing through" sounds like you only look at the light that makes it through, say, the coating of a lens. Would "Enter the lens system" be more correct? And where would you author what fraction of light doesn't make it through the lens system?

This is the light that gets all the way through to the sensor. The fraction of light that doesn't make it due to being absorbed (plus other effects) is 1-responsivity.

Copy link
Member

Choose a reason for hiding this comment

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

Thanks.

Reading "image ratio", I want to know what the numerator and the denominator is. Currently, it says "factor that converts from [1] to [2]". So I should be able to read of the denominator from [1] and numerator from [2]. [1] ends in "reaching the sensor". So if I read this literally, "image ratio" does not account for anything that happens in front of the sensor such as light being absorbed by lens coating. But you are just saying that such an affect would be accounted for by the responsivity.

I know that an expert would probably not read it this way. But I think the wording in the usd schema should be unambiguous. Breaking up that sentence should achieve this.

Copy link
Author

Choose a reason for hiding this comment

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

I'm sorry I'm not following, when you say "break up the sentence" what do you mean exactly?

Copy link
Member

Choose a reason for hiding this comment

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

I was thinking of first defining the "imaging ratio" by unambiguously saying what it is the ratio of or what it is converting from and to. And then listing intermediate steps or contributing factors.

"from incident luminance passing through the lens system and reaching the sensor" can be interpreted in multiple ways.

Copy link
Author

Choose a reason for hiding this comment

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

OK I've added the units of the imaging ratio (sr.s) and noted that nits are cd/m^2.

Copy link
Author

Choose a reason for hiding this comment

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

@unhyperbolic pushed another update that fixes my bad dimensional analysis and more explicitly clarifies the conversion and units involved. Let me know what you think.

Copy link
Author

Choose a reason for hiding this comment

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

@unhyperbolic polite bump here - does this address your concerns?

@anderslanglands anderslanglands marked this pull request as ready for review June 11, 2024 02:10
@jesschimein
Copy link
Collaborator

/AzurePipelines run

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@jesschimein
Copy link
Collaborator

/AzurePipelines run

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

Copy link
Member

@spiffmon spiffmon left a comment

Choose a reason for hiding this comment

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

a few formatting notes.

pxr/usd/usdGeom/schema.usda Outdated Show resolved Hide resolved
pxr/usd/usdGeom/schema.usda Outdated Show resolved Hide resolved
pxr/usd/usdGeom/schema.usda Outdated Show resolved Hide resolved
@jesschimein
Copy link
Collaborator

/AzurePipelines run

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@jesschimein
Copy link
Collaborator

/AzurePipelines run

@tcauchois
Copy link
Contributor

Apologies for getting sidetracked; Anders had requested I post my edits back here for NVidia to look at:
anderslanglands#1

From PR description:

Pixar schema feedback, UsdImaging 2.0 implementation.

I tried to pull the merge artifacts out, since some of these files are out of date w.r.t. top of tree, apologies if I missed anything. The header license text changed everywhere.

Schema feedback: there were some 80 characters changes but the only substantial change was renaming "GetExposureScale" to "ComputeLinearExposureScale". "Compute" because it's idiomatic to call it compute when it's doing a computation; Get is only for data access. "Linear" because exposure elsewhere in USD is log2-encoded, and it seemed like a nice clarification.

I added UsdImagingStageSceneIndex support as well as fixed some invalidation issues in UsdImagingDelegate support.

Further discussion topics:

I'm worried about the token swap in usdImaging: hydra exposure -> usd computed exposure, hydra exposureCompensation -> usd exposure, as a source of future bugs. Other potential avenues of dealing with this would be:

  • Revving the camera schema, e.g. adding UsdGeomCamera_1 which has no "exposure" but has an "exposureCompensation" attribute, and "exposure" is purely computed.
  • Passing "exposure" as "exposure" to hydra, and exposureScale as "exposureScale", although this would require updating render delegates to conform.
  • Only passing "exposure" (i.e. Computed Exposure Scale) to hydra, and treating it as a computed attribute that happens to have the same name as one of its scene inputs.

Let me know what you think of those three options.

We're happy with the math and the schema (with the one function rename), and just needed to iterate on the usdImaging code a bit. There were some additional internal complications that are resolved as well.

@anderslanglands
Copy link
Author

I like option 1 - updating to UsdGeomCamera_1 and changing exposure to exposureCompensation. I like that exposureCompensation is the correct name for the attribute, and the version bump makes it clear that semantics have changed, while renderers could still seamlessly get the upgrade if they're using exposure already.

@jesschimein
Copy link
Collaborator

/AzurePipelines run

Copy link

Pull request contains merge conflicts.

@pmolodo pmolodo force-pushed the usdGeomCamera-exposure branch from 8faf7ff to 43b89cd Compare December 10, 2024 02:23
...by making exposure hydra attribute map to exposure USD attribute,
and exposureScale map to ComputeLinearExposureScale.

This means that existing render delegates that already query exposure
will not get the more advanced behavior "for free", and will need to be
updated to query exposureScale instead, but should lessen future
confusion and potential bugs.
@pmolodo
Copy link
Contributor

pmolodo commented Dec 10, 2024

Hi - so despite initially leaning toward option 1, after @nvmkuruc pointed out that versioning the schema would make all uses of the UsdGeomCamera C++ class need to be adapted to instead use, ie, std::variant<UsdGeomCamera, UsdGeomCamera_1>, we decided this would probably be too disruptive.

So we now think Option 2 would be best:

  • Passing "exposure" as "exposure" to hydra, and exposureScale as "exposureScale", although this would require updating render delegates to conform.

The need to update render delegates seems an acceptable cost, compared to the much larger scale changes we'd need with the schema version change.

So I've pushed some commits which make that switch (as well as merge in dev). Sorry for the switch!


_inputs.resize(inputNames.size());
UsdPrim prim = _usdCamera.GetPrim();
for (TfToken inputName : inputNames) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

@pmolodo If possible, avoid a copy of TfToken as it increases the ref count-- It should be safe to use const TfToken& or const auto& here.

Copy link
Contributor

Choose a reason for hiding this comment

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


static UsdAttribute
_CreateExposureIsoAttr(UsdGeomCamera &self,
object defaultVal, bool writeSparsely) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is this spacing offset intentional?

Copy link
Contributor

Choose a reason for hiding this comment

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

Not sure - but every _CreateXXX function in that file has the same spacing. Should probably fix all or none.

CMakeLists.txt Outdated
@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.14)
cmake_minimum_required(VERSION 3.15)
Copy link
Collaborator

Choose a reason for hiding this comment

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

@pmolodo Was the cmake bump intentional?

Copy link
Contributor

Choose a reason for hiding this comment

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

@jesschimein
Copy link
Collaborator

/AzurePipelines run

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@pmolodo pmolodo force-pushed the usdGeomCamera-exposure branch from 86c6dcd to 0ae21ac Compare December 11, 2024 02:07
@jesschimein
Copy link
Collaborator

/AzurePipelines run

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@pmolodo
Copy link
Contributor

pmolodo commented Dec 13, 2024

Made a follow-up PR, that adds Storm support:

To see just the changes vs THIS PR, see here:

@@ -265,7 +265,7 @@ UsdImagingCameraAdapter::Get(UsdPrim const& prim,
return vExposureResponsivity;
} else if (key == HdCameraTokens->exposureScale) {
// The computed linear exposure multiplier ratio
return VtValue(log2(cam.ComputeLinearExposureScale(time)));
return VtValue(cam.ComputeLinearExposureScale(time));
Copy link
Contributor

Choose a reason for hiding this comment

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

This had been log2 throughout, to match the encoding of "exposure". Is the loss of precision such that this is necessary for you all? If so: (1) let's make the hydra token "linearExposureScale" and (2) let's update dataSourceCamera.cpp correspondingly.

Copy link
Author

Choose a reason for hiding this comment

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

This was just log2 so that it backdoored into the existing exposure field (since that's log2). Since we're not doing that hijack any more this wants to be linear. @pmolodo can you update the token per Tom's comment?

Copy link
Contributor

Choose a reason for hiding this comment

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

The thinking behind linear wasn't avoiding precision loss, so much as simplicity and consistency:

  • the value is most easily expressed and calculated in linear space (since of the 5 component attributes, only 1 is in log2 space)
  • the value will generally be used programmatically in linear space (ie, as a pixel value multiplier)
  • as @anderslanglands points out, we formerly only converted to log2 where necessary for shoe-horning back into the exposure attribute. Now that we're making "exposure" and "exposureScale" completely separate attributes, and not worrying about existing implementations picking up the new behavior / attributes "for free", we have the opportunity to make ALL usages of exposureScale in linear space.

Regardless, I totally agree with you that the possibility for confusion is high - so, in addition to making a commit where the token names are changed to "linearExposureScale", I also added a commit where ALL references to "exposureScale" in the code are changed to "linearExposureScale", for clarity. (You can leave that commit out if you feel it's too verbose, though!)

@pmolodo pmolodo force-pushed the usdGeomCamera-exposure branch from 33874a4 to 07496b0 Compare December 18, 2024 01:42
@jesschimein
Copy link
Collaborator

/AzurePipelines run

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants