From e2e5f7cee968872683d0fb8cdbe6dfc8bb4196fe Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Fri, 3 Mar 2023 21:34:17 -0800 Subject: [PATCH] Add way to retrieve the user world-space in materials (#6607) * Add way to retrieve the user world-space in materials added `getUserWorldFromWorldMatrix()` and `getUserWorldPosition()` to retrieve the API-level (user) world position in materials. Deprecated `getWorldOffset()` `getWorldOffset` didn't work when an IBL rotation was applied. * fix large scenes with an ibl rotation Rotate the IBL around the camera instead of the world so that the camera is always at the origin regardless of the rotation. --- NEW_RELEASE_NOTES.md | 8 +++--- docs/Materials.md.html | 20 ++++++++------ filament/src/PerShadowMapUniforms.cpp | 3 +-- filament/src/PerViewUniforms.cpp | 3 +-- filament/src/ShadowMap.cpp | 2 +- filament/src/details/Camera.h | 1 - filament/src/details/View.cpp | 27 +++++++++---------- .../include/private/filament/UibStructs.h | 24 +++++++---------- libs/filamat/src/shaders/UibGenerator.cpp | 5 +--- shaders/src/common_getters.glsl | 11 +++++--- shaders/src/getters.fs | 5 ++++ shaders/src/shading_parameters.fs | 4 +-- 12 files changed, 59 insertions(+), 54 deletions(-) diff --git a/NEW_RELEASE_NOTES.md b/NEW_RELEASE_NOTES.md index e535fc5f86..da5e611675 100644 --- a/NEW_RELEASE_NOTES.md +++ b/NEW_RELEASE_NOTES.md @@ -8,6 +8,8 @@ appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md). ## Release notes for next branch cut -fog: fixed fog height falloff and computation precision on mobile [⚠️ **Recompile Materials**] -materials: new alphaToCoverage property can be used to control alpha to coverage behavior -engine: fix precision issue with `shading_view` in large scenes +- fog: fixed fog height falloff and computation precision on mobile [⚠️ **Recompile Materials**] +- materials: new alphaToCoverage property can be used to control alpha to coverage behavior +- materials: added `getUserWorldFromWorldMatrix()` and `getUserWorldPosition()` to retrieve the + API-level (user) world position in materials. Deprecated `getWorldOffset()`. [⚠️ **Recompile Materials**] +- engine: fix precision issue with `shading_view` in large scenes diff --git a/docs/Materials.md.html b/docs/Materials.md.html index 5bc4ea4184..8441a903cc 100644 --- a/docs/Materials.md.html +++ b/docs/Materials.md.html @@ -1946,8 +1946,9 @@ struct MaterialVertexInputs { !!! TIP: worldPosition To achieve good precision, the `worldPosition` coordinate in the vertex shader is shifted by the - camera position. To get the true world-space position, users can add this to - `getWorldOffset()`. + camera position. To get the true world-space position, users can use + `getUserWorldPosition()`, however be aware that the true world-position might not + be able to fit in a `float` or might be represented with severely reduced precision. !!! TIP: UV attributes By default the vertex shader of a material will flip the Y coordinate of the UV attributes @@ -2300,8 +2301,9 @@ type aliases: Name | Type | Description :-----------------------------------|:--------:|:------------------------------------ **getResolution()** | float4 | Dimensions of the view's effective (physical) viewport in pixels: `width`, `height`, `1 / width`, `1 / height`. This might be different from `View::getViewport()` for instance because of added rendering guard-bands. -**getWorldCameraPosition()** | float3 | Position of the camera/eye in world space -**getWorldOffset()** | float3 | The shift required to obtain API-level world space +**getWorldCameraPosition()** | float3 | Position of the camera/eye in world space (see note below) +**getWorldOffset()** | float3 | [deprecated] The shift required to obtain API-level world space. Use getUserWorldPosition() instead +**getUserWorldFromWorldMatrix()** | float4x4 | Matrix that converts from world space to API-level (user) world space. **getTime()** | float | Current time as a remainder of 1 second. Yields a value between 0 and 1 **getUserTime()** | float4 | Current time in seconds: `time`, `(double)time - time`, `0`, `0` **getUserTimeMode(float m)** | float | Current time modulo m in seconds @@ -2311,7 +2313,7 @@ type aliases: !!! TIP: world space To achieve good precision, the "world space" in Filament's shading system does not necessarily match the API-level world space. To obtain the position of the API-level camera, custom - materials can add `getWorldOffset()` to `getWorldCameraPosition()`. + materials can use `getUserWorldFromWorldMatrix()` to transform `getWorldCameraPosition()`. ### Vertex only @@ -2333,6 +2335,7 @@ The following APIs are only available from the fragment block: :---------------------------------------|:--------:|:------------------------------------ **getWorldTangentFrame()** | float3x3 | Matrix containing in each column the `tangent` (`frame[0]`), `bi-tangent` (`frame[1]`) and `normal` (`frame[2]`) of the vertex in world space. If the material does not compute a tangent space normal for bump mapping or if the shading is not anisotropic, only the `normal` is valid in this matrix. **getWorldPosition()** | float3 | Position of the fragment in world space (see note below about world-space) +**getUserWorldPosition()** | float3 | Position of the fragment in API-level (user) world-space (see note below about world-space) **getWorldViewVector()** | float3 | Normalized vector in world space from the fragment position to the eye **getWorldNormalVector()** | float3 | Normalized normal in world space, after bump mapping (must be used after `prepareMaterial()`) **getWorldGeometricNormalVector()** | float3 | Normalized normal in world space, before bump mapping (can be used before `prepareMaterial()`) @@ -2349,9 +2352,10 @@ The following APIs are only available from the fragment block: **ycbcrToRgb(float, float2)** | float3 | Converts a luminance and CbCr pair to a sRGB color **uvToRenderTargetUV(float2)** | float2 | Transforms a UV coordinate to allow sampling from a `RenderTarget` attachment -!!! TIP: world space - To obtain API-level world space coordinates, custom materials should add `getWorldOffset()` to - `getWorldPosition()` (et al). +!!! TIP: world-space + To obtain API-level world-space coordinates, custom materials should use `getUserWorldPosition()` + or use `getUserWorldFromWorldMatrix()`. Note that API-level world-space coordinates should + never or rarely be used because they may not fit in a float3 or have severely reduced precision. !!! TIP: sampling from render targets When sampling from a `filament::Texture` that is attached to a `filament::RenderTarget` for diff --git a/filament/src/PerShadowMapUniforms.cpp b/filament/src/PerShadowMapUniforms.cpp index d31053f3f4..465e5920fe 100644 --- a/filament/src/PerShadowMapUniforms.cpp +++ b/filament/src/PerShadowMapUniforms.cpp @@ -60,9 +60,8 @@ void PerShadowMapUniforms::prepareCamera(Transaction const& transaction, s.viewFromClipMatrix = viewFromClip; // 1/projection s.clipFromWorldMatrix = clipFromWorld; // projection * view s.worldFromClipMatrix = worldFromClip; // 1/(projection * view) + s.userWorldFromWorldMatrix = mat4f(inverse(camera.worldOrigin)); s.clipTransform = camera.clipTransfrom; - s.cameraPosition = float3{ camera.getPosition() }; - 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/PerViewUniforms.cpp b/filament/src/PerViewUniforms.cpp index fbab226d8e..285b68a58a 100644 --- a/filament/src/PerViewUniforms.cpp +++ b/filament/src/PerViewUniforms.cpp @@ -76,9 +76,8 @@ void PerViewUniforms::prepareCamera(FEngine& engine, const CameraInfo& camera) n s.viewFromClipMatrix = viewFromClip; // 1/projection s.clipFromWorldMatrix = clipFromWorld; // projection * view s.worldFromClipMatrix = worldFromClip; // 1/(projection * view) + s.userWorldFromWorldMatrix = mat4f(inverse(camera.worldOrigin)); s.clipTransform = camera.clipTransfrom; - s.cameraPosition = float3{ camera.getPosition() }; - 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/ShadowMap.cpp b/filament/src/ShadowMap.cpp index b6690a08e5..c7b93e51c7 100644 --- a/filament/src/ShadowMap.cpp +++ b/filament/src/ShadowMap.cpp @@ -357,7 +357,7 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine, if (params.options.stable) { // Use the world origin as reference point, fixed w.r.t. the camera - snapLightFrustum(s, o, Mv, -camera.getWorldOffset(), + snapLightFrustum(s, o, Mv, camera.worldOrigin[3].xyz, 1.0f / float(shadowMapInfo.shadowDimension)); } diff --git a/filament/src/details/Camera.h b/filament/src/details/Camera.h index ebebb29eca..edf7e90025 100644 --- a/filament/src/details/Camera.h +++ b/filament/src/details/Camera.h @@ -219,7 +219,6 @@ struct CameraInfo { float d{}; // focus distance [m] 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; } }; diff --git a/filament/src/details/View.cpp b/filament/src/details/View.cpp index 17cd1f3da1..b95caa08c8 100644 --- a/filament/src/details/View.cpp +++ b/filament/src/details/View.cpp @@ -390,7 +390,7 @@ void FView::prepareLighting(FEngine& engine, FEngine::DriverApi& driver, ArenaSc * Directional light (always at index 0) */ - FLightManager::Instance directionalLight = lightData.elementAt(0); + FLightManager::Instance const directionalLight = lightData.elementAt(0); const float3 sceneSpaceDirection = lightData.elementAt(0); // guaranteed normalized mPerViewUniforms.prepareDirectionalLight(engine, exposure, sceneSpaceDirection, directionalLight); mHasDirectionalLight = directionalLight.isValid(); @@ -401,32 +401,31 @@ CameraInfo FView::computeCameraInfo(FEngine& engine) const noexcept { /* * We apply a "world origin" to "everything" in order to implement the IBL rotation. - * The "world origin" could also be useful for other things, like keeping the origin - * close to the camera position to improve fp precision in the shader for large scenes. + * The "world origin" is also be used to kee the origin close to the camera position to + * improve fp precision in the shader for large scenes. */ - mat4 worldOriginScene; - FIndirectLight const* const ibl = scene->getIndirectLight(); - if (ibl) { - // the IBL transformation must be a rigid transform - mat3f rotation{ scene->getIndirectLight()->getRotation() }; - // for a rigid-body transform, the inverse is the transpose - worldOriginScene = mat4{ transpose(rotation) }; - } + mat4 translation; + mat4 rotation; /* * Calculate all camera parameters needed to render this View for this frame. */ FCamera const* const camera = mViewingCamera ? mViewingCamera : mCullingCamera; - if (engine.debug.view.camera_at_origin) { // this moves the camera to the origin, effectively doing all shader computations in // view-space, which improves floating point precision in the shader by staying around // zero, where fp precision is highest. This also ensures that when the camera is placed // very far from the origin, objects are still rendered and lit properly. - worldOriginScene[3].xyz -= camera->getPosition(); + translation = mat4::translation( -camera->getPosition() ); } - return { *camera, worldOriginScene }; + FIndirectLight const* const ibl = scene->getIndirectLight(); + if (ibl) { + // the IBL transformation must be a rigid transform + rotation = mat4{ transpose(scene->getIndirectLight()->getRotation()) }; + } + + return { *camera, rotation * translation }; } void FView::prepare(FEngine& engine, DriverApi& driver, ArenaScope& arena, diff --git a/libs/filabridge/include/private/filament/UibStructs.h b/libs/filabridge/include/private/filament/UibStructs.h index a509abe0a0..ede611055e 100644 --- a/libs/filabridge/include/private/filament/UibStructs.h +++ b/libs/filabridge/include/private/filament/UibStructs.h @@ -43,16 +43,17 @@ struct PerViewUib { // NOLINT(cppcoreguidelines-pro-type-member-init) // Values that can be accessed in both surface and post-process materials // -------------------------------------------------------------------------------------------- - math::mat4f viewFromWorldMatrix; - math::mat4f worldFromViewMatrix; - math::mat4f clipFromViewMatrix; - math::mat4f viewFromClipMatrix; - math::mat4f clipFromWorldMatrix; - math::mat4f worldFromClipMatrix; - math::float4 clipTransform; // [sx, sy, tx, ty] only used by VERTEX_DOMAIN_DEVICE + math::mat4f viewFromWorldMatrix; // clip view <- world : view matrix + math::mat4f worldFromViewMatrix; // clip view -> world : model matrix + math::mat4f clipFromViewMatrix; // clip <- view world : projection matrix + math::mat4f viewFromClipMatrix; // clip -> view world : inverse projection matrix + math::mat4f clipFromWorldMatrix; // clip <- view <- world + math::mat4f worldFromClipMatrix; // clip -> view -> world + math::mat4f userWorldFromWorldMatrix; // userWorld <- world + math::float4 clipTransform; // [sx, sy, tx, ty] only used by VERTEX_DOMAIN_DEVICE math::float2 clipControl; // clip control - float time; // time in seconds, with a 1 second period + float time; // time in seconds, with a 1-second period float temporalNoise; // noise [0,1] when TAA is used, 0 otherwise math::float4 userTime; // time(s), (double)time - (float)time, 0, 0 @@ -67,14 +68,9 @@ struct PerViewUib { // NOLINT(cppcoreguidelines-pro-type-member-init) float lodBias; // load bias to apply to user materials float refractionLodOffset; - float padding1; - float padding2; // camera position in view space (when camera_at_origin is enabled), i.e. it's (0,0,0). - // Always add worldOffset in the shader to get the true world-space position of the camera. - math::float3 cameraPosition; float oneOverFarMinusNear; // 1 / (f-n), always positive - math::float3 worldOffset; // this is (0,0,0) when camera_at_origin is disabled float nearOverFarMinusNear; // n / (f-n), always positive float cameraFar; // camera *culling* far-plane distance, always positive (projection far is at +inf) float exposure; @@ -164,7 +160,7 @@ struct PerViewUib { // NOLINT(cppcoreguidelines-pro-type-member-init) float ssrStride; // ssr texel stride, >= 1.0 // bring PerViewUib to 2 KiB - math::float4 reserved[62]; + math::float4 reserved[60]; }; // 2 KiB == 128 float4s diff --git a/libs/filamat/src/shaders/UibGenerator.cpp b/libs/filamat/src/shaders/UibGenerator.cpp index 7b30605050..503adc3cf8 100644 --- a/libs/filamat/src/shaders/UibGenerator.cpp +++ b/libs/filamat/src/shaders/UibGenerator.cpp @@ -41,6 +41,7 @@ BufferInterfaceBlock const& UibGenerator::getPerViewUib() noexcept { { "viewFromClipMatrix", 0, Type::MAT4, Precision::HIGH }, { "clipFromWorldMatrix", 0, Type::MAT4, Precision::HIGH }, { "worldFromClipMatrix", 0, Type::MAT4, Precision::HIGH }, + { "userWorldFromWorldMatrix",0,Type::MAT4, Precision::HIGH }, { "clipTransform", 0, Type::FLOAT4, Precision::HIGH }, { "clipControl", 0, Type::FLOAT2 }, @@ -58,12 +59,8 @@ BufferInterfaceBlock const& UibGenerator::getPerViewUib() noexcept { { "lodBias", 0, Type::FLOAT }, { "refractionLodOffset", 0, Type::FLOAT }, - { "padding1", 0, Type::FLOAT }, - { "padding2", 0, Type::FLOAT }, - { "cameraPosition", 0, Type::FLOAT3, Precision::HIGH }, { "oneOverFarMinusNear", 0, Type::FLOAT, Precision::HIGH }, - { "worldOffset", 0, Type::FLOAT3 }, { "nearOverFarMinusNear", 0, Type::FLOAT, Precision::HIGH }, { "cameraFar", 0, Type::FLOAT }, { "exposure", 0, Type::FLOAT, Precision::HIGH }, // high precision to work around #3602 (qualcom), diff --git a/shaders/src/common_getters.glsl b/shaders/src/common_getters.glsl index 4ee94e5df9..d52ef12154 100644 --- a/shaders/src/common_getters.glsl +++ b/shaders/src/common_getters.glsl @@ -32,6 +32,11 @@ highp mat4 getWorldFromClipMatrix() { return frameUniforms.worldFromClipMatrix; } +/** @public-api */ +highp mat4 getUserWorldFromWorldMatrix() { + return frameUniforms.userWorldFromWorldMatrix; +} + /** @public-api */ float getTime() { return frameUniforms.time; @@ -81,12 +86,12 @@ highp vec4 getResolution() { /** @public-api */ highp vec3 getWorldCameraPosition() { - return frameUniforms.cameraPosition; + return frameUniforms.worldFromViewMatrix[3].xyz; } -/** @public-api */ +/** @public-api, @deprecated use getUserWorldPosition() or getUserWorldFromWorldMatrix() instead */ highp vec3 getWorldOffset() { - return frameUniforms.worldOffset; + return getUserWorldFromWorldMatrix()[3].xyz; } /** @public-api */ diff --git a/shaders/src/getters.fs b/shaders/src/getters.fs index e7fc029568..c7e445d1d5 100644 --- a/shaders/src/getters.fs +++ b/shaders/src/getters.fs @@ -49,6 +49,11 @@ highp vec3 getWorldPosition() { return shading_position; } +/** @public-api */ +highp vec3 getUserWorldPosition() { + return mulMat4x4Float3(getUserWorldFromWorldMatrix(), getWorldPosition()).xyz; +} + /** @public-api */ vec3 getWorldViewVector() { return shading_view; diff --git a/shaders/src/shading_parameters.fs b/shaders/src/shading_parameters.fs index 222f7831d9..3c7d3aa31b 100644 --- a/shaders/src/shading_parameters.fs +++ b/shaders/src/shading_parameters.fs @@ -37,8 +37,8 @@ void computeShadingParams() { // With perspective camera, the view vector is cast from the fragment pos to the eye position, // With ortho camera, however, the view vector is the same for all fragments: highp vec3 sv = isPerspectiveProjection() ? - (frameUniforms.cameraPosition - shading_position) : - frameUniforms.worldFromViewMatrix[2].xyz; // ortho camera backward dir + (frameUniforms.worldFromViewMatrix[3].xyz - shading_position) : + frameUniforms.worldFromViewMatrix[2].xyz; // ortho camera backward dir shading_view = normalize(sv); // we do this so we avoid doing (matrix multiply), but we burn 4 varyings: