Improvements to Camera APIs (#3701)

* move the focus distance from DofOptions to Camera

The DofOption parameter is now deprecated, but will still work if the
focus distance is not set on Camera.

* A few new helper APIs on Camera

- getFocalLength() which returns the focal length used internally for
  DoF computations.

- computeEffectiveFov() and computeEffectiveFocalLength() which can be
used to better simulate a real camera's FOV changes with the
focus distance.

Modified gltf_viewer so it uses the effective fov/focallendth when
DoF is active.

* simplify setLensProjection

We can call setProjection directly instead of converting to a fov
first, which ends up performing atan(tan(x)).

Also improve parameters names.
This commit is contained in:
Mathias Agopian
2021-03-25 15:22:19 -07:00
committed by GitHub
parent a0c2edd1a2
commit acb2da7759
14 changed files with 232 additions and 26 deletions
@@ -216,3 +216,36 @@ Java_com_google_android_filament_Camera_nGetSensitivity(JNIEnv*, jclass,
Camera *camera = (Camera *) nativeCamera;
return camera->getSensitivity();
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Camera_nSetFocusDistance(JNIEnv*, jclass,
jlong nativeCamera, jfloat focusDistance) {
Camera *camera = (Camera *) nativeCamera;
camera->setFocusDistance(focusDistance);
}
extern "C" JNIEXPORT jfloat JNICALL
Java_com_google_android_filament_Camera_nGetFocusDistance(JNIEnv*, jclass,
jlong nativeCamera) {
Camera *camera = (Camera *) nativeCamera;
return camera->getFocusDistance();
}
extern "C" JNIEXPORT jdouble JNICALL
Java_com_google_android_filament_Camera_nGetFocalLength(JNIEnv*, jclass,
jlong nativeCamera) {
Camera *camera = (Camera *) nativeCamera;
return camera->getFocalLength();
}
extern "C" JNIEXPORT jdouble JNICALL
Java_com_google_android_filament_Camera_nComputeEffectiveFocalLength(JNIEnv*, jclass,
jdouble focalLength, jdouble focusDistance) {
return Camera::computeEffectiveFocalLength(focalLength, focusDistance);
}
extern "C" JNIEXPORT jdouble JNICALL
Java_com_google_android_filament_Camera_nComputeEffectiveFov(JNIEnv*, jclass,
jdouble fovInDegrees, jdouble focusDistance) {
return Camera::computeEffectiveFov(fovInDegrees, focusDistance);
}
@@ -644,6 +644,29 @@ public class Camera {
return nGetShutterSpeed(getNativeObject());
}
/**
* @return focal length in meters [m]
*/
public double getFocalLength() {
return nGetFocalLength(getNativeObject());
}
/**
* Set the camera focus distance in world units
* @param distance Distance from the camera to the focus plane in world units. Must be
* positive and larger than the camera's near clipping plane.
*/
public void setFocusDistance(float distance) {
nSetFocusDistance(getNativeObject(), distance);
}
/**
* @return Distance from the camera to the focus plane in world units
*/
public float getFocusDistance() {
return nGetFocusDistance(getNativeObject());
}
/**
* @return Sensitivity in ISO
*/
@@ -651,6 +674,28 @@ public class Camera {
return nGetSensitivity(getNativeObject());
}
/**
* Helper to compute the effective focal length taking into account the focus distance
*
* @param focalLength focal length in any unit (e.g. [m] or [mm])
* @param focusDistance focus distance in same unit as focalLength
* @return the effective focal length in same unit as focalLength
*/
static double computeEffectiveFocalLength(double focalLength, double focusDistance) {
return nComputeEffectiveFocalLength(focalLength, focusDistance);
}
/**
* Helper to compute the effective field-of-view taking into account the focus distance
*
* @param fovInDegrees full field of view in degrees
* @param focusDistance focus distance in meters [m]
* @return effective full field of view in degrees
*/
static double computeEffectiveFov(double fovInDegrees, double focusDistance) {
return nComputeEffectiveFov(fovInDegrees, focusDistance);
}
public long getNativeObject() {
if (mNativeObject == 0) {
throw new IllegalStateException("Calling method on destroyed Camera");
@@ -685,4 +730,9 @@ public class Camera {
private static native float nGetAperture(long nativeCamera);
private static native float nGetShutterSpeed(long nativeCamera);
private static native float nGetSensitivity(long nativeCamera);
private static native void nSetFocusDistance(long nativeCamera, float distance);
private static native float nGetFocusDistance(long nativeCamera);
private static native double nGetFocalLength(long nativeCamera);
private static native double nComputeEffectiveFocalLength(double focalLength, double focusDistance);
private static native double nComputeEffectiveFov(double fovInDegrees, double focusDistance);
}
@@ -436,7 +436,12 @@ public class View {
MEDIAN
}
/** focus distance in world units */
/**
* focus distance in world units
*
* @deprecated use {@link Camera#setFocusDistance(float)}
*/
@Deprecated
public float focusDistance = 10.0f;
/**
+34 -2
View File
@@ -191,12 +191,13 @@ public:
/** Sets the projection matrix from the focal length.
*
* @param focalLength lens's focal length in millimeters. \p focalLength > 0.
* @param focalLengthInMillimeters lens's focal length in millimeters. \p focalLength > 0.
* @param aspect aspect ratio \f$ \frac{width}{height} \f$. \p aspect > 0.
* @param near distance in world units from the camera to the near plane. \p near > 0.
* @param far distance in world units from the camera to the far plane. \p far > \p near.
*/
void setLensProjection(double focalLength, double aspect, double near, double far) noexcept;
void setLensProjection(double focalLengthInMillimeters,
double aspect, double near, double far) noexcept;
/** Sets a custom projection matrix.
*
@@ -433,6 +434,19 @@ public:
//! returns this camera's sensitivity in ISO
float getSensitivity() const noexcept;
//! returns the focal length in meters [m] for a 35mm camera
double getFocalLength() const noexcept;
/**
* Sets the camera focus distance. This is used by the Depth-of-field PostProcessing effect.
* @param distance Distnace from the camera to the plane of focus in world units.
* Must be positive and larger than the near clipping plane.
*/
void setFocusDistance(float distance) noexcept;
//! Returns the focus distance in world units
float getFocusDistance() const noexcept;
/**
* Returns the inverse of a projection matrix.
*
@@ -473,6 +487,24 @@ public:
* @see inverseProjection(const math::mat4&)
*/
static math::mat4f inverseProjection(const math::mat4f& p) noexcept;
/**
* Helper to compute the effective focal length taking into account the focus distance
*
* @param focalLength focal length in any unit (e.g. [m] or [mm])
* @param focusDistance focus distance in same unit as focalLength
* @return the effective focal length in same unit as focalLength
*/
static double computeEffectiveFocalLength(double focalLength, double focusDistance) noexcept;
/**
* Helper to compute the effective field-of-view taking into account the focus distance
*
* @param fovInDegrees full field of view in degrees
* @param focusDistance focus distance in meters [m]
* @return effective full field of view in degrees
*/
static double computeEffectiveFov(double fovInDegrees, double focusDistance) noexcept;
};
} // namespace filament
+1 -1
View File
@@ -191,7 +191,7 @@ public:
NONE = 0,
MEDIAN = 2
};
float focusDistance = 10.0f; //!< focus distance in world units
float focusDistance = 10.0f; //!< @deprecated use Camera::setFocusDistance() instead
float cocScale = 1.0f; //!< circle of confusion scale factor (amount of blur)
float maxApertureDiameter = 0.01f; //!< maximum aperture diameter in meters (zero to disable rotation)
bool enabled = false; //!< enable or disable depth of field effect
+56 -12
View File
@@ -47,11 +47,11 @@ FCamera::FCamera(FEngine& engine, Entity e)
mEntity(e) {
}
void UTILS_NOINLINE FCamera::setProjection(double fov, double aspect, double near, double far,
void UTILS_NOINLINE FCamera::setProjection(double fovInDegrees, double aspect, double near, double far,
Camera::Fov direction) noexcept {
double w;
double h;
double s = std::tan(fov * (F_PI / 360.0)) * near;
double s = std::tan(fovInDegrees * math::d::DEG_TO_RAD / 2.0) * near;
if (direction == Fov::VERTICAL) {
w = s * aspect;
h = s;
@@ -62,10 +62,12 @@ void UTILS_NOINLINE FCamera::setProjection(double fov, double aspect, double nea
FCamera::setProjection(Projection::PERSPECTIVE, -w, w, -h, h, near, far);
}
void FCamera::setLensProjection(double focalLength, double aspect, double near, double far) noexcept {
void FCamera::setLensProjection(double focalLengthInMillimeters,
double aspect, double near, double far) noexcept {
// a 35mm camera has a 36x24mm wide frame size
double theta = 2.0 * std::atan(SENSOR_SIZE * 1000.0f / (2.0 * focalLength));
FCamera::setProjection(theta * math::d::RAD_TO_DEG, aspect, near, far, Fov::VERTICAL);
double h = (0.5 * near) * ((SENSOR_SIZE * 1000.0) / focalLengthInMillimeters);
double w = h * aspect;
FCamera::setProjection(Projection::PERSPECTIVE, -w, w, -h, h, near, far);
}
/*
@@ -214,6 +216,22 @@ void FCamera::setExposure(float aperture, float shutterSpeed, float sensitivity)
mSensitivity = clamp(sensitivity, MIN_SENSITIVITY, MAX_SENSITIVITY);
}
double FCamera::getFocalLength() const noexcept {
return (FCamera::SENSOR_SIZE * mProjection[1][1]) * 0.5;
}
double FCamera::computeEffectiveFocalLength(double focalLength, double focusDistance) noexcept {
focusDistance = std::max(focalLength, focusDistance);
return (focusDistance * focalLength) / (focusDistance - focalLength);
}
double FCamera::computeEffectiveFov(double fovInDegrees, double focusDistance) noexcept {
double f = 0.5 * FCamera::SENSOR_SIZE / std::tan(fovInDegrees * math::d::DEG_TO_RAD * 0.5);
focusDistance = std::max(f, focusDistance);
double fov = 2.0 * std::atan(FCamera::SENSOR_SIZE * (focusDistance - f) / (2.0 * focusDistance * f));
return fov * math::d::RAD_TO_DEG;
}
template<typename T>
math::details::TMat44<T> inverseProjection(const math::details::TMat44<T>& p) noexcept {
math::details::TMat44<T> r;
@@ -273,11 +291,15 @@ CameraInfo::CameraInfo(FCamera const& camera) noexcept {
zn = camera.getNear();
zf = camera.getCullingFar();
ev100 = Exposure::ev100(camera);
f = (FCamera::SENSOR_SIZE * (float)projection[1][1]) * 0.5f;
f = camera.getFocalLength();
A = f / camera.getAperture();
d = std::max(zn, camera.getFocusDistance());
}
CameraInfo::CameraInfo(FCamera const& camera, const math::mat4f& worldOriginCamera) noexcept {
CameraInfo::CameraInfo(FCamera const& camera, const math::mat4f& worldOriginCamera,
float focusDistance) noexcept {
// note: DepthOfFieldOptions is deprecated, but we continue to support it by passing it here
// and we're using it if the camera focus distance hasn't been set.
const mat4f modelMatrix{ worldOriginCamera * camera.getModelMatrix() };
projection = mat4f{ camera.getProjectionMatrix() };
cullingProjection = mat4f{ camera.getCullingProjectionMatrix() };
@@ -286,8 +308,9 @@ CameraInfo::CameraInfo(FCamera const& camera, const math::mat4f& worldOriginCame
zn = camera.getNear();
zf = camera.getCullingFar();
ev100 = Exposure::ev100(camera);
f = (FCamera::SENSOR_SIZE * (float)projection[1][1]) * 0.5f;
f = camera.getFocalLength();
A = f / camera.getAperture();
d = std::max(zn, camera.getFocusDistance() > 0.0f ? camera.getFocusDistance() : focusDistance);
worldOffset = camera.getPosition();
worldOrigin = worldOriginCamera;
}
@@ -308,13 +331,14 @@ void Camera::setProjection(Camera::Projection projection, double left, double ri
upcast(this)->setProjection(projection, left, right, bottom, top, near, far);
}
void Camera::setProjection(double fov, double aspect, double near, double far,
void Camera::setProjection(double fovInDegrees, double aspect, double near, double far,
Camera::Fov direction) noexcept {
upcast(this)->setProjection(fov, aspect, near, far, direction);
upcast(this)->setProjection(fovInDegrees, aspect, near, far, direction);
}
void Camera::setLensProjection(double focalLength, double aspect, double near, double far) noexcept {
upcast(this)->setLensProjection(focalLength, aspect, near, far);
void Camera::setLensProjection(double focalLengthInMillimeters,
double aspect, double near, double far) noexcept {
upcast(this)->setLensProjection(focalLengthInMillimeters, aspect, near, far);
}
void Camera::setCustomProjection(mat4 const& projection, double near, double far) noexcept {
@@ -426,4 +450,24 @@ float Camera::getSensitivity() const noexcept {
return upcast(this)->getSensitivity();
}
void Camera::setFocusDistance(float distance) noexcept {
upcast(this)->setFocusDistance(distance);
}
float Camera::getFocusDistance() const noexcept {
return upcast(this)->getFocusDistance();
}
double Camera::getFocalLength() const noexcept {
return upcast(this)->getFocalLength();
}
double Camera::computeEffectiveFocalLength(double focalLength, double focusDistance) noexcept {
return FCamera::computeEffectiveFocalLength(focalLength, focusDistance);
}
double Camera::computeEffectiveFov(double fovInDegrees, double focusDistance) noexcept {
return FCamera::computeEffectiveFov(fovInDegrees, focusDistance);
}
} // namespace filament
+1 -1
View File
@@ -900,7 +900,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::dof(FrameGraph& fg,
*
* It's just a madd!
*/
const float focusDistance = std::max(cameraInfo.zn, dofOptions.focusDistance);
const float focusDistance = cameraInfo.d;
auto const& desc = fg.getDescriptor<FrameGraphTexture>(input);
const float Kc = (cameraInfo.A * cameraInfo.f) / (focusDistance - cameraInfo.f);
const float Ks = ((float)desc.height) / FCamera::SENSOR_SIZE;
+1 -1
View File
@@ -397,7 +397,7 @@ void FView::prepare(FEngine& engine, backend::DriverApi& driver, ArenaScope& are
// Note: for debugging (i.e. visualize what the camera / objects are doing, using
// the viewing camera), we can set worldOriginScene to identity when mViewingCamera
// is set
mViewingCameraInfo = CameraInfo(*camera, worldOriginScene);
mViewingCameraInfo = CameraInfo(*camera, worldOriginScene, mDepthOfFieldOptions.focusDistance);
mCullingFrustum = FCamera::getFrustum(
mCullingCamera->getCullingProjectionMatrix(),
+23 -5
View File
@@ -51,11 +51,12 @@ public:
double near, double far) noexcept;
// sets the projection matrix
void setProjection(double fov, double aspect, double near, double far,
void setProjection(double fovInDegrees, double aspect, double near, double far,
Fov direction = Fov::VERTICAL) noexcept;
// sets the projection matrix
void setLensProjection(double focalLength, double aspect, double near, double far) noexcept;
void setLensProjection(double focalLengthInMillimeters,
double aspect, double near, double far) noexcept;
// Sets a custom projection matrix (sets both the viewing and culling projections).
void setCustomProjection(math::mat4 const& projection, double near, double far) noexcept;
@@ -157,6 +158,20 @@ public:
return mSensitivity;
}
void setFocusDistance(float distance) noexcept {
mFocusDistance = distance;
}
float getFocusDistance() const noexcept {
return mFocusDistance;
}
double getFocalLength() const noexcept;
static double computeEffectiveFocalLength(double focalLength, double focusDistance) noexcept;
static double computeEffectiveFov(double fovInDegrees, double focusDistance) noexcept;
utils::Entity getEntity() const noexcept {
return mEntity;
}
@@ -179,12 +194,14 @@ private:
float mAperture = 16.0f;
float mShutterSpeed = 1.0f / 125.0f;
float mSensitivity = 100.0f;
float mFocusDistance = 0.0f;
};
struct CameraInfo {
CameraInfo() noexcept = default;
explicit CameraInfo(FCamera const& camera) noexcept;
CameraInfo(FCamera const& camera, const math::mat4f& worldOriginCamera) noexcept;
CameraInfo(FCamera const& camera,
const math::mat4f& worldOriginCamera, float focusDistance) noexcept;
math::mat4f projection; // projection matrix for drawing (infinite zfar)
math::mat4f cullingProjection; // projection matrix for culling
@@ -193,8 +210,9 @@ struct CameraInfo {
float zn{}; // distance (positive) to the near plane
float zf{}; // distance (positive) to the far plane
float ev100{}; // exposure
float f{}; // focal length (in m)
float A{}; // f / aperture diameter (in m)
float f{}; // focal length [m]
float A{}; // f-number or f / aperture diameter [m]
float d{}; // focus distance [m]
math::float3 worldOffset{}; // world offset, API-level camera position
math::float3 const& getPosition() const noexcept { return model[3].xyz; }
math::float3 getForwardVector() const noexcept { return normalize(-model[2].xyz); }
+1
View File
@@ -173,6 +173,7 @@ struct ViewerOptions {
bool skyboxEnabled = true;
sRGBColor backgroundColor = { 0.0f };
float cameraFocalLength = 28.0f;
float cameraFocusDistance = { 0.0f };
};
struct Settings {
+7 -2
View File
@@ -775,6 +775,8 @@ static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, ViewerOp
i = parse(tokens, i + 1, jsonChunk, &out->backgroundColor);
} else if (compare(tok, jsonChunk, "cameraFocalLength") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->cameraFocalLength);
} else if (compare(tok, jsonChunk, "cameraFocusDistance") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->cameraFocusDistance);
} else {
slog.w << "Invalid viewer options key: '" << STR(tok, jsonChunk) << "'" << io::endl;
i = parse(tokens, i + 1);
@@ -887,6 +889,8 @@ void applySettings(const ViewerOptions& settings, Camera* camera, Skybox* skybox
settings.cameraAperture,
1.0f / settings.cameraSpeed,
settings.cameraISO);
camera->setFocusDistance(settings.cameraFocusDistance);
}
}
@@ -1170,7 +1174,8 @@ static std::ostream& operator<<(std::ostream& out, const ViewerOptions& in) {
<< "\"groundPlaneEnabled\": " << to_string(in.groundPlaneEnabled) << ",\n"
<< "\"skyboxEnabled\": " << to_string(in.skyboxEnabled) << ",\n"
<< "\"backgroundColor\": " << (in.backgroundColor) << ",\n"
<< "\"cameraFocalLength\": " << (in.cameraFocalLength) << "\n"
<< "\"cameraFocalLength\": " << (in.cameraFocalLength) << ",\n"
<< "\"cameraFocusDistance\": " << (in.cameraFocusDistance) << "\n"
<< "}";
}
@@ -1179,7 +1184,7 @@ static std::ostream& operator<<(std::ostream& out, const DepthOfFieldOptions& in
<< "\"focusDistance\": " << (in.focusDistance) << ",\n"
<< "\"cocScale\": " << (in.cocScale) << ",\n"
<< "\"maxApertureDiameter\": " << (in.maxApertureDiameter) << ",\n"
<< "\"enabled\": " << to_string(in.enabled) << "\n"
<< "\"enabled\": " << to_string(in.enabled) << ",\n"
<< "\"filter\": " << (in.filter) << "\n"
<< "}";
}
+8 -1
View File
@@ -629,10 +629,17 @@ int main(int argc, char** argv) {
auto& rcm = engine->getRenderableManager();
auto instance = rcm.getInstance(app.scene.groundPlane);
const auto& viewerOptions = app.viewer->getSettings().viewer;
const auto& dofOptions = app.viewer->getSettings().view.dof;
rcm.setLayerMask(instance,
0xff, viewerOptions.groundPlaneEnabled ? 0xff : 0x00);
FilamentApp::get().getCameraFocalLength() = viewerOptions.cameraFocalLength;
float fe = viewerOptions.cameraFocalLength;
if (dofOptions.enabled) {
fe = Camera::computeEffectiveFocalLength(fe / 1000.0,
std::max(0.1f, dofOptions.focusDistance)) * 1000.0;
}
FilamentApp::get().getCameraFocalLength() = fe;
const size_t cameraCount = app.asset->getCameraEntityCount();
view->setCamera(app.mainCamera);
+5
View File
@@ -421,7 +421,12 @@ export class Camera {
public getAperture(): number;
public getShutterSpeed(): number;
public getSensitivity(): number;
public getFocalLength(): number;
public getFocusDistance(): number;
public setFocusDistance(distance: number): void;
public static inverseProjection(p: mat4): mat4;
public static computeEffectiveFocalLength(focalLength: number, focusDistance: number) : number;
public static computeEffectiveFov(fovInDegrees: number, focusDistance: number) : number;
}
export class ColorGrading$Builder {
+6
View File
@@ -757,6 +757,12 @@ class_<Camera>("Camera")
.function("getAperture", &Camera::getAperture)
.function("getShutterSpeed", &Camera::getShutterSpeed)
.function("getSensitivity", &Camera::getSensitivity)
.function("setFocusDistance", &Camera::setFocusDistance)
.function("getFocusDistance", &Camera::getFocusDistance)
.function("getFocalLength", &Camera::getFocalLength)
.class_function("computeEffectiveFocalLength", &Camera::computeEffectiveFocalLength)
.class_function("computeEffectiveFov", &Camera::computeEffectiveFov)
.class_function("inverseProjection", (flatmat4 (*)(flatmat4)) [] (flatmat4 m) {
return flatmat4 { filament::math::mat4f(Camera::inverseProjection(m.m)) };