diff --git a/filament/src/PerViewUniforms.cpp b/filament/src/PerViewUniforms.cpp index dc88e662ee..4b2a516c1d 100644 --- a/filament/src/PerViewUniforms.cpp +++ b/filament/src/PerViewUniforms.cpp @@ -81,7 +81,7 @@ void PerViewUniforms::prepareCamera(const CameraInfo& camera) noexcept { s.clipFromWorldMatrix = clipFromWorld; // projection * view s.worldFromClipMatrix = worldFromClip; // 1/(projection * view) s.cameraPosition = float3{ camera.getPosition() }; - s.worldOffset = camera.worldOffset; + s.worldOffset = camera.getWorldOffset(); s.cameraFar = camera.zf; s.oneOverFarMinusNear = 1.0f / (camera.zf - camera.zn); s.nearOverFarMinusNear = camera.zn / (camera.zf - camera.zn); diff --git a/filament/src/PostProcessManager.cpp b/filament/src/PostProcessManager.cpp index cfdcea11af..04886fe285 100644 --- a/filament/src/PostProcessManager.cpp +++ b/filament/src/PostProcessManager.cpp @@ -531,8 +531,8 @@ FrameGraphId PostProcessManager::ssr(FrameGraph& fg, data.history = builder.sample(history); } }, - [this, projection = cameraInfo.projection, viewMatrix = cameraInfo.view, - worldOrigin = cameraInfo.worldOrigin, uvFromClipMatrix, historyProjection, + [this, projection = cameraInfo.projection, + userViewMatrix = cameraInfo.getUserViewMatrix(), uvFromClipMatrix, historyProjection, options, &uniforms, renderPass = pass] (FrameGraphResources const& resources, auto const& data, DriverApi& driver) mutable { // set structure sampler @@ -542,7 +542,7 @@ FrameGraphId PostProcessManager::ssr(FrameGraph& fg, // set screen-space reflections and screen-space refractions mat4f uvFromViewMatrix = uvFromClipMatrix * projection; mat4f reprojection = mat4f{ uvFromClipMatrix * historyProjection - * inverse(viewMatrix * worldOrigin) }; + * inverse(userViewMatrix) }; // the history sampler is a regular texture2D TextureHandle history = data.history ? @@ -805,11 +805,9 @@ FrameGraphId PostProcessManager::screenSpaceAmbientOcclusion( mi->setParameter("ssctConeAngleTangeant", std::tan(options.ssct.lightConeRad * 0.5f)); mi->setParameter("ssctContactDistanceMaxInv", 1.0f / options.ssct.contactDistanceMax); // light direction in view space - // (note: this is actually equivalent to using the camera view matrix -- before the - // world matrix is accounted for) - const mat4f m{ cameraInfo.view * cameraInfo.worldOrigin }; + const mat4f view{ cameraInfo.getUserViewMatrix() }; const float3 l = normalize( - mat3f::getTransformForNormals(m.upperLeft()) + mat3f::getTransformForNormals(view.upperLeft()) * options.ssct.lightDirection); mi->setParameter("ssctIntensity", options.ssct.enabled ? options.ssct.intensity : 0.0f); @@ -2464,7 +2462,7 @@ void PostProcessManager::prepareTaa(FrameGraph& fg, filament::Viewport const& sv auto& current = frameHistory.getCurrent().*pTaa; // compute projection - current.projection = mat4f{ inoutCameraInfo->projection * (inoutCameraInfo->view * inoutCameraInfo->worldOrigin) }; + current.projection = mat4f{ inoutCameraInfo->projection * inoutCameraInfo->getUserViewMatrix() }; current.frameId = previous.frameId + 1; // sample position within a pixel [-0.5, 0.5] @@ -2561,7 +2559,7 @@ FrameGraphId PostProcessManager::taa(FrameGraph& fg, float2 d = sampleOffsets[i] - current.jitter; d *= 1.0f / taaOptions.filterWidth; // this is a gaussian fit of a 3.3 Blackman Harris window - // see: "High Quality Temporal Supersampling" by Bruan Karis + // see: "High Quality Temporal Supersampling" by Brian Karis weights[i] = std::exp2(-3.3f * (d.x * d.x + d.y * d.y)); sum += weights[i]; } diff --git a/filament/src/ShadowMap.cpp b/filament/src/ShadowMap.cpp index 1abfd67713..87ef75a493 100644 --- a/filament/src/ShadowMap.cpp +++ b/filament/src/ShadowMap.cpp @@ -318,7 +318,7 @@ void ShadowMap::updateDirectional(const FScene::LightSoa& lightData, size_t inde if (params.options.stable) { // Use the world origin as reference point, fixed w.r.t. the camera - snapLightFrustum(s, o, Mv, camera.worldOrigin[3].xyz, + snapLightFrustum(s, o, Mv, -camera.getWorldOffset(), 1.0f / mShadowMapInfo.shadowDimension); } diff --git a/filament/src/details/Camera.cpp b/filament/src/details/Camera.cpp index 4167664b62..b13efbf77a 100644 --- a/filament/src/details/Camera.cpp +++ b/filament/src/details/Camera.cpp @@ -275,7 +275,7 @@ CameraInfo::CameraInfo(FCamera const& camera) noexcept { zn = camera.getNear(); zf = camera.getCullingFar(); ev100 = Exposure::ev100(camera); - f = camera.getFocalLength(); + f = (float)camera.getFocalLength(); A = f / camera.getAperture(); d = std::max(zn, camera.getFocusDistance()); } @@ -286,14 +286,13 @@ CameraInfo::CameraInfo(FCamera const& camera, const math::mat4& worldOriginCamer cullingProjection = mat4f{ camera.getCullingProjectionMatrix() }; model = mat4f{ modelMatrix }; view = mat4f{ inverse(modelMatrix) }; + worldOrigin = worldOriginCamera; zn = camera.getNear(); zf = camera.getCullingFar(); ev100 = Exposure::ev100(camera); - f = camera.getFocalLength(); + f = (float)camera.getFocalLength(); A = f / camera.getAperture(); d = std::max(zn, camera.getFocusDistance()); - worldOffset = camera.getPosition(); - worldOrigin = worldOriginCamera; } } // namespace filament diff --git a/filament/src/details/Camera.h b/filament/src/details/Camera.h index f6cc6bf2b4..0e70aab3fd 100644 --- a/filament/src/details/Camera.h +++ b/filament/src/details/Camera.h @@ -209,18 +209,18 @@ struct CameraInfo { math::mat4f projection; // projection matrix for drawing (infinite zfar) math::mat4f cullingProjection; // projection matrix for culling math::mat4f model; // camera model matrix - math::mat4f view; // camera view matrix + math::mat4f view; // camera view matrix (inverse(model)) + math::mat4 worldOrigin; // world origin transform (already applied to model and view) float zn{}; // distance (positive) to the near plane float zf{}; // distance (positive) to the far plane float ev100{}; // exposure 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::mat4 worldOrigin; // this is already applied to model and view math::float3 const& getPosition() const noexcept { return model[3].xyz; } math::float3 getForwardVector() const noexcept { return normalize(-model[2].xyz); } - + math::float3 getWorldOffset() const noexcept { return -worldOrigin[3].xyz; } + math::mat4 getUserViewMatrix() const noexcept { return view * worldOrigin; } }; FILAMENT_UPCAST(Camera) diff --git a/filament/src/details/Renderer.cpp b/filament/src/details/Renderer.cpp index 781c250ad2..c11bd878ef 100644 --- a/filament/src/details/Renderer.cpp +++ b/filament/src/details/Renderer.cpp @@ -608,8 +608,7 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) { FrameGraphId history; }; // FIXME: should we use the TAA-modified cameraInfo here or not? (we are). - auto projection = mat4f{ - cameraInfo.projection * (cameraInfo.view * cameraInfo.worldOrigin) }; + auto projection = mat4f{ cameraInfo.projection * cameraInfo.getUserViewMatrix() }; fg.addPass("Export SSR history", [&](FrameGraph::Builder& builder, auto& data) { // We need to use sideEffect here to ensure this pass won't be culled. diff --git a/filament/src/details/View.cpp b/filament/src/details/View.cpp index a9f178d998..36189d7a33 100644 --- a/filament/src/details/View.cpp +++ b/filament/src/details/View.cpp @@ -407,8 +407,8 @@ void FView::prepareLighting(FEngine& engine, FEngine::DriverApi& driver, ArenaSc mHasDirectionalLight = directionalLight.isValid(); } -CameraInfo FView::computeCameraInfo(FEngine& engine) noexcept { - FScene* const scene = getScene(); +CameraInfo FView::computeCameraInfo(FEngine& engine) const noexcept { + FScene const* const scene = getScene(); /* * We apply a "world origin" to "everything" in order to implement the IBL rotation. @@ -451,11 +451,25 @@ void FView::prepare(FEngine& engine, DriverApi& driver, ArenaScope& arena, * and in particular their world-space AABB. */ - FScene* const scene = getScene(); + auto getFrustum = [this, &cameraInfo]() -> Frustum { + if (UTILS_LIKELY(mViewingCamera == nullptr)) { + // In the common case when we don't have a viewing camera, cameraInfo.view is + // already the culling view matrix + return Frustum{ mat4f{ highPrecisionMultiply(cameraInfo.projection, cameraInfo.view) }}; + } else { + // Otherwise, we need to recalculate it from the culling camera. + // Note: it is correct to always do the math from mCullingCamera, but it hides the + // intent of the code, which is that we should only depend on CameraInfo here. + // This is an extremely uncommon case. + const mat4 projection = mCullingCamera->getCullingProjectionMatrix(); + const mat4 view = inverse(cameraInfo.worldOrigin * mCullingCamera->getModelMatrix()); + return Frustum{ mat4f{ projection * view }}; + } + }; - mCullingFrustum = Frustum(mat4f{ - mCullingCamera->getCullingProjectionMatrix() * - inverse(cameraInfo.worldOrigin * mCullingCamera->getModelMatrix()) }); + const Frustum cullingFrustum = getFrustum(); + + FScene* const scene = getScene(); /* * Gather all information needed to render this scene. Apply the world origin to all @@ -470,9 +484,9 @@ void FView::prepare(FEngine& engine, DriverApi& driver, ArenaScope& arena, JobSystem::Job* prepareVisibleLightsJob = nullptr; if (scene->getLightData().size() > FScene::DIRECTIONAL_LIGHTS_COUNT) { prepareVisibleLightsJob = js.runAndRetain(js.createJob(nullptr, - [this, &engine, &arena, &cameraInfo, scene](JobSystem&, JobSystem::Job*) { + [&cullingFrustum, &engine, &arena, &cameraInfo, scene](JobSystem&, JobSystem::Job*) { FView::prepareVisibleLights(engine.getLightManager(), arena, - cameraInfo.view, mCullingFrustum, scene->getLightData()); + cameraInfo.view, cullingFrustum, scene->getLightData()); })); } @@ -489,7 +503,7 @@ void FView::prepare(FEngine& engine, DriverApi& driver, ArenaScope& arena, * (this will set the VISIBLE_RENDERABLE bit) */ - prepareVisibleRenderables(js, mCullingFrustum, renderableData); + prepareVisibleRenderables(js, cullingFrustum, renderableData); /* diff --git a/filament/src/details/View.h b/filament/src/details/View.h index dd260c970a..ff3cde47f1 100644 --- a/filament/src/details/View.h +++ b/filament/src/details/View.h @@ -119,7 +119,7 @@ public: void terminate(FEngine& engine); - CameraInfo computeCameraInfo(FEngine& engine) noexcept; + CameraInfo computeCameraInfo(FEngine& engine) const noexcept; void prepare(FEngine& engine, backend::DriverApi& driver, ArenaScope& arena, filament::Viewport const& viewport, CameraInfo const& cameraInfo, @@ -500,11 +500,11 @@ private: backend::Handle mRenderableUbh; FScene* mScene = nullptr; + // The camera set by the user, used for culling and viewing FCamera* mCullingCamera = nullptr; + // The optional (debug) camera, used only for viewing FCamera* mViewingCamera = nullptr; - Frustum mCullingFrustum{}; - mutable Froxelizer mFroxelizer; Viewport mViewport; diff --git a/samples/gltf_viewer.cpp b/samples/gltf_viewer.cpp index 92cb656651..7b5bae0776 100644 --- a/samples/gltf_viewer.cpp +++ b/samples/gltf_viewer.cpp @@ -580,6 +580,7 @@ int main(int argc, char** argv) { debug.getPropertyAddress("d.renderer.doFrameCapture"); *captureFrame = true; } + ImGui::Checkbox("Camera at origin", debug.getPropertyAddress("d.view.camera_at_origin")); auto dataSource = debug.getDataSource("d.view.frame_info"); if (dataSource.data) { ImGuiExt::PlotLinesSeries("FrameInfo", 6,