|
|
|
|
@@ -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;
|
|
|
|
|
|