From 62476d2f06672922f9b30b2049d925f5d214a646 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20R=C3=B6hling?= Date: Tue, 26 Oct 2021 20:07:39 +0200 Subject: [PATCH 01/18] Fix compatibility with libstdc++ (#4763) The codebase needs only a few additional standard #includes to compile against libstdc++ on Linux; presumably those headers are implicitly included with MSVC's and Clang's standard C++ library, but even if libstdc++ compatibility is not a goal, it is advisable to have them included directly. --- filament/backend/include/backend/Handle.h | 2 ++ libs/imageio/src/HDRDecoder.cpp | 1 + libs/utils/include/utils/FixedCapacityVector.h | 1 + libs/viewer/src/RemoteServer.cpp | 1 + 4 files changed, 5 insertions(+) diff --git a/filament/backend/include/backend/Handle.h b/filament/backend/include/backend/Handle.h index 8dd8ed6f4c..bbea5a1b91 100644 --- a/filament/backend/include/backend/Handle.h +++ b/filament/backend/include/backend/Handle.h @@ -21,6 +21,8 @@ #include #include +#include + namespace filament { namespace backend { diff --git a/libs/imageio/src/HDRDecoder.cpp b/libs/imageio/src/HDRDecoder.cpp index be3527217d..10091348fc 100644 --- a/libs/imageio/src/HDRDecoder.cpp +++ b/libs/imageio/src/HDRDecoder.cpp @@ -22,6 +22,7 @@ #include +#include #include #include #include diff --git a/libs/utils/include/utils/FixedCapacityVector.h b/libs/utils/include/utils/FixedCapacityVector.h index 5a8c33cef9..72efe30c92 100644 --- a/libs/utils/include/utils/FixedCapacityVector.h +++ b/libs/utils/include/utils/FixedCapacityVector.h @@ -21,6 +21,7 @@ #include #include +#include #include #include #include diff --git a/libs/viewer/src/RemoteServer.cpp b/libs/viewer/src/RemoteServer.cpp index d083dd1410..441c730f78 100644 --- a/libs/viewer/src/RemoteServer.cpp +++ b/libs/viewer/src/RemoteServer.cpp @@ -20,6 +20,7 @@ #include +#include #include using namespace utils; From 728ebf50237699183ca3cfb0eb0720da4c61fc94 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Tue, 19 Oct 2021 15:42:45 -0700 Subject: [PATCH 02/18] cleanup shadowing shader code - remove PCF MEDIUM and HIGH quality -- these were never used in practice, and "quality" really meant "blurriness", now we have VSM for doing that with much more control and more efficiently. PCF_HARD is now the default. PCF_LOW is still available at compile time for now. It shouldn't be needed though because VSM can achieve the same. Moved the divide-by-w of light space into the shadow() method -- which known if it should be done or not. --- shaders/src/getters.fs | 38 ++------- shaders/src/shadowing.fs | 165 +++++---------------------------------- 2 files changed, 24 insertions(+), 179 deletions(-) diff --git a/shaders/src/getters.fs b/shaders/src/getters.fs index 957eb1c4ed..19c4a8d392 100644 --- a/shaders/src/getters.fs +++ b/shaders/src/getters.fs @@ -81,19 +81,6 @@ highp vec2 uvToRenderTargetUV(highp vec2 uv) { return uv; } -#if defined(HAS_SHADOWING) && defined(HAS_DIRECTIONAL_LIGHTING) -highp vec3 getLightSpacePosition() { -#if defined(HAS_VSM) - // For VSM, do not project the Z coordinate. It remains as linear Z in light space. - // See the computeVsmLightSpaceMatrix comments in ShadowMap.cpp. - return vec3(vertex_lightSpacePosition.xy * (1.0 / vertex_lightSpacePosition.w), - vertex_lightSpacePosition.z); -#else - return vertex_lightSpacePosition.xyz * (1.0 / vertex_lightSpacePosition.w); -#endif -} -#endif - /** * Returns the normalized [0, 1] viewport coordinates with the origin at the viewport's bottom-left. * Z coordinate is in the [0, 1] range as well. @@ -112,19 +99,11 @@ highp vec3 getNormalizedViewportCoord2() { } #if defined(HAS_SHADOWING) && defined(HAS_DYNAMIC_LIGHTING) -highp vec3 getSpotLightSpacePosition(uint index) { +highp vec4 getSpotLightSpacePosition(uint index) { vec3 dir = shadowUniforms.directionShadowBias[index].xyz; float bias = shadowUniforms.directionShadowBias[index].w; - highp vec4 position = computeLightSpacePosition(vertex_worldPosition, + return computeLightSpacePosition(vertex_worldPosition, vertex_worldNormal, dir, bias, shadowUniforms.spotLightFromWorldMatrix[index]); - -#if defined(HAS_VSM) - // For VSM, do not project the Z coordinate. It remains as linear Z in light space. - // See the computeVsmLightSpaceMatrix comments in ShadowMap.cpp. - return vec3(position.xy * (1.0 / position.w), position.z); -#else - return position.xyz * (1.0 / position.w); -#endif } #endif @@ -146,25 +125,18 @@ uint getShadowCascade() { #if defined(HAS_SHADOWING) && defined(HAS_DIRECTIONAL_LIGHTING) -highp vec3 getCascadeLightSpacePosition(uint cascade) { +highp vec4 getCascadeLightSpacePosition(uint cascade) { // For the first cascade, return the interpolated light space position. // This branch will be coherent (mostly) for neighboring fragments, and it's worth avoiding // the matrix multiply inside computeLightSpacePosition. if (cascade == 0u) { // Note: this branch may cause issues with derivatives - return getLightSpacePosition(); + return vertex_lightSpacePosition; } - highp vec4 pos = computeLightSpacePosition(getWorldPosition(), getWorldNormalVector(), + return computeLightSpacePosition(getWorldPosition(), getWorldNormalVector(), frameUniforms.lightDirection, frameUniforms.shadowBias.y, frameUniforms.lightFromWorldMatrix[cascade]); -#if defined(HAS_VSM) - // For VSM, do not project the Z coordinate. It remains as linear Z in light space. - // See the computeVsmLightSpaceMatrix comments in ShadowMap.cpp. - return vec3(pos.xy * (1.0 / pos.w), pos.z); -#else - return pos.xyz * (1.0 / pos.w); -#endif } #endif diff --git a/shaders/src/shadowing.fs b/shaders/src/shadowing.fs index 5337129427..9c7f3f7bbc 100644 --- a/shaders/src/shadowing.fs +++ b/shaders/src/shadowing.fs @@ -2,31 +2,19 @@ // Shadowing configuration //------------------------------------------------------------------------------ -#define SHADOW_SAMPLING_PCF_HARD 0 -#define SHADOW_SAMPLING_PCF_LOW 1 -#define SHADOW_SAMPLING_PCF_MEDIUM 2 -#define SHADOW_SAMPLING_PCF_HIGH 3 +#define SHADOW_SAMPLING_PCF_HARD 0 +#define SHADOW_SAMPLING_PCF_LOW 1 -#define SHADOW_SAMPLING_ERROR_DISABLED 0 -#define SHADOW_SAMPLING_ERROR_ENABLED 1 +#define SHADOW_SAMPLING_ERROR_DISABLED 0 +#define SHADOW_SAMPLING_ERROR_ENABLED 1 #define SHADOW_RECEIVER_PLANE_DEPTH_BIAS_DISABLED 0 #define SHADOW_RECEIVER_PLANE_DEPTH_BIAS_ENABLED 1 -#define SHADOW_RECEIVER_PLANE_DEPTH_BIAS_MIN_SAMPLING_METHOD SHADOW_SAMPLING_PCF_MEDIUM - -#define SHADOW_SAMPLING_METHOD SHADOW_SAMPLING_PCF_LOW +#define SHADOW_SAMPLING_METHOD SHADOW_SAMPLING_PCF_HARD #define SHADOW_SAMPLING_ERROR SHADOW_SAMPLING_ERROR_DISABLED #define SHADOW_RECEIVER_PLANE_DEPTH_BIAS SHADOW_RECEIVER_PLANE_DEPTH_BIAS_DISABLED -#if SHADOW_SAMPLING_ERROR == SHADOW_SAMPLING_ERROR_ENABLED - #undef SHADOW_RECEIVER_PLANE_DEPTH_BIAS - #define SHADOW_RECEIVER_PLANE_DEPTH_BIAS SHADOW_RECEIVER_PLANE_DEPTH_BIAS_ENABLED -#elif SHADOW_SAMPLING_METHOD < SHADOW_RECEIVER_PLANE_DEPTH_BIAS_MIN_SAMPLING_METHOD - #undef SHADOW_RECEIVER_PLANE_DEPTH_BIAS - #define SHADOW_RECEIVER_PLANE_DEPTH_BIAS SHADOW_RECEIVER_PLANE_DEPTH_BIAS_DISABLED -#endif - //------------------------------------------------------------------------------ // Shadow sampling methods //------------------------------------------------------------------------------ @@ -61,9 +49,7 @@ float samplingBias(float depth, const vec2 rpdb, const highp vec2 texelSize) { float sampleDepth(const mediump sampler2DArrayShadow map, const uint layer, const highp vec2 base, const highp vec2 dudv, float depth, vec2 rpdb) { #if SHADOW_RECEIVER_PLANE_DEPTH_BIAS == SHADOW_RECEIVER_PLANE_DEPTH_BIAS_ENABLED - #if SHADOW_SAMPLING_METHOD >= SHADOW_RECEIVER_PLANE_DEPTH_BIAS_MIN_SAMPLING_METHOD depth += dot(dudv, rpdb); - #endif #endif // depth must be clamped to support floating-point depth formats. This is to avoid comparing a // value from the depth texture (which is never greater than 1.0) with a greater-than-one @@ -77,7 +63,7 @@ float ShadowSample_Hard(const mediump sampler2DArrayShadow map, const uint layer highp vec2 texelSize = vec2(1.0) / size; vec2 rpdb = computeReceiverPlaneDepthBias(position); float depth = samplingBias(position.z, rpdb, texelSize); - return texture(map, vec4(position.xy, layer, saturate(depth))); + return sampleDepth(map,layer, position.xy, vec2(0.0f), depth, rpdb); } #endif @@ -86,6 +72,8 @@ float ShadowSample_PCF_Low(const mediump sampler2DArrayShadow map, const uint la const highp vec2 size, highp vec3 position) { // Castaño, 2013, "Shadow Mapping Summary Part 1" highp vec2 texelSize = vec2(1.0) / size; + vec2 rpdb = computeReceiverPlaneDepthBias(position); + float depth = samplingBias(position.z, rpdb, texelSize); // clamp position to avoid overflows below, which cause some GPUs to abort position.xy = clamp(position.xy, vec2(-1.0), vec2(2.0)); @@ -104,9 +92,6 @@ float ShadowSample_PCF_Low(const mediump sampler2DArrayShadow map, const uint la u *= texelSize.x; v *= texelSize.y; - vec2 rpdb = computeReceiverPlaneDepthBias(position); - - float depth = samplingBias(position.z, rpdb, texelSize); float sum = 0.0; sum += uw.x * vw.x * sampleDepth(map, layer, base, vec2(u.x, v.x), depth, rpdb); @@ -119,118 +104,6 @@ float ShadowSample_PCF_Low(const mediump sampler2DArrayShadow map, const uint la } #endif -#if SHADOW_SAMPLING_METHOD == SHADOW_SAMPLING_PCF_MEDIUM -float ShadowSample_PCF_Medium(const mediump sampler2DArrayShadow map, const uint layer, - const highp vec2 size, highp vec3 position) { - // Castaño, 2013, "Shadow Mapping Summary Part 1" - highp vec2 texelSize = vec2(1.0) / size; - - // clamp position to avoid overflows below, which cause some GPUs to abort - position.xy = clamp(position.xy, vec2(-1.0), vec2(2.0)); - - vec2 offset = vec2(0.5); - highp vec2 uv = (position.xy * size) + offset; - highp vec2 base = (floor(uv) - offset) * texelSize; - highp vec2 st = fract(uv); - - vec3 uw = vec3(4.0 - 3.0 * st.x, 7.0, 1.0 + 3.0 * st.x); - vec3 vw = vec3(4.0 - 3.0 * st.y, 7.0, 1.0 + 3.0 * st.y); - - highp vec3 u = vec3((3.0 - 2.0 * st.x) / uw.x - 2.0, (3.0 + st.x) / uw.y, st.x / uw.z + 2.0); - highp vec3 v = vec3((3.0 - 2.0 * st.y) / vw.x - 2.0, (3.0 + st.y) / vw.y, st.y / vw.z + 2.0); - - u *= texelSize.x; - v *= texelSize.y; - - vec2 rpdb = computeReceiverPlaneDepthBias(position); - - float depth = samplingBias(position.z, rpdb, texelSize); - float sum = 0.0; - - sum += uw.x * vw.x * sampleDepth(map, layer, base, vec2(u.x, v.x), depth, rpdb); - sum += uw.y * vw.x * sampleDepth(map, layer, base, vec2(u.y, v.x), depth, rpdb); - sum += uw.z * vw.x * sampleDepth(map, layer, base, vec2(u.z, v.x), depth, rpdb); - - sum += uw.x * vw.y * sampleDepth(map, layer, base, vec2(u.x, v.y), depth, rpdb); - sum += uw.y * vw.y * sampleDepth(map, layer, base, vec2(u.y, v.y), depth, rpdb); - sum += uw.z * vw.y * sampleDepth(map, layer, base, vec2(u.z, v.y), depth, rpdb); - - sum += uw.x * vw.z * sampleDepth(map, layer, base, vec2(u.x, v.z), depth, rpdb); - sum += uw.y * vw.z * sampleDepth(map, layer, base, vec2(u.y, v.z), depth, rpdb); - sum += uw.z * vw.z * sampleDepth(map, layer, base, vec2(u.z, v.z), depth, rpdb); - - return sum * (1.0 / 144.0); -} -#endif - -#if SHADOW_SAMPLING_METHOD == SHADOW_SAMPLING_PCF_HIGH -float ShadowSample_PCF_High(const mediump sampler2DArrayShadow map, const uint layer, - const highp vec2 size, highp vec3 position) { - // Castaño, 2013, "Shadow Mapping Summary Part 1" - highp vec2 texelSize = vec2(1.0) / size; - - // clamp position to avoid overflows below, which cause some GPUs to abort - position.xy = clamp(position.xy, vec2(-1.0), vec2(2.0)); - - vec2 offset = vec2(0.5); - highp vec2 uv = (position.xy * size) + offset; - highp vec2 base = (floor(uv) - offset) * texelSize; - highp vec2 st = fract(uv); - - vec4 uw = vec4( - 5.0 * st.x - 6.0, - 11.0 * st.x - 28.0, - -(11.0 * st.x + 17.0), - -(5.0 * st.x + 1.0)); - vec4 vw = vec4( - 5.0 * st.y - 6.0, - 11.0 * st.y - 28.0, - -(11.0 * st.y + 17.0), - -(5.0 * st.y + 1.0)); - - vec4 u = vec4( - (4.0 * st.x - 5.0) / uw.x - 3.0, - (4.0 * st.x - 16.0) / uw.y - 1.0, - -(7.0 * st.x + 5.0) / uw.z + 1.0, - -st.x / uw.w + 3.0); - vec4 v = vec4( - (4.0 * st.y - 5.0) / vw.x - 3.0, - (4.0 * st.y - 16.0) / vw.y - 1.0, - -(7.0 * st.y + 5.0) / vw.z + 1.0, - -st.y / vw.w + 3.0); - - u *= texelSize.x; - v *= texelSize.y; - - vec2 rpdb = computeReceiverPlaneDepthBias(position); - - float depth = samplingBias(position.z, rpdb, texelSize); - highp float sum = 0.0; - - sum += uw.x * vw.x * sampleDepth(map, layer, base, vec2(u.x, v.x), depth, rpdb); - sum += uw.y * vw.x * sampleDepth(map, layer, base, vec2(u.y, v.x), depth, rpdb); - sum += uw.z * vw.x * sampleDepth(map, layer, base, vec2(u.z, v.x), depth, rpdb); - sum += uw.w * vw.x * sampleDepth(map, layer, base, vec2(u.w, v.x), depth, rpdb); - - sum += uw.x * vw.y * sampleDepth(map, layer, base, vec2(u.x, v.y), depth, rpdb); - sum += uw.y * vw.y * sampleDepth(map, layer, base, vec2(u.y, v.y), depth, rpdb); - sum += uw.z * vw.y * sampleDepth(map, layer, base, vec2(u.z, v.y), depth, rpdb); - sum += uw.w * vw.y * sampleDepth(map, layer, base, vec2(u.w, v.y), depth, rpdb); - - sum += uw.x * vw.z * sampleDepth(map, layer, base, vec2(u.x, v.z), depth, rpdb); - sum += uw.y * vw.z * sampleDepth(map, layer, base, vec2(u.y, v.z), depth, rpdb); - sum += uw.z * vw.z * sampleDepth(map, layer, base, vec2(u.z, v.z), depth, rpdb); - sum += uw.w * vw.z * sampleDepth(map, layer, base, vec2(u.w, v.z), depth, rpdb); - - sum += uw.x * vw.w * sampleDepth(map, layer, base, vec2(u.x, v.w), depth, rpdb); - sum += uw.y * vw.w * sampleDepth(map, layer, base, vec2(u.y, v.w), depth, rpdb); - sum += uw.z * vw.w * sampleDepth(map, layer, base, vec2(u.z, v.w), depth, rpdb); - sum += uw.w * vw.w * sampleDepth(map, layer, base, vec2(u.w, v.w), depth, rpdb); - - return sum * (1.0 / 2704.0); -} -#endif - //------------------------------------------------------------------------------ // Screen-space Contact Shadows //------------------------------------------------------------------------------ @@ -347,30 +220,30 @@ float chebyshevUpperBound(const highp vec2 moments, const highp float mean, // PCF sampling float shadow(const mediump sampler2DArrayShadow shadowMap, - const uint layer, const highp vec3 shadowPosition) { + const uint layer, const highp vec4 shadowPosition) { + highp vec3 position = shadowPosition.xyz * (1.0 / shadowPosition.w); highp vec2 size = vec2(textureSize(shadowMap, 0)); // note: shadowPosition.z is in the [1, 0] range (reversed Z) #if SHADOW_SAMPLING_METHOD == SHADOW_SAMPLING_PCF_HARD - return ShadowSample_Hard(shadowMap, layer, size, shadowPosition); + return ShadowSample_Hard(shadowMap, layer, size, position); #elif SHADOW_SAMPLING_METHOD == SHADOW_SAMPLING_PCF_LOW - return ShadowSample_PCF_Low(shadowMap, layer, size, shadowPosition); -#elif SHADOW_SAMPLING_METHOD == SHADOW_SAMPLING_PCF_MEDIUM - return ShadowSample_PCF_Medium(shadowMap, layer, size, shadowPosition); -#elif SHADOW_SAMPLING_METHOD == SHADOW_SAMPLING_PCF_HIGH - return ShadowSample_PCF_High(shadowMap, layer, size, shadowPosition); + return ShadowSample_PCF_Low(shadowMap, layer, size, position); #endif } -// VSM sampling +// VSM or DPCF sampling float shadow(const mediump sampler2DArray shadowMap, - const uint layer, const highp vec3 shadowPosition) { + const uint layer, const highp vec4 shadowPosition) { + // note: shadowPosition.z is in linear light space normalized to [0, 1] // see: ShadowMap::computeVsmLightSpaceMatrix() in ShadowMap.cpp // see: computeLightSpacePosition() in common_shadowing.fs + highp vec3 position = vec3(shadowPosition.xy * (1.0 / shadowPosition.w), shadowPosition.z); + // Read the shadow map with all available filtering - highp vec2 moments = texture(shadowMap, vec3(shadowPosition.xy, layer)).xy; - highp float depth = shadowPosition.z; + highp vec2 moments = texture(shadowMap, vec3(position.xy, layer)).xy; + highp float depth = position.z; // EVSM depth warping depth = depth * 2.0 - 1.0; From 7a22f7dfc53de8888a56b15a2f322a658a4c1ca2 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Thu, 21 Oct 2021 12:56:47 -0700 Subject: [PATCH 03/18] move some VSM computations into the vertex shader --- filament/src/ShadowMapManager.cpp | 12 ++-- .../include/private/filament/Variant.h | 55 ++++++++++++------- shaders/src/depth_main.fs | 14 +---- shaders/src/depth_main.vs | 11 +++- shaders/src/getters.fs | 2 +- shaders/src/inputs.fs | 2 +- shaders/src/inputs.vs | 3 +- shaders/src/light_punctual.fs | 2 +- shaders/src/main.vs | 18 +++++- shaders/src/shading_parameters.fs | 2 +- 10 files changed, 75 insertions(+), 46 deletions(-) diff --git a/filament/src/ShadowMapManager.cpp b/filament/src/ShadowMapManager.cpp index 725fa818c2..36c5e2052a 100644 --- a/filament/src/ShadowMapManager.cpp +++ b/filament/src/ShadowMapManager.cpp @@ -360,8 +360,8 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps( FView::cullRenderables(engine.getJobSystem(), renderableData, frustum, VISIBLE_DIR_SHADOW_RENDERABLE_BIT); - // note: normalBias is ignored for VSM - const float normalBias = lcm.getShadowNormalBias(0); + // note: normalBias is set to zero for VSM + const float normalBias = shadowMapInfo.vsm ? 0.0f : lcm.getShadowNormalBias(0); // Set shadowBias, using the first directional cascade. const float texelSizeWorldSpace = map.getTexelSizeWorldSpace(); mShadowMappingUniforms.shadowBias = float3{ 0, normalBias * texelSizeWorldSpace, 0 }; @@ -486,14 +486,14 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateSpotShadowMaps( size_t l = entry.getLightIndex(); const size_t textureDimension = entry.getShadowOptions()->mapSize; - const ShadowMap::ShadowMapInfo layout{ + const ShadowMap::ShadowMapInfo shadowMapInfo{ .zResolution = mTextureZResolution, .atlasDimension = textureSize, .textureDimension = (uint16_t)textureDimension, .shadowDimension = (uint16_t)(textureDimension - 2), .vsm = view.hasVsm() }; - shadowMap.update(lightData, l, viewingCameraInfo, layout, {}); + shadowMap.update(lightData, l, viewingCameraInfo, shadowMapInfo, {}); FLightManager::Instance light = lightData.elementAt(l); if (shadowMap.hasVisibleShadows()) { @@ -509,10 +509,10 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateSpotShadowMaps( shadowInfo[l].index = i; shadowInfo[l].layer = mSpotShadowMaps[i].getLayer(); - // note: normalBias is ignored for VSM + // note: normalBias is set to zero for VSM const float3 dir = lightData.elementAt(l); const float texelSizeWorldSpace = shadowMap.getTexelSizeWorldSpace(); - const float normalBias = lcm.getShadowNormalBias(light); + const float normalBias = shadowMapInfo.vsm ? 0.0f : lcm.getShadowNormalBias(light); s.directionShadowBias[i] = float4{ dir, normalBias * texelSizeWorldSpace }; shadowTechnique |= ShadowTechnique::SHADOW_MAP; diff --git a/libs/filabridge/include/private/filament/Variant.h b/libs/filabridge/include/private/filament/Variant.h index 55255b232a..596acfbba3 100644 --- a/libs/filabridge/include/private/filament/Variant.h +++ b/libs/filabridge/include/private/filament/Variant.h @@ -53,7 +53,7 @@ namespace filament { // // Standard variants: // +-----+-----+-----+-----+-----+-----+-----+-----+ - // | 0 | VSM | FOG | 0 | SKN | SRE | DYN | DIR | 64 (-24) + // | 0 | VSM | FOG | 0 | SKN | SRE | DYN | DIR | 40 (-24) // +-----+-----+-----+-----+-----+-----+-----+-----+ // Vertex shader 0 0 0 X X X X // Fragment shader X X 0 0 X X X @@ -62,14 +62,18 @@ namespace filament { // // Depth variants: // +-----+-----+-----+-----+-----+-----+-----+-----+ - // | 0 | VSM | PCK | 1 | SKN | 0 | 0 | 0 | 8 (-58) + // | 0 | VSM | PCK | 1 | SKN | 0 | 0 | 0 | 6 (-58) // +-----+-----+-----+-----+-----+-----+-----+-----+ // Vertex depth X 0 1 X 0 0 0 // Fragment depth X X 1 0 0 0 0 // Reserved 1 1 1 X 0 0 0 - // Reserved X X 1 X X X 1 - // Reserved X X 1 X X 1 X - // Reserved X X 1 X 1 X X + // Reserved X X 1 X 0 0 1 + // Reserved X X 1 X 0 1 0 + // Reserved X X 1 X 0 1 1 + // Reserved X X 1 X 1 0 0 + // Reserved X X 1 X 1 0 1 + // Reserved X X 1 X 1 1 0 + // Reserved X X 1 X 1 1 1 // // 46 variants used, 82 reserved // @@ -87,14 +91,16 @@ namespace filament { static constexpr uint8_t PICKING = 0x20; // picking (depth) static constexpr uint8_t VSM = 0x40; // variance shadow maps - static constexpr uint8_t DEPTH_MASK = DIRECTIONAL_LIGHTING | - DYNAMIC_LIGHTING | - SHADOW_RECEIVER | - DEPTH; + static constexpr uint8_t STANDARD_VARIANT = 0u; + static constexpr uint8_t STANDARD_MASK = DEPTH; // the depth variant deactivates all variants that make no sense when writing the depth // only -- essentially, all fragment-only variants. static constexpr uint8_t DEPTH_VARIANT = DEPTH; + static constexpr uint8_t DEPTH_MASK = DIRECTIONAL_LIGHTING | + DYNAMIC_LIGHTING | + SHADOW_RECEIVER | + DEPTH; // this mask filters out the lighting variants static constexpr uint8_t UNLIT_MASK = SKINNING_OR_MORPHING | FOG; @@ -118,22 +124,31 @@ namespace filament { inline void setVsm(bool v) noexcept { set(v, VSM); } inline static constexpr bool isValidDepthVariant(uint8_t variantKey) noexcept { - // VSM and PICKING are mutually exclusive for DEPTH variants + // Can't have VSM and PICKING together with DEPTH variants + constexpr uint8_t RESERVED_MASK = VSM | PICKING | DEPTH; + constexpr uint8_t RESERVED_VALUE = VSM | PICKING | DEPTH; return (variantKey & DEPTH_MASK) == DEPTH_VARIANT && - variantKey != 0b1110000u && - variantKey != 0b1111000u; + ((variantKey & RESERVED_MASK) != RESERVED_VALUE); + } + + inline static constexpr bool isValidStandardVariant(uint8_t variantKey) noexcept { + // can't have shadow receiver if we don't have any lighting + constexpr uint8_t RESERVED0_MASK = SHADOW_RECEIVER | DYNAMIC_LIGHTING | DIRECTIONAL_LIGHTING; + constexpr uint8_t RESERVED0_VALUE = SHADOW_RECEIVER; + // can't have VSM without shadow receiver + constexpr uint8_t RESERVED1_MASK = VSM | SHADOW_RECEIVER; + constexpr uint8_t RESERVED1_VALUE = VSM; + return (variantKey & STANDARD_MASK) == STANDARD_VARIANT && + (variantKey & RESERVED0_MASK) != RESERVED0_VALUE && + (variantKey & RESERVED1_MASK) != RESERVED1_VALUE; } static constexpr bool isReserved(uint8_t variantKey) noexcept { // reserved variants that should just be skipped - // 1. If the DEPTH bit is set, then it must be a valid depth variant. Otherwise, the - // variant is reserved. - // 2. If SRE is set, either DYN or DIR must also be set (it makes no sense to have - // shadows without lights). - // 3. If VSM is set, then SRE must be set. - return ((variantKey & DEPTH) && !isValidDepthVariant(variantKey)) || - (variantKey & 0b0010111u) == 0b0000100u || - (variantKey & 0b1010100u) == 0b1000000u; + if (variantKey & DEPTH) { + return !isValidDepthVariant(variantKey); + } + return !isValidStandardVariant(variantKey); } static constexpr uint8_t filterVariantVertex(uint8_t variantKey) noexcept { diff --git a/shaders/src/depth_main.fs b/shaders/src/depth_main.fs index 9c0948c186..5be49c6002 100644 --- a/shaders/src/depth_main.fs +++ b/shaders/src/depth_main.fs @@ -37,18 +37,8 @@ void main() { #endif #if defined(HAS_VSM) - // For VSM, we use the linear light space Z coordinate as the depth metric, which works for both - // directional and spot lights. - // The value is guaranteed to be between [0, -zfar] by construction of viewFromWorldMatrix, - // (see ShadowMap.cpp). - highp float z = (frameUniforms.viewFromWorldMatrix * vec4(vertex_worldPosition, 1.0)).z; - - // rescale the depth between [0, 1] - highp float depth = -z / abs(frameUniforms.cameraFar); - - // We use positive only EVSM which helps a lot with light bleeding. - depth = depth * 2.0 - 1.0; - depth = exp(frameUniforms.vsmExponent * depth); + // interpolated depth is stored in vertex_worldPosition.w (see depth_main.vs / main.vs) + highp float depth = exp(vertex_worldPosition.w); // computes the moments // See GPU Gems 3 diff --git a/shaders/src/depth_main.vs b/shaders/src/depth_main.vs index 2c99f8f558..e9bddec7e5 100644 --- a/shaders/src/depth_main.vs +++ b/shaders/src/depth_main.vs @@ -25,7 +25,16 @@ void main() { #endif #if defined(HAS_VSM) - vertex_worldPosition = material.worldPosition.xyz; + // For VSM, we use the linear-light space Z coordinate as the depth metric, which works for both + // directional and spot lights and can be safely interpolated. + // The value is guaranteed to be between [0, -zfar] by construction of viewFromWorldMatrix, + // (see ShadowMap.cpp). + // Use vertex_worldPosition.w which is otherwise not used to store the interpolated + // light-space depth. + highp float z = (frameUniforms.viewFromWorldMatrix * vec4(material.worldPosition.xyz, 1.0)).z; + highp float depth = -z / abs(frameUniforms.cameraFar); // rescale the depth between [0, 1] + depth = depth * 2.0 - 1.0; + vertex_worldPosition.w = frameUniforms.vsmExponent * depth; #endif // this must happen before we compensate for vulkan below diff --git a/shaders/src/getters.fs b/shaders/src/getters.fs index 19c4a8d392..f8eb9c542d 100644 --- a/shaders/src/getters.fs +++ b/shaders/src/getters.fs @@ -102,7 +102,7 @@ highp vec3 getNormalizedViewportCoord2() { highp vec4 getSpotLightSpacePosition(uint index) { vec3 dir = shadowUniforms.directionShadowBias[index].xyz; float bias = shadowUniforms.directionShadowBias[index].w; - return computeLightSpacePosition(vertex_worldPosition, + return computeLightSpacePosition(vertex_worldPosition.xyz, vertex_worldNormal, dir, bias, shadowUniforms.spotLightFromWorldMatrix[index]); } #endif diff --git a/shaders/src/inputs.fs b/shaders/src/inputs.fs index ed6400c600..614d214ff9 100644 --- a/shaders/src/inputs.fs +++ b/shaders/src/inputs.fs @@ -2,7 +2,7 @@ // Attributes and uniforms //------------------------------------------------------------------------------ -LAYOUT_LOCATION(4) in highp vec3 vertex_worldPosition; +LAYOUT_LOCATION(4) in highp vec4 vertex_worldPosition; #if defined(HAS_ATTRIBUTE_TANGENTS) LAYOUT_LOCATION(5) SHADING_INTERPOLATION in mediump vec3 vertex_worldNormal; diff --git a/shaders/src/inputs.vs b/shaders/src/inputs.vs index 7e7ac92508..f999f351e2 100644 --- a/shaders/src/inputs.vs +++ b/shaders/src/inputs.vs @@ -56,7 +56,8 @@ layout(location = LOCATION_CUSTOM6) in vec4 mesh_custom6; layout(location = LOCATION_CUSTOM7) in vec4 mesh_custom7; #endif -LAYOUT_LOCATION(4) out highp vec3 vertex_worldPosition; +LAYOUT_LOCATION(4) out highp vec4 vertex_worldPosition; + #if defined(HAS_ATTRIBUTE_TANGENTS) LAYOUT_LOCATION(5) SHADING_INTERPOLATION out mediump vec3 vertex_worldNormal; #if defined(MATERIAL_NEEDS_TBN) diff --git a/shaders/src/light_punctual.fs b/shaders/src/light_punctual.fs index 8fa71898d6..8a53227c12 100644 --- a/shaders/src/light_punctual.fs +++ b/shaders/src/light_punctual.fs @@ -146,7 +146,7 @@ Light getLight(const uint index) { highp uint channels = floatBitsToUint(data[2][3]); // poition-to-light vector - highp vec3 worldPosition = vertex_worldPosition; + highp vec3 worldPosition = vertex_worldPosition.xyz; highp vec3 posToLight = positionFalloff.xyz - worldPosition; // and populate the Light structure diff --git a/shaders/src/main.vs b/shaders/src/main.vs index 7da50c1d18..fa4d920e97 100644 --- a/shaders/src/main.vs +++ b/shaders/src/main.vs @@ -82,13 +82,14 @@ void main() { #endif // The world position can be changed by the user in materialVertex() - vertex_worldPosition = material.worldPosition.xyz; + vertex_worldPosition.xyz = material.worldPosition.xyz; + #ifdef HAS_ATTRIBUTE_TANGENTS vertex_worldNormal = material.worldNormal; #endif #if defined(HAS_SHADOWING) && defined(HAS_DIRECTIONAL_LIGHTING) - vertex_lightSpacePosition = computeLightSpacePosition(vertex_worldPosition, vertex_worldNormal, + vertex_lightSpacePosition = computeLightSpacePosition(vertex_worldPosition.xyz, vertex_worldNormal, frameUniforms.lightDirection, frameUniforms.shadowBias.y, getLightFromWorldMatrix()); #endif @@ -108,6 +109,19 @@ void main() { gl_Position.z = gl_Position.z * -0.5 + 0.5; #endif +#if defined(HAS_VSM) + // For VSM, we use the linear-light space Z coordinate as the depth metric, which works for both + // directional and spot lights and can be safely interpolated. + // The value is guaranteed to be between [0, -zfar] by construction of viewFromWorldMatrix, + // (see ShadowMap.cpp). + // Use vertex_worldPosition.w which is otherwise not used to store the interpolated + // light-space depth. + highp float z = (frameUniforms.viewFromWorldMatrix * vec4(material.worldPosition.xyz, 1.0)).z; + highp float depth = -z / abs(frameUniforms.cameraFar); // rescale the depth between [0, 1] + depth = depth * 2.0 - 1.0; + vertex_worldPosition.w = frameUniforms.vsmExponent * depth; +#endif + // this must happen before we compensate for vulkan below vertex_position = gl_Position; diff --git a/shaders/src/shading_parameters.fs b/shaders/src/shading_parameters.fs index 96457915aa..34fc249683 100644 --- a/shaders/src/shading_parameters.fs +++ b/shaders/src/shading_parameters.fs @@ -32,7 +32,7 @@ void computeShadingParams() { #endif #endif - shading_position = vertex_worldPosition; + shading_position = vertex_worldPosition.xyz; shading_view = normalize(frameUniforms.cameraPosition - shading_position); // we do this so we avoid doing (matrix multiply), but we burn 4 varyings: From 4031c6f0d3d5b64d1c7720ee5759e165d1715883 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20R=C3=B6hling?= Date: Tue, 26 Oct 2021 20:31:35 +0200 Subject: [PATCH 04/18] Simplify devendoring (#4765) * Perform sanity check when combining static libraries This is a small modification that simplifies Debian packaging when one or more vendored dependencies are replaced by system shared libraries and no longer need to be combined with a Filament library. * Verify vendoring before including license texts This is another small modification to simplify Debian packaging when one or more vendored dependencies are removed and replaced by system libraries, so they no longer need to be considered when compiling the license texts. --- CMakeLists.txt | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fe21ebc543..459786bbae 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -502,11 +502,15 @@ function(list_licenses OUTPUT MODULES) foreach(module ${_MODULES}) set(license_path "../../third_party/${module}/LICENSE") get_filename_component(fullname "${license_path}" ABSOLUTE) - string(APPEND CONTENT "${STR_OPENER}License and copyrights for ${module}:\n${STR_CLOSER},\n") - file(READ ${license_path} license_long) - string(REPLACE "\n" "${STR_CLOSER},\n${STR_OPENER}" license ${license_long}) - string(APPEND CONTENT ${STR_OPENER}${license}\n${STR_CLOSER},) - string(APPEND CONTENT "\n\n") + if(EXISTS ${license_path}) + string(APPEND CONTENT "${STR_OPENER}License and copyrights for ${module}:\n${STR_CLOSER},\n") + file(READ ${license_path} license_long) + string(REPLACE "\n" "${STR_CLOSER},\n${STR_OPENER}" license ${license_long}) + string(APPEND CONTENT ${STR_OPENER}${license}\n${STR_CLOSER},) + string(APPEND CONTENT "\n\n") + else() + message(AUTHOR_WARNING "${license_path} not found. You can ignore this warning if you have devendored ${module}.") + endif() endforeach() configure_file(${FILAMENT}/build/licenses.inc.in ${OUTPUT}) endfunction(list_licenses) @@ -522,7 +526,12 @@ function(combine_static_libs TARGET OUTPUT DEPS) # Loop through the dependent libraries and query their location on disk. set(DEPS_FILES ) foreach(DEPENDENCY ${DEPS}) - list(APPEND DEPS_FILES "$") + if(TARGET ${DEPENDENCY}) + get_property(dep_type TARGET ${DEPENDENCY} PROPERTY TYPE) + if(dep_type STREQUAL "STATIC_LIBRARY") + list(APPEND DEPS_FILES "$") + endif() + endif() endforeach() add_custom_command( From 69b48eaadbb32475098b471f7c56d89e544ffe66 Mon Sep 17 00:00:00 2001 From: Romain Guy Date: Tue, 26 Oct 2021 12:05:49 -0700 Subject: [PATCH 05/18] Fix erroenous warning --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 459786bbae..de7c1734f8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -502,7 +502,7 @@ function(list_licenses OUTPUT MODULES) foreach(module ${_MODULES}) set(license_path "../../third_party/${module}/LICENSE") get_filename_component(fullname "${license_path}" ABSOLUTE) - if(EXISTS ${license_path}) + if(EXISTS ${fullname}) string(APPEND CONTENT "${STR_OPENER}License and copyrights for ${module}:\n${STR_CLOSER},\n") file(READ ${license_path} license_long) string(REPLACE "\n" "${STR_CLOSER},\n${STR_OPENER}" license ${license_long}) From 095179eeb32176eb0a89673d73c52d1ba42b43ec Mon Sep 17 00:00:00 2001 From: Romain Guy Date: Tue, 26 Oct 2021 12:47:35 -0700 Subject: [PATCH 06/18] Enabled Gradle configuration caching and rename properties (#4769) * Fix configurationg caching problems * Enable Gradle configuration caching * Update BUILDING.md * Update RELEASE_NOTES.md Co-authored-by: Mathias Agopian Co-authored-by: Benjamin Doherty --- BUILDING.md | 6 +- RELEASE_NOTES.md | 7 ++ android/Windows.md | 10 +-- android/build.gradle | 27 ++++---- .../src/main/groovy/FilamentPlugin.groovy | 66 ++++++++++++++----- android/gradle.properties | 2 + .../filament/pagecurl/MainActivity.java | 4 +- build.sh | 26 ++++---- 8 files changed, 95 insertions(+), 53 deletions(-) diff --git a/BUILDING.md b/BUILDING.md index 1a29b02410..0326df7026 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -291,11 +291,11 @@ Alternatively you can build the AAR from the command line by executing the follo `android/` directory: ``` -$ ./gradlew -Pfilament_dist_dir=../../out/android-release/filament assembleRelease +$ ./gradlew -Pcom.google.android.filament.dist-dir=../../out/android-release/filament assembleRelease ``` -The `-Pfilament_dist_dir` can be used to specify a different installation directory (it must match -the CMake install prefix used in the previous steps). +The `-Pcom.google.android.filament.dist-dir` can be used to specify a different installation +directory (it must match the CMake install prefix used in the previous steps). #### Using Filament's AAR diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index d05d6bc22a..5675b3c38c 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -5,6 +5,13 @@ A new header is inserted each time a *tag* is created. ## v1.12.12 (currently main branch) +- Android: Gradle configuration caching is now enabled +- Android: Filament's Gradle properties have all been renamed to `com.google.android.filament.xxx` + where `xxx` is the property name. See `android/build.gradle` for a complete list [⚠️] +- Android: The Gradle property `filament_tools_dir` (now called + `com.google.android.filament.tools-dir`) does not have a default value anymore. Please specify + one in your `gradle.properties` if you reuse the Gradle plugin in your projects [⚠️] + ## v1.12.11 ## v1.12.10 diff --git a/android/Windows.md b/android/Windows.md index 8bf142c0ef..0e80198226 100644 --- a/android/Windows.md +++ b/android/Windows.md @@ -122,20 +122,20 @@ The Gradle project used to generate the AAR is located at `\android`. ``` cd android -gradlew -Pfilament_dist_dir=..\out\android-release\filament assembleRelease +gradlew -Pcom.google.android.filament.dist-dir=..\out\android-release\filament assembleRelease copy filament-android\build\outputs\aar\filament-android-release.aar ..\..\out\ ``` -If you're only interested in building for a single ABI, you'll need to pass a `filament_abis` parameter: +If you're only interested in building for a single ABI, you'll need to pass a `com.google.android.filament.abis` parameter: ``` -gradlew -Pfilament_dist_dir=..\out\android-release\filament assembleRelease -Pfilament_abis=x86 +gradlew -Pcom.google.android.filament.dist-dir=..\out\android-release\filament assembleRelease -Pcom.google.android.filament.abis=x86 ``` -If you're only interested in building SDK, you may skip samples build by passing a `filament_skip_samples` flag: +If you're only interested in building SDK, you may skip samples build by passing a `com.google.android.filament.skip-samples` flag: ``` -gradlew -Pfilament_dist_dir=..\out\android-release\filament assembleRelease -Pfilament_skip_samples +gradlew -Pcom.google.android.filament.dist-dir=..\out\android-release\filament assembleRelease -Pfilament_skip_samples ``` diff --git a/android/build.gradle b/android/build.gradle index 71008d4a9a..0ea3922804 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,26 +1,28 @@ // This script accepts the following parameters: // -// filament_dist_dir +// com.google.android.filament.dist-dir // Path to the Filament distribution/install directory for Android // (produced by make/ninja install). This directory must contain lib/arm64-v8a/ etc. // -// filament_tools_dir +// com.google.android.filament.tools-dir // Path to the Filament distribution/install directory for desktop. // This directory must contain bin/matc. // -// filament_exclude_vulkan +// com.google.android.filament.exclude-vulkan // When set, support for Vulkan will be excluded. // -// filament_skip_samples +// com.google.android.filament.skip-samples // Exclude samples from the project. Useful to speed up compilation. // -// filament_abis +// com.google.android.filament.abis // List of supported ABIs to build as a comma separated list. Available options are: // arm64-v8a, armeabi-v7a, x86_64, x86, all // Defaults to all. // // Example: -// ./gradlew -Pfilament_dist_dir=../dist-android-release assembleRelease -Pfilament_abis=x86 +// ./gradlew -Pcom.google.android.filament.dist-dir=../dist-android-release \ +// -Pcom.google.android.filament.abis=x86 \ +// assembleRelease // Publishing to Maven Central: // - Build and upload artifacts with ./gradlew publish @@ -41,19 +43,19 @@ buildscript { def filamentPath = file("../out/android-release/filament").absolutePath - if (project.hasProperty("filament_dist_dir")) { - filamentPath = file(project.property("filament_dist_dir")).absolutePath + if (project.hasProperty("com.google.android.filament.dist-dir")) { + filamentPath = file(project.property("com.google.android.filament.dist-dir")).absolutePath } // Our CMake scripts require a forward-slash path for the FILAMENT_DIST_DIR // variable, so here we convert the native path to a forward-slash path. filamentPath = filamentPath.replace(File.separator, '/') // Warning: changing this property does not work well with incremental builds. - def excludeVulkan = project.hasProperty("filament_exclude_vulkan") + def excludeVulkan = project.hasProperty("com.google.android.filament.exclude-vulkan") def abis = ["arm64-v8a", "armeabi-v7a", "x86_64", "x86"] - if (project.hasProperty("filament_abis")) { - def newAbis = project.property("filament_abis").split(',') + if (project.hasProperty("com.google.android.filament.abis")) { + def newAbis = project.property("com.google.android.filament.abis").split(',') if (!newAbis.contains("all")) { abis = newAbis } @@ -187,7 +189,8 @@ subprojects { gradle.taskGraph.whenReady { gradle.taskGraph.allTasks.each { it.onlyIf { - !it.project.ext.has('isSample') || !project.hasProperty('filament_skip_samples') + !it.project.ext.has('isSample') || + !project.hasProperty('com.google.android.filament.skip-samples') } } } diff --git a/android/buildSrc/src/main/groovy/FilamentPlugin.groovy b/android/buildSrc/src/main/groovy/FilamentPlugin.groovy index cad4b90d19..e32fcc249e 100644 --- a/android/buildSrc/src/main/groovy/FilamentPlugin.groovy +++ b/android/buildSrc/src/main/groovy/FilamentPlugin.groovy @@ -1,20 +1,22 @@ // This plugin accepts the following parameters: // -// filament_tools_dir +// com.google.android.filament.tools-dir // Path to the Filament distribution/install directory for desktop. // This directory must contain bin/matc. // -// filament_exclude_vulkan +// com.google.android.filament.exclude-vulkan // When set, support for Vulkan will be excluded. // // Example: -// ./gradlew -Pfilament_tools_dir=../../dist-release assembleDebug +// ./gradlew -Pcom.google.android.filament.tools-dir=../../dist-release assembleDebug import org.gradle.api.DefaultTask import org.gradle.api.GradleException import org.gradle.api.Plugin import org.gradle.api.Project +import org.gradle.api.provider.ProviderFactory import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.FileSystemOperations import org.gradle.api.file.FileType import org.gradle.api.file.RegularFileProperty import org.gradle.api.logging.LogLevel @@ -27,14 +29,18 @@ import org.gradle.api.tasks.Optional import org.gradle.api.tasks.OutputDirectory import org.gradle.api.tasks.TaskAction import org.gradle.api.tasks.incremental.InputFileDetails +import org.gradle.api.model.ObjectFactory import org.gradle.internal.os.OperatingSystem +import org.gradle.process.ExecOperations import org.gradle.work.ChangeType import org.gradle.work.Incremental import org.gradle.work.InputChanges import java.nio.file.Paths -class TaskWithBinary extends DefaultTask { +import javax.inject.Inject + +abstract class TaskWithBinary extends DefaultTask { private final String binaryName private Property binaryPath = null @@ -42,15 +48,23 @@ class TaskWithBinary extends DefaultTask { binaryName = name } + @Inject abstract ObjectFactory getObjects() + @Inject abstract ProviderFactory getProviders() + @Input Property getBinary() { if (binaryPath == null) { def tool = ["/bin/${binaryName}.exe", "/bin/${binaryName}"] def fullPath = tool.collect { path -> - Paths.get(project.ext.filamentToolsPath.absolutePath, path).toFile() + def filamentToolsPath = providers + .gradleProperty("com.google.android.filament.tools-dir") + .forUseAtConfigurationTime().get() + def directory = objects.fileProperty() + .fileValue(new File(filamentToolsPath)).getAsFile().get() + Paths.get(directory.absolutePath, path).toFile() } - binaryPath = project.objects.property(String.class) + binaryPath = objects.property(String.class) binaryPath.set( (OperatingSystem.current().isWindows() ? fullPath[0] : fullPath[1]).toString()) } @@ -84,6 +98,11 @@ abstract class MaterialCompiler extends TaskWithBinary { @OutputDirectory abstract DirectoryProperty getOutputDir() + @Inject abstract FileSystemOperations getFs() + @Inject abstract ExecOperations getExec() + @Inject abstract ObjectFactory getObjects() + @Inject abstract ProviderFactory getProviders() + MaterialCompiler() { super("matc") } @@ -91,7 +110,9 @@ abstract class MaterialCompiler extends TaskWithBinary { @TaskAction void execute(InputChanges inputs) { if (!inputs.incremental) { - project.delete(project.fileTree(outputDir.asFile.get()).matching { include '*.filamat' }) + fs.delete({ + delete(objects.fileTree().from(outputDir).matching { include '*.filamat' }) + }) } inputs.getFileChanges(inputDir).each { InputFileDetails change -> @@ -115,12 +136,15 @@ abstract class MaterialCompiler extends TaskWithBinary { } def matcArgs = [] - if (!project.hasProperty("filament_exclude_vulkan")) { + def exclude_vulkan = providers + .gradleProperty("com.google.android.filament.exclude-vulkan") + .forUseAtConfigurationTime().present + if (!exclude_vulkan) { matcArgs += ['-a', 'vulkan'] } matcArgs += ['-a', 'opengl', '-p', 'mobile', '-o', getOutputFile(file), file] - project.exec { + exec.exec { standardOutput out errorOutput err executable "${binary.get()}" @@ -149,6 +173,10 @@ abstract class IblGenerator extends TaskWithBinary { @OutputDirectory abstract DirectoryProperty getOutputDir() + @Inject abstract FileSystemOperations getFs() + @Inject abstract ExecOperations getExec() + @Inject abstract ObjectFactory getObjects() + IblGenerator() { super("cmgen") } @@ -156,7 +184,9 @@ abstract class IblGenerator extends TaskWithBinary { @TaskAction void execute(InputChanges inputs) { if (!inputs.incremental) { - project.delete(project.fileTree(outputDir.asFile.get()).matching { include '*' }) + fs.delete({ + delete(objects.fileTree().from(outputDir).matching { include '*' }) + }) } inputs.getFileChanges(inputFile).each { InputFileDetails change -> @@ -188,7 +218,7 @@ abstract class IblGenerator extends TaskWithBinary { } commandArgs = commandArgs + " " + file - project.exec { + exec.exec { standardOutput out errorOutput err executable "${binary.get()}" @@ -213,6 +243,9 @@ abstract class MeshCompiler extends TaskWithBinary { @OutputDirectory abstract DirectoryProperty getOutputDir() + @Inject abstract FileSystemOperations getFs() + @Inject abstract ExecOperations getExec() + MeshCompiler() { super("filamesh") } @@ -220,7 +253,9 @@ abstract class MeshCompiler extends TaskWithBinary { @TaskAction void execute(InputChanges inputs) { if (!inputs.incremental) { - project.delete(project.fileTree(outputDir.asFile.get()).matching { include '*.filamesh' }) + fs.delete({ + delete(objects.fileTree().from(outputDir).matching { include '*.filamesh' }) + }) } inputs.getFileChanges(inputFile).each { InputFileDetails change -> @@ -243,7 +278,7 @@ abstract class MeshCompiler extends TaskWithBinary { " Ensure Filament has been built/installed before building this app.") } - project.exec { + exec.exec { standardOutput out errorOutput err executable "${binary.get()}" @@ -280,11 +315,6 @@ class FilamentToolsPlugin implements Plugin { extension.meshInputFile = project.objects.fileProperty() extension.meshOutputDir = project.objects.directoryProperty() - project.ext.filamentToolsPath = project.file("../../../out/release/filament") - if (project.hasProperty("filament_tools_dir")) { - project.ext.filamentToolsPath = project.file(project.property("filament_tools_dir")) - } - project.tasks.register("filamentCompileMaterials", MaterialCompiler) { enabled = extension.materialInputDir.isPresent() && diff --git a/android/gradle.properties b/android/gradle.properties index 28712c5450..349cd8c460 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -20,3 +20,5 @@ org.gradle.jvmargs=-Xmx1536m android.useAndroidX=true org.gradle.unsafe.configuration-cache=true + +com.google.android.filament.tools-dir=../../../out/release/filament diff --git a/android/samples/sample-page-curl/src/main/java/com/google/android/filament/pagecurl/MainActivity.java b/android/samples/sample-page-curl/src/main/java/com/google/android/filament/pagecurl/MainActivity.java index 5373470b91..be13ed91c4 100644 --- a/android/samples/sample-page-curl/src/main/java/com/google/android/filament/pagecurl/MainActivity.java +++ b/android/samples/sample-page-curl/src/main/java/com/google/android/filament/pagecurl/MainActivity.java @@ -70,11 +70,11 @@ public class MainActivity extends Activity private Page mPage; private PageMaterials mPageMaterials; private Scene mScene; - private Texture[] mTextures = new Texture[2]; + private final Texture[] mTextures = new Texture[2]; private @Entity int mLight; private IndirectLight mIndirectLight; - private float[] mTouchDownPoint = new float[2]; + private final float[] mTouchDownPoint = new float[2]; private float mTouchDownValue = 0; private float mPageAnimationRadians = 0; private float mPageAnimationValue = 0; diff --git a/build.sh b/build.sh index 7aeb83cd07..45b3cf1ae9 100755 --- a/build.sh +++ b/build.sh @@ -453,23 +453,23 @@ function build_android { if [[ "${ISSUE_DEBUG_BUILD}" == "true" ]]; then ./gradlew \ - -Pfilament_dist_dir=../out/android-debug/filament \ - -Pfilament_abis=${ABI_GRADLE_OPTION} \ + -Pcom.google.android.filament.dist-dir=../out/android-debug/filament \ + -Pcom.google.android.filament.abis=${ABI_GRADLE_OPTION} \ ${VULKAN_ANDROID_GRADLE_OPTION} \ :filament-android:assembleDebug \ :gltfio-android:assembleDebug \ :filament-utils-android:assembleDebug ./gradlew \ - -Pfilament_dist_dir=../out/android-debug/filament \ - -Pfilament_abis=${ABI_GRADLE_OPTION} \ + -Pcom.google.android.filament.dist-dir=../out/android-debug/filament \ + -Pcom.google.android.filament.abis=${ABI_GRADLE_OPTION} \ :filamat-android:assembleDebug if [[ "${BUILD_ANDROID_SAMPLES}" == "true" ]]; then for sample in ${ANDROID_SAMPLES}; do ./gradlew \ - -Pfilament_dist_dir=../out/android-debug/filament \ - -Pfilament_abis=${ABI_GRADLE_OPTION} \ + -Pcom.google.android.filament.dist-dir=../out/android-debug/filament \ + -Pcom.google.android.filament.abis=${ABI_GRADLE_OPTION} \ :samples:${sample}:assembleDebug done fi @@ -502,23 +502,23 @@ function build_android { if [[ "${ISSUE_RELEASE_BUILD}" == "true" ]]; then ./gradlew \ - -Pfilament_dist_dir=../out/android-release/filament \ - -Pfilament_abis=${ABI_GRADLE_OPTION} \ + -Pcom.google.android.filament.dist-dir=../out/android-release/filament \ + -Pcom.google.android.filament.abis=${ABI_GRADLE_OPTION} \ ${VULKAN_ANDROID_GRADLE_OPTION} \ :filament-android:assembleRelease \ :gltfio-android:assembleRelease \ :filament-utils-android:assembleRelease ./gradlew \ - -Pfilament_dist_dir=../out/android-release/filament \ - -Pfilament_abis=${ABI_GRADLE_OPTION} \ + -Pcom.google.android.filament.dist-dir=../out/android-release/filament \ + -Pcom.google.android.filament.abis=${ABI_GRADLE_OPTION} \ :filamat-android:assembleRelease if [[ "${BUILD_ANDROID_SAMPLES}" == "true" ]]; then for sample in ${ANDROID_SAMPLES}; do ./gradlew \ - -Pfilament_dist_dir=../out/android-release/filament \ - -Pfilament_abis=${ABI_GRADLE_OPTION} \ + -Pcom.google.android.filament.dist-dir=../out/android-release/filament \ + -Pcom.google.android.filament.abis=${ABI_GRADLE_OPTION} \ :samples:${sample}:assembleRelease done fi @@ -831,7 +831,7 @@ while getopts ":hacCfijmp:q:uvslwtdk:" opt; do ;; v) VULKAN_ANDROID_OPTION="-DFILAMENT_SUPPORTS_VULKAN=OFF" - VULKAN_ANDROID_GRADLE_OPTION="-Pfilament_exclude_vulkan" + VULKAN_ANDROID_GRADLE_OPTION="-Pcom.google.android.filament.exclude-vulkan" echo "Disabling support for Vulkan in the core Filament library." echo "Consider using -c after changing this option to clear the Gradle cache." ;; From 1182d30fb909fee4af1d9ebab14c15dd434f3655 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Tue, 26 Oct 2021 15:16:06 -0700 Subject: [PATCH 07/18] MSAA VSMs seem to now work on Qualcomm h/w The bug that prevented them to work has been worked around, but we don't know when or how. The original bug still exists as demonstrated by our standalone test apk. For now, we reenable MSAA VSM on Adreno. --- filament/backend/src/opengl/OpenGLContext.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/filament/backend/src/opengl/OpenGLContext.cpp b/filament/backend/src/opengl/OpenGLContext.cpp index ad9ac069f0..3fa3d2fb50 100644 --- a/filament/backend/src/opengl/OpenGLContext.cpp +++ b/filament/backend/src/opengl/OpenGLContext.cpp @@ -87,8 +87,14 @@ OpenGLContext::OpenGLContext() noexcept { } // On Adreno (As of 3/20) timer query seem to return the CPU time, not the GPU time. bugs.dont_use_timer_query = true; + // Blits to texture arrays are failing - bugs.disable_sidecar_blit_into_texture_array = true; + // This bug doesn't happen anymore, but we don't know why. The standalone sample + // app that was written to show this problem still does. We have tested this on + // several V@0490.0 on several devices and the problem appears to have gone away. + // The working hypthesis is that some other state affects this behavior. + bugs.disable_sidecar_blit_into_texture_array = false; + // early exit condition is flattened in EASU code bugs.split_easu = true; bugs.invalidate_end_only_if_invalidate_start = true; From 50f33f7196f32da873f42b0988cc8dac3de39992 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Wed, 27 Oct 2021 00:15:35 -0700 Subject: [PATCH 08/18] fix some warnings and typos --- filament/src/ShadowMap.cpp | 58 ++++++++++++++++--------------- filament/src/ShadowMap.h | 2 +- filament/src/ShadowMapManager.cpp | 25 +++++++------ 3 files changed, 43 insertions(+), 42 deletions(-) diff --git a/filament/src/ShadowMap.cpp b/filament/src/ShadowMap.cpp index feea841bed..d630e8d30f 100644 --- a/filament/src/ShadowMap.cpp +++ b/filament/src/ShadowMap.cpp @@ -52,7 +52,7 @@ ShadowMap::ShadowMap(FEngine& engine) noexcept : FDebugRegistry& debugRegistry = engine.getDebugRegistry(); debugRegistry.registerProperty("d.shadowmap.focus_shadowcasters", &engine.debug.shadowmap.focus_shadowcasters); debugRegistry.registerProperty("d.shadowmap.far_uses_shadowcasters", &engine.debug.shadowmap.far_uses_shadowcasters); - if (ENABLE_LISPSM) { + if constexpr (ENABLE_LISPSM) { debugRegistry.registerProperty("d.shadowmap.lispsm", &engine.debug.shadowmap.lispsm); debugRegistry.registerProperty("d.shadowmap.dzn", &engine.debug.shadowmap.dzn); debugRegistry.registerProperty("d.shadowmap.dzf", &engine.debug.shadowmap.dzf); @@ -148,7 +148,7 @@ void ShadowMap::update(const FScene::LightSoa& lightData, size_t index, cullingProjection[2].z = (f + n) / (n - f); cullingProjection[3].z = (2 * f * n) / (n - f); } else { - // ortho projection + // orthographic projection cullingProjection[2].z = 2.0f / (n - f); cullingProjection[3].z = (f + n) / (n - f); } @@ -193,16 +193,17 @@ void ShadowMap::update(const FScene::LightSoa& lightData, size_t index, } void ShadowMap::computeShadowCameraDirectional( - float3 const& dir, ShadowCameraInfo const& camera, + float3 const& dir, + ShadowCameraInfo const& camera, FLightManager::ShadowParams const& params, - SceneInfo cascadeParams) noexcept { + SceneInfo const& cascadeParams) noexcept { /* * Compute the light's model matrix */ // We compute the directional light's model matrix using the origin's as the light position. - // The choice of the light's origin initially doesn't mater for a directional light. + // The choice of the light's origin initially doesn't matter for a directional light. // This will be adjusted later because of how we compute the depth metric for VSM. const mat4f MvAtOrigin = getLightViewMatrix({}, dir); @@ -220,10 +221,10 @@ void ShadowMap::computeShadowCameraDirectional( cascadeParams.csNearFar); // we use aligned_storage<> here to avoid the default initialization of std::array<> - std::aligned_storage::type localStorage; + std::aligned_storage::type localStorage; // NOLINT(cppcoreguidelines-pro-type-member-init) FrustumBoxIntersection& wsClippedShadowReceiverVolume{ reinterpret_cast(localStorage) }; - // compute the intersection of the shadow receivers volume with the view volume + // compute the intersection of the shadow receivers' volume with the view volume // in world space. This returns a set of points on the convex-hull of the intersection. size_t vertexCount = intersectFrustumWithBox(wsClippedShadowReceiverVolume, wsViewFrustumVertices, wsShadowReceiversVolume); @@ -243,16 +244,16 @@ void ShadowMap::computeShadowCameraDirectional( */ Aabb lsLightFrustumBounds; - if (!USE_DEPTH_CLAMP) { + if constexpr (!USE_DEPTH_CLAMP) { // near plane from shadow caster volume lsLightFrustumBounds.max.z = cascadeParams.lsNearFar[0]; } for (size_t i = 0; i < vertexCount; ++i) { - // far: figure out farthest shadow receivers + // far: figure out the farthest shadow receivers float3 v = mat4f::project(MvAtOrigin, wsClippedShadowReceiverVolume[i]); lsLightFrustumBounds.min.z = std::min(lsLightFrustumBounds.min.z, v.z); - if (USE_DEPTH_CLAMP) { - // further tighten to the shadow receiver volume + if constexpr (USE_DEPTH_CLAMP) { + // tighten the shadow receiver volume further lsLightFrustumBounds.max.z = std::max(lsLightFrustumBounds.max.z, v.z); } } @@ -279,7 +280,7 @@ void ShadowMap::computeShadowCameraDirectional( float4 viewVolumeBoundingSphere = {}; if (params.options.stable) { // In stable mode, the light frustum size must be fixed, so we can choose either the - // whole view frustum, or the whole scene bounding volume. We simply pick whichever is + // whole view frustum, or the whole scene bounding volume. We simply pick whichever // is smaller. // in stable mode we simply take the shadow receivers volume @@ -411,7 +412,7 @@ void ShadowMap::computeShadowCameraDirectional( const mat4f S = F * WLMpMv; // Computes St the transform to use in the shader to access the shadow map texture - // i.e. it transform a world-space vertex to a texture coordinate in the shadow-map + // i.e. it transforms a world-space vertex to a texture coordinate in the shadowmap const mat4 MbMt = getTextureCoordsMapping(); const mat4f St = mat4f(MbMt * S); @@ -451,15 +452,16 @@ void ShadowMap::computeShadowCameraDirectional( } } -void ShadowMap::computeShadowCameraSpot(math::float3 const& position, math::float3 const& dir, - float outerConeAngle, float radius, ShadowCameraInfo const& camera, +void ShadowMap::computeShadowCameraSpot( + math::float3 const& position, math::float3 const& dir, float outerConeAngle, float radius, + ShadowCameraInfo const& camera, FLightManager::ShadowParams const& params) noexcept { // TODO: correctly compute if this spot light has any visible shadows. mHasVisibleShadows = true; /* - * Compute the light models matrix. + * Compute the light model matrix. */ // Choose a reasonable value for the near plane. @@ -514,7 +516,7 @@ mat4f ShadowMap::applyLISPSM(math::mat4f& Wp, const float LoV = dot(camera.getForwardVector(), dir); const float sinLV = std::sqrt(std::max(0.0f, 1.0f - LoV * LoV)); - // Virtual near plane -- the default is 1m, can be changed by the user. + // Virtual near plane -- the default is 1 m, can be changed by the user. // The virtual near plane prevents too much resolution to be wasted in the area near the eye // where shadows might not be visible (e.g. a character standing won't see shadows at her feet). const float dzn = std::max(0.0f, params.options.shadowNearHint - camera.zn); @@ -528,8 +530,8 @@ mat4f ShadowMap::applyLISPSM(math::mat4f& Wp, // compute n and f, the near and far planes coordinates of Wp (warp space). // It's found by looking down the Y axis in light space (i.e. -Z axis of Wp, // i.e. the axis orthogonal to the light direction) and taking the min/max - // of the shadow receivers volume. - // Note: znear/zfar encoded in Mp has no influence here (b/c we're interested only by the y axis) + // of the shadow receivers' volume. + // Note: znear/zfar encoded in Mp has no influence here (b/c we're interested only by the y-axis) const float2 nf = computeNearFarOfWarpSpace(LMpMv, wsShadowReceiversVolume.data(), vertexCount); const float n = nf[0]; // near plane coordinate of Mp (light space) const float f = nf[1]; // far plane coordinate of Mp (light space) @@ -563,10 +565,10 @@ mat4f ShadowMap::applyLISPSM(math::mat4f& Wp, const float3 lsCameraPosition = mat4f::project(LMpMv, camera.getPosition()); const float3 p = { // Another option here is to use lsShadowReceiversCenter.x, which skews less the - // x axis. Doesn't seem to make a big difference in the end. + // x-axis. Doesn't seem to make a big difference in the end. lsCameraPosition.x, n - nopt, - // note: various papers suggest to use the shadow receiver's center z coordinate in light + // note: various papers suggest using the shadow receiver's center z coordinate in light // space, i.e. to center "vertically" on the shadow receiver volume. // e.g. (LMpMv * wsShadowReceiversVolume.center()).z // However, simply using 0, guarantees to be centered on the light frustum, which itself @@ -632,7 +634,7 @@ mat4 ShadowMap::getTextureCoordsMapping() const noexcept { math::mat4f ShadowMap::computeVsmLightSpaceMatrix(const math::mat4f& lightSpacePcf, const math::mat4f& Mv, float zfar) noexcept { // The lightSpacePcf matrix transforms coordinates from world space into (u, v, z) coordinates, - // where (u, v) are used to access the shadow map, and z is the (non linear) PCF comparison + // where (u, v) are used to access the shadow map, and z is the (non-linear) PCF comparison // value [0, 1]. // // For VSM, we want to leave the z coordinate in linear light space, normalized between [0, 1] @@ -694,7 +696,7 @@ float2 ShadowMap::computeNearFarOfWarpSpace(mat4f const& lightView, float2 nearFar = { std::numeric_limits::max(), std::numeric_limits::lowest() }; #pragma nounroll for (size_t i = 0; i < count; i++) { - // we're on the y axis in light space (looking down to +y) + // we're on the y-axis in light space (looking down to +y) float c = mat4f::project(lightView, wsVertices[i]).y; nearFar.x = std::min(nearFar.x, c); nearFar.y = std::max(nearFar.y, c); @@ -805,7 +807,7 @@ void ShadowMap::snapLightFrustum(float2& s, float2& o, const float2 r = 2.0f * shadowMapResolution; o -= fmod(o, r); - // This offsets the texture coordinates so it has a fixed offset w.r.t the world + // This offsets the texture coordinates, so it has a fixed offset w.r.t the world const float2 lsOrigin = mat4f::project(Mv, worldOrigin).xy * s; o -= fmod(lsOrigin, r); } @@ -854,7 +856,7 @@ size_t ShadowMap::intersectFrustumWithBox( // b) add the scene's vertices that are known to be inside the view frustum // // We need to handle the case where a corner of the box lies exactly on a plane of - // the frustum. This actually happens often due to fitting light-space + // the frustum. This actually often happens due to fitting light-space // We fudge the distance to the plane by a small amount. #pragma nounroll for (float3 p : wsSceneReceiversCorners) { @@ -988,7 +990,7 @@ float ShadowMap::texelSizeWorldSpace(const mat3f& worldToShadowTexture) const no // The Jacobian of the transformation from texture-to-world is the matrix itself for // orthographic projections. We just need to inverse worldToShadowTexture, // which is guaranteed to be orthographic. - // The two first columns give us the how a texel maps in world-space. + // The two first columns give us how a texel maps in world-space. const float ures = 1.0f / mShadowMapInfo.shadowDimension; const float vres = 1.0f / mShadowMapInfo.shadowDimension; const mat3f shadowTextureToWorld(inverse(worldToShadowTexture)); @@ -1001,7 +1003,7 @@ float ShadowMap::texelSizeWorldSpace(const mat3f& worldToShadowTexture) const no float ShadowMap::texelSizeWorldSpace(const mat4f& Wp, const mat4f& MbMtF) const noexcept { // Here we compute the Jacobian of inverse(MbMtF * Wp). // The expression below has been computed with Mathematica. However, it's not very hard, - // albeit error prone, to do it by hand because MbMtF is a linear transform. + // albeit error-prone, to do it by hand because MbMtF is a linear transform. // So we really only need to calculate the Jacobian of inverse(Wp) at inverse(MbMtF). // // Because we're only interested in the length of the columns of the Jacobian, we can use @@ -1018,7 +1020,7 @@ float ShadowMap::texelSizeWorldSpace(const mat4f& Wp, const mat4f& MbMtF) const const float dres = mShadowMapInfo.zResolution; constexpr bool JACOBIAN_ESTIMATE = false; - if (JACOBIAN_ESTIMATE) { + if constexpr (JACOBIAN_ESTIMATE) { // this estimates the Jacobian -- this is a lot heavier. This is mostly for reference // and testing. const mat4f Si(inverse(MbMtF * Wp)); diff --git a/filament/src/ShadowMap.h b/filament/src/ShadowMap.h index fb30b1ccf7..30e16f30bf 100644 --- a/filament/src/ShadowMap.h +++ b/filament/src/ShadowMap.h @@ -148,7 +148,7 @@ private: void computeShadowCameraDirectional( math::float3 const& direction, ShadowCameraInfo const& camera, FLightManager::ShadowParams const& params, - SceneInfo cascadeParams) noexcept; + SceneInfo const& cascadeParams) noexcept; void computeShadowCameraSpot(math::float3 const& position, math::float3 const& dir, float outerConeAngle, float radius, ShadowCameraInfo const& camera, FLightManager::ShadowParams const& params) noexcept; diff --git a/filament/src/ShadowMapManager.cpp b/filament/src/ShadowMapManager.cpp index 36c5e2052a..e753b4e92d 100644 --- a/filament/src/ShadowMapManager.cpp +++ b/filament/src/ShadowMapManager.cpp @@ -34,7 +34,7 @@ namespace filament { using namespace backend; using namespace math; -ShadowMapManager::ShadowMapManager(FEngine& engine) { +ShadowMapManager::ShadowMapManager(FEngine& engine) { // NOLINT(cppcoreguidelines-pro-type-member-init) // initialize our ShadowMap array in-place for (auto& entry : mShadowMapCache) { new (&entry) ShadowMap(engine); @@ -163,9 +163,9 @@ void ShadowMapManager::render(FrameGraph& fg, FEngine& engine, backend::DriverAp const float4 vsmClearColor{ vsmMoment1, vsmMoment2, 0.0f, 0.0f }; struct ShadowPassData { - FrameGraphId tempBlurSrc; // temporary shadowmap when blurring - uint32_t blurRt; - uint32_t shadowRt; + FrameGraphId tempBlurSrc{}; // temporary shadowmap when blurring + uint32_t blurRt{}; + uint32_t shadowRt{}; }; auto shadows = prepareShadowPass.getData().shadows; @@ -240,7 +240,7 @@ void ShadowMapManager::render(FrameGraph& fg, FEngine& engine, backend::DriverAp renderTargetDesc.clearFlags = TargetBufferFlags::DEPTH; } - // finally create the shadowmap render target -- one per layer. + // finally, create the shadowmap render target -- one per layer. data.shadowRt = builder.declareRenderPass("Shadow RT", renderTargetDesc); }, [=, &engine, &view](FrameGraphResources const& resources, @@ -261,7 +261,7 @@ void ShadowMapManager::render(FrameGraph& fg, FEngine& engine, backend::DriverAp view.prepareCamera(cameraInfo); - // We set a viewport with a 1-texel border for when we index outside of the + // We set a viewport with a 1-texel border for when we index outside the // texture. // DON'T CHANGE this unless ShadowMap::getTextureCoordsMapping() is updated too. // see: ShadowMap::getTextureCoordsMapping() @@ -276,7 +276,7 @@ void ShadowMapManager::render(FrameGraph& fg, FEngine& engine, backend::DriverAp view.prepareViewport(viewport); // set uniforms needed to render this ShadowMap - // Currently these uniforms are owned by View and are global, but eventully + // Currently these uniforms are owned by View and are global, but eventually // this will set a separate per shadowmap UBO view.prepareShadowMap(); @@ -298,7 +298,7 @@ void ShadowMapManager::render(FrameGraph& fg, FEngine& engine, backend::DriverAp const float sigma = (blurWidth + 1.0f) / 6.0f; size_t kernelWidth = std::ceil((blurWidth - 5.0f) / 4.0f); kernelWidth = kernelWidth * 4 + 5; - const float ratio = (kernelWidth + 1.0f) / sigma; + const float ratio = float(kernelWidth + 1) / sigma; ppm.gaussianBlurPass(fg, shadowPass->tempBlurSrc, 0, shadows, 0, layer, @@ -414,7 +414,6 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps( ShadowTechnique shadowTechnique{}; uint32_t directionalShadowsMask = 0; uint32_t cascadeHasVisibleShadows = 0; - float screenSpaceShadowDistance = 0.0f; for (size_t i = 0, c = mCascadeShadowMaps.size(); i < c; i++) { auto& entry = mCascadeShadowMaps[i]; @@ -440,7 +439,7 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps( } // screen-space contact shadows for the directional light - screenSpaceShadowDistance = options.maxShadowDistance; + float screenSpaceShadowDistance = options.maxShadowDistance; if (options.screenSpaceContactShadows) { shadowTechnique |= ShadowTechnique::SCREEN_SPACE; } @@ -475,7 +474,7 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateSpotShadowMaps( const CameraInfo& viewingCameraInfo = view.getCameraInfo(); const uint16_t textureSize = mTextureRequirements.size; - // shadow-map shadows for point/spot lights + // shadow-map shadows for point/spotlights auto& lcm = engine.getLightManager(); FScene::ShadowInfo* const shadowInfo = lightData.data(); for (size_t i = 0, c = mSpotShadowMaps.size(); i < c; i++) { @@ -519,7 +518,7 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateSpotShadowMaps( } } - // screen-space contact shadows for point/spot lights + // screen-space contact shadows for point/spotlights auto *pInstance = lightData.data(); for (size_t i = 0, c = lightData.size(); i < c; i++) { // screen-space contact shadows @@ -539,7 +538,7 @@ void ShadowMapManager::calculateTextureRequirements(FEngine& engine, FView& view // Lay out the shadow maps. For now, we take the largest requested dimension and allocate a // texture of that size. Each cascade / shadow map gets its own layer in the array texture. - // The directional shadow cascades start on layer 0, followed by spot lights. + // The directional shadow cascades start on layer 0, followed by spotlights. uint8_t layer = 0; uint32_t maxDimension = 0; for (auto& entry : mCascadeShadowMaps) { From 51e92cd142703ec7435afa0f104ed32a250648c6 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Thu, 28 Oct 2021 20:29:53 -0700 Subject: [PATCH 09/18] Fix spotlight direction and falloff Direction and falloff were recently changed to fp16 int the shader, which is not enough (far from it) when a spotlight is over 100m away. --- RELEASE_NOTES.md | 3 ++- filament/src/Scene.cpp | 7 +++--- .../include/filament/MaterialEnums.h | 2 +- .../include/private/filament/UibStructs.h | 15 +++++------ shaders/src/light_punctual.fs | 25 ++++++++----------- 5 files changed, 26 insertions(+), 26 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 5675b3c38c..bf1c03ba3a 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -3,7 +3,7 @@ This file contains one line summaries of commits that are worthy of mentioning in release notes. A new header is inserted each time a *tag* is created. -## v1.12.12 (currently main branch) +## v1.13.0 (currently main branch) - Android: Gradle configuration caching is now enabled - Android: Filament's Gradle properties have all been renamed to `com.google.android.filament.xxx` @@ -11,6 +11,7 @@ A new header is inserted each time a *tag* is created. - Android: The Gradle property `filament_tools_dir` (now called `com.google.android.filament.tools-dir`) does not have a default value anymore. Please specify one in your `gradle.properties` if you reuse the Gradle plugin in your projects [⚠️] +- Engine: Fix spotlights direction and falloff. [⚠️ **Material breakage**]. ## v1.12.11 diff --git a/filament/src/Scene.cpp b/filament/src/Scene.cpp index 7c27b6bf53..e17400af84 100644 --- a/filament/src/Scene.cpp +++ b/filament/src/Scene.cpp @@ -316,9 +316,11 @@ void FScene::prepareDynamicLights(const CameraInfo& camera, ArenaScope& rootAren const size_t gpuIndex = i - DIRECTIONAL_LIGHTS_COUNT; auto li = instances[i]; lp[gpuIndex].positionFalloff = { spheres[i].xyz, lcm.getSquaredFalloffInv(li) }; - lp[gpuIndex].color = { lcm.getColor(li), 0.0f }; - lp[gpuIndex].directionIES = { directions[i], 0.0f }; + lp[gpuIndex].direction = directions[i]; + lp[gpuIndex].reserved1 = {}; + lp[gpuIndex].colorIES = { lcm.getColor(li), 0.0f }; lp[gpuIndex].spotScaleOffset = lcm.getSpotParams(li).scaleOffset; + lp[gpuIndex].reserved3 = {}; lp[gpuIndex].intensity = lcm.getIntensity(li); lp[gpuIndex].typeShadow = LightsUib::packTypeShadow( lcm.isPointLight(li) ? 0u : 1u, @@ -326,7 +328,6 @@ void FScene::prepareDynamicLights(const CameraInfo& camera, ArenaScope& rootAren shadowInfo[i].index, shadowInfo[i].layer); lp[gpuIndex].channels = LightsUib::packChannels(lcm.getLightChannels(li), shadowInfo[i].castsShadows); - lp[gpuIndex].reserved = {}; } driver.updateBufferObject(lightUbh, { lp, positionalLightCount * sizeof(LightsUib) }, 0); diff --git a/libs/filabridge/include/filament/MaterialEnums.h b/libs/filabridge/include/filament/MaterialEnums.h index 4a7a35e8ca..7488131380 100644 --- a/libs/filabridge/include/filament/MaterialEnums.h +++ b/libs/filabridge/include/filament/MaterialEnums.h @@ -27,7 +27,7 @@ namespace filament { // update this when a new version of filament wouldn't work with older materials -static constexpr size_t MATERIAL_VERSION = 12; +static constexpr size_t MATERIAL_VERSION = 13; /** * Supported shading models diff --git a/libs/filabridge/include/private/filament/UibStructs.h b/libs/filabridge/include/private/filament/UibStructs.h index befd6b48ed..0551e82dd3 100644 --- a/libs/filabridge/include/private/filament/UibStructs.h +++ b/libs/filabridge/include/private/filament/UibStructs.h @@ -164,13 +164,14 @@ static_assert(sizeof(PerRenderableUib) % 256 == 0, "sizeof(Transform) should be struct LightsUib { static constexpr utils::StaticString _name{ "LightsUniforms" }; math::float4 positionFalloff; // { float3(pos), 1/falloff^2 } - math::half4 color; // { half3(col), 0 } - math::half4 directionIES; // { half3(dir), IES index } - math::half2 spotScaleOffset; // { scale, offset } - float intensity; // float - uint32_t typeShadow; // 0x00.ll.ii.ct (t: 0=point, 1=spot, c:contact, ii: index, ll: layer) - uint32_t channels; // 0x000c00ll (ll: light channels, c: caster) - math::float4 reserved; // 0 + math::float3 direction; // dir + float reserved1; // 0 + math::half4 colorIES; // { half3(col), IES index } + math::float2 spotScaleOffset; // { scale, offset } + float reserved3; // 0 + float intensity; // float + uint32_t typeShadow; // 0x00.ll.ii.ct (t: 0=point, 1=spot, c:contact, ii: index, ll: layer) + uint32_t channels; // 0x000c00ll (ll: light channels, c: caster) static uint32_t packTypeShadow(uint8_t type, bool contactShadow, uint8_t index, uint8_t layer) noexcept { return (type & 0xF) | (contactShadow ? 0x10 : 0x00) | (index << 8) | (layer << 16); diff --git a/shaders/src/light_punctual.fs b/shaders/src/light_punctual.fs index 8a53227c12..4fdd182967 100644 --- a/shaders/src/light_punctual.fs +++ b/shaders/src/light_punctual.fs @@ -110,7 +110,7 @@ float getDistanceAttenuation(const highp vec3 posToLight, float falloff) { return attenuation * 1.0 / max(distanceSquare, 1e-4); } -float getAngleAttenuation(const vec3 lightDir, const vec3 l, const vec2 scaleOffset) { +float getAngleAttenuation(const highp vec3 lightDir, const highp vec3 l, const highp vec2 scaleOffset) { float cd = dot(lightDir, l); float attenuation = saturate(cd * scaleOffset.x + scaleOffset.y); return attenuation * attenuation; @@ -132,18 +132,15 @@ Light getLight(const uint index) { highp mat4 data = lightsUniforms.lights[lightIndex]; highp vec4 positionFalloff = data[0]; - vec4 color = vec4( - unpackHalf2x16(floatBitsToUint(data[1][0])), - unpackHalf2x16(floatBitsToUint(data[1][1])) + highp vec3 direction = data[1].xyz; + vec4 colorIES = vec4( + unpackHalf2x16(floatBitsToUint(data[2][0])), + unpackHalf2x16(floatBitsToUint(data[2][1])) ); - vec4 directionIES = vec4( - unpackHalf2x16(floatBitsToUint(data[1][2])), - unpackHalf2x16(floatBitsToUint(data[1][3])) - ); - vec2 scaleOffset = unpackHalf2x16(floatBitsToUint(data[2][0])); - highp float intensity = data[2][1]; - highp uint typeShadow = floatBitsToUint(data[2][2]); - highp uint channels = floatBitsToUint(data[2][3]); + highp vec2 scaleOffset = data[2].zw; + highp float intensity = data[3][1]; + highp uint typeShadow = floatBitsToUint(data[3][2]); + highp uint channels = floatBitsToUint(data[3][3]); // poition-to-light vector highp vec3 worldPosition = vertex_worldPosition.xyz; @@ -151,7 +148,7 @@ Light getLight(const uint index) { // and populate the Light structure Light light; - light.colorIntensity.rgb = color.rgb; + light.colorIntensity.rgb = colorIES.rgb; light.colorIntensity.w = computePreExposedIntensity(intensity, frameUniforms.exposure); light.l = normalize(posToLight); light.attenuation = getDistanceAttenuation(posToLight, positionFalloff.w); @@ -165,7 +162,7 @@ Light getLight(const uint index) { uint type = typeShadow & 0x1u; if (type == LIGHT_TYPE_SPOT) { - light.attenuation *= getAngleAttenuation(-directionIES.xyz, light.l, scaleOffset); + light.attenuation *= getAngleAttenuation(-direction, light.l, scaleOffset); light.contactShadows = bool(typeShadow & 0x10u); light.shadowIndex = (typeShadow >> 8u) & 0xFFu; light.shadowLayer = (typeShadow >> 16u) & 0xFFu; From fd68144ba246d271621d61fb9b4cf530c9145f29 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Thu, 28 Oct 2021 22:05:52 -0700 Subject: [PATCH 10/18] fix depth precision with VSM When calculating the linear depth for VSM, we were using the whole range between 0 and the far plane, the near plane wasn't taken into account. This can be a problem is the light is very far, but it's near plane is closer to the camera/scene, in this case the depth precision wasn't used optimally. Note that we don't hit this problem currently, because the directional light is constructed such that its origin is at the near plane, and the spotlights have a fixed near plane (which is a problem and will be fixed at a later time). --- filament/src/PerViewUniforms.cpp | 2 ++ filament/src/ShadowMap.cpp | 26 +++++++++---------- filament/src/ShadowMap.h | 4 +-- .../include/private/filament/UibStructs.h | 6 ++--- libs/filamat/src/UibGenerator.cpp | 4 +-- shaders/src/depth_main.vs | 15 +++++++---- shaders/src/main.vs | 15 +++++++---- 7 files changed, 42 insertions(+), 30 deletions(-) diff --git a/filament/src/PerViewUniforms.cpp b/filament/src/PerViewUniforms.cpp index aa7f032f7c..dee21d0695 100644 --- a/filament/src/PerViewUniforms.cpp +++ b/filament/src/PerViewUniforms.cpp @@ -84,6 +84,8 @@ void PerViewUniforms::prepareCamera(const CameraInfo& camera) noexcept { s.cameraPosition = float3{ camera.getPosition() }; s.worldOffset = camera.worldOffset; s.cameraFar = camera.zf; + s.oneOverFarMinusNear = 1.0f / (camera.zf - camera.zn); + s.nearOverFarMinusNear = camera.zn / (camera.zf - camera.zn); s.clipControl = mClipControl; } diff --git a/filament/src/ShadowMap.cpp b/filament/src/ShadowMap.cpp index d630e8d30f..5283fc9c87 100644 --- a/filament/src/ShadowMap.cpp +++ b/filament/src/ShadowMap.cpp @@ -427,7 +427,7 @@ void ShadowMap::computeShadowCameraDirectional( if (!mShadowMapInfo.vsm) { mLightSpace = St; } else { - mLightSpace = computeVsmLightSpaceMatrix(St, Mv, zfar); + mLightSpace = computeVsmLightSpaceMatrix(St, Mv, znear, zfar); } // We apply the constant bias in world space (as opposed to light-space) to account @@ -485,7 +485,7 @@ void ShadowMap::computeShadowCameraSpot( if (!mShadowMapInfo.vsm) { mLightSpace = St; } else { - mLightSpace = computeVsmLightSpaceMatrix(St, Mv, farPlane); + mLightSpace = computeVsmLightSpaceMatrix(St, Mv, nearPlane, farPlane); } const float constantBias = mShadowMapInfo.vsm ? 0.0f : params.options.constantBias; @@ -631,22 +631,22 @@ mat4 ShadowMap::getTextureCoordsMapping() const noexcept { return mat4(Mf * Mb * Mv * Mt); } -math::mat4f ShadowMap::computeVsmLightSpaceMatrix(const math::mat4f& lightSpacePcf, - const math::mat4f& Mv, float zfar) noexcept { +mat4f ShadowMap::computeVsmLightSpaceMatrix(const math::mat4f& lightSpacePcf, + const math::mat4f& Mv, float znear, float zfar) noexcept { // The lightSpacePcf matrix transforms coordinates from world space into (u, v, z) coordinates, // where (u, v) are used to access the shadow map, and z is the (non-linear) PCF comparison // value [0, 1]. - // - // For VSM, we want to leave the z coordinate in linear light space, normalized between [0, 1] - // (the normalization factor is therefore -1/zfar). - // + // For VSM, we want to leave the z coordinate in linear light-space, normalized between [0, 1], + // i.e. remapping [near, far] to [0, 1]. // When sampling a VSM shadow map, the shader follows suit, and doesn't divide by w for the z - // coordinate. See getters.fs. + // coordinate. See shadowing.fs. + // compute z' = -(Mv * position).z / (far - near) - (near / (far - near)) + const float scale = 1.0f / (zfar - znear); math::mat4f lightSpaceVsm{ lightSpacePcf }; - lightSpaceVsm[0].z = Mv[0].z * (-1.0f / zfar); - lightSpaceVsm[1].z = Mv[1].z * (-1.0f / zfar); - lightSpaceVsm[2].z = Mv[2].z * (-1.0f / zfar); - lightSpaceVsm[3].z = Mv[3].z * (-1.0f / zfar); + lightSpaceVsm[0].z = -Mv[0].z * scale; + lightSpaceVsm[1].z = -Mv[1].z * scale; + lightSpaceVsm[2].z = -Mv[2].z * scale; + lightSpaceVsm[3].z = (-Mv[3].z - znear) * scale; return lightSpaceVsm; } diff --git a/filament/src/ShadowMap.h b/filament/src/ShadowMap.h index 30e16f30bf..5bfd141163 100644 --- a/filament/src/ShadowMap.h +++ b/filament/src/ShadowMap.h @@ -213,8 +213,8 @@ private: math::mat4 getTextureCoordsMapping() const noexcept; - static math::mat4f computeVsmLightSpaceMatrix(const math::mat4f& lightSpace, - const math::mat4f& Mv, float zfar) noexcept; + static math::mat4f computeVsmLightSpaceMatrix(const math::mat4f& lightSpacePcf, + const math::mat4f& Mv, float znear, float zfar) noexcept; float texelSizeWorldSpace(const math::mat3f& worldToShadowTexture) const noexcept; float texelSizeWorldSpace(const math::mat4f& W, const math::mat4f& MbMtF) const noexcept; diff --git a/libs/filabridge/include/private/filament/UibStructs.h b/libs/filabridge/include/private/filament/UibStructs.h index 0551e82dd3..f43a7a4d14 100644 --- a/libs/filabridge/include/private/filament/UibStructs.h +++ b/libs/filabridge/include/private/filament/UibStructs.h @@ -88,7 +88,7 @@ struct PerViewUib { // NOLINT(cppcoreguidelines-pro-type-member-init) math::float4 userTime; // time(s), (double)time - (float)time, 0, 0 float iblRoughnessOneLevel; // level for roughness == 1 - float cameraFar; // camera *culling* far-plane distance (projection far is at +inf) + float cameraFar; // camera *culling* far-plane distance, always positive (projection far is at +inf) float refractionLodOffset; // bit 0: directional (sun) shadow enabled @@ -129,8 +129,8 @@ struct PerViewUib { // NOLINT(cppcoreguidelines-pro-type-member-init) float vsmReserved0; float lodBias; - float reserved1; - float reserved2; + float oneOverFarMinusNear; // 1 / (f-n), always positive + float nearOverFarMinusNear; // n / (f-n), always positive float reserved3; // bring PerViewUib to 2 KiB diff --git a/libs/filamat/src/UibGenerator.cpp b/libs/filamat/src/UibGenerator.cpp index d0c6f56f3b..8c0938031f 100644 --- a/libs/filamat/src/UibGenerator.cpp +++ b/libs/filamat/src/UibGenerator.cpp @@ -110,8 +110,8 @@ UniformInterfaceBlock const& UibGenerator::getPerViewUib() noexcept { .add("vsmReserved0", 1, UniformInterfaceBlock::Type::FLOAT) .add("lodBias", 1, UniformInterfaceBlock::Type::FLOAT) - .add("reserved1", 1, UniformInterfaceBlock::Type::FLOAT) - .add("reserved2", 1, UniformInterfaceBlock::Type::FLOAT) + .add("oneOverFarMinusNear", 1, UniformInterfaceBlock::Type::FLOAT, Precision::HIGH) + .add("nearOverFarMinusNear", 1, UniformInterfaceBlock::Type::FLOAT, Precision::HIGH) .add("reserved3", 1, UniformInterfaceBlock::Type::FLOAT) // bring PerViewUib to 2 KiB diff --git a/shaders/src/depth_main.vs b/shaders/src/depth_main.vs index e9bddec7e5..9de5344313 100644 --- a/shaders/src/depth_main.vs +++ b/shaders/src/depth_main.vs @@ -25,16 +25,21 @@ void main() { #endif #if defined(HAS_VSM) - // For VSM, we use the linear-light space Z coordinate as the depth metric, which works for both + // For VSM, we use the linear light-space Z coordinate as the depth metric, which works for both // directional and spot lights and can be safely interpolated. - // The value is guaranteed to be between [0, -zfar] by construction of viewFromWorldMatrix, + // The value is guaranteed to be between [-znear, -zfar] by construction of viewFromWorldMatrix, // (see ShadowMap.cpp). // Use vertex_worldPosition.w which is otherwise not used to store the interpolated // light-space depth. highp float z = (frameUniforms.viewFromWorldMatrix * vec4(material.worldPosition.xyz, 1.0)).z; - highp float depth = -z / abs(frameUniforms.cameraFar); // rescale the depth between [0, 1] - depth = depth * 2.0 - 1.0; - vertex_worldPosition.w = frameUniforms.vsmExponent * depth; + + // rescale [near, far] to [0, 1] + highp float depth = -z * frameUniforms.oneOverFarMinusNear - frameUniforms.nearOverFarMinusNear; + + // EVSM pre-mapping + depth = frameUniforms.vsmExponent * (depth * 2.0 - 1.0); + + vertex_worldPosition.w = depth; #endif // this must happen before we compensate for vulkan below diff --git a/shaders/src/main.vs b/shaders/src/main.vs index fa4d920e97..4b90130bb3 100644 --- a/shaders/src/main.vs +++ b/shaders/src/main.vs @@ -110,16 +110,21 @@ void main() { #endif #if defined(HAS_VSM) - // For VSM, we use the linear-light space Z coordinate as the depth metric, which works for both + // For VSM, we use the linear light-space Z coordinate as the depth metric, which works for both // directional and spot lights and can be safely interpolated. - // The value is guaranteed to be between [0, -zfar] by construction of viewFromWorldMatrix, + // The value is guaranteed to be between [-znear, -zfar] by construction of viewFromWorldMatrix, // (see ShadowMap.cpp). // Use vertex_worldPosition.w which is otherwise not used to store the interpolated // light-space depth. highp float z = (frameUniforms.viewFromWorldMatrix * vec4(material.worldPosition.xyz, 1.0)).z; - highp float depth = -z / abs(frameUniforms.cameraFar); // rescale the depth between [0, 1] - depth = depth * 2.0 - 1.0; - vertex_worldPosition.w = frameUniforms.vsmExponent * depth; + + // rescale [near, far] to [0, 1] + highp float depth = -z * frameUniforms.oneOverFarMinusNear - frameUniforms.nearOverFarMinusNear; + + // EVSM pre-mapping + depth = frameUniforms.vsmExponent * (depth * 2.0 - 1.0); + + vertex_worldPosition.w = depth; #endif // this must happen before we compensate for vulkan below From 23dab0a0138397b4cef9c5377c9eb1a17271a2d8 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Fri, 29 Oct 2021 12:03:14 -0700 Subject: [PATCH 11/18] Fix very narrow spotlight lighting and other minor issues The froxel code was failing when a spotlight cone was too narrow. This is fixed here by never using a cone smaller than 0.5 degrees during forxelization. Additionally we now silently clamp the cone angles to 0.5 degrees at the API level because, the falloff was also failing due to floating point precision in the shader. Finally, we clamp the inner cone upper value to the outer cone's instead of the other way around. i.e. the outer cone defines the spotlight while the inner cone just controls the falloff. --- filament/include/filament/LightManager.h | 11 ++++++----- filament/src/Froxelizer.cpp | 11 ++++++++--- filament/src/components/LightManager.cpp | 14 +++++++------- 3 files changed, 21 insertions(+), 15 deletions(-) diff --git a/filament/include/filament/LightManager.h b/filament/include/filament/LightManager.h index eb542da2b4..71a7355f3b 100644 --- a/filament/include/filament/LightManager.h +++ b/filament/include/filament/LightManager.h @@ -558,10 +558,11 @@ public: * and are defined by the angle from the center axis to where the falloff begins (i.e. * cones are defined by their half-angle). * - * @param inner inner cone angle in *radians* between 0 and @f$ \pi/2 @f$ - * - * @param outer outer cone angle in *radians* between \p inner and @f$ \pi/2 @f$ + * Both inner and outer are silently clamped to a minimum value of 0.5 degrees + * (~0.00873 radians) to avoid floating-point precision issues during rendering. * + * @param inner inner cone angle in *radians* between 0.00873 and \p outer + * @param outer outer cone angle in *radians* between 0.00873 inner and @f$ \pi/2 @f$ * @return This Builder, for chaining calls. * * @note @@ -812,8 +813,8 @@ public: * Dynamically updates a spot light's cone as angles * * @param i Instance of the component obtained from getInstance(). - * @param inner inner cone angle in *radians* between 0 and pi/2 - * @param outer outer cone angle in *radians* between inner and pi/2 + * @param inner inner cone angle in *radians* between 0.00873 and outer + * @param outer outer cone angle in *radians* between 0.00873 and pi/2 * * @see Builder.spotLightCone() */ diff --git a/filament/src/Froxelizer.cpp b/filament/src/Froxelizer.cpp index 44f30d993d..ca54105b86 100644 --- a/filament/src/Froxelizer.cpp +++ b/filament/src/Froxelizer.cpp @@ -560,14 +560,19 @@ void Froxelizer::froxelizeLoop(FEngine& engine, const mat4f& projection = mProjection; const mat3f& vn = camera.view.upperLeft(); + // We use minimum cone angle of 0.5 degrees because too small angles cause issues in the + // sphere/cone intersection test, due to floating-point precision. + constexpr float maxInvSin = 114.59301f; // 1 / sin(0.5 degrees) + constexpr float maxCosSquared = 0.99992385f; // cos(0.5 degrees)^2 + for (size_t i = offset; i < count; i += stride) { const size_t j = i + FScene::DIRECTIONAL_LIGHTS_COUNT; FLightManager::Instance li = instances[j]; LightParams light = { .position = (camera.view * float4{ spheres[j].xyz, 1 }).xyz, // to view-space - .cosSqr = lcm.getCosOuterSquared(li), // spot only - .axis = vn * directions[j], // spot only - .invSin = lcm.getSinInverse(li), // spot only + .cosSqr = std::min(maxCosSquared, lcm.getCosOuterSquared(li)), // spot only + .axis = vn * directions[j], // spot only + .invSin = std::min(maxInvSin, lcm.getSinInverse(li)), // spot only .radius = spheres[j].w, }; diff --git a/filament/src/components/LightManager.cpp b/filament/src/components/LightManager.cpp index 64f1825545..a344b09264 100644 --- a/filament/src/components/LightManager.cpp +++ b/filament/src/components/LightManager.cpp @@ -352,23 +352,23 @@ void FLightManager::setFalloff(Instance i, float falloff) noexcept { void FLightManager::setSpotLightCone(Instance i, float inner, float outer) noexcept { auto& manager = mManager; if (i && isSpotLight(i)) { - // clamp the inner/outer angles to pi - float innerClamped = std::min(std::abs(inner), f::PI_2); - float outerClamped = std::min(std::abs(outer), f::PI_2); + // clamp the inner/outer angles to [0.5 degrees, 90 degrees] + float innerClamped = std::clamp(std::abs(inner), 0.5f * f::DEG_TO_RAD, f::PI_2); + float outerClamped = std::clamp(std::abs(outer), 0.5f * f::DEG_TO_RAD, f::PI_2); - // outer must always be bigger than inner - outerClamped = std::max(innerClamped, outerClamped); + // inner must always be smaller than outer + innerClamped = std::min(innerClamped, outerClamped); float cosOuter = fast::cos(outerClamped); float cosInner = fast::cos(innerClamped); float cosOuterSquared = cosOuter * cosOuter; - float scale = 1 / std::max(1.0f / 1024.0f, cosInner - cosOuter); + float scale = 1.0f / std::max(1.0f / 1024.0f, cosInner - cosOuter); float offset = -cosOuter * scale; SpotParams& spotParams = manager[i].spotParams; spotParams.outerClamped = outerClamped; spotParams.cosOuterSquared = cosOuterSquared; - spotParams.sinInverse = 1 / std::sqrt(1 - cosOuterSquared); + spotParams.sinInverse = 1.0f / std::sin(outerClamped); spotParams.scaleOffset = { scale, offset }; // we need to recompute the luminous intensity From 3073b03d56e43643a04956780359f651a85f618b Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Fri, 29 Oct 2021 15:07:43 -0700 Subject: [PATCH 12/18] Improve computation of spotlight near/far plane Near/far plane is now computed dynamically, this significantly improves the shadowmap depth resolution. Quite a bit of refactoring was necessary to get all the right data in the right place, in particular SceneInfo is now passed around for both directional and spot shadows. --- filament/src/ShadowMap.cpp | 153 +++++++++++++++++------------- filament/src/ShadowMap.h | 40 ++++---- filament/src/ShadowMapManager.cpp | 51 ++++++---- filament/src/ShadowMapManager.h | 11 ++- 4 files changed, 146 insertions(+), 109 deletions(-) diff --git a/filament/src/ShadowMap.cpp b/filament/src/ShadowMap.cpp index 5283fc9c87..45c4358896 100644 --- a/filament/src/ShadowMap.cpp +++ b/filament/src/ShadowMap.cpp @@ -79,49 +79,15 @@ void ShadowMap::render(FScene const& scene, utils::Range range, pass->sortCommands(); } -mat4f ShadowMap::getLightViewMatrix(float3 position, float3 direction) noexcept { +mat4f ShadowMap::getDirectionalLightViewMatrix(float3 direction, float3 position) noexcept { const mat4f M = mat4f::lookAt(position, position + direction, float3{ 0, 1, 0 }); return FCamera::rigidTransformInverse(M); } -void ShadowMap::computeSceneInfo(float3 dir, - FScene const& scene, filament::CameraInfo const& camera, uint8_t visibleLayers, - SceneInfo& sceneInfo) { - // We assume the light is at the origin to compute the SceneInfo. This is consumed later by - // computeShadowCameraDirectional() which takes this into account. - const mat4f Mv = getLightViewMatrix({}, dir); - const mat4f V = camera.view; - - // Compute scene bounds in world space, as well as the light-space and view-space near/far planes - sceneInfo.lsNearFar = { std::numeric_limits::lowest(), std::numeric_limits::max() }; - sceneInfo.vsNearFar = { std::numeric_limits::lowest(), std::numeric_limits::max() }; - sceneInfo.wsShadowCastersVolume = {}; - sceneInfo.wsShadowReceiversVolume = {}; - visitScene(scene, visibleLayers, - [&](Aabb caster) { - sceneInfo.wsShadowCastersVolume.min = - min(sceneInfo.wsShadowCastersVolume.min, caster.min); - sceneInfo.wsShadowCastersVolume.max = - max(sceneInfo.wsShadowCastersVolume.max, caster.max); - float2 nf = computeNearFar(Mv, caster); - sceneInfo.lsNearFar.x = std::max(sceneInfo.lsNearFar.x, nf.x); // near - sceneInfo.lsNearFar.y = std::min(sceneInfo.lsNearFar.y, nf.y); // far - }, - [&](Aabb receiver) { - sceneInfo.wsShadowReceiversVolume.min = - min(sceneInfo.wsShadowReceiversVolume.min, receiver.min); - sceneInfo.wsShadowReceiversVolume.max = - max(sceneInfo.wsShadowReceiversVolume.max, receiver.max); - float2 nf = computeNearFar(V, receiver); - sceneInfo.vsNearFar.x = std::max(sceneInfo.vsNearFar.x, nf.x); - sceneInfo.vsNearFar.y = std::min(sceneInfo.vsNearFar.y, nf.y); - } - ); -} - void ShadowMap::update(const FScene::LightSoa& lightData, size_t index, - filament::CameraInfo const& camera, const ShadowMapInfo& shadowMapInfo, - const SceneInfo& cascadeParams) noexcept { + filament::CameraInfo const& camera, + const ShadowMapInfo& shadowMapInfo, FScene const& scene, + SceneInfo& sceneInfo) noexcept { // this is the hard part here, find a good frustum for our camera auto& lcm = mEngine.getLightManager(); @@ -178,14 +144,15 @@ void ShadowMap::update(const FScene::LightSoa& lightData, size_t index, case LightType::DIRECTIONAL: computeShadowCameraDirectional( lightData.elementAt(index), - cameraInfo, params, cascadeParams); + cameraInfo, params, scene, sceneInfo); break; case LightType::FOCUSED_SPOT: case LightType::SPOT: computeShadowCameraSpot( lightData.elementAt(index).xyz, lightData.elementAt(index), lcm.getSpotLightOuterCone(li), - lightData.elementAt(index).w, cameraInfo, params); + lightData.elementAt(index).w, + cameraInfo, params, scene, sceneInfo); break; case LightType::POINT: break; @@ -193,10 +160,9 @@ void ShadowMap::update(const FScene::LightSoa& lightData, size_t index, } void ShadowMap::computeShadowCameraDirectional( - float3 const& dir, - ShadowCameraInfo const& camera, + float3 const& dir, ShadowCameraInfo const& camera, FLightManager::ShadowParams const& params, - SceneInfo const& cascadeParams) noexcept { + FScene const& scene, SceneInfo& sceneInfo) noexcept { /* * Compute the light's model matrix @@ -205,10 +171,13 @@ void ShadowMap::computeShadowCameraDirectional( // We compute the directional light's model matrix using the origin's as the light position. // The choice of the light's origin initially doesn't matter for a directional light. // This will be adjusted later because of how we compute the depth metric for VSM. - const mat4f MvAtOrigin = getLightViewMatrix({}, dir); + const mat4f MvAtOrigin = getDirectionalLightViewMatrix(dir); - const Aabb wsShadowCastersVolume = cascadeParams.wsShadowCastersVolume; - const Aabb wsShadowReceiversVolume = cascadeParams.wsShadowReceiversVolume; + // Compute scene-dependent values shared across all cascades + ShadowMap::updateSceneInfo(MvAtOrigin, scene, sceneInfo); + + const Aabb wsShadowCastersVolume = sceneInfo.wsShadowCastersVolume; + const Aabb wsShadowReceiversVolume = sceneInfo.wsShadowReceiversVolume; if (wsShadowCastersVolume.isEmpty() || wsShadowReceiversVolume.isEmpty()) { mHasVisibleShadows = false; return; @@ -218,7 +187,7 @@ void ShadowMap::computeShadowCameraDirectional( float3 wsViewFrustumVertices[8]; computeFrustumCorners(wsViewFrustumVertices, camera.model * FCamera::inverseProjection(camera.projection), - cascadeParams.csNearFar); + sceneInfo.csNearFar); // we use aligned_storage<> here to avoid the default initialization of std::array<> std::aligned_storage::type localStorage; // NOLINT(cppcoreguidelines-pro-type-member-init) @@ -246,7 +215,7 @@ void ShadowMap::computeShadowCameraDirectional( Aabb lsLightFrustumBounds; if constexpr (!USE_DEPTH_CLAMP) { // near plane from shadow caster volume - lsLightFrustumBounds.max.z = cascadeParams.lsNearFar[0]; + lsLightFrustumBounds.max.z = sceneInfo.lsNearFar[0]; } for (size_t i = 0; i < vertexCount; ++i) { // far: figure out the farthest shadow receivers @@ -259,12 +228,12 @@ void ShadowMap::computeShadowCameraDirectional( } if (mEngine.debug.shadowmap.far_uses_shadowcasters) { // far: closest of the farthest shadow casters and receivers - lsLightFrustumBounds.min.z = std::max(lsLightFrustumBounds.min.z, cascadeParams.lsNearFar[1]); + lsLightFrustumBounds.min.z = std::max(lsLightFrustumBounds.min.z, sceneInfo.lsNearFar[1]); } // Now that we know the znear (-lsLightFrustumBounds.max.z), adjust the light's position such // that znear = 0, this is only need for VSM, but doesn't hurt PCF. - const mat4f Mv = getLightViewMatrix(dir * -lsLightFrustumBounds.max.z, dir); + const mat4f Mv = getDirectionalLightViewMatrix(dir, dir * -lsLightFrustumBounds.max.z); // near / far planes are specified relative to the direction the eye is looking at // i.e. the -z axis (see: ortho) @@ -452,10 +421,10 @@ void ShadowMap::computeShadowCameraDirectional( } } -void ShadowMap::computeShadowCameraSpot( - math::float3 const& position, math::float3 const& dir, float outerConeAngle, float radius, - ShadowCameraInfo const& camera, - FLightManager::ShadowParams const& params) noexcept { +void ShadowMap::computeShadowCameraSpot(float3 const& position, float3 const& dir, + float outerConeAngle, float radius, ShadowCameraInfo const& camera, + FLightManager::ShadowParams const& params, FScene const& scene, + SceneInfo& sceneInfo) noexcept { // TODO: correctly compute if this spot light has any visible shadows. mHasVisibleShadows = true; @@ -465,22 +434,28 @@ void ShadowMap::computeShadowCameraSpot( */ // Choose a reasonable value for the near plane. - const float nearPlane = radius * (1.0f / 1024.0f); - const float farPlane = radius; - const mat4f Mv = getLightViewMatrix(position, dir); + const mat4f Mv = getDirectionalLightViewMatrix(dir, position); + + // find decent near/far + // TODO: we can do much better by rejecting objects that don't intersect our frustum + ShadowMap::updateSceneInfo(Mv, scene, sceneInfo); + float nearPlane = -sceneInfo.lsNearFar.x; + float farPlane = std::min(radius, -sceneInfo.lsNearFar.y); float outerConeAngleDegrees = outerConeAngle * f::RAD_TO_DEG; - const mat4f Mp = mat4f::perspective(outerConeAngleDegrees * 2, 1.0f, nearPlane, farPlane, - mat4f::Fov::HORIZONTAL); - - const mat4f MpMv(Mp * Mv); + const mat4f Mp = mat4f::perspective(outerConeAngleDegrees * 2.0f, 1.0f, nearPlane, farPlane); + const mat4f MpMv(math::highPrecisionMultiply(Mp, Mv)); // Final shadow transform const mat4f S = MpMv; const mat4 MbMt = getTextureCoordsMapping(); const mat4f St = mat4f(MbMt * S); - mTexelSizeWs = texelSizeWorldSpace(Mp, mat4f(MbMt)); + + // TODO: focus projection + + // FIXME: texelSizeWorldSpace doesn't work for spotlights + mTexelSizeWs = 0; //texelSizeWorldSpace(Mp, mat4f(MbMt)); if (!mShadowMapInfo.vsm) { mLightSpace = St; @@ -507,7 +482,7 @@ void ShadowMap::computeShadowCameraSpot( mDebugCamera->setCustomProjection(mat4(Sb * camera.worldOrigin), nearPlane, radius); } -mat4f ShadowMap::applyLISPSM(math::mat4f& Wp, +mat4f ShadowMap::applyLISPSM(mat4f& Wp, ShadowCameraInfo const& camera, FLightManager::ShadowParams const& params, mat4f const& LMpMv, FrustumBoxIntersection const& wsShadowReceiversVolume, size_t vertexCount, @@ -631,8 +606,8 @@ mat4 ShadowMap::getTextureCoordsMapping() const noexcept { return mat4(Mf * Mb * Mv * Mt); } -mat4f ShadowMap::computeVsmLightSpaceMatrix(const math::mat4f& lightSpacePcf, - const math::mat4f& Mv, float znear, float zfar) noexcept { +mat4f ShadowMap::computeVsmLightSpaceMatrix(const mat4f& lightSpacePcf, + const mat4f& Mv, float znear, float zfar) noexcept { // The lightSpacePcf matrix transforms coordinates from world space into (u, v, z) coordinates, // where (u, v) are used to access the shadow map, and z is the (non-linear) PCF comparison // value [0, 1]. @@ -642,7 +617,7 @@ mat4f ShadowMap::computeVsmLightSpaceMatrix(const math::mat4f& lightSpacePcf, // coordinate. See shadowing.fs. // compute z' = -(Mv * position).z / (far - near) - (near / (far - near)) const float scale = 1.0f / (zfar - znear); - math::mat4f lightSpaceVsm{ lightSpacePcf }; + mat4f lightSpaceVsm{ lightSpacePcf }; lightSpaceVsm[0].z = -Mv[0].z * scale; lightSpaceVsm[1].z = -Mv[1].z * scale; lightSpaceVsm[2].z = -Mv[2].z * scale; @@ -665,7 +640,7 @@ mat4f ShadowMap::warpFrustum(float n, float f) noexcept { return Wp; } -math::mat4f ShadowMap::directionalLightFrustum(float near, float far) noexcept { +mat4f ShadowMap::directionalLightFrustum(float near, float far) noexcept { const float d = far - near; mat4f m; m[2][2] = -2 / d; @@ -1065,7 +1040,6 @@ float ShadowMap::texelSizeWorldSpace(const mat4f& Wp, const mat4f& MbMtF) const return s; } - template void ShadowMap::visitScene(const FScene& scene, uint32_t visibleLayers, Casters casters, Receivers receivers) noexcept { @@ -1092,4 +1066,47 @@ void ShadowMap::visitScene(const FScene& scene, uint32_t visibleLayers, } } +void ShadowMap::initSceneInfo(FScene const& scene, filament::CameraInfo const& camera, + ShadowMap::SceneInfo& sceneInfo) { + // We assume the light is at the origin to compute the SceneInfo. This is consumed later by + // computeShadowCameraDirectional() which takes this into account. + const mat4f V = camera.view; + + // Compute scene bounds in world space, as well as the light-space and view-space near/far planes + sceneInfo.wsShadowCastersVolume = {}; + sceneInfo.wsShadowReceiversVolume = {}; + visitScene(scene, sceneInfo.visibleLayers, + [&](Aabb caster) { + sceneInfo.wsShadowCastersVolume.min = + min(sceneInfo.wsShadowCastersVolume.min, caster.min); + sceneInfo.wsShadowCastersVolume.max = + max(sceneInfo.wsShadowCastersVolume.max, caster.max); + }, + [&](Aabb receiver) { + sceneInfo.wsShadowReceiversVolume.min = + min(sceneInfo.wsShadowReceiversVolume.min, receiver.min); + sceneInfo.wsShadowReceiversVolume.max = + max(sceneInfo.wsShadowReceiversVolume.max, receiver.max); + float2 nf = ShadowMap::computeNearFar(V, receiver); + sceneInfo.vsNearFar.x = std::max(sceneInfo.vsNearFar.x, nf.x); + sceneInfo.vsNearFar.y = std::min(sceneInfo.vsNearFar.y, nf.y); + } + ); +} + +void ShadowMap::updateSceneInfo(const mat4f& Mv, FScene const& scene, + ShadowMap::SceneInfo& sceneInfo) { + sceneInfo.lsNearFar = { std::numeric_limits::lowest(), std::numeric_limits::max() }; + sceneInfo.vsNearFar = { std::numeric_limits::lowest(), std::numeric_limits::max() }; + visitScene(scene, sceneInfo.visibleLayers, + [&](Aabb caster) { + float2 nf = ShadowMap::computeNearFar(Mv, caster); + sceneInfo.lsNearFar.x = std::max(sceneInfo.lsNearFar.x, nf.x); // near + sceneInfo.lsNearFar.y = std::min(sceneInfo.lsNearFar.y, nf.y); // far + }, + [&](Aabb receiver) { + } + ); +} + } // namespace filament diff --git a/filament/src/ShadowMap.h b/filament/src/ShadowMap.h index 5bfd141163..d927552d6e 100644 --- a/filament/src/ShadowMap.h +++ b/filament/src/ShadowMap.h @@ -64,6 +64,8 @@ public: }; struct SceneInfo { + explicit SceneInfo(uint8_t visibleLayers) noexcept : visibleLayers(visibleLayers) { } + // The near and far planes, in clip space, to use for this shadow map math::float2 csNearFar = { -1.0f, 1.0f }; @@ -81,22 +83,18 @@ public: // World-space shadow-receivers volume Aabb wsShadowReceiversVolume; + + uint8_t visibleLayers; }; - static math::mat4f getLightViewMatrix( - math::float3 position, math::float3 direction) noexcept; - - // Call once per frame to populate the CascadeParameters struct, then pass to update(). - // This computes values constant across all cascades. - static void computeSceneInfo(math::float3 dir, - FScene const& scene, filament::CameraInfo const& camera, uint8_t visibleLayers, - SceneInfo& sceneInfo); + static math::mat4f getDirectionalLightViewMatrix( + math::float3 direction, math::float3 position = {}) noexcept; // Call once per frame if the light, scene (or visible layers) or camera changes. // This computes the light's camera. - void update(const FScene::LightSoa& lightData, size_t index, - filament::CameraInfo const& camera, - const ShadowMapInfo& shadowMapInfo, const SceneInfo& cascadeParams) noexcept; + void update(const FScene::LightSoa& lightData, size_t index, filament::CameraInfo const& camera, + const ShadowMapInfo& shadowMapInfo, FScene const& scene, + SceneInfo& sceneInfo) noexcept; void render(FScene const& scene, utils::Range range, FScene::VisibleMaskType visibilityMask, filament::CameraInfo const& cameraInfo, @@ -120,6 +118,15 @@ public: backend::PolygonOffset getPolygonOffset() const noexcept { return mPolygonOffset; } + // Call once per frame to populate the SceneInfo struct, then pass to update(). + // This computes values constant across all shadow maps. + static void initSceneInfo(FScene const& scene, filament::CameraInfo const& camera, + ShadowMap::SceneInfo& sceneInfo); + + // Update SceneInfo struct for a given light + static void updateSceneInfo(const math::mat4f& Mv, FScene const& scene, + ShadowMap::SceneInfo& sceneInfo); + private: struct ShadowCameraInfo { math::mat4f projection; @@ -145,13 +152,14 @@ private: // 8 corners, 12 segments w/ 2 intersection max -- all of this twice (8 + 12 * 2) * 2 (768 bytes) using FrustumBoxIntersection = std::array; - void computeShadowCameraDirectional( - math::float3 const& direction, - ShadowCameraInfo const& camera, FLightManager::ShadowParams const& params, - SceneInfo const& cascadeParams) noexcept; + void computeShadowCameraDirectional(math::float3 const& dir, ShadowCameraInfo const& camera, + FLightManager::ShadowParams const& params, FScene const& scene, + SceneInfo& sceneInfo) noexcept; + void computeShadowCameraSpot(math::float3 const& position, math::float3 const& dir, float outerConeAngle, float radius, ShadowCameraInfo const& camera, - FLightManager::ShadowParams const& params) noexcept; + FLightManager::ShadowParams const& params, FScene const& scene, + SceneInfo& sceneInfo) noexcept; static math::mat4f applyLISPSM(math::mat4f& Wp, ShadowCameraInfo const& camera, FLightManager::ShadowParams const& params, diff --git a/filament/src/ShadowMapManager.cpp b/filament/src/ShadowMapManager.cpp index e753b4e92d..311c4197e4 100644 --- a/filament/src/ShadowMapManager.cpp +++ b/filament/src/ShadowMapManager.cpp @@ -59,8 +59,20 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::update( FScene::LightSoa& lightData) noexcept { calculateTextureRequirements(engine, view, lightData); ShadowTechnique shadowTechnique = {}; - shadowTechnique |= updateCascadeShadowMaps(engine, view, renderableData, lightData); - shadowTechnique |= updateSpotShadowMaps(engine, view, shadowUb, renderableData, lightData); + + ShadowMap::SceneInfo sceneInfo(view.getVisibleLayers()); + + // Compute scene-dependent values shared across all shadow maps + ShadowMap::initSceneInfo( + *view.getScene(), view.getCameraInfo(), + sceneInfo); + + shadowTechnique |= updateCascadeShadowMaps( + engine, view, renderableData, lightData, sceneInfo); + + shadowTechnique |= updateSpotShadowMaps( + engine, view, renderableData, lightData, sceneInfo, shadowUb); + return shadowTechnique; } @@ -321,26 +333,18 @@ void ShadowMapManager::render(FrameGraph& fg, FEngine& engine, backend::DriverAp fg.getBlackboard().put("shadows", shadows); } -ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps( - FEngine& engine, FView& view, FScene::RenderableSoa& renderableData, - FScene::LightSoa& lightData) noexcept { +ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEngine& engine, + FView& view, FScene::RenderableSoa& renderableData, FScene::LightSoa& lightData, + ShadowMap::SceneInfo& sceneInfo) noexcept { FScene* scene = view.getScene(); const CameraInfo& viewingCameraInfo = view.getCameraInfo(); - uint8_t visibleLayers = view.getVisibleLayers(); const uint16_t textureSize = mTextureRequirements.size; auto& lcm = engine.getLightManager(); FLightManager::Instance directionalLight = lightData.elementAt(0); LightManager::ShadowOptions const& options = lcm.getShadowOptions(directionalLight); - ShadowMap::SceneInfo sceneInfo; - if (!mCascadeShadowMaps.empty()) { - // Compute scene-dependent values shared across all cascades - const float3 dir = lightData.elementAt(0); - ShadowMap::computeSceneInfo(dir, - *scene, viewingCameraInfo, visibleLayers, sceneInfo); - // Even if we have more than one cascade, we cull directional shadow casters against the // entire camera frustum, as if we only had a single cascade. ShadowMapEntry& entry = mCascadeShadowMaps[0]; @@ -354,7 +358,7 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps( .vsm = view.hasVsm() }; - map.update(lightData, 0, viewingCameraInfo, shadowMapInfo, sceneInfo); + map.update(lightData, 0, viewingCameraInfo, shadowMapInfo, *scene, sceneInfo); Frustum const& frustum = map.getCamera().getCullingFrustum(); FView::cullRenderables(engine.getJobSystem(), renderableData, frustum, @@ -430,7 +434,11 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps( .vsm = view.hasVsm() }; sceneInfo.csNearFar = { csSplitPosition[i], csSplitPosition[i + 1] }; - shadowMap.update(lightData, 0, viewingCameraInfo, shadowMapInfo, sceneInfo); + + shadowMap.update(lightData, 0, + viewingCameraInfo, shadowMapInfo, + *scene,sceneInfo); + if (shadowMap.hasVisibleShadows()) { mShadowMappingUniforms.lightFromWorldMatrix[i] = shadowMap.getLightSpaceMatrix(); shadowTechnique |= ShadowTechnique::SHADOW_MAP; @@ -466,9 +474,9 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps( return shadowTechnique; } -ShadowMapManager::ShadowTechnique ShadowMapManager::updateSpotShadowMaps( - FEngine& engine, FView& view, TypedUniformBuffer& shadowUb, - FScene::RenderableSoa& renderableData, FScene::LightSoa& lightData) noexcept { +ShadowMapManager::ShadowTechnique ShadowMapManager::updateSpotShadowMaps(FEngine& engine, + FView& view, FScene::RenderableSoa& renderableData, FScene::LightSoa& lightData, + ShadowMap::SceneInfo& sceneInfo, TypedUniformBuffer & shadowUb) noexcept { ShadowTechnique shadowTechnique{}; const CameraInfo& viewingCameraInfo = view.getCameraInfo(); @@ -492,16 +500,19 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateSpotShadowMaps( .shadowDimension = (uint16_t)(textureDimension - 2), .vsm = view.hasVsm() }; - shadowMap.update(lightData, l, viewingCameraInfo, shadowMapInfo, {}); + + shadowMap.update(lightData, l, + viewingCameraInfo, shadowMapInfo, + *view.getScene(), sceneInfo); FLightManager::Instance light = lightData.elementAt(l); if (shadowMap.hasVisibleShadows()) { // Cull shadow casters - auto& s = shadowUb.edit(); Frustum const& frustum = shadowMap.getCamera().getCullingFrustum(); FView::cullRenderables(engine.getJobSystem(), renderableData, frustum, VISIBLE_SPOT_SHADOW_RENDERABLE_N_BIT(i)); + auto& s = shadowUb.edit(); s.spotLightFromWorldMatrix[i] = shadowMap.getLightSpaceMatrix(); shadowInfo[l].castsShadows = true; diff --git a/filament/src/ShadowMapManager.h b/filament/src/ShadowMapManager.h index fc07aaeafb..838c6ebc72 100644 --- a/filament/src/ShadowMapManager.h +++ b/filament/src/ShadowMapManager.h @@ -120,12 +120,13 @@ private: uint8_t levels = 0; } mTextureRequirements; - ShadowTechnique updateCascadeShadowMaps(FEngine& engine, FView& view, - FScene::RenderableSoa& renderableData, FScene::LightSoa& lightData) noexcept; + ShadowTechnique updateCascadeShadowMaps(FEngine& engine, + FView& view, FScene::RenderableSoa& renderableData, FScene::LightSoa& lightData, + ShadowMap::SceneInfo& sceneInfo) noexcept; - ShadowTechnique updateSpotShadowMaps(FEngine& engine, FView& view, - TypedUniformBuffer& shadowUb, - FScene::RenderableSoa& renderableData, FScene::LightSoa& lightData) noexcept; + ShadowTechnique updateSpotShadowMaps(FEngine& engine, + FView& view, FScene::RenderableSoa& renderableData, FScene::LightSoa& lightData, + ShadowMap::SceneInfo& sceneInfo, TypedUniformBuffer& shadowUb) noexcept; void calculateTextureRequirements(FEngine& engine, FView& view, FScene::LightSoa& lightData) noexcept; From af7f87e19b320ddb97cda5ee56c03acad808e596 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Mon, 1 Nov 2021 14:18:36 -0700 Subject: [PATCH 13/18] make sure the near plane for spot shadows is not negative --- filament/src/ShadowMap.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/filament/src/ShadowMap.cpp b/filament/src/ShadowMap.cpp index 45c4358896..c9fb1a239a 100644 --- a/filament/src/ShadowMap.cpp +++ b/filament/src/ShadowMap.cpp @@ -439,7 +439,8 @@ void ShadowMap::computeShadowCameraSpot(float3 const& position, float3 const& di // find decent near/far // TODO: we can do much better by rejecting objects that don't intersect our frustum ShadowMap::updateSceneInfo(Mv, scene, sceneInfo); - float nearPlane = -sceneInfo.lsNearFar.x; + // FIXME: we need a configuration minimum near plane (for now hardcoded to 1cm) + float nearPlane = std::max(0.01f, -sceneInfo.lsNearFar.x); float farPlane = std::min(radius, -sceneInfo.lsNearFar.y); float outerConeAngleDegrees = outerConeAngle * f::RAD_TO_DEG; From 81fa33abeb5ff8f0d17b8109cefbf783f098f47b Mon Sep 17 00:00:00 2001 From: Benjamin Doherty Date: Thu, 28 Oct 2021 16:13:34 -0700 Subject: [PATCH 14/18] Release Filament 1.12.11 --- README.md | 4 ++-- RELEASE_NOTES.md | 7 ++++++- android/gradle.properties | 2 +- ios/CocoaPods/Filament.podspec | 4 ++-- web/filament-js/package.json | 2 +- 5 files changed, 12 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 4c0d4d7505..b454300bfa 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ repositories { } dependencies { - implementation 'com.google.android.filament:filament-android:1.12.10' + implementation 'com.google.android.filament:filament-android:1.12.11' } ``` @@ -52,7 +52,7 @@ Here are all the libraries available in the group `com.google.android.filament`: iOS projects can use CocoaPods to install the latest release: ``` -pod 'Filament', '~> 1.12.10' +pod 'Filament', '~> 1.12.11' ``` ### Snapshots diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index bf1c03ba3a..f5095a342b 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -3,7 +3,9 @@ This file contains one line summaries of commits that are worthy of mentioning in release notes. A new header is inserted each time a *tag* is created. -## v1.13.0 (currently main branch) +## v1.13.1 (currently main branch) + +## v1.13.0 - Android: Gradle configuration caching is now enabled - Android: Filament's Gradle properties have all been renamed to `com.google.android.filament.xxx` @@ -15,6 +17,9 @@ A new header is inserted each time a *tag* is created. ## v1.12.11 +- Metal: Color grading performance improvement on M1 devices. +- samples: Fix glitchy animation seen in gltf-viewer iOS sample. + ## v1.12.10 - engine: rewrite dynamic resolution scaling controller for better accuracy and less jittering. diff --git a/android/gradle.properties b/android/gradle.properties index 349cd8c460..6ffbe2bd44 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,5 +1,5 @@ GROUP=com.google.android.filament -VERSION_NAME=1.12.10 +VERSION_NAME=1.12.11 POM_DESCRIPTION=Real-time physically based rendering engine for Android. diff --git a/ios/CocoaPods/Filament.podspec b/ios/CocoaPods/Filament.podspec index 8b9cc223f2..71c8b62198 100644 --- a/ios/CocoaPods/Filament.podspec +++ b/ios/CocoaPods/Filament.podspec @@ -1,12 +1,12 @@ Pod::Spec.new do |spec| spec.name = "Filament" - spec.version = "1.12.10" + spec.version = "1.12.11" spec.license = { :type => "Apache 2.0", :file => "LICENSE" } spec.homepage = "https://google.github.io/filament" spec.authors = "Google LLC." spec.summary = "Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WASM/WebGL." spec.platform = :ios, "11.0" - spec.source = { :http => "https://github.com/google/filament/releases/download/v1.12.10/filament-v1.12.10-ios.tgz" } + spec.source = { :http => "https://github.com/google/filament/releases/download/v1.12.11/filament-v1.12.11-ios.tgz" } # Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon. spec.pod_target_xcconfig = { diff --git a/web/filament-js/package.json b/web/filament-js/package.json index b1248ad603..34ae334466 100644 --- a/web/filament-js/package.json +++ b/web/filament-js/package.json @@ -1,6 +1,6 @@ { "name": "filament", - "version": "1.12.10", + "version": "1.12.11", "description": "Real-time physically based rendering engine", "main": "filament.js", "module": "filament.js", From 9987e8b6ab753f60e35c7f45cd2f9ccb87c6103b Mon Sep 17 00:00:00 2001 From: Benjamin Doherty Date: Mon, 1 Nov 2021 14:59:11 -0700 Subject: [PATCH 15/18] Bump version to 1.13.0 --- README.md | 4 ++-- android/gradle.properties | 2 +- ios/CocoaPods/Filament.podspec | 4 ++-- web/filament-js/package.json | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index b454300bfa..564b41fde2 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ repositories { } dependencies { - implementation 'com.google.android.filament:filament-android:1.12.11' + implementation 'com.google.android.filament:filament-android:1.13.0' } ``` @@ -52,7 +52,7 @@ Here are all the libraries available in the group `com.google.android.filament`: iOS projects can use CocoaPods to install the latest release: ``` -pod 'Filament', '~> 1.12.11' +pod 'Filament', '~> 1.13.0' ``` ### Snapshots diff --git a/android/gradle.properties b/android/gradle.properties index 6ffbe2bd44..63e61ee5ae 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,5 +1,5 @@ GROUP=com.google.android.filament -VERSION_NAME=1.12.11 +VERSION_NAME=1.13.0 POM_DESCRIPTION=Real-time physically based rendering engine for Android. diff --git a/ios/CocoaPods/Filament.podspec b/ios/CocoaPods/Filament.podspec index 71c8b62198..54d2876077 100644 --- a/ios/CocoaPods/Filament.podspec +++ b/ios/CocoaPods/Filament.podspec @@ -1,12 +1,12 @@ Pod::Spec.new do |spec| spec.name = "Filament" - spec.version = "1.12.11" + spec.version = "1.13.0" spec.license = { :type => "Apache 2.0", :file => "LICENSE" } spec.homepage = "https://google.github.io/filament" spec.authors = "Google LLC." spec.summary = "Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WASM/WebGL." spec.platform = :ios, "11.0" - spec.source = { :http => "https://github.com/google/filament/releases/download/v1.12.11/filament-v1.12.11-ios.tgz" } + spec.source = { :http => "https://github.com/google/filament/releases/download/v1.13.0/filament-v1.13.0-ios.tgz" } # Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon. spec.pod_target_xcconfig = { diff --git a/web/filament-js/package.json b/web/filament-js/package.json index 34ae334466..9ffe7b243a 100644 --- a/web/filament-js/package.json +++ b/web/filament-js/package.json @@ -1,6 +1,6 @@ { "name": "filament", - "version": "1.12.11", + "version": "1.13.0", "description": "Real-time physically based rendering engine", "main": "filament.js", "module": "filament.js", From b2dc8aa84caa84bdf2a346e3423c207f5fc8e765 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Mon, 1 Nov 2021 16:39:12 -0700 Subject: [PATCH 16/18] Fix typo that broke the directional shadowmap --- filament/src/ShadowMap.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/filament/src/ShadowMap.cpp b/filament/src/ShadowMap.cpp index c9fb1a239a..8e16878b50 100644 --- a/filament/src/ShadowMap.cpp +++ b/filament/src/ShadowMap.cpp @@ -1069,6 +1069,8 @@ void ShadowMap::visitScene(const FScene& scene, uint32_t visibleLayers, void ShadowMap::initSceneInfo(FScene const& scene, filament::CameraInfo const& camera, ShadowMap::SceneInfo& sceneInfo) { + sceneInfo.vsNearFar = { std::numeric_limits::lowest(), std::numeric_limits::max() }; + // We assume the light is at the origin to compute the SceneInfo. This is consumed later by // computeShadowCameraDirectional() which takes this into account. const mat4f V = camera.view; @@ -1098,7 +1100,6 @@ void ShadowMap::initSceneInfo(FScene const& scene, filament::CameraInfo const& c void ShadowMap::updateSceneInfo(const mat4f& Mv, FScene const& scene, ShadowMap::SceneInfo& sceneInfo) { sceneInfo.lsNearFar = { std::numeric_limits::lowest(), std::numeric_limits::max() }; - sceneInfo.vsNearFar = { std::numeric_limits::lowest(), std::numeric_limits::max() }; visitScene(scene, sceneInfo.visibleLayers, [&](Aabb caster) { float2 nf = ShadowMap::computeNearFar(Mv, caster); From bc0ea16ff01093c9abee5f53d0fd7cdebe6f0d2e Mon Sep 17 00:00:00 2001 From: Benjamin Doherty Date: Tue, 2 Nov 2021 13:15:53 -0700 Subject: [PATCH 17/18] Update RELEASE_NOTES for 1.13.0 --- RELEASE_NOTES.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index f5095a342b..5ed6b5349e 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -7,13 +7,14 @@ A new header is inserted each time a *tag* is created. ## v1.13.0 -- Android: Gradle configuration caching is now enabled +- Android: Gradle configuration caching is now enabled. - Android: Filament's Gradle properties have all been renamed to `com.google.android.filament.xxx` where `xxx` is the property name. See `android/build.gradle` for a complete list [⚠️] - Android: The Gradle property `filament_tools_dir` (now called - `com.google.android.filament.tools-dir`) does not have a default value anymore. Please specify - one in your `gradle.properties` if you reuse the Gradle plugin in your projects [⚠️] -- Engine: Fix spotlights direction and falloff. [⚠️ **Material breakage**]. + `com.google.android.filament.tools-dir`) does not have a default value anymore. Please specify one + in your `gradle.properties` if you reuse the Gradle plugin in your projects [⚠️] +- engine: Fix spotlights direction and falloff [⚠️ **Material breakage**]. +- engine: Improvements to VSM and spotlight shadows. ## v1.12.11 From a74a95cc650bc9ee7ea6b3ab7eb3a1dd7639c64e Mon Sep 17 00:00:00 2001 From: Ben Doherty Date: Thu, 28 Oct 2021 16:02:23 -0700 Subject: [PATCH 18/18] Call VirtualMachineEnv::JNI_OnLoad for non-Android Java builds (better fix) (#4779) --- android/filament-android/src/main/cpp/Filament.cpp | 10 ++++------ .../src/opengl/platforms/PlatformEGLAndroid.cpp | 5 ----- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/android/filament-android/src/main/cpp/Filament.cpp b/android/filament-android/src/main/cpp/Filament.cpp index 03ac36b840..0ae41e9270 100644 --- a/android/filament-android/src/main/cpp/Filament.cpp +++ b/android/filament-android/src/main/cpp/Filament.cpp @@ -16,9 +16,7 @@ #include -namespace filament { - extern jint JNI_OnLoad(JavaVM* vm, void* reserved); -}; +#include "private/backend/VirtualMachineEnv.h" JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) { JNIEnv* env; @@ -26,9 +24,9 @@ JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) { return -1; } -#if ANDROID - ::filament::JNI_OnLoad(vm, reserved); -#endif + // This must be called when the library is loaded. We need this to get a reference to the + // global VM + ::filament::VirtualMachineEnv::JNI_OnLoad(vm); return JNI_VERSION_1_6; } diff --git a/filament/backend/src/opengl/platforms/PlatformEGLAndroid.cpp b/filament/backend/src/opengl/platforms/PlatformEGLAndroid.cpp index 572ee44c80..fdbdbe0fe5 100644 --- a/filament/backend/src/opengl/platforms/PlatformEGLAndroid.cpp +++ b/filament/backend/src/opengl/platforms/PlatformEGLAndroid.cpp @@ -245,11 +245,6 @@ AcquiredImage PlatformEGLAndroid::transformAcquiredImage(AcquiredImage source) n return { eglImage, patchedCallback, closure, source.handler }; } -// This must be called when the library is loaded. We need this to get a reference to the global VM -void JNI_OnLoad(JavaVM* vm, void* reserved) { - ::filament::VirtualMachineEnv::JNI_OnLoad(vm); -} - } // namespace filament // ---------------------------------------------------------------------------------------------