Add directional shadow request planning
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
#include "Rendering/SceneRenderRequestPlanner.h"
|
||||
|
||||
#include "Components/LightComponent.h"
|
||||
#include "Core/Math/Matrix4.h"
|
||||
#include "Rendering/SceneRenderRequestUtils.h"
|
||||
|
||||
#include "Scene/Scene.h"
|
||||
@@ -9,6 +11,139 @@
|
||||
namespace XCEngine {
|
||||
namespace Rendering {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr uint32_t kDirectionalShadowMapDimension = 1024;
|
||||
constexpr float kMinShadowFocusDistance = 5.0f;
|
||||
constexpr float kMaxShadowFocusDistance = 25.0f;
|
||||
constexpr float kPerspectiveShadowFocusFactor = 0.25f;
|
||||
constexpr float kOrthographicShadowFocusFactor = 2.0f;
|
||||
constexpr float kMinShadowHalfExtent = 10.0f;
|
||||
constexpr float kMaxShadowHalfExtent = 40.0f;
|
||||
constexpr float kPerspectiveShadowCoverageFactor = 0.20f;
|
||||
constexpr float kOrthographicShadowCoverageFactor = 2.0f;
|
||||
constexpr float kShadowNearClipPlane = 0.1f;
|
||||
constexpr float kMinShadowDepthRange = 20.0f;
|
||||
|
||||
bool IsUsableDirectionalLight(const Components::LightComponent* light) {
|
||||
return light != nullptr &&
|
||||
light->IsEnabled() &&
|
||||
light->GetGameObject() != nullptr &&
|
||||
light->GetGameObject()->IsActiveInHierarchy() &&
|
||||
light->GetLightType() == Components::LightType::Directional;
|
||||
}
|
||||
|
||||
Components::LightComponent* FindMainDirectionalLight(const Components::Scene& scene) {
|
||||
const std::vector<Components::LightComponent*> lights =
|
||||
scene.FindObjectsOfType<Components::LightComponent>();
|
||||
|
||||
Components::LightComponent* mainDirectionalLight = nullptr;
|
||||
for (Components::LightComponent* light : lights) {
|
||||
if (!IsUsableDirectionalLight(light)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (mainDirectionalLight == nullptr ||
|
||||
light->GetIntensity() > mainDirectionalLight->GetIntensity()) {
|
||||
mainDirectionalLight = light;
|
||||
}
|
||||
}
|
||||
|
||||
return mainDirectionalLight;
|
||||
}
|
||||
|
||||
bool ShouldPlanDirectionalShadowForCamera(
|
||||
const Components::CameraComponent& camera,
|
||||
size_t renderedBaseCameraCount,
|
||||
size_t renderedRequestCount) {
|
||||
if (camera.GetStackType() == Components::CameraStackType::Overlay) {
|
||||
return renderedBaseCameraCount == 0u &&
|
||||
renderedRequestCount == 0u;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
DirectionalShadowRenderPlan BuildDirectionalShadowRenderPlan(
|
||||
const Components::CameraComponent& camera,
|
||||
const Components::LightComponent& light) {
|
||||
DirectionalShadowRenderPlan plan = {};
|
||||
if (!light.GetCastsShadows()) {
|
||||
return plan;
|
||||
}
|
||||
|
||||
Math::Vector3 lightDirection = light.transform().GetForward() * -1.0f;
|
||||
if (lightDirection.SqrMagnitude() <= Math::EPSILON) {
|
||||
lightDirection = Math::Vector3::Back();
|
||||
} else {
|
||||
lightDirection = lightDirection.Normalized();
|
||||
}
|
||||
|
||||
const Math::Vector3 viewForward = camera.transform().GetForward().SqrMagnitude() <= Math::EPSILON
|
||||
? Math::Vector3::Forward()
|
||||
: camera.transform().GetForward().Normalized();
|
||||
const Math::Vector3 cameraPosition = camera.transform().GetPosition();
|
||||
|
||||
const float focusDistance = camera.GetProjectionType() == Components::CameraProjectionType::Perspective
|
||||
? std::clamp(
|
||||
camera.GetFarClipPlane() * kPerspectiveShadowFocusFactor,
|
||||
kMinShadowFocusDistance,
|
||||
kMaxShadowFocusDistance)
|
||||
: std::clamp(
|
||||
camera.GetOrthographicSize() * kOrthographicShadowFocusFactor,
|
||||
kMinShadowFocusDistance,
|
||||
kMaxShadowFocusDistance);
|
||||
const Math::Vector3 focusPoint = cameraPosition + viewForward * focusDistance;
|
||||
|
||||
const float shadowHalfExtent = camera.GetProjectionType() == Components::CameraProjectionType::Perspective
|
||||
? std::clamp(
|
||||
camera.GetFarClipPlane() * kPerspectiveShadowCoverageFactor,
|
||||
kMinShadowHalfExtent,
|
||||
kMaxShadowHalfExtent)
|
||||
: std::clamp(
|
||||
camera.GetOrthographicSize() * kOrthographicShadowCoverageFactor,
|
||||
kMinShadowHalfExtent,
|
||||
kMaxShadowHalfExtent);
|
||||
const float shadowDepthRange = std::max(shadowHalfExtent * 4.0f, kMinShadowDepthRange);
|
||||
const Math::Vector3 shadowWorldPosition =
|
||||
focusPoint - lightDirection * (shadowDepthRange * 0.5f);
|
||||
|
||||
Math::Vector3 shadowUp = Math::Vector3::Up();
|
||||
if (std::abs(Math::Vector3::Dot(lightDirection, shadowUp)) > 0.98f) {
|
||||
shadowUp = Math::Vector3::Forward();
|
||||
}
|
||||
|
||||
const Math::Matrix4x4 view =
|
||||
Math::Matrix4x4::LookAt(shadowWorldPosition, focusPoint, shadowUp);
|
||||
const Math::Matrix4x4 projection = Math::Matrix4x4::Orthographic(
|
||||
-shadowHalfExtent,
|
||||
shadowHalfExtent,
|
||||
-shadowHalfExtent,
|
||||
shadowHalfExtent,
|
||||
kShadowNearClipPlane,
|
||||
shadowDepthRange);
|
||||
|
||||
plan.enabled = true;
|
||||
plan.lightDirection = lightDirection;
|
||||
plan.focusPoint = focusPoint;
|
||||
plan.orthographicHalfExtent = shadowHalfExtent;
|
||||
plan.nearClipPlane = kShadowNearClipPlane;
|
||||
plan.farClipPlane = shadowDepthRange;
|
||||
plan.mapWidth = kDirectionalShadowMapDimension;
|
||||
plan.mapHeight = kDirectionalShadowMapDimension;
|
||||
plan.cameraData.view = view.Transpose();
|
||||
plan.cameraData.projection = projection.Transpose();
|
||||
plan.cameraData.viewProjection = (projection * view).Transpose();
|
||||
plan.cameraData.worldPosition = shadowWorldPosition;
|
||||
plan.cameraData.clearColor = Math::Color::Black();
|
||||
plan.cameraData.clearFlags = RenderClearFlags::Depth;
|
||||
plan.cameraData.viewportWidth = plan.mapWidth;
|
||||
plan.cameraData.viewportHeight = plan.mapHeight;
|
||||
return plan;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::vector<Components::CameraComponent*> SceneRenderRequestPlanner::CollectCameras(
|
||||
const Components::Scene& scene,
|
||||
Components::CameraComponent* overrideCamera) const {
|
||||
@@ -56,6 +191,23 @@ std::vector<CameraRenderRequest> SceneRenderRequestPlanner::BuildRequests(
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ShouldPlanDirectionalShadowForCamera(
|
||||
*camera,
|
||||
renderedBaseCameraCount,
|
||||
requests.size())) {
|
||||
if (Components::LightComponent* mainDirectionalLight = FindMainDirectionalLight(scene);
|
||||
mainDirectionalLight != nullptr &&
|
||||
mainDirectionalLight->GetCastsShadows()) {
|
||||
request.directionalShadow =
|
||||
BuildDirectionalShadowRenderPlan(*camera, *mainDirectionalLight);
|
||||
if (request.directionalShadow.IsValid()) {
|
||||
request.shadowCaster.clearFlags = RenderClearFlags::Depth;
|
||||
request.shadowCaster.hasCameraDataOverride = true;
|
||||
request.shadowCaster.cameraDataOverride = request.directionalShadow.cameraData;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
requests.push_back(request);
|
||||
if (camera->GetStackType() == Components::CameraStackType::Base) {
|
||||
++renderedBaseCameraCount;
|
||||
|
||||
Reference in New Issue
Block a user