From 7afd5e5963c6ccdc71fe5487514f34fb4f34e72a Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Thu, 7 Jul 2022 11:42:55 -0700 Subject: [PATCH] Camera API and documentation improvements - getNear() and getCullingFar() now return doubles - updated documentation - all setProjection() calls can now throw (when enabled) and will do so if preconditions are not met (instead of setting a default projection). - Frustum can now be logged on debug builds --- RELEASE_NOTES.md | 2 + .../filament-android/src/main/cpp/Camera.cpp | 4 +- .../com/google/android/filament/Camera.java | 8 +-- filament/include/filament/Camera.h | 36 +++++++++---- filament/include/filament/Frustum.h | 8 ++- filament/src/Camera.cpp | 10 ++-- filament/src/Frustum.cpp | 17 ++++++ filament/src/ShadowMapManager.cpp | 6 +-- filament/src/details/Camera.cpp | 54 ++++++++----------- filament/src/details/Camera.h | 14 ++--- libs/math/include/math/mat4.h | 11 ++++ samples/rendertarget.cpp | 3 +- 12 files changed, 110 insertions(+), 63 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 662ec52440..d7a2e27a43 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -4,6 +4,8 @@ This file contains one line summaries of commits that are worthy of mentioning i A new header is inserted each time a *tag* is created. ## main branch + +- engine: `Camera::getNear()` and `Camera::getCullingFar()` now return `doubles` - Metal: implement scissor support. diff --git a/android/filament-android/src/main/cpp/Camera.cpp b/android/filament-android/src/main/cpp/Camera.cpp index 800a4da303..cef0260588 100644 --- a/android/filament-android/src/main/cpp/Camera.cpp +++ b/android/filament-android/src/main/cpp/Camera.cpp @@ -84,13 +84,13 @@ Java_com_google_android_filament_Camera_nLookAt(JNIEnv*, jclass, jlong nativeCam camera->lookAt({eye_x, eye_y, eye_z}, {center_x, center_y, center_z}, {up_x, up_y, up_z}); } -extern "C" JNIEXPORT jfloat JNICALL +extern "C" JNIEXPORT jdouble JNICALL Java_com_google_android_filament_Camera_nGetNear(JNIEnv*, jclass, jlong nativeCamera) { Camera *camera = (Camera *) nativeCamera; return camera->getNear(); } -extern "C" JNIEXPORT jfloat JNICALL +extern "C" JNIEXPORT jdouble JNICALL Java_com_google_android_filament_Camera_nGetCullingFar(JNIEnv*, jclass, jlong nativeCamera) { Camera *camera = (Camera *) nativeCamera; diff --git a/android/filament-android/src/main/java/com/google/android/filament/Camera.java b/android/filament-android/src/main/java/com/google/android/filament/Camera.java index 8bdfebf7af..852af29c1c 100644 --- a/android/filament-android/src/main/java/com/google/android/filament/Camera.java +++ b/android/filament-android/src/main/java/com/google/android/filament/Camera.java @@ -456,7 +456,7 @@ public class Camera { * @return Distance to the near plane */ public float getNear() { - return nGetNear(getNativeObject()); + return (float)nGetNear(getNativeObject()); } /** @@ -464,7 +464,7 @@ public class Camera { * @return Distance to the far plane */ public float getCullingFar() { - return nGetCullingFar(getNativeObject()); + return (float)nGetCullingFar(getNativeObject()); } /** @@ -787,8 +787,8 @@ public class Camera { private static native void nSetModelMatrix(long nativeCamera, float[] in); private static native void nSetModelMatrixFp64(long nativeCamera, double[] in); private static native void nLookAt(long nativeCamera, double eyeX, double eyeY, double eyeZ, double centerX, double centerY, double centerZ, double upX, double upY, double upZ); - private static native float nGetNear(long nativeCamera); - private static native float nGetCullingFar(long nativeCamera); + private static native double nGetNear(long nativeCamera); + private static native double nGetCullingFar(long nativeCamera); private static native void nGetProjectionMatrix(long nativeCamera, double[] out); private static native void nGetCullingProjectionMatrix(long nativeCamera, double[] out); private static native void nGetScaling(long nativeCamera, double[] out); diff --git a/filament/include/filament/Camera.h b/filament/include/filament/Camera.h index 09ff27109c..5084f12c40 100644 --- a/filament/include/filament/Camera.h +++ b/filament/include/filament/Camera.h @@ -98,7 +98,7 @@ namespace filament { * The *near* plane distance greatly affects the depth-buffer resolution. * * Example: Precision at 1m, 10m, 100m and 1Km for various near distances assuming a 32-bit float - * depth-buffer + * depth-buffer: * * near (m) | 1 m | 10 m | 100 m | 1 Km * -----------:|:------:|:-------:|:--------:|:--------: @@ -107,11 +107,31 @@ namespace filament { * 0.1 | 3.6e-7 | 7.0e-5 | 0.0072 | 0.43 * 1.0 | 0 | 3.8e-6 | 0.0007 | 0.07 * - * * As can be seen in the table above, the depth-buffer precision drops rapidly with the * distance to the camera. + * * Make sure to pick the highest *near* plane distance possible. * + * On Vulkan and Metal platforms (or OpenGL platforms supporting either EXT_clip_control or + * ARB_clip_control extensions), the depth-buffer precision is much less dependent on the *near* + * plane value: + * + * near (m) | 1 m | 10 m | 100 m | 1 Km + * -----------:|:------:|:-------:|:--------:|:--------: + * 0.001 | 1.2e-7 | 9.5e-7 | 7.6e-6 | 6.1e-5 + * 0.01 | 1.2e-7 | 9.5e-7 | 7.6e-6 | 6.1e-5 + * 0.1 | 5.9e-8 | 9.5e-7 | 1.5e-5 | 1.2e-4 + * 1.0 | 0 | 9.5e-7 | 7.6e-6 | 1.8e-4 + * + * + * Choosing the *far* plane distance + * ================================= + * + * The far plane distance is always set internally to infinity for rendering, however it is used for + * culling and shadowing calculations. It is important to keep a reasonable ratio between + * the near and far plane distances. Typically a ratio in the range 1:100 to 1:100000 is + * commanded. Larger values may causes rendering artifacts or trigger assertions in debug builds. + * * * Exposure * ======== @@ -167,14 +187,12 @@ public: * Precondition: \p far > near for PROJECTION::PERSPECTIVE or * \p far != near for PROJECTION::ORTHO * - * @attention these parameters are silently modified to meet the preconditions above. - * * @see Projection, Frustum */ void setProjection(Projection projection, double left, double right, double bottom, double top, - double near, double far) noexcept; + double near, double far); /** Sets the projection matrix from the field-of-view. * @@ -187,7 +205,7 @@ public: * @see Fov. */ void setProjection(double fovInDegrees, double aspect, double near, double far, - Fov direction = Fov::VERTICAL) noexcept; + Fov direction = Fov::VERTICAL); /** Sets the projection matrix from the focal length. * @@ -197,7 +215,7 @@ public: * @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) noexcept; + double aspect, double near, double far); /** Sets a custom projection matrix. * @@ -308,10 +326,10 @@ public: //! Returns the frustum's near plane - float getNear() const noexcept; + double getNear() const noexcept; //! Returns the frustum's far plane used for culling - float getCullingFar() const noexcept; + double getCullingFar() const noexcept; /** Sets the camera's model matrix. * diff --git a/filament/include/filament/Frustum.h b/filament/include/filament/Frustum.h index a19a92bbe8..bfd01b0436 100644 --- a/filament/include/filament/Frustum.h +++ b/filament/include/filament/Frustum.h @@ -115,7 +115,13 @@ private: math::float4 mPlanes[6]; }; - } // namespace filament +#if !defined(NDEBUG) +namespace utils::io { +class ostream; +} // namespace utils::io +utils::io::ostream& operator<<(utils::io::ostream& out, filament::Frustum const& frustum); +#endif + #endif // TNT_FILAMENT_FRUSTUM_H diff --git a/filament/src/Camera.cpp b/filament/src/Camera.cpp index 7782b216c7..953ed093cc 100644 --- a/filament/src/Camera.cpp +++ b/filament/src/Camera.cpp @@ -68,17 +68,17 @@ mat4 Camera::inverseProjection(const mat4 & p) noexcept { } void Camera::setProjection(Camera::Projection projection, double left, double right, double bottom, - double top, double near, double far) noexcept { + double top, double near, double far) { upcast(this)->setProjection(projection, left, right, bottom, top, near, far); } void Camera::setProjection(double fovInDegrees, double aspect, double near, double far, - Camera::Fov direction) noexcept { + Camera::Fov direction) { upcast(this)->setProjection(fovInDegrees, aspect, near, far, direction); } void Camera::setLensProjection(double focalLengthInMillimeters, - double aspect, double near, double far) noexcept { + double aspect, double near, double far) { upcast(this)->setLensProjection(focalLengthInMillimeters, aspect, near, far); } @@ -115,11 +115,11 @@ double2 Camera::getShift() const noexcept { return upcast(this)->getShift(); } -float Camera::getNear() const noexcept { +double Camera::getNear() const noexcept { return upcast(this)->getNear(); } -float Camera::getCullingFar() const noexcept { +double Camera::getCullingFar() const noexcept { return upcast(this)->getCullingFar(); } diff --git a/filament/src/Frustum.cpp b/filament/src/Frustum.cpp index bc8a7e636a..bc246746b4 100644 --- a/filament/src/Frustum.cpp +++ b/filament/src/Frustum.cpp @@ -19,6 +19,7 @@ #include "Culler.h" #include +#include using namespace filament::math; @@ -98,3 +99,19 @@ float Frustum::contains(float3 p) const noexcept { } } // namespace filament + +#if !defined(NDEBUG) + +utils::io::ostream& operator<<(utils::io::ostream& out, filament::Frustum const& frustum) { + float4 planes[6]; + frustum.getNormalizedPlanes(planes); + out << planes[0] << '\n' + << planes[1] << '\n' + << planes[2] << '\n' + << planes[3] << '\n' + << planes[4] << '\n' + << planes[5] << utils::io::endl; + return out; +} + +#endif diff --git a/filament/src/ShadowMapManager.cpp b/filament/src/ShadowMapManager.cpp index 5caded46bb..5cdd83e27b 100644 --- a/filament/src/ShadowMapManager.cpp +++ b/filament/src/ShadowMapManager.cpp @@ -549,14 +549,14 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateSpotShadowMaps(FEngine const float normalBias = shadowMapInfo.vsm ? 0.0f : options->normalBias; auto& s = mShadowUb.edit(); - const float n = shadowMap.getCamera().getNear(); - const float f = shadowMap.getCamera().getCullingFar(); + const double n = shadowMap.getCamera().getNear(); + const double f = shadowMap.getCamera().getCullingFar(); s.shadows[i].lightFromWorldMatrix = shadowMap.getLightSpaceMatrix(); s.shadows[i].direction = direction; s.shadows[i].normalBias = normalBias * wsTexelSizeAtOneMeter; s.shadows[i].lightFromWorldZ = shadowMap.getLightFromWorldZ(); s.shadows[i].texelSizeAtOneMeter = wsTexelSizeAtOneMeter; - s.shadows[i].nearOverFarMinusNear = n / (f - n); + s.shadows[i].nearOverFarMinusNear = float(n / (f - n)); s.shadows[i].bulbRadiusLs = mSoftShadowOptions.penumbraScale * options->shadowBulbRadius / wsTexelSizeAtOneMeter; diff --git a/filament/src/details/Camera.cpp b/filament/src/details/Camera.cpp index b13efbf77a..fb696170e1 100644 --- a/filament/src/details/Camera.cpp +++ b/filament/src/details/Camera.cpp @@ -48,7 +48,7 @@ FCamera::FCamera(FEngine& engine, Entity e) } void UTILS_NOINLINE FCamera::setProjection(double fovInDegrees, double aspect, double near, double far, - Camera::Fov direction) noexcept { + Camera::Fov direction) { double w; double h; double s = std::tan(fovInDegrees * math::d::DEG_TO_RAD / 2.0) * near; @@ -63,7 +63,7 @@ void UTILS_NOINLINE FCamera::setProjection(double fovInDegrees, double aspect, d } void FCamera::setLensProjection(double focalLengthInMillimeters, - double aspect, double near, double far) noexcept { + double aspect, double near, double far) { // a 35mm camera has a 36x24mm wide frame size double h = (0.5 * near) * ((SENSOR_SIZE * 1000.0) / focalLengthInMillimeters); double w = h * aspect; @@ -82,31 +82,25 @@ void UTILS_NOINLINE FCamera::setCustomProjection(mat4 const& p, mat4 const& c, double near, double far) noexcept { mProjection = p; mProjectionForCulling = c; - mNear = (float)near; - mFar = (float)far; + mNear = near; + mFar = far; } void UTILS_NOINLINE FCamera::setProjection(Camera::Projection projection, double left, double right, double bottom, double top, - double near, double far) noexcept { + double near, double far) { - // we make sure our preconditions are verified, using default values, - // to avoid inconsistent states in the renderer later. - if (UTILS_UNLIKELY(left == right || - bottom == top || - (projection == Projection::PERSPECTIVE && (near <= 0 || far <= near)) || - (projection == Projection::ORTHO && (near == far)))) { - PANIC_LOG("Camera preconditions not met. Using default projection."); - left = -0.1; - right = 0.1; - bottom = -0.1; - top = 0.1; - near = 0.1; - far = 100.0; - } + ASSERT_PRECONDITION(!( + left == right || + bottom == top || + (projection == Projection::PERSPECTIVE && (near <= 0 || far <= near)) || + (projection == Projection::ORTHO && (near == far))), + "Camera preconditions not met in setProjection(%s, %f, %f, %f, %f, %f, %f)", + projection == Camera::Projection::PERSPECTIVE ? "PERSPECTIVE" : "ORTHO", + left, right, bottom, top, near, far); - mat4 p; + mat4 c, p; switch (projection) { case Projection::PERSPECTIVE: /* @@ -117,8 +111,8 @@ void UTILS_NOINLINE FCamera::setProjection(Camera::Projection projection, * 0 0 F+N/N-F 2*F*N/N-F * 0 0 -1 0 */ - p = mat4::frustum(left, right, bottom, top, near, far); - mProjectionForCulling = p; + c = mat4::frustum(left, right, bottom, top, near, far); + p = c; /* * but we're using a far plane at infinity @@ -141,13 +135,11 @@ void UTILS_NOINLINE FCamera::setProjection(Camera::Projection projection, * 0 0 -2/F-N - F+N/F-N * 0 0 0 1 */ - p = mat4::ortho(left, right, bottom, top, near, far); - mProjectionForCulling = p; + c = mat4::ortho(left, right, bottom, top, near, far); + p = c; break; } - mProjection = p; - mNear = float(near); - mFar = float(far); + FCamera::setCustomProjection(p, c, near, far); } math::mat4 FCamera::getProjectionMatrix() const noexcept { @@ -272,8 +264,8 @@ CameraInfo::CameraInfo(FCamera const& camera) noexcept { cullingProjection = mat4f{ camera.getCullingProjectionMatrix() }; model = mat4f{ camera.getModelMatrix() }; view = mat4f{ camera.getViewMatrix() }; - zn = camera.getNear(); - zf = camera.getCullingFar(); + zn = (float)camera.getNear(); + zf = (float)camera.getCullingFar(); ev100 = Exposure::ev100(camera); f = (float)camera.getFocalLength(); A = f / camera.getAperture(); @@ -287,8 +279,8 @@ CameraInfo::CameraInfo(FCamera const& camera, const math::mat4& worldOriginCamer model = mat4f{ modelMatrix }; view = mat4f{ inverse(modelMatrix) }; worldOrigin = worldOriginCamera; - zn = camera.getNear(); - zf = camera.getCullingFar(); + zn = (float)camera.getNear(); + zf = (float)camera.getCullingFar(); ev100 = Exposure::ev100(camera); f = (float)camera.getFocalLength(); A = f / camera.getAperture(); diff --git a/filament/src/details/Camera.h b/filament/src/details/Camera.h index 67167efd13..90fdbd850d 100644 --- a/filament/src/details/Camera.h +++ b/filament/src/details/Camera.h @@ -48,15 +48,15 @@ public: // sets the projection matrix void setProjection(Projection projection, double left, double right, double bottom, double top, - double near, double far) noexcept; + double near, double far); // sets the projection matrix void setProjection(double fovInDegrees, double aspect, double near, double far, - Fov direction = Fov::VERTICAL) noexcept; + Fov direction = Fov::VERTICAL); // sets the projection matrix void setLensProjection(double focalLengthInMillimeters, - double aspect, double near, double far) noexcept; + double aspect, double near, double far); // Sets a custom projection matrix (sets both the viewing and culling projections). void setCustomProjection(math::mat4 const& projection, double near, double far) noexcept; @@ -84,9 +84,9 @@ public: // culling projection matrix set by the user math::mat4 getUserCullingProjectionMatrix() const noexcept { return mProjectionForCulling; } - float getNear() const noexcept { return mNear; } + double getNear() const noexcept { return mNear; } - float getCullingFar() const noexcept { return mFar; } + double getCullingFar() const noexcept { return mFar; } // sets the camera's model matrix (must be a rigid transform) void setModelMatrix(const math::mat4& modelMatrix) noexcept; @@ -192,8 +192,8 @@ private: math::double2 mScalingCS = { 1.0 }; // additional scaling applied to projection math::double2 mShiftCS = { 0.0 }; // additional translation applied to projection - float mNear{}; - float mFar{}; + double mNear{}; + double mFar{}; // exposure settings float mAperture = 16.0f; float mShutterSpeed = 1.0f / 125.0f; diff --git a/libs/math/include/math/mat4.h b/libs/math/include/math/mat4.h index f97c5e526c..1655e86e37 100644 --- a/libs/math/include/math/mat4.h +++ b/libs/math/include/math/mat4.h @@ -491,11 +491,22 @@ template constexpr TMat44 TMat44::frustum(T left, T right, T bottom, T top, T near, T far) noexcept { TMat44 m; m[0][0] = (2 * near) / (right - left); + // 0 + // 0 + // 0 + + // 0 m[1][1] = (2 * near) / (top - bottom); + // 0 + // 0 + m[2][0] = (right + left) / (right - left); m[2][1] = (top + bottom) / (top - bottom); m[2][2] = -(far + near) / (far - near); m[2][3] = -1; + + // 0 + // 0 m[3][2] = -(2 * far * near) / (far - near); m[3][3] = 0; return m; diff --git a/samples/rendertarget.cpp b/samples/rendertarget.cpp index 60dc9f1416..9ff53ccc37 100644 --- a/samples/rendertarget.cpp +++ b/samples/rendertarget.cpp @@ -360,7 +360,8 @@ int main(int argc, char** argv) { const auto model = camera.getModelMatrix(); const auto renderingProjection = camera.getProjectionMatrix(); const auto cullingProjection = camera.getCullingProjectionMatrix(); - app.offscreenCamera->setCustomProjection(renderingProjection, cullingProjection, camera.getNear(), camera.getCullingFar()); + app.offscreenCamera->setCustomProjection(renderingProjection, cullingProjection, + camera.getNear(), camera.getCullingFar()); switch (app.mode) { case App::ReflectionMode::RENDERABLES: tcm.setTransform(tcm.getInstance(app.reflectedMonkey), reflection * xform);