Compare commits

..

8 Commits

Author SHA1 Message Date
Benjamin Doherty
b236bd4f50 Fix cancelled vertices on iOS OpenGL ES simulator 2022-11-15 20:24:21 -08:00
Benjamin Doherty
b7b7afb62a Bump version to 1.29.0 2022-11-09 16:43:08 -08:00
Benjamin Doherty
957380b258 Release Filament 1.28.3 (ignore prev commit) 2022-11-09 16:41:13 -08:00
Benjamin Doherty
fe3f16924d Release Filament 1.29.0 2022-11-09 16:37:39 -08:00
Ben Doherty
ffc3128377 Skip rendering renderables with missing geometry (#6281) 2022-11-09 16:35:02 -08:00
Mathias Agopian
c05e2ec47d improve variant filter
- variant filter didn't filter the VSM variant of depth variants

Fix #6274
2022-11-08 16:48:54 -08:00
Mathias Agopian
9768f49714 unify point and spotlight shadows
point light shadows are not sampled just like spotlight shadows, the
only difference is that we're calculating the face first to access the
corresponding shadowmap data (including the light transform).
2022-11-08 16:35:54 -08:00
Mathias Agopian
6c54cfe88a Shadowmaping code improvements
- the layer is now stored in the shadow ubo
- we now have one shadow ubo per face
- ShadowMap holds UBO index even for cascades
- move some shadow "view" uniforms into the shadow UBO
2022-11-08 15:25:33 -08:00
18 changed files with 108 additions and 153 deletions

View File

@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.28.2'
implementation 'com.google.android.filament:filament-android:1.29.0'
}
```
@@ -51,7 +51,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.28.2'
pod 'Filament', '~> 1.29.0'
```
### Snapshots

View File

@@ -5,6 +5,8 @@ A new header is inserted each time a *tag* is created.
## main branch
## v1.29.0
- gltfio: calculate primitive's AABB correctly.
- gltfio: recompute bounding boxes with morph targets
- engine: add missing getters on `MaterialInstance`

View File

@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.28.2
VERSION_NAME=1.29.0
POM_DESCRIPTION=Real-time physically based rendering engine for Android.

View File

@@ -750,6 +750,11 @@ void RenderPass::Executor::execute(backend::DriverApi& driver,
continue;
}
// primitiveHandle may be invalid if no geometry was set on the renderable.
if (UTILS_UNLIKELY(!first->primitive.primitiveHandle)) {
continue;
}
// per-renderable uniform
const PrimitiveInfo info = first->primitive;
pipeline.rasterState = info.rasterState;

View File

@@ -413,41 +413,10 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
return shaderParameters;
}
ShadowMap::ShaderParameters ShadowMap::updateSpot(FEngine& engine,
const FScene::LightSoa& lightData, size_t index,
filament::CameraInfo const& camera,
const ShadowMapInfo& shadowMapInfo,
FScene const& scene, SceneInfo sceneInfo) noexcept {
ShaderParameters shaderParameters;
auto& lcm = engine.getLightManager();
auto li = lightData.elementAt<FScene::LIGHT_INSTANCE>(index);
auto position = lightData.elementAt<FScene::POSITION_RADIUS>(index).xyz;
auto direction = lightData.elementAt<FScene::DIRECTION>(index);
auto radius = lightData.elementAt<FScene::POSITION_RADIUS>(index).w;
auto outerConeAngle = lcm.getSpotLightOuterCone(li);
const FLightManager::ShadowParams& params = lcm.getShadowParams(li);
/*
* Compute the light model matrix.
*/
// Choose a reasonable value for the near plane.
const mat4f Mv = getDirectionalLightViewMatrix(direction, position);
// find decent near/far
ShadowMap::updateSceneInfoSpot(Mv, scene, sceneInfo);
// if the scene was empty, near > far
mHasVisibleShadows = -sceneInfo.lsNearFar[0] < -sceneInfo.lsNearFar[1];
// FIXME: we need a configuration for minimum near plane (for now hardcoded to 1cm)
float nearPlane = std::max(0.01f, -sceneInfo.lsNearFar[0]);
float farPlane = std::min(radius, -sceneInfo.lsNearFar[1]);
float outerConeAngleDegrees = outerConeAngle * f::RAD_TO_DEG;
const mat4f Mp = mat4f::perspective(outerConeAngleDegrees * 2.0f, 1.0f, nearPlane, farPlane);
ShadowMap::ShaderParameters ShadowMap::updateSpotOrPoint(
mat4f const& Mv, float outerConeAngle, float nearPlane, float farPlane,
const ShadowMapInfo& shadowMapInfo, const FLightManager::ShadowParams& params) noexcept {
const mat4f Mp = mat4f::perspective(outerConeAngle * f::RAD_TO_DEG * 2.0f, 1.0f, nearPlane, farPlane);
const mat4f MpMv(math::highPrecisionMultiply(Mp, Mv));
// Final shadow transform
@@ -469,7 +438,9 @@ ShadowMap::ShaderParameters ShadowMap::updateSpot(FEngine& engine,
// = zInLightSpace * texelSizeAtOneMeter
// = zInLightSpace * (2*tan(halfConeAngle)/dimension)
// Note: this would not work with LISPSM, which warps the texture space.
shaderParameters.texelSizeAtOneMeterWs = (2.0f * std::tan(outerConeAngle) / float(shadowMapInfo.shadowDimension));
ShaderParameters shaderParameters;
shaderParameters.texelSizeAtOneMeterWs =
(2.0f * std::tan(outerConeAngle) / float(shadowMapInfo.shadowDimension));
shaderParameters.lightFromWorldZ = -transpose(Mv)[2]; // negate because camera looks in -Z
if (!shadowMapInfo.vsm) {
@@ -478,9 +449,9 @@ ShadowMap::ShaderParameters ShadowMap::updateSpot(FEngine& engine,
shaderParameters.lightSpace = computeVsmLightSpaceMatrix(St, Mv, nearPlane, farPlane);
}
const float3 direction = -transpose(Mv)[2].xyz;
const float constantBias = shadowMapInfo.vsm ? 0.0f : params.options.constantBias;
const mat4f b = mat4f::translation(direction * constantBias);
const mat4f Sb = S * b;
// It's important to set the light camera's model matrix separately from its projection, so that
// the cameraPosition uniform gets set correctly.
@@ -493,19 +464,44 @@ ShadowMap::ShaderParameters ShadowMap::updateSpot(FEngine& engine,
mCamera->setModelMatrix(mat4{ FCamera::rigidTransformInverse(Mv * b) });
mCamera->setCustomProjection(mat4(Mp), nearPlane, farPlane);
// for the debug camera, we need to undo the world origin
mDebugCamera->setCustomProjection(mat4(Sb * camera.worldOrigin), nearPlane, radius);
return shaderParameters;
}
ShadowMap::ShaderParameters ShadowMap::updateSpot(FEngine& engine,
const FScene::LightSoa& lightData, size_t index,
filament::CameraInfo const& camera,
const ShadowMapInfo& shadowMapInfo,
FScene const& scene, SceneInfo sceneInfo) noexcept {
auto& lcm = engine.getLightManager();
auto position = lightData.elementAt<FScene::POSITION_RADIUS>(index).xyz;
auto direction = lightData.elementAt<FScene::DIRECTION>(index);
auto radius = lightData.elementAt<FScene::POSITION_RADIUS>(index).w;
auto li = lightData.elementAt<FScene::LIGHT_INSTANCE>(index);
const FLightManager::ShadowParams& params = lcm.getShadowParams(li);
const mat4f Mv = getDirectionalLightViewMatrix(direction, position);
// find decent near/far
ShadowMap::updateSceneInfoSpot(Mv, scene, sceneInfo);
// if the scene was empty, near > far
mHasVisibleShadows = -sceneInfo.lsNearFar[0] < -sceneInfo.lsNearFar[1];
if (!mHasVisibleShadows) {
return {};
}
// FIXME: we need a configuration for minimum near plane (for now hardcoded to 1cm)
float nearPlane = std::max(0.01f, -sceneInfo.lsNearFar[0]);
float farPlane = std::min(radius, -sceneInfo.lsNearFar[1]);
auto outerConeAngle = lcm.getSpotLightOuterCone(li);
return updateSpotOrPoint(Mv, outerConeAngle, nearPlane, farPlane, shadowMapInfo, params);
}
ShadowMap::ShaderParameters ShadowMap::updatePoint(FEngine& engine,
const FScene::LightSoa& lightData, size_t index,
filament::CameraInfo const& camera, const ShadowMapInfo& shadowMapInfo, FScene const& scene,
SceneInfo, uint8_t face) noexcept {
ShaderParameters shaderParameters;
// check if this shadow map has anything to render
mHasVisibleShadows = false;
FScene::RenderableSoa const& UTILS_RESTRICT soa = scene.getRenderableData();
@@ -518,53 +514,16 @@ ShadowMap::ShaderParameters ShadowMap::updatePoint(FEngine& engine,
}
}
if (!mHasVisibleShadows) {
return shaderParameters;
return {};
}
auto& lcm = engine.getLightManager();
auto li = lightData.elementAt<FScene::LIGHT_INSTANCE>(index);
auto position = lightData.elementAt<FScene::POSITION_RADIUS>(index).xyz;
auto radius = lightData.elementAt<FScene::POSITION_RADIUS>(index).w;
auto li = lightData.elementAt<FScene::LIGHT_INSTANCE>(index);
const FLightManager::ShadowParams& params = lcm.getShadowParams(li);
/*
* Compute the light model matrix.
*/
const mat4f Mv = getPointLightViewMatrix(TextureCubemapFace(face), position);
const float3 direction = -transpose(Mv)[2].xyz;
// TODO: don't hardcode near plane
// Choose a reasonable value for the near plane.
float nearPlane = 0.01f;
float farPlane = radius;
const mat4f Mp = mat4f::perspective(90.0f, 1.0f, nearPlane, farPlane);
// For calculating the point light normal bias, we need the texel size in world space at the
// sample location. Using Thales's theorem, we find:
// texelSize(zInLightSpace) = zInLightSpace * texelSizeOnTheNearPlane / near
// = zInLightSpace * texelSizeAtOneMeter
// = zInLightSpace * (2*tan(halfConeAngle)/dimension)
// Note: this would not work with LISPSM, which warps the texture space.
shaderParameters.texelSizeAtOneMeterWs =
(2.0f * std::tan(f::PI_4) / float(shadowMapInfo.shadowDimension));
const float constantBias = shadowMapInfo.vsm ? 0.0f : params.options.constantBias;
const mat4f b = mat4f::translation(direction * constantBias);
// It's important to set the light camera's model matrix separately from its projection, so that
// the cameraPosition uniform gets set correctly.
// mLightSpace is used in the shader to access the shadow map texture, and has the model matrix
// baked in.
// The model matrix below is in fact inverted to get the view matrix and passed to the
// shader as 'viewFromWorldMatrix', and is used in the VSM case to compute the depth metric.
// (see depth_main.fs). Note that in the case of VSM, 'b' below is identity.
mCamera->setModelMatrix(mat4{ FCamera::rigidTransformInverse(Mv * b) });
mCamera->setCustomProjection(mat4(Mp), nearPlane, farPlane);
return shaderParameters;
return updateSpotOrPoint(Mv, 45.0f * f::DEG_TO_RAD, 0.01f, radius, shadowMapInfo, params);
}
mat4f ShadowMap::applyLISPSM(mat4f& Wp,

View File

@@ -214,6 +214,11 @@ private:
// 8 corners, 12 segments w/ 2 intersection max -- all of this twice (8 + 12 * 2) * 2 (768 bytes)
using FrustumBoxIntersection = std::array<math::float3, 64>;
ShaderParameters updateSpotOrPoint(
math::mat4f const& Mv, float outerConeAngle, float nearPlane, float farPlane,
const ShadowMapInfo& shadowMapInfo,
const FLightManager::ShadowParams& params) noexcept;
static math::mat4f applyLISPSM(math::mat4f& Wp,
filament::CameraInfo const& camera, FLightManager::ShadowParams const& params,
const math::mat4f& LMpMv,

View File

@@ -690,7 +690,7 @@ void ShadowMapManager::prepareSpotShadowMap(ShadowMap& shadowMap,
s.shadows[shadowIndex].elvsm = options->vsm.elvsm;
s.shadows[shadowIndex].bulbRadiusLs =
mSoftShadowOptions.penumbraScale * options->shadowBulbRadius
/ wsTexelSizeAtOneMeter;
/ wsTexelSizeAtOneMeter;
}
}
@@ -763,15 +763,10 @@ void ShadowMapManager::preparePointShadowMap(ShadowMap& shadowMap,
const double n = shadowMap.getCamera().getNear();
const double f = shadowMap.getCamera().getCullingFar();
s.shadows[shadowIndex].layer = shadowMap.getLayer();
s.shadows[shadowIndex].lightFromWorldMatrix = {}; // no texture matrix for point lights
s.shadows[shadowIndex].lightFromWorldMatrix = shaderParameters.lightSpace;
s.shadows[shadowIndex].direction = {}; // no direction of point lights
s.shadows[shadowIndex].normalBias = normalBias * wsTexelSizeAtOneMeter;
s.shadows[shadowIndex].lightFromWorldZ = {
-((n + f) / (f - n)) * 0.5f + 0.5f,
(f * n) / (f - n),
-n / (f - n),
1.0f / (f - n),
};
s.shadows[shadowIndex].lightFromWorldZ = shaderParameters.lightFromWorldZ;
s.shadows[shadowIndex].texelSizeAtOneMeter = wsTexelSizeAtOneMeter;
s.shadows[shadowIndex].nearOverFarMinusNear = float(n / (f - n));
s.shadows[shadowIndex].elvsm = options->vsm.elvsm;

View File

@@ -442,9 +442,15 @@ Program FMaterial::getProgramBuilderWithVariants(
}
}
int platformId = 0;
#if defined(IOS)
platformId = 1;
#endif
program.specializationConstants({
{ 0, (int)mEngine.getSupportedFeatureLevel() },
{ 1, (int)CONFIG_MAX_INSTANCES }
{ 1, (int)CONFIG_MAX_INSTANCES },
{ 2, platformId }
});
return program;

View File

@@ -1,12 +1,12 @@
Pod::Spec.new do |spec|
spec.name = "Filament"
spec.version = "1.28.2"
spec.version = "1.29.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.28.2/filament-v1.28.2-ios.tgz" }
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.29.0/filament-v1.29.0-ios.tgz" }
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
spec.pod_target_xcconfig = {

View File

@@ -247,10 +247,10 @@ static_assert(sizeof(LightsUib) == 64,
struct ShadowUib { // NOLINT(cppcoreguidelines-pro-type-member-init)
static constexpr std::string_view _name{ "ShadowUniforms" };
struct alignas(16) ShadowData {
math::mat4f lightFromWorldMatrix; // 64 - unused for point lights
math::float3 direction; // 12 - unused for point lights
float normalBias; // 4 - unused for point lights
math::float4 lightFromWorldZ; // 16 - point lights { depth reconstruction values }
math::mat4f lightFromWorldMatrix; // 64
math::float3 direction; // 12
float normalBias; // 4
math::float4 lightFromWorldZ; // 16
float texelSizeAtOneMeter; // 4
float bulbRadiusLs; // 4

View File

@@ -35,6 +35,11 @@ Variant Variant::filterUserVariant(
if (filterMask & (uint32_t)UserVariantFilterBit::FOG) {
variant.key &= ~(filterMask & FOG);
}
} else {
// depth variants can have their VSM bit filtered
if (filterMask & (uint32_t)UserVariantFilterBit::VSM) {
variant.key &= ~(filterMask & VSM);
}
}
if (!isSSRVariant(variant)) {
// SSR variant needs to be handled separately
@@ -53,8 +58,6 @@ Variant Variant::filterUserVariant(
return variant;
}
namespace details {
// compile time sanity-check tests

View File

@@ -123,6 +123,7 @@ utils::io::sstream& CodeGenerator::generateProlog(utils::io::sstream& out, Shade
out << '\n';
generateSpecificationConstant(out, "BACKEND_FEATURE_LEVEL", 0, 1);
generateSpecificationConstant(out, "CONFIG_MAX_INSTANCES", 1, (int)CONFIG_MAX_INSTANCES);
generateSpecificationConstant(out, "TARGET_PLATFORM", 2, 0);
out << '\n';
out << SHADERS_COMMON_DEFINES_GLSL_DATA;

View File

@@ -22,3 +22,11 @@
#define float3x3 mat3
#define float4x4 mat4
#define TARGET_PLATFORM_UNKNOWN 0
#define TARGET_PLATFORM_IOS 1
#if defined(TARGET_GLES_ENVIRONMENT)
const bool openGlesIos = TARGET_PLATFORM == TARGET_PLATFORM_IOS;
#else
const bool openGlesIos = false;
#endif

View File

@@ -128,7 +128,7 @@ 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) {
if (cascade == 0u && !openGlesIos) {
// Note: this branch may cause issues with derivatives
return vertex_lightSpacePosition;
}

View File

@@ -211,18 +211,15 @@ void evaluatePunctualLights(const MaterialInputs material,
if (light.NoL > 0.0) {
if (light.castsShadows) {
uint shadowIndex = light.shadowIndex;
highp vec4 shadowPosition;
if (light.type == LIGHT_TYPE_POINT) {
// point-light shadows are sampled from a direction
highp vec3 r = getWorldPosition() - light.worldPosition;
highp uint face = 0u;
// getShadowPosition returns zLight which is needed for PCSS/DPCF
shadowPosition = getShadowPosition(r, shadowIndex, light.zLight, face);
uint face = getPointLightFace(r);
shadowIndex += face;
} else {
// getShadowPosition needs zLight for applying the normal bias
shadowPosition = getShadowPosition(false, shadowIndex, 0u, light.zLight);
light.zLight = dot(shadowUniforms.shadows[shadowIndex].lightFromWorldZ,
vec4(getWorldPosition(), 1.0));
}
highp vec4 shadowPosition = getShadowPosition(false, shadowIndex, 0u, light.zLight);
visibility = shadow(false, light_shadowMap, shadowIndex,
shadowPosition, light.zLight);
}

View File

@@ -141,11 +141,16 @@ void main() {
#endif
#if defined(VARIANT_HAS_SHADOWING) && defined(VARIANT_HAS_DIRECTIONAL_LIGHTING)
vertex_lightSpacePosition = computeLightSpacePosition(
vertex_worldPosition.xyz, vertex_worldNormal,
frameUniforms.lightDirection,
shadowUniforms.shadows[0].normalBias,
shadowUniforms.shadows[0].lightFromWorldMatrix);
if (openGlesIos) {
// Hack for OpenGL ES on iOS.
vertex_lightSpacePosition = vec4(1.0);
} else {
vertex_lightSpacePosition = computeLightSpacePosition(
vertex_worldPosition.xyz, vertex_worldNormal,
frameUniforms.lightDirection,
shadowUniforms.shadows[0].normalBias,
shadowUniforms.shadows[0].lightFromWorldMatrix);
}
#endif
#endif // !defined(USE_OPTIMIZED_DEPTH_VERTEX_SHADER)

View File

@@ -494,51 +494,20 @@ highp vec4 getShadowPosition(const bool DIRECTIONAL,
return p;
}
// get {texture coordinate, layer} for point shadow maps
highp vec4 getShadowPosition(const highp vec3 r, const highp uint shadowIndex,
out highp float d, out highp uint face) {
uint getPointLightFace(const highp vec3 r) {
highp vec4 tc;
highp float rx = abs(r.x);
highp float ry = abs(r.y);
highp float rz = abs(r.z);
d = max(rx, max(ry, rz));
highp float ma = 1.0 / d;
highp float d = max(rx, max(ry, rz));
if (d == rx) {
tc.x = r.x >= 0.0 ? r.z : -r.z;
tc.y = r.y;
face = (r.x >= 0.0 ? 0u : 1u);
return (r.x >= 0.0 ? 0u : 1u);
} else if (d == ry) {
tc.x = r.y >= 0.0 ? r.x : -r.x;
tc.y = r.z;
face = (r.y >= 0.0 ? 2u : 3u);
return (r.y >= 0.0 ? 2u : 3u);
} else {
tc.x = r.z >= 0.0 ? -r.x : r.x;
tc.y = r.y;
face = (r.z >= 0.0 ? 4u : 5u);
return (r.z >= 0.0 ? 4u : 5u);
}
// ma is guaranteed to be >= sc and tc
tc.xy = (tc.xy * ma + vec2(1.0)) * 0.5;
highp vec4 nf = shadowUniforms.shadows[shadowIndex + face].lightFromWorldZ;
// z coordinate of the normalized fragment position in light-space
// i.e.: remap [near, far] to [0,1] : d = (d - n) / (f - n)
d = nf[2] + nf[3] * d;
if (frameUniforms.shadowSamplingType == SHADOW_SAMPLING_RUNTIME_EVSM) {
// for VSM, the depth metric is linear normalized in light-space
tc.z = d;
} else {
// for other types of shadows it's clip-space depth. Below is an optimized version of
// (lightProjection * position).z
tc.z = nf[0] + nf[1] * ma;
}
// FIXME: the normal bias is not applied
tc.w = 1.0;
return tc;
}
// PCF sampling

View File

@@ -1,6 +1,6 @@
{
"name": "filament",
"version": "1.28.2",
"version": "1.29.0",
"description": "Real-time physically based rendering engine",
"main": "filament.js",
"module": "filament.js",