improve camera precondition checks and default state (#9420)

* improve camera precondition checks and default state

- Camera is now initialized with a default projection matrix with
left/right, top/bottom set to (-1,1) and near/far set to (0.1,1).

- setLensProjection() and setProjection(FOV) now check the near/far
precondition (near>0 and far>near).

- sanitize user's light near/far to avoid infs and nans during
  froxelization

- finally froxelizer asserts that near>0 and far>near on debug builds.

FIXES=[458030186]
This commit is contained in:
Mathias Agopian
2025-11-10 14:48:48 -08:00
committed by GitHub
parent e8349ab5cc
commit ce6fa82026
4 changed files with 94 additions and 32 deletions

View File

@@ -243,10 +243,10 @@ public:
/** Utility to set the projection matrix from the field-of-view.
*
* @param fovInDegrees full field-of-view in degrees. 0 < \p fov < 180.
* @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.
* @param direction direction of the \p fovInDegrees parameter.
* @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.
* @param direction direction of the \p fovInDegrees parameter.
*
* @see Fov.
*/
@@ -256,9 +256,9 @@ public:
/** Utility to set the projection matrix from the focal length.
*
* @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.
* @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 focalLengthInMillimeters,
double aspect, double near, double far);
@@ -270,8 +270,8 @@ public:
* that is all 3 axis are mapped to [-1, 1].
*
* @param projection custom projection matrix used for rendering and culling
* @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.
* @param near distance in world units from the camera to the near plane.
* @param far distance in world units from the camera to the far plane. \p far != \p near.
*/
void setCustomProjection(math::mat4 const& projection, double near, double far) noexcept;
@@ -282,8 +282,8 @@ public:
*
* @param projection custom projection matrix used for rendering
* @param projectionForCulling custom projection matrix used for culling
* @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.
* @param near distance in world units from the camera to the near plane.
* @param far distance in world units from the camera to the far plane. \p far != \p near.
*/
void setCustomProjection(math::mat4 const& projection, math::mat4 const& projectionForCulling,
double near, double far) noexcept;

View File

@@ -16,14 +16,30 @@
#include "details/Camera.h"
#include <filament/Camera.h>
#include <math/mat4.h>
#include <utils/Panic.h>
#include <math/vec2.h>
#include <math/vec3.h>
#include <math/vec4.h>
#include <cstddef>
#include <cstdint>
namespace filament {
using namespace math;
void Camera::setProjection(double const fovInDegrees, double const aspect, double const near, double const far,
Fov const direction) {
void Camera::setProjection(double const fovInDegrees, double const aspect,
double const near, double const far, Fov const direction) {
FILAMENT_CHECK_PRECONDITION(near > 0 && far > near)
<< "Camera preconditions not met in setProjection(): near <= 0 or far <= near, near="
<< near << ", far=" << far;
setCustomProjection(
projection(direction, fovInDegrees, aspect, near),
projection(direction, fovInDegrees, aspect, near, far),
@@ -32,6 +48,11 @@ void Camera::setProjection(double const fovInDegrees, double const aspect, doubl
void Camera::setLensProjection(double const focalLengthInMillimeters,
double const aspect, double const near, double const far) {
FILAMENT_CHECK_PRECONDITION(near > 0 && far > near)
<< "Camera preconditions not met in setLensProjection(): near <= 0 or far <= near, near="
<< near << ", far=" << far;
setCustomProjection(
projection(focalLengthInMillimeters, aspect, near),
projection(focalLengthInMillimeters, aspect, near, far),
@@ -149,8 +170,9 @@ utils::Entity Camera::getEntity() const noexcept {
return downcast(this)->getEntity();
}
void Camera::setExposure(float const aperture, float const shutterSpeed, float const ISO) noexcept {
downcast(this)->setExposure(aperture, shutterSpeed, ISO);
void Camera::setExposure(
float const aperture, float const shutterSpeed, float const sensitivity) noexcept {
downcast(this)->setExposure(aperture, shutterSpeed, sensitivity);
}
float Camera::getAperture() const noexcept {
@@ -177,7 +199,8 @@ double Camera::getFocalLength() const noexcept {
return downcast(this)->getFocalLength();
}
double Camera::computeEffectiveFocalLength(double const focalLength, double const focusDistance) noexcept {
double Camera::computeEffectiveFocalLength(
double const focalLength, double const focusDistance) noexcept {
return FCamera::computeEffectiveFocalLength(focalLength, focusDistance);
}

View File

@@ -226,6 +226,8 @@ bool Froxelizer::prepare(
filament::Viewport const& viewport,
const mat4f& projection, float const projectionNear, float const projectionFar,
float4 const& clipTransform) noexcept {
assert_invariant(projectionFar > projectionNear);
assert_invariant(projectionNear > 0);
setViewport(viewport);
setProjection(projection, projectionNear, projectionFar);
@@ -383,8 +385,28 @@ bool Froxelizer::update() noexcept {
bool uniformsNeedUpdating = false;
if (UTILS_UNLIKELY(mDirtyFlags & (OPTIONS_CHANGED|PROJECTION_CHANGED))) {
float const zLightFar = clamp(mUserZLightFar, mNear, mFar);
float zLightNear = clamp(mUserZLightNear, mNear, mFar);
// sanitize the user's near/far
float zLightNear = mUserZLightNear;
float zLightFar = mUserZLightFar;
if (zLightFar == zLightNear) {
zLightNear = mNear;
zLightFar = mFar;
}
if (zLightFar < zLightNear) {
std::swap(zLightFar, zLightNear);
}
if (zLightNear < mNear || zLightNear >= mFar) {
zLightNear = mNear;
}
if (zLightFar > mFar || zLightFar <= mNear) {
zLightFar = mFar;
}
assert_invariant(zLightNear < zLightFar);
assert_invariant(zLightNear >= mNear && zLightNear <= mFar);
assert_invariant(zLightFar <= mFar && zLightNear >= mNear);
zLightNear = std::min(zLightNear, zLightFar);
if (zLightFar != mZLightFar || zLightNear != mZLightNear) {
mDirtyFlags |= VIEWPORT_CHANGED;

View File

@@ -24,27 +24,35 @@
#include <filament/Camera.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/Panic.h>
#include <math/scalar.h>
#include <math/mat4.h>
#include <math/vec2.h>
#include <math/vec3.h>
#include <algorithm>
#include <cmath>
#include <cstdint>
#include <limits>
using namespace filament::math;
using namespace utils;
namespace filament {
static constexpr const float MIN_APERTURE = 0.5f;
static constexpr const float MAX_APERTURE = 64.0f;
static constexpr const float MIN_SHUTTER_SPEED = 1.0f / 25000.0f;
static constexpr const float MAX_SHUTTER_SPEED = 60.0f;
static constexpr const float MIN_SENSITIVITY = 10.0f;
static constexpr const float MAX_SENSITIVITY = 204800.0f;
static constexpr float MIN_APERTURE = 0.5f;
static constexpr float MAX_APERTURE = 64.0f;
static constexpr float MIN_SHUTTER_SPEED = 1.0f / 25000.0f;
static constexpr float MAX_SHUTTER_SPEED = 60.0f;
static constexpr float MIN_SENSITIVITY = 10.0f;
static constexpr float MAX_SENSITIVITY = 204800.0f;
FCamera::FCamera(FEngine& engine, Entity const e)
: mEngine(engine),
mEntity(e) {
setProjection(Projection::PERSPECTIVE, -1.0, 1.0, -1.0, 1.0, 0.1, 1.0);
}
mat4 FCamera::projection(Fov const direction, double const fovInDegrees,
@@ -84,12 +92,16 @@ mat4 FCamera::projection(double const focalLengthInMillimeters,
* All methods for setting the projection funnel through here
*/
void UTILS_NOINLINE FCamera::setCustomProjection(mat4 const& p,
mat4 const& c, double const near, double const far) noexcept {
void UTILS_NOINLINE FCamera::setCustomProjection(mat4 const& projection,
mat4 const& projectionForCulling, double const near, double const far) noexcept {
FILAMENT_CHECK_PRECONDITION(near != far)
<< "Camera preconditions not met in setCustomProjection(): near = far = " << near;
for (auto& eyeProjection: mEyeProjection) {
eyeProjection = p;
eyeProjection = projection;
}
mProjectionForCulling = c;
mProjectionForCulling = projectionForCulling;
mNear = near;
mFar = far;
}
@@ -97,10 +109,15 @@ void UTILS_NOINLINE FCamera::setCustomProjection(mat4 const& p,
void UTILS_NOINLINE FCamera::setCustomEyeProjection(mat4 const* projection, size_t const count,
mat4 const& projectionForCulling, double const near, double const far) {
const Engine::Config& config = mEngine.getConfig();
FILAMENT_CHECK_PRECONDITION(near != far)
<< "Camera preconditions not met in setCustomEyeProjection(): near = far = " << near;
FILAMENT_CHECK_PRECONDITION(count >= config.stereoscopicEyeCount)
<< "All eye projections must be supplied together, count must be >= "
"config.stereoscopicEyeCount ("
<< config.stereoscopicEyeCount << ")";
for (int i = 0; i < config.stereoscopicEyeCount; i++) {
mEyeProjection[i] = projection[i];
}
@@ -290,10 +307,10 @@ CameraInfo::CameraInfo(FCamera const& camera,
model = mat4f{ modelMatrix };
view = mat4f{ inverse(modelMatrix) };
worldTransform = inWorldTransform;
zn = (float)camera.getNear();
zf = (float)camera.getCullingFar();
zn = float(camera.getNear());
zf = float(camera.getCullingFar());
ev100 = Exposure::ev100(camera);
f = (float)camera.getFocalLength();
f = float(camera.getFocalLength());
A = f / camera.getAperture();
d = std::max(zn, camera.getFocusDistance());
}