Revert "fix a couple shadow stability bugs"
This reverts commit 1b0db0fca2.
This commit is contained in:
@@ -15,7 +15,6 @@ Instead, if you are authoring a PR for the main branch, add your release note to
|
||||
- engine: New tone mapper: `AgXTonemapper`.
|
||||
- matinfo: Add support for viewing ESSL 1.0 shaders
|
||||
- engine: Add `Renderer::getClearOptions()` [b/243846268]
|
||||
- engine: Fix stable shadows (again) when an IBL rotation is used
|
||||
|
||||
## v1.45.0
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ void PerShadowMapUniforms::prepareCamera(Transaction const& transaction,
|
||||
s.viewFromClipMatrix = viewFromClip; // 1/projection
|
||||
s.clipFromWorldMatrix[0] = clipFromWorld; // projection * view
|
||||
s.worldFromClipMatrix = worldFromClip; // 1/(projection * view)
|
||||
s.userWorldFromWorldMatrix = mat4f(inverse(camera.worldTransform));
|
||||
s.userWorldFromWorldMatrix = mat4f(inverse(camera.worldOrigin));
|
||||
s.clipTransform = camera.clipTransform;
|
||||
s.cameraFar = camera.zf;
|
||||
s.oneOverFarMinusNear = 1.0f / (camera.zf - camera.zn);
|
||||
|
||||
@@ -75,7 +75,7 @@ void PerViewUniforms::prepareCamera(FEngine& engine, const CameraInfo& camera) n
|
||||
s.clipFromViewMatrix = clipFromView; // projection
|
||||
s.viewFromClipMatrix = viewFromClip; // 1/projection
|
||||
s.worldFromClipMatrix = worldFromClip; // 1/(projection * view)
|
||||
s.userWorldFromWorldMatrix = mat4f(inverse(camera.worldTransform));
|
||||
s.userWorldFromWorldMatrix = mat4f(inverse(camera.worldOrigin));
|
||||
s.clipTransform = camera.clipTransform;
|
||||
s.cameraFar = camera.zf;
|
||||
s.oneOverFarMinusNear = 1.0f / (camera.zf - camera.zn);
|
||||
@@ -151,7 +151,7 @@ void PerViewUniforms::prepareFog(FEngine& engine, const CameraInfo& cameraInfo,
|
||||
// why we store the cofactor matrix.
|
||||
|
||||
mat4f const viewFromWorld = cameraInfo.view;
|
||||
mat4 const worldFromUserWorld = cameraInfo.worldTransform;
|
||||
mat4 const worldFromUserWorld = cameraInfo.worldOrigin;
|
||||
mat4 const worldFromFog = worldFromUserWorld * userWorldFromFog;
|
||||
mat4 const viewFromFog = viewFromWorld * worldFromFog;
|
||||
|
||||
|
||||
@@ -77,13 +77,20 @@ void ShadowMap::initialize(size_t lightIndex, ShadowType shadowType,
|
||||
mFace = face;
|
||||
}
|
||||
|
||||
math::mat4f ShadowMap::getDirectionalLightViewMatrix(math::float3 direction, math::float3 up,
|
||||
math::float3 position) noexcept {
|
||||
// 1. we use the x-axis as the "up" reference so that the math is stable when the light
|
||||
// is pointing down, which is a common case for lights.
|
||||
// 2. we do the math in double to avoid some precision issues when the light is almost
|
||||
// straight (i.e. parallel to the x-axis)
|
||||
mat4f const Mm = mat4f{ mat4::lookTo(direction, position, up) };
|
||||
mat4f ShadowMap::getDirectionalLightViewMatrix(float3 direction, float3 position) noexcept {
|
||||
auto z_axis = direction;
|
||||
auto norm_up = float3{ 0, 1, 0 };
|
||||
if (UTILS_UNLIKELY(std::abs(dot(z_axis, norm_up)) > 0.999f)) {
|
||||
// Fix up vector if we're degenerate (looking straight up, basically)
|
||||
norm_up = { norm_up.z, norm_up.x, norm_up.y };
|
||||
}
|
||||
auto x_axis = normalize(cross(z_axis, norm_up));
|
||||
auto y_axis = cross(x_axis, z_axis);
|
||||
const mat4f Mm{
|
||||
float4{ x_axis, 0 },
|
||||
float4{ y_axis, 0 },
|
||||
float4{ -z_axis, 0 },
|
||||
float4{ position, 1 }};
|
||||
return FCamera::rigidTransformInverse(Mm);
|
||||
}
|
||||
|
||||
@@ -98,7 +105,7 @@ math::mat4f ShadowMap::getPointLightViewMatrix(backend::TextureCubemapFace face,
|
||||
case TextureCubemapFace::POSITIVE_Z: direction = { 0, 0, 1 }; break;
|
||||
case TextureCubemapFace::NEGATIVE_Z: direction = { 0, 0, -1 }; break;
|
||||
}
|
||||
const mat4f Mv = getDirectionalLightViewMatrix(direction, { 0, 1, 0 }, position);
|
||||
const mat4f Mv = getDirectionalLightViewMatrix(direction, position);
|
||||
return Mv;
|
||||
}
|
||||
|
||||
@@ -113,7 +120,7 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
|
||||
FLightManager::ShadowParams const params = lcm.getShadowParams(li);
|
||||
|
||||
// We can't use LISPSM in stable mode
|
||||
const auto direction = lightData.elementAt<FScene::SHADOW_DIRECTION>(index);
|
||||
const auto direction = params.options.transform * lightData.elementAt<FScene::DIRECTION>(index);
|
||||
|
||||
auto [Mv, znear, zfar, lsClippedShadowVolume, vertexCount, visibleShadows] =
|
||||
computeDirectionalShadowBounds(engine, direction, params, camera, sceneInfo);
|
||||
@@ -159,22 +166,11 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
|
||||
// This is the most important step to increase the quality of the shadow map.
|
||||
//
|
||||
// In LiPSM mode, we're using the warped space here.
|
||||
float4 f = computeFocusParams(LMpMv, WLMp,
|
||||
const mat4f F = computeFocusMatrix(LMpMv, WLMp,
|
||||
sceneInfo.wsShadowReceiversVolume,
|
||||
lsClippedShadowVolume, vertexCount,
|
||||
camera, sceneInfo.csNearFar,
|
||||
params.options.shadowFar, params.options.stable);
|
||||
|
||||
if (params.options.stable) {
|
||||
const auto lsRef = lightData.elementAt<FScene::SHADOW_REF>(index);
|
||||
snapLightFrustum(f.xy, f.zw, lsRef, shadowMapInfo.shadowDimension);
|
||||
}
|
||||
|
||||
const mat4f F(mat4f::row_major_init {
|
||||
f.x, 0.0f, 0.0f, f.z,
|
||||
0.0f, f.y, 0.0f, f.w,
|
||||
0.0f, 0.0f, 1.0f, 0.0f,
|
||||
0.0f, 0.0f, 0.0f, 1.0f,
|
||||
});
|
||||
shadowMapInfo.shadowDimension, params.options.stable);
|
||||
|
||||
/*
|
||||
* Final shadow map transform
|
||||
@@ -226,7 +222,7 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
|
||||
mCamera->setCustomProjection(mat4(Mn * F * WLMp), znear, zfar);
|
||||
|
||||
// for the debug camera, we need to undo the world origin
|
||||
mDebugCamera->setCustomProjection(mat4(S * b * camera.worldTransform), znear, zfar);
|
||||
mDebugCamera->setCustomProjection(mat4(S * b * camera.worldOrigin), znear, zfar);
|
||||
|
||||
mHasVisibleShadows = true;
|
||||
|
||||
@@ -301,7 +297,7 @@ ShadowMap::ShaderParameters ShadowMap::updateSpot(FEngine& engine,
|
||||
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, { 0, 1, 0 }, position);
|
||||
const mat4f Mv = getDirectionalLightViewMatrix(direction, position);
|
||||
|
||||
// We only keep this for reference. updateSceneInfoSpot() is quite expensive on large scenes
|
||||
// currently, and only needed to find a near/far. Instead, we just use a small near and the
|
||||
@@ -405,8 +401,7 @@ ShadowMap::DirectionalShadowBounds ShadowMap::computeDirectionalShadowBounds(
|
||||
// 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.
|
||||
mat4f const MvAtOrigin = ShadowMap::getDirectionalLightViewMatrix(direction,
|
||||
normalize(camera.worldTransform[0].xyz));
|
||||
mat4f const MvAtOrigin = ShadowMap::getDirectionalLightViewMatrix(direction);
|
||||
|
||||
|
||||
Aabb lsLightFrustumBounds = computeLightFrustumBounds(
|
||||
@@ -460,6 +455,10 @@ ShadowMap::DirectionalShadowBounds ShadowMap::computeDirectionalShadowBounds(
|
||||
std::max(lsLightFrustumBounds.min.z, sceneInfo.lsCastersNearFar[1]);
|
||||
}
|
||||
|
||||
// Now that we know the znear (-lsLightFrustumBounds.max.z), adjust the light's position such
|
||||
// that znear = 0, this is only needed for VSM, but doesn't hurt PCF.
|
||||
const mat4f Mv = getDirectionalLightViewMatrix(direction, direction * -lsLightFrustumBounds.max.z);
|
||||
|
||||
// near / far planes are specified relative to the direction the eye is looking at
|
||||
// i.e. the -z axis (see: ortho)
|
||||
const float znear = 0.0f;
|
||||
@@ -474,11 +473,6 @@ ShadowMap::DirectionalShadowBounds ShadowMap::computeDirectionalShadowBounds(
|
||||
v.z -= lsLightFrustumBounds.max.z;
|
||||
}
|
||||
|
||||
// Now that we know the znear (-lsLightFrustumBounds.max.z), adjust the light's position such
|
||||
// that znear = 0, this is only needed for VSM, but doesn't hurt PCF.
|
||||
const mat4f Mv = getDirectionalLightViewMatrix(direction, normalize(camera.worldTransform[0].xyz),
|
||||
direction * -lsLightFrustumBounds.max.z);
|
||||
|
||||
return { Mv, znear, zfar, lsClippedShadowVolume, vertexCount, true };
|
||||
}
|
||||
|
||||
@@ -583,36 +577,65 @@ math::mat4f ShadowMap::computeLightRotation(math::float3 const& lsDirection) noe
|
||||
return L;
|
||||
}
|
||||
|
||||
math::float4 ShadowMap::computeFocusParams(
|
||||
mat4f const& LMpMv,
|
||||
mat4f const& WLMp,
|
||||
math::mat4f ShadowMap::computeFocusMatrix(
|
||||
const mat4f& LMpMv, const mat4f& WLMp,
|
||||
Aabb const& wsShadowReceiversVolume,
|
||||
FrustumBoxIntersection const& lsShadowVolume, size_t vertexCount,
|
||||
filament::CameraInfo const& camera, float2 const& csNearFar,
|
||||
float shadowFar, bool stable) noexcept {
|
||||
uint16_t shadowDimension, bool stable) noexcept {
|
||||
|
||||
float2 s, o;
|
||||
float4 wsViewVolumeBoundingSphere = {};
|
||||
|
||||
if (stable) {
|
||||
// In stable mode, the light frustum size must be fixed, so we choose the
|
||||
// whole view frustum.
|
||||
// We simply take the view volume bounding sphere, but we calculate it
|
||||
// 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 smaller.
|
||||
|
||||
// in stable mode we simply take the shadow receivers volume
|
||||
const float4 shadowReceiverVolumeBoundingSphere = computeBoundingSphere(
|
||||
wsShadowReceiversVolume.getCorners().data(), 8);
|
||||
|
||||
// in stable mode we simply take the view volume bounding sphere, but we calculate it
|
||||
// in view space, so that it's perfectly stable.
|
||||
mat4f const viewFromClip = inverse(camera.cullingProjection);
|
||||
Corners const wsFrustumVertices = computeFrustumCorners(viewFromClip, csNearFar);
|
||||
wsViewVolumeBoundingSphere = computeBoundingSphere(wsFrustumVertices.vertices, 8);
|
||||
|
||||
auto getViewVolumeBoundingSphere = [&]() {
|
||||
if (shadowFar > 0) {
|
||||
float4 const wsViewVolumeBoundingSphere = { camera.getPosition(), shadowFar };
|
||||
return wsViewVolumeBoundingSphere;
|
||||
} else {
|
||||
mat4f const viewFromClip = inverse(camera.cullingProjection);
|
||||
Corners const wsFrustumVertices = computeFrustumCorners(viewFromClip, csNearFar);
|
||||
float4 const wsViewVolumeBoundingSphere =
|
||||
computeBoundingSphere(wsFrustumVertices.vertices, 8);
|
||||
return wsViewVolumeBoundingSphere;
|
||||
}
|
||||
};
|
||||
if (shadowReceiverVolumeBoundingSphere.w < wsViewVolumeBoundingSphere.w) {
|
||||
// When using the shadowReceiver volume, we don't have to use its enclosing sphere
|
||||
// because (we assume) the scene volume doesn't change. Seen from the light it only
|
||||
// changes when the light moves or rotates, and it is acceptable in that case to have
|
||||
// non "stable" shadows (the shadow will never be stable when the light moves).
|
||||
//
|
||||
// On the other hand, when using the view volume, we must use a sphere because otherwise
|
||||
// its projection's bounds in light space change with the camera, leading to unstable
|
||||
// shadows with camera movement.
|
||||
|
||||
float4 const wsViewVolumeBoundingSphere = getViewVolumeBoundingSphere();
|
||||
s = 1.0f / wsViewVolumeBoundingSphere.w;
|
||||
o = mat4f::project(LMpMv * camera.model, wsViewVolumeBoundingSphere.xyz).xy;
|
||||
o = -s * o;
|
||||
wsViewVolumeBoundingSphere.w = 0;
|
||||
}
|
||||
|
||||
if (wsViewVolumeBoundingSphere.w > 0) {
|
||||
s = 1.0f / wsViewVolumeBoundingSphere.w;
|
||||
o = mat4f::project(LMpMv * camera.model, wsViewVolumeBoundingSphere.xyz).xy;
|
||||
} else {
|
||||
// TODO: another options is the sphere around the intersections of receiver & casters
|
||||
// FIXME: this is not stable with the global rotation because wsShadowReceiversVolume
|
||||
// is not stable with it.
|
||||
Aabb const bounds = compute2DBounds(LMpMv,
|
||||
wsShadowReceiversVolume.getCorners().data(),
|
||||
wsShadowReceiversVolume.getCorners().size());
|
||||
assert_invariant(bounds.min.x < bounds.max.x);
|
||||
assert_invariant(bounds.min.y < bounds.max.y);
|
||||
|
||||
s = 2.0f / float2(bounds.max.xy - bounds.min.xy);
|
||||
o = float2(bounds.max.xy + bounds.min.xy) * 0.5f;
|
||||
|
||||
// Quantize the scale in world-space units. This value can be very small because
|
||||
// if it wasn't for floating-point imprecision, the scale would be a constant.
|
||||
double2 const quantizer = 0.0625;
|
||||
s = 1.0 / (ceil(1.0 / (s * quantizer)) * quantizer);
|
||||
}
|
||||
} else {
|
||||
Aabb const bounds = compute2DBounds(WLMp, lsShadowVolume.data(), vertexCount);
|
||||
assert_invariant(bounds.min.x < bounds.max.x);
|
||||
@@ -620,11 +643,29 @@ math::float4 ShadowMap::computeFocusParams(
|
||||
|
||||
s = 2.0f / float2(bounds.max.xy - bounds.min.xy);
|
||||
o = float2(bounds.max.xy + bounds.min.xy) * 0.5f;
|
||||
o = -s * o;
|
||||
|
||||
// TODO: we could quantize `s` here to give some stability when lispsm is disabled,
|
||||
// however, the quantization paramater should probably be user settable.
|
||||
}
|
||||
return { s, o };
|
||||
|
||||
// adjust offset for scale
|
||||
o = -s * o;
|
||||
|
||||
if (stable) {
|
||||
snapLightFrustum(s, o, LMpMv, wsShadowReceiversVolume.center(), shadowDimension);
|
||||
}
|
||||
|
||||
const mat4f F(mat4f::row_major_init {
|
||||
s.x, 0.0f, 0.0f, o.x,
|
||||
0.0f, s.y, 0.0f, o.y,
|
||||
0.0f, 0.0f, 1.0f, 0.0f,
|
||||
0.0f, 0.0f, 0.0f, 1.0f,
|
||||
});
|
||||
|
||||
return F;
|
||||
}
|
||||
|
||||
|
||||
// Apply these remapping in double to maintain a high precision for the depth axis
|
||||
ShadowMap::TextureCoordsMapping ShadowMap::getTextureCoordsMapping(ShadowMapInfo const& info,
|
||||
backend::Viewport const& viewport) noexcept {
|
||||
@@ -638,8 +679,6 @@ ShadowMap::TextureCoordsMapping ShadowMap::getTextureCoordsMapping(ShadowMapInfo
|
||||
0.0f, 0.0f, 0.0f, 1.0f
|
||||
}};
|
||||
|
||||
constexpr mat4f MtInverse = inverse(Mt);
|
||||
|
||||
// apply the viewport transform
|
||||
const float2 o = float2{ viewport.left, viewport.bottom } / float(info.atlasDimension);
|
||||
const float2 s = float2{ viewport.width, viewport.height } / float(info.atlasDimension);
|
||||
@@ -661,7 +700,7 @@ ShadowMap::TextureCoordsMapping ShadowMap::getTextureCoordsMapping(ShadowMapInfo
|
||||
}} : mat4f{};
|
||||
|
||||
// Compute shadow-map texture access and viewport transform
|
||||
return { Mf * (Mv * Mt), MtInverse * (Mv * Mt) };
|
||||
return { Mf * (Mv * Mt), inverse(Mt) * (Mv * Mt) };
|
||||
}
|
||||
|
||||
mat4f ShadowMap::computeVsmLightSpaceMatrix(const mat4f& lightSpacePcf,
|
||||
@@ -802,8 +841,8 @@ ShadowMap::Corners ShadowMap::computeFrustumCorners(
|
||||
|
||||
Aabb ShadowMap::computeLightFrustumBounds(mat4f const& lightView,
|
||||
Aabb const& wsShadowReceiversVolume, Aabb const& wsShadowCastersVolume,
|
||||
ShadowMap::SceneInfo const& sceneInfo,
|
||||
bool stable, bool focusShadowCasters, bool farUsesShadowCasters) noexcept {
|
||||
ShadowMap::SceneInfo const& sceneInfo, bool stable, bool focusShadowCasters,
|
||||
bool farUsesShadowCasters) noexcept {
|
||||
Aabb lsLightFrustumBounds{};
|
||||
|
||||
float const receiversFar = sceneInfo.lsReceiversNearFar[1];
|
||||
@@ -834,13 +873,13 @@ Aabb ShadowMap::computeLightFrustumBounds(mat4f const& lightView,
|
||||
}
|
||||
|
||||
void ShadowMap::snapLightFrustum(float2& s, float2& o,
|
||||
double2 lsRef, int2 resolution) noexcept {
|
||||
mat4f const& Mv, double3 wsSnapCoords, int2 resolution) noexcept {
|
||||
|
||||
auto proj2 = [](mat4 m, double2 v) -> double2 {
|
||||
double2 p;
|
||||
p.x = dot(double2{ m[0].x, m[1].x }, v) + m[3].x;
|
||||
p.y = dot(double2{ m[0].y, m[1].y }, v) + m[3].y;
|
||||
return p;
|
||||
auto proj = [](mat4 m, double3 v) -> double3 {
|
||||
// for directional light p.w == 1, exactly
|
||||
auto p = m * v;
|
||||
assert_invariant(p.w == 1.0);
|
||||
return p.xyz;
|
||||
};
|
||||
|
||||
auto fract = [](auto v) {
|
||||
@@ -857,13 +896,13 @@ void ShadowMap::snapLightFrustum(float2& s, float2& o,
|
||||
});
|
||||
|
||||
// The (resolution * 0.5) comes from Mv having a NDC in the range -1,1 (so a range of 2).
|
||||
// Another (resolution * 0.5) is there to snap on even texels, which helps with debugging
|
||||
|
||||
// focused light-space
|
||||
mat4 const FMv{ F * Mv };
|
||||
|
||||
// This offsets the texture coordinates, so it has a fixed offset w.r.t the world
|
||||
// F * Mv * ref
|
||||
|
||||
double2 const lsFocusedOrigin = proj2(F, lsRef);
|
||||
double2 const d = fract(lsFocusedOrigin * (resolution * 0.25)) / (resolution * 0.25);
|
||||
double2 const lsOrigin = proj(FMv, wsSnapCoords).xy;
|
||||
double2 const d = (fract(lsOrigin * resolution * 0.5) * 2.0) / resolution;
|
||||
|
||||
// adjust offset
|
||||
o -= d;
|
||||
|
||||
@@ -122,8 +122,8 @@ public:
|
||||
uint8_t visibleLayers;
|
||||
};
|
||||
|
||||
static math::mat4f getDirectionalLightViewMatrix(math::float3 direction, math::float3 up,
|
||||
math::float3 position = {}) noexcept;
|
||||
static math::mat4f getDirectionalLightViewMatrix(
|
||||
math::float3 direction, math::float3 position = {}) noexcept;
|
||||
|
||||
static math::mat4f getPointLightViewMatrix(backend::TextureCubemapFace face,
|
||||
math::float3 position) noexcept;
|
||||
@@ -246,15 +246,16 @@ private:
|
||||
|
||||
static inline math::mat4f computeLightRotation(math::float3 const& lsDirection) noexcept;
|
||||
|
||||
static inline math::float4 computeFocusParams(
|
||||
math::mat4f const& LMpMv,
|
||||
math::mat4f const& WLMp,
|
||||
static inline math::mat4f computeFocusMatrix(
|
||||
const math::mat4f& LMpMv,
|
||||
const math::mat4f& WLMp,
|
||||
Aabb const& wsShadowReceiversVolume,
|
||||
FrustumBoxIntersection const& lsShadowVolume, size_t vertexCount,
|
||||
filament::CameraInfo const& camera, math::float2 const& csNearFar,
|
||||
float shadowFar, bool stable) noexcept;
|
||||
uint16_t shadowDimension, bool stable) noexcept;
|
||||
|
||||
static inline void snapLightFrustum(math::float2& s, math::float2& o,
|
||||
math::double2 lsRef, math::int2 resolution) noexcept;
|
||||
math::mat4f const& Mv, math::double3 wsSnapCoords, math::int2 resolution) noexcept;
|
||||
|
||||
static inline Aabb computeLightFrustumBounds(const math::mat4f& lightView,
|
||||
Aabb const& wsShadowReceiversVolume, Aabb const& wsShadowCastersVolume,
|
||||
|
||||
@@ -527,8 +527,7 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
|
||||
// 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 = ShadowMap::getDirectionalLightViewMatrix(direction,
|
||||
normalize(cameraInfo.worldTransform[0].xyz));
|
||||
const mat4f MvAtOrigin = ShadowMap::getDirectionalLightViewMatrix(direction);
|
||||
|
||||
// Compute scene-dependent values shared across all cascades
|
||||
ShadowMap::updateSceneInfoDirectional(MvAtOrigin, *scene, sceneInfo);
|
||||
@@ -696,7 +695,7 @@ void ShadowMapManager::prepareSpotShadowMap(ShadowMap& shadowMap,
|
||||
const auto outerConeAngle = lcm.getSpotLightOuterCone(li);
|
||||
|
||||
// compute shadow map frustum for culling
|
||||
const mat4f Mv = ShadowMap::getDirectionalLightViewMatrix(direction, { 0, 1, 0 }, position);
|
||||
const mat4f Mv = ShadowMap::getDirectionalLightViewMatrix(direction, position);
|
||||
const mat4f Mp = mat4f::perspective(outerConeAngle * f::RAD_TO_DEG * 2.0f, 1.0f, 0.01f, radius);
|
||||
const mat4f MpMv = math::highPrecisionMultiply(Mp, Mv);
|
||||
const Frustum frustum(MpMv);
|
||||
|
||||
@@ -266,8 +266,8 @@ CameraInfo::CameraInfo(FCamera const& camera) noexcept {
|
||||
d = std::max(zn, camera.getFocusDistance());
|
||||
}
|
||||
|
||||
CameraInfo::CameraInfo(FCamera const& camera, math::mat4 const& inWorldTransform) noexcept {
|
||||
const mat4 modelMatrix{ inWorldTransform * camera.getModelMatrix() };
|
||||
CameraInfo::CameraInfo(FCamera const& camera, const math::mat4& worldOriginCamera) noexcept {
|
||||
const mat4 modelMatrix{ worldOriginCamera * camera.getModelMatrix() };
|
||||
for (uint8_t i = 0; i < CONFIG_STEREOSCOPIC_EYES; i++) {
|
||||
eyeProjection[i] = mat4f{ camera.getProjectionMatrix(i) };
|
||||
eyeFromView[i] = mat4f{ camera.getEyeFromViewMatrix(i) };
|
||||
@@ -275,7 +275,7 @@ CameraInfo::CameraInfo(FCamera const& camera, math::mat4 const& inWorldTransform
|
||||
cullingProjection = mat4f{ camera.getCullingProjectionMatrix() };
|
||||
model = mat4f{ modelMatrix };
|
||||
view = mat4f{ inverse(modelMatrix) };
|
||||
worldTransform = inWorldTransform;
|
||||
worldOrigin = worldOriginCamera;
|
||||
zn = (float)camera.getNear();
|
||||
zf = (float)camera.getCullingFar();
|
||||
ev100 = Exposure::ev100(camera);
|
||||
|
||||
@@ -223,7 +223,7 @@ private:
|
||||
struct CameraInfo {
|
||||
CameraInfo() noexcept {}
|
||||
explicit CameraInfo(FCamera const& camera) noexcept;
|
||||
CameraInfo(FCamera const& camera, math::mat4 const& inWorldTransform) noexcept;
|
||||
CameraInfo(FCamera const& camera, const math::mat4& worldOriginCamera) noexcept;
|
||||
|
||||
union {
|
||||
// projection matrix for drawing (infinite zfar)
|
||||
@@ -239,7 +239,7 @@ struct CameraInfo {
|
||||
math::mat4f model; // camera model matrix
|
||||
math::mat4f view; // camera view matrix (inverse(model))
|
||||
math::mat4f eyeFromView[CONFIG_STEREOSCOPIC_EYES]; // eye view matrix (only for stereoscopic)
|
||||
math::mat4 worldTransform; // world transform (already applied
|
||||
math::mat4 worldOrigin; // world origin transform (already applied
|
||||
// to model and view)
|
||||
math::float4 clipTransform{1, 1, 0, 0}; // clip-space transform, only for VERTEX_DOMAIN_DEVICE
|
||||
float zn{}; // distance (positive) to the near plane
|
||||
@@ -250,7 +250,7 @@ struct CameraInfo {
|
||||
float d{}; // focus distance [m]
|
||||
math::float3 const& getPosition() const noexcept { return model[3].xyz; }
|
||||
math::float3 getForwardVector() const noexcept { return normalize(-model[2].xyz); }
|
||||
math::mat4 getUserViewMatrix() const noexcept { return view * worldTransform; }
|
||||
math::mat4 getUserViewMatrix() const noexcept { return view * worldOrigin; }
|
||||
};
|
||||
|
||||
FILAMENT_DOWNCAST(Camera)
|
||||
|
||||
@@ -33,8 +33,6 @@
|
||||
#include <utils/Range.h>
|
||||
#include <utils/Systrace.h>
|
||||
|
||||
#include <math/quat.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
using namespace filament::backend;
|
||||
@@ -54,7 +52,7 @@ FScene::~FScene() noexcept = default;
|
||||
|
||||
void FScene::prepare(utils::JobSystem& js,
|
||||
LinearAllocatorArena& allocator,
|
||||
mat4 const& worldTransform,
|
||||
const mat4& worldOriginTransform,
|
||||
bool shadowReceiversAreCasters) noexcept {
|
||||
// TODO: can we skip this in most cases? Since we rely on indices staying the same,
|
||||
// we could only skip, if nothing changed in the RCM.
|
||||
@@ -170,7 +168,7 @@ void FScene::prepare(utils::JobSystem& js,
|
||||
* Fill the SoA with the JobSystem
|
||||
*/
|
||||
|
||||
auto renderableWork = [first = renderableInstances.data(), &rcm, &tcm, &worldTransform,
|
||||
auto renderableWork = [first = renderableInstances.data(), &rcm, &tcm, &worldOriginTransform,
|
||||
&sceneData, shadowReceiversAreCasters](auto* p, auto c) {
|
||||
SYSTRACE_NAME("renderableWork");
|
||||
|
||||
@@ -178,12 +176,12 @@ void FScene::prepare(utils::JobSystem& js,
|
||||
auto [ri, ti] = p[i];
|
||||
|
||||
// this is where we go from double to float for our transforms
|
||||
const mat4f shaderWorldTransform{
|
||||
worldTransform * tcm.getWorldTransformAccurate(ti) };
|
||||
const bool reversedWindingOrder = det(shaderWorldTransform.upperLeft()) < 0;
|
||||
const mat4f worldTransform{
|
||||
worldOriginTransform * tcm.getWorldTransformAccurate(ti) };
|
||||
const bool reversedWindingOrder = det(worldTransform.upperLeft()) < 0;
|
||||
|
||||
// compute the world AABB so we can perform culling
|
||||
const Box worldAABB = rigidTransform(rcm.getAABB(ri), shaderWorldTransform);
|
||||
const Box worldAABB = rigidTransform(rcm.getAABB(ri), worldTransform);
|
||||
|
||||
auto visibility = rcm.getVisibility(ri);
|
||||
visibility.reversedWindingOrder = reversedWindingOrder;
|
||||
@@ -201,7 +199,7 @@ void FScene::prepare(utils::JobSystem& js,
|
||||
assert_invariant(index < sceneData.size());
|
||||
|
||||
sceneData.elementAt<RENDERABLE_INSTANCE>(index) = ri;
|
||||
sceneData.elementAt<WORLD_TRANSFORM>(index) = shaderWorldTransform;
|
||||
sceneData.elementAt<WORLD_TRANSFORM>(index) = worldTransform;
|
||||
sceneData.elementAt<VISIBILITY_STATE>(index) = visibility;
|
||||
sceneData.elementAt<SKINNING_BUFFER>(index) = rcm.getSkinningBufferInfo(ri);
|
||||
sceneData.elementAt<MORPHING_BUFFER>(index) = rcm.getMorphingBufferInfo(ri);
|
||||
@@ -218,20 +216,19 @@ void FScene::prepare(utils::JobSystem& js,
|
||||
}
|
||||
};
|
||||
|
||||
auto lightWork = [first = lightInstances.data(), &lcm, &tcm, &worldTransform,
|
||||
auto lightWork = [first = lightInstances.data(), &lcm, &tcm, &worldOriginTransform,
|
||||
&lightData](auto* p, auto c) {
|
||||
SYSTRACE_NAME("lightWork");
|
||||
for (size_t i = 0; i < c; i++) {
|
||||
auto [li, ti] = p[i];
|
||||
// this is where we go from double to float for our transforms
|
||||
mat4f const shaderWorldTransform{
|
||||
worldTransform * tcm.getWorldTransformAccurate(ti) };
|
||||
float4 const position = shaderWorldTransform * float4{ lcm.getLocalPosition(li), 1 };
|
||||
const mat4f worldTransform{ worldOriginTransform * tcm.getWorldTransformAccurate(ti) };
|
||||
const float4 position = worldTransform * float4{ lcm.getLocalPosition(li), 1 };
|
||||
float3 d = 0;
|
||||
if (!lcm.isPointLight(li) || lcm.isIESLight(li)) {
|
||||
d = lcm.getLocalDirection(li);
|
||||
// using mat3f::getTransformForNormals handles non-uniform scaling
|
||||
d = normalize(mat3f::getTransformForNormals(shaderWorldTransform.upperLeft()) * d);
|
||||
d = normalize(mat3f::getTransformForNormals(worldTransform.upperLeft()) * d);
|
||||
}
|
||||
size_t const index = DIRECTIONAL_LIGHTS_COUNT + std::distance(first, p) + i;
|
||||
assert_invariant(index < lightData.size());
|
||||
@@ -264,43 +261,14 @@ void FScene::prepare(utils::JobSystem& js,
|
||||
*/
|
||||
|
||||
if (auto [li, ti] = directionalLightInstances ; li) {
|
||||
// in the code below, we only transform directions, so the translation of the
|
||||
// world transform is irrelevant, and we don't need to use getWorldTransformAccurate()
|
||||
|
||||
FLightManager::ShadowParams const params = lcm.getShadowParams(li);
|
||||
float3 const localDirection = lcm.getLocalDirection(li);
|
||||
float3 const shadowLocalDirection = params.options.transform * localDirection;
|
||||
mat3 const worldDirectionTransform = tcm.getWorldTransformAccurate(ti).upperLeft();
|
||||
mat3 const shaderWorldTransform = worldTransform.upperLeft() * worldDirectionTransform;
|
||||
|
||||
// using mat3::getTransformForNormals handles non-uniform scaling
|
||||
// note: in the common case of the rigid-body transform, getTransformForNormals() returns
|
||||
// identity.
|
||||
mat3 const worlTransformNormals = mat3::getTransformForNormals(shaderWorldTransform);
|
||||
double3 const d = worlTransformNormals * localDirection;
|
||||
double3 const s = worlTransformNormals * shadowLocalDirection;
|
||||
|
||||
// We compute the reference point for snapping shadowmaps without applying the
|
||||
// rotation of `worldOriginTransform` on both sides, so that we don't have any instability
|
||||
// due to the limited precision of the "light space" matrix (even at double precision).
|
||||
|
||||
// getMv() Returns the world-to-lightspace transformation. See ShadowMap.cpp.
|
||||
auto getMv = [](double3 direction) -> mat3 {
|
||||
// We use the x-axis as the "up" reference so that the math is stable when the light
|
||||
// is pointing down, which is a common case for lights. See ShadowMap.cpp.
|
||||
return transpose(mat3::lookTo(direction, double3{ 1, 0, 0 }));
|
||||
};
|
||||
double3 const worldDirection =
|
||||
mat3::getTransformForNormals(worldDirectionTransform) * shadowLocalDirection;
|
||||
double3 const worldOrigin = transpose(worldTransform.upperLeft()) * worldTransform[3].xyz;
|
||||
mat3 const Mv = getMv(worldDirection);
|
||||
double2 const lsReferencePoint = (Mv * worldOrigin).xy;
|
||||
|
||||
const mat4f worldTransform{
|
||||
worldOriginTransform * tcm.getWorldTransformAccurate(ti) };
|
||||
// using mat3f::getTransformForNormals handles non-uniform scaling
|
||||
float3 d = lcm.getLocalDirection(li);
|
||||
d = normalize(mat3f::getTransformForNormals(worldTransform.upperLeft()) * d);
|
||||
constexpr float inf = std::numeric_limits<float>::infinity();
|
||||
lightData.elementAt<POSITION_RADIUS>(0) = float4{ 0, 0, 0, inf };
|
||||
lightData.elementAt<DIRECTION>(0) = normalize(d);
|
||||
lightData.elementAt<SHADOW_DIRECTION>(0) = normalize(s);
|
||||
lightData.elementAt<SHADOW_REF>(0) = lsReferencePoint;
|
||||
lightData.elementAt<DIRECTION>(0) = d;
|
||||
lightData.elementAt<LIGHT_INSTANCE>(0) = li;
|
||||
} else {
|
||||
lightData.elementAt<LIGHT_INSTANCE>(0) = 0;
|
||||
|
||||
@@ -71,7 +71,7 @@ public:
|
||||
void terminate(FEngine& engine);
|
||||
|
||||
void prepare(utils::JobSystem& js, LinearAllocatorArena& allocator,
|
||||
math::mat4 const& worldTransform, bool shadowReceiversAreCasters) noexcept;
|
||||
math::mat4 const& worldOriginTransform, bool shadowReceiversAreCasters) noexcept;
|
||||
|
||||
void prepareVisibleRenderables(utils::Range<uint32_t> visibleRenderables) noexcept;
|
||||
|
||||
@@ -162,8 +162,6 @@ public:
|
||||
enum {
|
||||
POSITION_RADIUS,
|
||||
DIRECTION,
|
||||
SHADOW_DIRECTION,
|
||||
SHADOW_REF,
|
||||
LIGHT_INSTANCE,
|
||||
VISIBILITY,
|
||||
SCREEN_SPACE_Z_RANGE,
|
||||
@@ -173,8 +171,6 @@ public:
|
||||
using LightSoa = utils::StructureOfArrays<
|
||||
math::float4,
|
||||
math::float3,
|
||||
math::float3,
|
||||
math::double2,
|
||||
FLightManager::Instance,
|
||||
Culler::result_type,
|
||||
math::float2,
|
||||
|
||||
@@ -397,8 +397,8 @@ CameraInfo FView::computeCameraInfo(FEngine& engine) const noexcept {
|
||||
* The "world origin" is also used to keep the origin close to the camera position to
|
||||
* improve fp precision in the shader for large scenes.
|
||||
*/
|
||||
double3 translation;
|
||||
mat3 rotation;
|
||||
mat4 translation;
|
||||
mat4 rotation;
|
||||
|
||||
/*
|
||||
* Calculate all camera parameters needed to render this View for this frame.
|
||||
@@ -409,18 +409,16 @@ CameraInfo FView::computeCameraInfo(FEngine& engine) const noexcept {
|
||||
// view-space, which improves floating point precision in the shader by staying around
|
||||
// zero, where fp precision is highest. This also ensures that when the camera is placed
|
||||
// very far from the origin, objects are still rendered and lit properly.
|
||||
translation = -camera->getPosition();
|
||||
translation = mat4::translation( -camera->getPosition() );
|
||||
}
|
||||
|
||||
FIndirectLight const* const ibl = scene->getIndirectLight();
|
||||
if (ibl) {
|
||||
// the IBL transformation must be a rigid transform
|
||||
rotation = mat3{ transpose(scene->getIndirectLight()->getRotation()) };
|
||||
// it is important to orthogonalize the matrix when converting it to doubles, because
|
||||
// as float, it only has about a 1e-8 precision on the size of the basis vectors
|
||||
rotation = orthogonalize(rotation);
|
||||
rotation = mat4{ transpose(scene->getIndirectLight()->getRotation()) };
|
||||
}
|
||||
return { *camera, mat4{ rotation } * mat4::translation(translation) };
|
||||
|
||||
return { *camera, rotation * translation };
|
||||
}
|
||||
|
||||
void FView::prepare(FEngine& engine, DriverApi& driver, ArenaScope& arena,
|
||||
@@ -448,7 +446,7 @@ void FView::prepare(FEngine& engine, DriverApi& driver, ArenaScope& arena,
|
||||
// intent of the code, which is that we should only depend on CameraInfo here.
|
||||
// This is an extremely uncommon case.
|
||||
const mat4 projection = mCullingCamera->getCullingProjectionMatrix();
|
||||
const mat4 view = inverse(cameraInfo.worldTransform * mCullingCamera->getModelMatrix());
|
||||
const mat4 view = inverse(cameraInfo.worldOrigin * mCullingCamera->getModelMatrix());
|
||||
return Frustum{ mat4f{ projection * view }};
|
||||
}
|
||||
};
|
||||
@@ -461,9 +459,7 @@ void FView::prepare(FEngine& engine, DriverApi& driver, ArenaScope& arena,
|
||||
* Gather all information needed to render this scene. Apply the world origin to all
|
||||
* objects in the scene.
|
||||
*/
|
||||
scene->prepare(js, arena.getAllocator(),
|
||||
cameraInfo.worldTransform,
|
||||
hasVSM());
|
||||
scene->prepare(js, arena.getAllocator(), cameraInfo.worldOrigin, hasVSM());
|
||||
|
||||
/*
|
||||
* Light culling: runs in parallel with Renderable culling (below)
|
||||
|
||||
@@ -702,8 +702,8 @@ TEST(FilamentTest, FroxelData) {
|
||||
LightManager::Instance instance = engine->getLightManager().getInstance(e);
|
||||
|
||||
FScene::LightSoa lights;
|
||||
lights.push_back({}, {}, {}, {}, {}, {}, {}, {}); // first one is always skipped
|
||||
lights.push_back(float4{ 0, 0, -5, 1 }, {}, {}, {}, instance, 1, {}, {});
|
||||
lights.push_back({}, {}, {}, {}, {}, {}); // first one is always skipped
|
||||
lights.push_back(float4{ 0, 0, -5, 1 }, {}, instance, 1, {}, {});
|
||||
|
||||
{
|
||||
froxelData.froxelizeLights(*engine, {}, lights);
|
||||
|
||||
@@ -289,14 +289,6 @@ public:
|
||||
return matrix::cof(m);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns a matrix representing the pose of a virtual camera looking towards -Z in its
|
||||
* local Y-up coordinate system. "up" defines where the Y axis of the camera's local coordinate
|
||||
* system is.
|
||||
*/
|
||||
template<typename A, typename B>
|
||||
static TMat33 lookTo(const TVec3<A>& direction, const TVec3<B>& up) noexcept;
|
||||
|
||||
/**
|
||||
* Packs the tangent frame represented by the specified matrix into a quaternion.
|
||||
* Reflection is preserved by encoding it as the sign of the w component in the
|
||||
@@ -414,29 +406,6 @@ constexpr TMat33<T>::TMat33(const TQuaternion<U>& q) noexcept : m_value{} {
|
||||
m_value[2] = col_type(xz + yw, yz - xw, 1 - xx - yy); // NOLINT
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr T dot_tolerance() noexcept;
|
||||
|
||||
template<>
|
||||
constexpr float dot_tolerance<float>() noexcept { return 0.999f; }
|
||||
|
||||
template<>
|
||||
constexpr double dot_tolerance<double>() noexcept { return 0.9999; }
|
||||
|
||||
template<typename T>
|
||||
template<typename A, typename B>
|
||||
TMat33<T> TMat33<T>::lookTo(const TVec3<A>& direction, const TVec3<B>& up) noexcept {
|
||||
auto const z_axis = direction;
|
||||
auto norm_up = up;
|
||||
if (std::abs(dot(z_axis, norm_up)) > dot_tolerance< arithmetic_result_t<A, B> >()) {
|
||||
// Fix up vector if we're degenerate (looking straight up, basically)
|
||||
norm_up = { norm_up.z, norm_up.x, norm_up.y };
|
||||
}
|
||||
auto const x_axis = normalize(cross(z_axis, norm_up));
|
||||
auto const y_axis = cross(x_axis, z_axis);
|
||||
return { x_axis, y_axis, -z_axis };
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
template<typename T>
|
||||
constexpr TQuaternion<T> TMat33<T>::packTangentFrame(const TMat33<T>& m, size_t storageSize) noexcept {
|
||||
|
||||
@@ -285,9 +285,6 @@ public:
|
||||
template<typename A, typename B, typename C>
|
||||
static TMat44 lookAt(const TVec3<A>& eye, const TVec3<B>& center, const TVec3<C>& up) noexcept;
|
||||
|
||||
template<typename A, typename B, typename C>
|
||||
static TMat44 lookTo(const TVec3<A>& direction, const TVec3<B>& position, const TVec3<C>& up) noexcept;
|
||||
|
||||
template<typename A>
|
||||
static constexpr TVec3<A> project(const TMat44& projectionMatrix, TVec3<A> vertice) noexcept{
|
||||
TVec4<A> r = projectionMatrix * TVec4<A>{ vertice, 1 };
|
||||
@@ -520,19 +517,19 @@ template<typename T>
|
||||
template<typename A, typename B, typename C>
|
||||
TMat44<T> TMat44<T>::lookAt(const TVec3<A>& eye, const TVec3<B>& center,
|
||||
const TVec3<C>& up) noexcept {
|
||||
return lookTo(normalize(center - eye), eye, normalize(up));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
template<typename A, typename B, typename C>
|
||||
TMat44<T> TMat44<T>::lookTo(const TVec3<A>& direction, const TVec3<B>& position,
|
||||
const TVec3<C>& up) noexcept {
|
||||
auto r = TMat33<T>::lookTo(direction, up);
|
||||
return TMat44<T>{
|
||||
TVec4<T>{ r[0], 0 },
|
||||
TVec4<T>{ r[1], 0 },
|
||||
TVec4<T>{ r[2], 0 },
|
||||
TVec4<T>{ position, 1 } };
|
||||
TVec3<T> z_axis(normalize(center - eye));
|
||||
TVec3<T> norm_up(normalize(up));
|
||||
if (std::abs(dot(z_axis, norm_up)) > T(0.999)) {
|
||||
// Fix up vector if we're degenerate (looking straight up, basically)
|
||||
norm_up = { norm_up.z, norm_up.x, norm_up.y };
|
||||
}
|
||||
TVec3<T> x_axis(normalize(cross(z_axis, norm_up)));
|
||||
TVec3<T> y_axis(cross(x_axis, z_axis));
|
||||
return TMat44<T>(
|
||||
TVec4<T>(x_axis, 0),
|
||||
TVec4<T>(y_axis, 0),
|
||||
TVec4<T>(-z_axis, 0),
|
||||
TVec4<T>(eye, 1));
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
|
||||
@@ -32,71 +32,6 @@ class MatTest : public testing::Test {
|
||||
protected:
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// A macro to help with vector comparisons within floating point range.
|
||||
#define EXPECT_VEC_EQ(VEC1, VEC2) \
|
||||
do { \
|
||||
const decltype(VEC1) v1 = VEC1; \
|
||||
const decltype(VEC2) v2 = VEC2; \
|
||||
if (std::is_same<TypeParam,float>::value) { \
|
||||
for (int i = 0; i < v1.size(); ++i) { \
|
||||
EXPECT_FLOAT_EQ(v1[i], v2[i]); \
|
||||
} \
|
||||
} else if (std::is_same<TypeParam,double>::value) { \
|
||||
for (int i = 0; i < v1.size(); ++i) { \
|
||||
EXPECT_DOUBLE_EQ(v1[i], v2[i]); \
|
||||
} \
|
||||
} else { \
|
||||
for (int i = 0; i < v1.size(); ++i) { \
|
||||
EXPECT_EQ(v1[i], v2[i]); \
|
||||
} \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// A macro to help with vector comparisons within a range.
|
||||
#define EXPECT_VEC_NEAR(VEC1, VEC2, eps) \
|
||||
do { \
|
||||
const decltype(VEC1) v1 = VEC1; \
|
||||
const decltype(VEC2) v2 = VEC2; \
|
||||
for (int i = 0; i < v1.size(); ++i) { \
|
||||
EXPECT_NEAR(v1[i], v2[i], eps); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// A macro to help with type comparisons within floating point range.
|
||||
#define ASSERT_TYPE_EQ(T1, T2) \
|
||||
do { \
|
||||
const decltype(T1) t1 = T1; \
|
||||
const decltype(T2) t2 = T2; \
|
||||
if (std::is_same<TypeParam,float>::value) { \
|
||||
ASSERT_FLOAT_EQ(t1, t2); \
|
||||
} else if (std::is_same<TypeParam,double>::value) { \
|
||||
ASSERT_DOUBLE_EQ(t1, t2); \
|
||||
} else { \
|
||||
ASSERT_EQ(t1, t2); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
|
||||
|
||||
TEST_F(MatTest, LargeFloatRotationsWithOrthogonalization) {
|
||||
double3 const t = { 2304097.1410110965, -4688442.9915525438, -3639452.5611694567 };
|
||||
mat4 const T = mat4::translation(t);
|
||||
for (float d = 0; d < 90; d = d + 1.0) {
|
||||
mat3f const R = mat3f::rotation(d * f::DEG_TO_RAD, float3{ 0, 1, 0 });
|
||||
mat3 RR = orthogonalize(mat3{ R });
|
||||
ASSERT_NEAR(dot(RR[0], RR[0]), 1.0, 1e-12);
|
||||
ASSERT_NEAR(dot(RR[1], RR[1]), 1.0, 1e-12);
|
||||
ASSERT_NEAR(dot(RR[2], RR[2]), 1.0, 1e-12);
|
||||
mat4 M = mat4{ RR } * T;
|
||||
double3 const t2 = transpose(M.upperLeft()) * M[3].xyz;
|
||||
EXPECT_VEC_NEAR(t, t2, 0.0001); // 0.1mm
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(MatTest, ConstexprMat2) {
|
||||
constexpr float a = F_PI;
|
||||
constexpr mat2f M;
|
||||
@@ -617,6 +552,53 @@ TYPED_TEST(MatTestT, Inverse2) {
|
||||
TEST_MATRIX_INVERSE(m4, 20.0 * std::numeric_limits<TypeParam>::epsilon());
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// A macro to help with vector comparisons within floating point range.
|
||||
#define EXPECT_VEC_EQ(VEC1, VEC2) \
|
||||
do { \
|
||||
const decltype(VEC1) v1 = VEC1; \
|
||||
const decltype(VEC2) v2 = VEC2; \
|
||||
if (std::is_same<TypeParam,float>::value) { \
|
||||
for (int i = 0; i < v1.size(); ++i) { \
|
||||
EXPECT_FLOAT_EQ(v1[i], v2[i]); \
|
||||
} \
|
||||
} else if (std::is_same<TypeParam,double>::value) { \
|
||||
for (int i = 0; i < v1.size(); ++i) { \
|
||||
EXPECT_DOUBLE_EQ(v1[i], v2[i]); \
|
||||
} \
|
||||
} else { \
|
||||
for (int i = 0; i < v1.size(); ++i) { \
|
||||
EXPECT_EQ(v1[i], v2[i]); \
|
||||
} \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// A macro to help with vector comparisons within a range.
|
||||
#define EXPECT_VEC_NEAR(VEC1, VEC2, eps) \
|
||||
do { \
|
||||
const decltype(VEC1) v1 = VEC1; \
|
||||
const decltype(VEC2) v2 = VEC2; \
|
||||
for (int i = 0; i < v1.size(); ++i) { \
|
||||
EXPECT_NEAR(v1[i], v2[i], eps); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// A macro to help with type comparisons within floating point range.
|
||||
#define ASSERT_TYPE_EQ(T1, T2) \
|
||||
do { \
|
||||
const decltype(T1) t1 = T1; \
|
||||
const decltype(T2) t2 = T2; \
|
||||
if (std::is_same<TypeParam,float>::value) { \
|
||||
ASSERT_FLOAT_EQ(t1, t2); \
|
||||
} else if (std::is_same<TypeParam,double>::value) { \
|
||||
ASSERT_DOUBLE_EQ(t1, t2); \
|
||||
} else { \
|
||||
ASSERT_EQ(t1, t2); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
TYPED_TEST(MatTestT, NormalsNegativeScale) {
|
||||
typedef filament::math::details::TMat33<TypeParam> M33T;
|
||||
|
||||
@@ -101,7 +101,7 @@ struct App {
|
||||
|
||||
bool actualSize = false;
|
||||
bool originIsFarAway = false;
|
||||
float originDistance = 1.0f;
|
||||
float originDistance = 6378137; // Earth's radius in [m]
|
||||
|
||||
struct Scene {
|
||||
Entity groundPlane;
|
||||
@@ -762,7 +762,7 @@ int main(int argc, char** argv) {
|
||||
ImGui::Checkbox("Camera at origin",
|
||||
debug.getPropertyAddress<bool>("d.view.camera_at_origin"));
|
||||
ImGui::Checkbox("Far Origin", &app.originIsFarAway);
|
||||
ImGui::SliderFloat("Origin", &app.originDistance, 0, 1);
|
||||
ImGui::SliderFloat("Origin", &app.originDistance, 0, 10000000);
|
||||
ImGui::Checkbox("Far uses shadow casters",
|
||||
debug.getPropertyAddress<bool>("d.shadowmap.far_uses_shadowcasters"));
|
||||
ImGui::Checkbox("Focus shadow casters",
|
||||
@@ -981,12 +981,7 @@ int main(int argc, char** argv) {
|
||||
tcm.setParent(tcm.getInstance(camera.getEntity()), root);
|
||||
tcm.setParent(tcm.getInstance(app.asset->getRoot()), root);
|
||||
tcm.setParent(tcm.getInstance(view->getFogEntity()), root);
|
||||
|
||||
// these values represent a point somewhere on Earth's surface
|
||||
float const d = app.originIsFarAway ? app.originDistance : 0.0f;
|
||||
// tcm.setTransform(root, mat4::translation(double3{ 67.0, -6366759.0, -21552.0 } * d));
|
||||
tcm.setTransform(root, mat4::translation(
|
||||
double3{ 2304097.1410110965, -4688442.9915525438, -3639452.5611694567 } * d));
|
||||
tcm.setTransform(root, mat4f::translation(float3{ app.originIsFarAway ? app.originDistance : 0.0f }));
|
||||
|
||||
// Check if color grading has changed.
|
||||
ColorGradingSettings const& options = app.viewer->getSettings().view.colorGrading;
|
||||
|
||||
Reference in New Issue
Block a user