Graphics: Improve directional light stabilization and split
This commit is contained in:
parent
413dd2ce71
commit
b3a43eb5ed
|
|
@ -11,6 +11,7 @@
|
||||||
#include <Nazara/Math/Quaternion.hpp>
|
#include <Nazara/Math/Quaternion.hpp>
|
||||||
#include <Nazara/Math/Sphere.hpp>
|
#include <Nazara/Math/Sphere.hpp>
|
||||||
#include <NazaraUtils/Algorithm.hpp>
|
#include <NazaraUtils/Algorithm.hpp>
|
||||||
|
#include <NazaraUtils/StackArray.hpp>
|
||||||
#include <NazaraUtils/StackVector.hpp>
|
#include <NazaraUtils/StackVector.hpp>
|
||||||
#include <Nazara/Graphics/Debug.hpp>
|
#include <Nazara/Graphics/Debug.hpp>
|
||||||
|
|
||||||
|
|
@ -44,24 +45,53 @@ namespace Nz
|
||||||
assert(viewer);
|
assert(viewer);
|
||||||
PerViewerData& viewerData = *Retrieve(m_viewerData, viewer);
|
PerViewerData& viewerData = *Retrieve(m_viewerData, viewer);
|
||||||
|
|
||||||
// Extract frustum from main viewer
|
const ViewerInstance& viewerInstance = viewer->GetViewerInstance();
|
||||||
const Vector3f& eyePosition = viewer->GetViewerInstance().GetEyePosition();
|
|
||||||
const Matrix4f& viewProjMatrix = viewer->GetViewerInstance().GetViewProjMatrix();
|
|
||||||
|
|
||||||
//std::array planePct = { 0.1f, 0.3f, 0.6f }; // TODO: Generate the separations based on other settings
|
// Extract frustum from main viewer
|
||||||
std::array planePct = { 0.03f, 0.1f, 0.2f, 0.5f }; // TODO: Generate the separations based on other settings
|
const Vector3f& eyePosition = viewerInstance.GetEyePosition();
|
||||||
assert(m_cascadeCount <= planePct.size() + 1);
|
const Matrix4f& viewProjMatrix = viewerInstance.GetViewProjMatrix();
|
||||||
|
|
||||||
|
float nearPlane = viewerInstance.GetNearPlane();
|
||||||
|
float farPlane = viewerInstance.GetFarPlane();
|
||||||
|
|
||||||
|
// Calculate split depths based on view camera frustum
|
||||||
|
// Based on method presented in https://developer.nvidia.com/gpugems/GPUGems3/gpugems3_ch10.html
|
||||||
|
constexpr float lambda = 0.95f;
|
||||||
|
|
||||||
|
float ratio = farPlane / nearPlane;
|
||||||
|
float clipRange = farPlane - nearPlane;
|
||||||
|
|
||||||
|
StackArray<float> cascadeSplits = NazaraStackArrayNoInit(float, m_cascadeCount - 1);
|
||||||
|
for (uint32_t i = 0; i < m_cascadeCount - 1; i++)
|
||||||
|
{
|
||||||
|
float p = float(i + 1) / float(m_cascadeCount);
|
||||||
|
float log = nearPlane * std::pow(ratio, p);
|
||||||
|
float uniform = nearPlane + clipRange * p;
|
||||||
|
float d = lambda * (log - uniform) + uniform;
|
||||||
|
cascadeSplits[i] = (d - nearPlane) / clipRange;
|
||||||
|
}
|
||||||
|
|
||||||
StackVector<Frustumf> frustums = NazaraStackVector(Frustumf, m_cascadeCount);
|
StackVector<Frustumf> frustums = NazaraStackVector(Frustumf, m_cascadeCount);
|
||||||
StackVector<float> frustumDists = NazaraStackVector(float, m_cascadeCount);
|
StackVector<float> frustumDists = NazaraStackVector(float, m_cascadeCount);
|
||||||
|
|
||||||
Frustumf frustum = Frustumf::Extract(viewProjMatrix);
|
Frustumf frustum = Frustumf::Extract(viewProjMatrix);
|
||||||
frustum.Split(planePct.data(), m_cascadeCount - 1, [&](float zNearPct, float zFarPct)
|
frustum.Split(cascadeSplits.data(), m_cascadeCount - 1, [&](float zNearPct, float zFarPct)
|
||||||
{
|
{
|
||||||
frustums.push_back(frustum.Reduce(zNearPct, zFarPct));
|
frustums.push_back(frustum.Reduce(zNearPct, zFarPct));
|
||||||
frustumDists.push_back(frustums.back().GetPlane(FrustumPlane::Far).SignedDistance(eyePosition));
|
frustumDists.push_back(frustums.back().GetPlane(FrustumPlane::Far).SignedDistance(eyePosition));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
constexpr std::array cascadeColors = {
|
||||||
|
Color::Green(),
|
||||||
|
Color::Yellow(),
|
||||||
|
Color::Red(),
|
||||||
|
Color::Blue(),
|
||||||
|
Color::Cyan(),
|
||||||
|
Color::Magenta(),
|
||||||
|
Color::Orange(),
|
||||||
|
Color::Gray()
|
||||||
|
};
|
||||||
|
|
||||||
for (std::size_t cascadeIndex = 0; cascadeIndex < m_cascadeCount; ++cascadeIndex)
|
for (std::size_t cascadeIndex = 0; cascadeIndex < m_cascadeCount; ++cascadeIndex)
|
||||||
{
|
{
|
||||||
CascadeData& cascade = viewerData.cascades[cascadeIndex];
|
CascadeData& cascade = viewerData.cascades[cascadeIndex];
|
||||||
|
|
@ -75,13 +105,15 @@ namespace Nz
|
||||||
0.0f, 0.0f, 1.0f, 0.0f,
|
0.0f, 0.0f, 1.0f, 0.0f,
|
||||||
0.5f, 0.5f, 0.0f, 1.0f);
|
0.5f, 0.5f, 0.0f, 1.0f);
|
||||||
|
|
||||||
ViewerInstance& viewerInstance = cascade.viewer.GetViewerInstance();
|
ViewerInstance& cascadeViewerInstance = cascade.viewer.GetViewerInstance();
|
||||||
cascade.viewProjMatrix = viewerInstance.GetViewProjMatrix() * biasMatrix;
|
cascade.viewProjMatrix = cascadeViewerInstance.GetViewProjMatrix() * biasMatrix;
|
||||||
|
|
||||||
m_pipeline.QueueTransfer(&viewerInstance);
|
m_pipeline.QueueTransfer(&cascadeViewerInstance);
|
||||||
|
|
||||||
// Prepare depth pass
|
// Prepare depth pass
|
||||||
Frustumf lightFrustum = Frustumf::Extract(viewerInstance.GetViewProjMatrix());
|
Frustumf lightFrustum = Frustumf::Extract(cascadeViewerInstance.GetViewProjMatrix());
|
||||||
|
|
||||||
|
//m_pipeline.GetDebugDrawer().DrawFrustum(lightFrustum, cascadeColors[cascadeIndex]);
|
||||||
|
|
||||||
std::size_t visibilityHash = 5U;
|
std::size_t visibilityHash = 5U;
|
||||||
const auto& visibleRenderables = m_pipeline.FrustumCull(lightFrustum, 0xFFFFFFFF, visibilityHash);
|
const auto& visibleRenderables = m_pipeline.FrustumCull(lightFrustum, 0xFFFFFFFF, visibilityHash);
|
||||||
|
|
@ -92,7 +124,7 @@ namespace Nz
|
||||||
|
|
||||||
void DirectionalLightShadowData::ComputeLightView(CascadeData& cascade, const Frustumf& cascadeFrustum, float cascadeDist)
|
void DirectionalLightShadowData::ComputeLightView(CascadeData& cascade, const Frustumf& cascadeFrustum, float cascadeDist)
|
||||||
{
|
{
|
||||||
ViewerInstance& viewerInstance = cascade.viewer.GetViewerInstance();
|
ViewerInstance& shadowViewer = cascade.viewer.GetViewerInstance();
|
||||||
|
|
||||||
EnumArray<BoxCorner, Vector3f> frustumCorners = cascadeFrustum.ComputeCorners();
|
EnumArray<BoxCorner, Vector3f> frustumCorners = cascadeFrustum.ComputeCorners();
|
||||||
|
|
||||||
|
|
@ -113,7 +145,10 @@ namespace Nz
|
||||||
Matrix4f lightView = Matrix4f::TransformInverse(frustumCenter, m_light.GetRotation());
|
Matrix4f lightView = Matrix4f::TransformInverse(frustumCenter, m_light.GetRotation());
|
||||||
|
|
||||||
// Compute light projection matrix
|
// Compute light projection matrix
|
||||||
Boxf aabb = Boxf::FromExtends(frustumCenter - Vector3f(radius), frustumCenter + Vector3f(radius));
|
Vector3f maxExtent = frustumCenter + Vector3f(radius);
|
||||||
|
Vector3f minExtent = frustumCenter - Vector3f(radius);
|
||||||
|
|
||||||
|
Boxf aabb = Boxf::FromExtents(minExtent, maxExtent);
|
||||||
|
|
||||||
float left = std::numeric_limits<float>::infinity();
|
float left = std::numeric_limits<float>::infinity();
|
||||||
float right = -std::numeric_limits<float>::infinity();
|
float right = -std::numeric_limits<float>::infinity();
|
||||||
|
|
@ -133,44 +168,31 @@ namespace Nz
|
||||||
zFar = std::max(zFar, viewCorner.z);
|
zFar = std::max(zFar, viewCorner.z);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tune this parameter according to the scene
|
|
||||||
constexpr float zMult = 2.0f;
|
|
||||||
if (zNear < 0)
|
|
||||||
zNear *= zMult;
|
|
||||||
else
|
|
||||||
zNear /= zMult;
|
|
||||||
|
|
||||||
if (zFar < 0)
|
|
||||||
zFar /= zMult;
|
|
||||||
else
|
|
||||||
zFar *= zMult;
|
|
||||||
|
|
||||||
cascade.distance = cascadeDist;
|
cascade.distance = cascadeDist;
|
||||||
|
|
||||||
Matrix4f lightProj = Matrix4f::Ortho(left, right, top, bottom, zNear, zFar);
|
Matrix4f lightProj = Matrix4f::Ortho(left, right, top, bottom, zNear, zFar);
|
||||||
|
|
||||||
viewerInstance.UpdateProjViewMatrices(lightProj, lightView);
|
shadowViewer.UpdateProjViewMatrices(lightProj, lightView);
|
||||||
viewerInstance.UpdateEyePosition(frustumCenter);
|
shadowViewer.UpdateEyePosition(frustumCenter);
|
||||||
viewerInstance.UpdateNearFarPlanes(zNear, zFar);
|
shadowViewer.UpdateNearFarPlanes(zNear, zFar);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DirectionalLightShadowData::StabilizeShadows(CascadeData& cascade)
|
void DirectionalLightShadowData::StabilizeShadows(CascadeData& cascade)
|
||||||
{
|
{
|
||||||
ViewerInstance& viewerInstance = cascade.viewer.GetViewerInstance();
|
ViewerInstance& shadowViewer = cascade.viewer.GetViewerInstance();
|
||||||
|
|
||||||
// Stabilize cascade shadows by keeping the center to a texel boundary of the previous frame
|
// Stabilize cascade shadows by keeping the center to a texel boundary
|
||||||
// https://www.junkship.net/News/2020/11/22/shadow-of-a-doubt-part-2
|
// see Michal Valient's article "Stable Cascaded Shadow Maps"
|
||||||
Vector4f shadowOrigin(0.0f, 0.0f, 0.0f, 1.0f);
|
Vector4f shadowOrigin = shadowViewer.GetViewProjMatrix() * Vector4f(0.f, 0.f, 0.f, 1.f);
|
||||||
shadowOrigin = viewerInstance.GetViewProjMatrix() * shadowOrigin;
|
shadowOrigin *= m_invTexelScale;
|
||||||
shadowOrigin *= m_texelScale;
|
|
||||||
|
|
||||||
Vector2f roundedOrigin = { std::round(shadowOrigin.x), std::round(shadowOrigin.y) };
|
Vector2f roundedOrigin = { std::round(shadowOrigin.x), std::round(shadowOrigin.y) };
|
||||||
Vector2f roundOffset = roundedOrigin - Vector2f(shadowOrigin);
|
Vector2f roundOffset = roundedOrigin - Vector2f(shadowOrigin);
|
||||||
roundOffset *= m_invTexelScale;
|
roundOffset *= m_texelScale;
|
||||||
|
|
||||||
Matrix4f lightProj = viewerInstance.GetProjectionMatrix();
|
Matrix4f lightProj = shadowViewer.GetProjectionMatrix();
|
||||||
lightProj.ApplyTranslation(Vector3f(roundOffset.x, roundOffset.y, 0.f));
|
lightProj.ApplyTranslation(Vector3f(roundOffset.x, roundOffset.y, 0.f));
|
||||||
viewerInstance.UpdateProjectionMatrix(lightProj);
|
shadowViewer.UpdateProjectionMatrix(lightProj);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DirectionalLightShadowData::RegisterMaterialInstance(const MaterialInstance& matInstance)
|
void DirectionalLightShadowData::RegisterMaterialInstance(const MaterialInstance& matInstance)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue