diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index 59e73793..bc789fe2 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -513,6 +513,8 @@ add_library(XCEngine STATIC ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Extraction/RenderSceneUtility.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Caches/RenderResourceCache.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Planning/SceneRenderRequestPlanner.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Planning/Internal/DirectionalShadowPlanning.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Planning/Internal/DirectionalShadowPlanning.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Execution/SceneRenderer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Pipelines/BuiltinForwardPipeline.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Pipelines/BuiltinForwardPipelineResources.cpp diff --git a/engine/src/Rendering/Planning/Internal/DirectionalShadowPlanning.cpp b/engine/src/Rendering/Planning/Internal/DirectionalShadowPlanning.cpp new file mode 100644 index 00000000..1ebfaa8e --- /dev/null +++ b/engine/src/Rendering/Planning/Internal/DirectionalShadowPlanning.cpp @@ -0,0 +1,335 @@ +#include "Rendering/Planning/Internal/DirectionalShadowPlanning.h" + +#include "Components/CameraComponent.h" +#include "Components/GameObject.h" +#include "Components/LightComponent.h" +#include "Components/MeshFilterComponent.h" +#include "Components/MeshRendererComponent.h" +#include "Core/Math/Matrix4.h" +#include "Core/Math/Quaternion.h" +#include "Scene/Scene.h" + +#include +#include +#include + +namespace XCEngine { +namespace Rendering { +namespace Internal { + +namespace { + +std::array BuildBoundsCorners(const Math::Bounds& bounds) { + const Math::Vector3 min = bounds.GetMin(); + const Math::Vector3 max = bounds.GetMax(); + return { + min, + Math::Vector3(max.x, min.y, min.z), + Math::Vector3(min.x, max.y, min.z), + Math::Vector3(min.x, min.y, max.z), + max, + Math::Vector3(min.x, max.y, max.z), + Math::Vector3(max.x, min.y, max.z), + Math::Vector3(max.x, max.y, min.z) + }; +} + +Math::Bounds TransformBoundsToWorldSpace( + const Math::Bounds& localBounds, + const Math::Matrix4x4& localToWorld, + std::array* outWorldCorners = nullptr) { + const std::array localCorners = BuildBoundsCorners(localBounds); + std::array worldCorners = {}; + for (size_t index = 0; index < localCorners.size(); ++index) { + worldCorners[index] = localToWorld.MultiplyPoint(localCorners[index]); + } + + Math::Bounds worldBounds(worldCorners[0], Math::Vector3::Zero()); + for (size_t index = 1; index < worldCorners.size(); ++index) { + worldBounds.Encapsulate(worldCorners[index]); + } + + if (outWorldCorners != nullptr) { + *outWorldCorners = worldCorners; + } + + return worldBounds; +} + +void ExpandLightSpaceBounds( + const Math::Matrix4x4& view, + const std::array& worldCorners, + float& minX, + float& maxX, + float& minY, + float& maxY, + float& minZ, + float& maxZ) { + for (const Math::Vector3& corner : worldCorners) { + const Math::Vector3 cornerLS = view.MultiplyPoint(corner); + minX = std::min(minX, cornerLS.x); + maxX = std::max(maxX, cornerLS.x); + minY = std::min(minY, cornerLS.y); + maxY = std::max(maxY, cornerLS.y); + minZ = std::min(minZ, cornerLS.z); + maxZ = std::max(maxZ, cornerLS.z); + } +} + +} // namespace + +DirectionalShadowPlanningSettings SanitizeDirectionalShadowPlanningSettings( + const DirectionalShadowPlanningSettings& settings) { + DirectionalShadowPlanningSettings sanitized = settings; + const DirectionalShadowPlanningSettings defaults = {}; + + if (sanitized.mapDimension == 0u) { + sanitized.mapDimension = defaults.mapDimension; + } + if (sanitized.minFocusDistance <= Math::EPSILON) { + sanitized.minFocusDistance = defaults.minFocusDistance; + } + if (sanitized.maxFocusDistance < sanitized.minFocusDistance) { + sanitized.maxFocusDistance = (settings.maxFocusDistance >= defaults.minFocusDistance) + ? settings.maxFocusDistance + : defaults.maxFocusDistance; + sanitized.maxFocusDistance = std::max(sanitized.maxFocusDistance, sanitized.minFocusDistance); + } + if (sanitized.perspectiveFocusFactor <= Math::EPSILON) { + sanitized.perspectiveFocusFactor = defaults.perspectiveFocusFactor; + } + if (sanitized.orthographicFocusFactor <= Math::EPSILON) { + sanitized.orthographicFocusFactor = defaults.orthographicFocusFactor; + } + if (sanitized.minDepthRange <= Math::EPSILON) { + sanitized.minDepthRange = defaults.minDepthRange; + } + if (sanitized.boundsPadding < 0.0f) { + sanitized.boundsPadding = defaults.boundsPadding; + } + if (sanitized.minDepthPadding < 0.0f) { + sanitized.minDepthPadding = defaults.minDepthPadding; + } + + return sanitized; +} + +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::Scene& scene, + const Components::CameraComponent& camera, + const Components::LightComponent& light, + const DirectionalShadowPlanningSettings& shadowSettings, + float viewportAspect) { + 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 viewRight = camera.transform().GetRight().SqrMagnitude() <= Math::EPSILON + ? Math::Vector3::Right() + : camera.transform().GetRight().Normalized(); + const Math::Vector3 viewUp = camera.transform().GetUp().SqrMagnitude() <= Math::EPSILON + ? Math::Vector3::Up() + : camera.transform().GetUp().Normalized(); + const Math::Vector3 cameraPosition = camera.transform().GetPosition(); + const float aspect = viewportAspect > Math::EPSILON ? viewportAspect : 1.0f; + + const float shadowDistance = camera.GetProjectionType() == Components::CameraProjectionType::Perspective + ? std::clamp( + camera.GetFarClipPlane() * shadowSettings.perspectiveFocusFactor, + shadowSettings.minFocusDistance, + shadowSettings.maxFocusDistance) + : std::clamp( + camera.GetOrthographicSize() * shadowSettings.orthographicFocusFactor, + shadowSettings.minFocusDistance, + shadowSettings.maxFocusDistance); + const float sliceNear = std::max(camera.GetNearClipPlane(), 0.1f); + const float sliceFar = std::max(sliceNear + 0.1f, shadowDistance); + + std::array frustumCorners = {}; + if (camera.GetProjectionType() == Components::CameraProjectionType::Perspective) { + const float tanHalfFov = std::tan(camera.GetFieldOfView() * Math::DEG_TO_RAD * 0.5f); + const float nearHalfHeight = tanHalfFov * sliceNear; + const float nearHalfWidth = nearHalfHeight * aspect; + const float farHalfHeight = tanHalfFov * sliceFar; + const float farHalfWidth = farHalfHeight * aspect; + + const Math::Vector3 nearCenter = cameraPosition + viewForward * sliceNear; + const Math::Vector3 farCenter = cameraPosition + viewForward * sliceFar; + + frustumCorners[0] = nearCenter - viewRight * nearHalfWidth - viewUp * nearHalfHeight; + frustumCorners[1] = nearCenter + viewRight * nearHalfWidth - viewUp * nearHalfHeight; + frustumCorners[2] = nearCenter - viewRight * nearHalfWidth + viewUp * nearHalfHeight; + frustumCorners[3] = nearCenter + viewRight * nearHalfWidth + viewUp * nearHalfHeight; + frustumCorners[4] = farCenter - viewRight * farHalfWidth - viewUp * farHalfHeight; + frustumCorners[5] = farCenter + viewRight * farHalfWidth - viewUp * farHalfHeight; + frustumCorners[6] = farCenter - viewRight * farHalfWidth + viewUp * farHalfHeight; + frustumCorners[7] = farCenter + viewRight * farHalfWidth + viewUp * farHalfHeight; + } else { + const float orthoHalfHeight = camera.GetOrthographicSize(); + const float orthoHalfWidth = orthoHalfHeight * aspect; + const Math::Vector3 nearCenter = cameraPosition + viewForward * sliceNear; + const Math::Vector3 farCenter = cameraPosition + viewForward * sliceFar; + + frustumCorners[0] = nearCenter - viewRight * orthoHalfWidth - viewUp * orthoHalfHeight; + frustumCorners[1] = nearCenter + viewRight * orthoHalfWidth - viewUp * orthoHalfHeight; + frustumCorners[2] = nearCenter - viewRight * orthoHalfWidth + viewUp * orthoHalfHeight; + frustumCorners[3] = nearCenter + viewRight * orthoHalfWidth + viewUp * orthoHalfHeight; + frustumCorners[4] = farCenter - viewRight * orthoHalfWidth - viewUp * orthoHalfHeight; + frustumCorners[5] = farCenter + viewRight * orthoHalfWidth - viewUp * orthoHalfHeight; + frustumCorners[6] = farCenter - viewRight * orthoHalfWidth + viewUp * orthoHalfHeight; + frustumCorners[7] = farCenter + viewRight * orthoHalfWidth + viewUp * orthoHalfHeight; + } + + Math::Vector3 focusPoint = Math::Vector3::Zero(); + for (const Math::Vector3& corner : frustumCorners) { + focusPoint += corner; + } + focusPoint /= static_cast(frustumCorners.size()); + + Math::Bounds frustumWorldBounds(frustumCorners[0], Math::Vector3::Zero()); + for (size_t index = 1; index < frustumCorners.size(); ++index) { + frustumWorldBounds.Encapsulate(frustumCorners[index]); + } + + const float shadowViewDistance = std::max(sliceFar, shadowSettings.minDepthRange * 0.5f); + const Math::Vector3 shadowWorldPosition = + focusPoint + lightDirection * shadowViewDistance; + + Math::Vector3 shadowUp = Math::Vector3::Up(); + if (std::abs(Math::Vector3::Dot(lightDirection, shadowUp)) > 0.98f) { + shadowUp = Math::Vector3::Forward(); + } + + const Math::Vector3 shadowForward = (focusPoint - shadowWorldPosition).SqrMagnitude() <= Math::EPSILON + ? (lightDirection * -1.0f) + : (focusPoint - shadowWorldPosition).Normalized(); + const Math::Quaternion shadowRotation = Math::Quaternion::LookRotation(shadowForward, shadowUp); + const Math::Matrix4x4 view = Math::Matrix4x4::TRS( + shadowWorldPosition, + shadowRotation, + Math::Vector3::One()).Inverse(); + + float minX = std::numeric_limits::max(); + float maxX = std::numeric_limits::lowest(); + float minY = std::numeric_limits::max(); + float maxY = std::numeric_limits::lowest(); + float minZ = std::numeric_limits::max(); + float maxZ = std::numeric_limits::lowest(); + ExpandLightSpaceBounds(view, frustumCorners, minX, maxX, minY, maxY, minZ, maxZ); + + const uint32_t cullingMask = camera.GetCullingMask(); + const std::vector meshFilters = + scene.FindObjectsOfType(); + for (Components::MeshFilterComponent* meshFilter : meshFilters) { + if (meshFilter == nullptr || + !meshFilter->IsEnabled() || + meshFilter->GetGameObject() == nullptr) { + continue; + } + + Components::GameObject* gameObject = meshFilter->GetGameObject(); + if (!gameObject->IsActiveInHierarchy()) { + continue; + } + + const uint32_t gameObjectLayerMask = 1u << gameObject->GetLayer(); + if ((cullingMask & gameObjectLayerMask) == 0u) { + continue; + } + + Components::MeshRendererComponent* meshRenderer = + gameObject->GetComponent(); + if (meshRenderer == nullptr || + !meshRenderer->IsEnabled() || + (!meshRenderer->GetCastShadows() && !meshRenderer->GetReceiveShadows())) { + continue; + } + + Resources::Mesh* mesh = meshFilter->GetMesh(); + if (mesh == nullptr || !mesh->IsValid()) { + continue; + } + + std::array worldCorners = {}; + const Math::Bounds worldBounds = TransformBoundsToWorldSpace( + mesh->GetBounds(), + gameObject->GetTransform()->GetLocalToWorldMatrix(), + &worldCorners); + if (!frustumWorldBounds.Intersects(worldBounds)) { + continue; + } + + ExpandLightSpaceBounds(view, worldCorners, minX, maxX, minY, maxY, minZ, maxZ); + } + + minX -= shadowSettings.boundsPadding; + maxX += shadowSettings.boundsPadding; + minY -= shadowSettings.boundsPadding; + maxY += shadowSettings.boundsPadding; + minZ -= shadowSettings.minDepthPadding; + maxZ += shadowSettings.minDepthPadding; + + const float shadowHalfExtent = std::max( + std::max(std::abs(minX), std::abs(maxX)), + std::max(std::abs(minY), std::abs(maxY))); + const float unclampedShadowDepthRange = maxZ - minZ; + if (unclampedShadowDepthRange < shadowSettings.minDepthRange) { + const float centerZ = (minZ + maxZ) * 0.5f; + const float halfDepthRange = shadowSettings.minDepthRange * 0.5f; + minZ = centerZ - halfDepthRange; + maxZ = centerZ + halfDepthRange; + } + + const Math::Matrix4x4 projection = Math::Matrix4x4::Orthographic( + minX, + maxX, + minY, + maxY, + minZ, + maxZ); + + plan.enabled = true; + plan.lightDirection = lightDirection; + plan.focusPoint = focusPoint; + plan.orthographicHalfExtent = shadowHalfExtent; + plan.nearClipPlane = minZ; + plan.farClipPlane = maxZ; + plan.mapWidth = shadowSettings.mapDimension; + plan.mapHeight = shadowSettings.mapDimension; + 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 Internal +} // namespace Rendering +} // namespace XCEngine diff --git a/engine/src/Rendering/Planning/Internal/DirectionalShadowPlanning.h b/engine/src/Rendering/Planning/Internal/DirectionalShadowPlanning.h new file mode 100644 index 00000000..f9caf12e --- /dev/null +++ b/engine/src/Rendering/Planning/Internal/DirectionalShadowPlanning.h @@ -0,0 +1,34 @@ +#pragma once + +#include "Rendering/Planning/SceneRenderRequestPlanner.h" + +#include + +namespace XCEngine { +namespace Components { +class CameraComponent; +class LightComponent; +class Scene; +} // namespace Components + +namespace Rendering { +namespace Internal { + +DirectionalShadowPlanningSettings SanitizeDirectionalShadowPlanningSettings( + const DirectionalShadowPlanningSettings& settings); + +bool ShouldPlanDirectionalShadowForCamera( + const Components::CameraComponent& camera, + size_t renderedBaseCameraCount, + size_t renderedRequestCount); + +DirectionalShadowRenderPlan BuildDirectionalShadowRenderPlan( + const Components::Scene& scene, + const Components::CameraComponent& camera, + const Components::LightComponent& light, + const DirectionalShadowPlanningSettings& shadowSettings, + float viewportAspect); + +} // namespace Internal +} // namespace Rendering +} // namespace XCEngine diff --git a/engine/src/Rendering/Planning/SceneRenderRequestPlanner.cpp b/engine/src/Rendering/Planning/SceneRenderRequestPlanner.cpp index 5b970e31..6f47c63a 100644 --- a/engine/src/Rendering/Planning/SceneRenderRequestPlanner.cpp +++ b/engine/src/Rendering/Planning/SceneRenderRequestPlanner.cpp @@ -1,339 +1,21 @@ #include "Rendering/Planning/SceneRenderRequestPlanner.h" -#include "Components/GameObject.h" +#include "Components/CameraComponent.h" #include "Components/LightComponent.h" -#include "Components/MeshFilterComponent.h" -#include "Components/MeshRendererComponent.h" -#include "Core/Math/Matrix4.h" -#include "Core/Math/Quaternion.h" #include "Rendering/Extraction/RenderSceneUtility.h" +#include "Rendering/Planning/Internal/DirectionalShadowPlanning.h" #include "Rendering/Planning/SceneRenderRequestUtils.h" - #include "Scene/Scene.h" -#include #include -#include namespace XCEngine { namespace Rendering { -namespace { - -DirectionalShadowPlanningSettings SanitizeDirectionalShadowPlanningSettings( - const DirectionalShadowPlanningSettings& settings) { - DirectionalShadowPlanningSettings sanitized = settings; - const DirectionalShadowPlanningSettings defaults = {}; - - if (sanitized.mapDimension == 0u) { - sanitized.mapDimension = defaults.mapDimension; - } - if (sanitized.minFocusDistance <= Math::EPSILON) { - sanitized.minFocusDistance = defaults.minFocusDistance; - } - if (sanitized.maxFocusDistance < sanitized.minFocusDistance) { - sanitized.maxFocusDistance = (settings.maxFocusDistance >= defaults.minFocusDistance) - ? settings.maxFocusDistance - : defaults.maxFocusDistance; - sanitized.maxFocusDistance = std::max(sanitized.maxFocusDistance, sanitized.minFocusDistance); - } - if (sanitized.perspectiveFocusFactor <= Math::EPSILON) { - sanitized.perspectiveFocusFactor = defaults.perspectiveFocusFactor; - } - if (sanitized.orthographicFocusFactor <= Math::EPSILON) { - sanitized.orthographicFocusFactor = defaults.orthographicFocusFactor; - } - if (sanitized.minDepthRange <= Math::EPSILON) { - sanitized.minDepthRange = defaults.minDepthRange; - } - if (sanitized.boundsPadding < 0.0f) { - sanitized.boundsPadding = defaults.boundsPadding; - } - if (sanitized.minDepthPadding < 0.0f) { - sanitized.minDepthPadding = defaults.minDepthPadding; - } - - return sanitized; -} - -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; -} - -std::array BuildBoundsCorners(const Math::Bounds& bounds) { - const Math::Vector3 min = bounds.GetMin(); - const Math::Vector3 max = bounds.GetMax(); - return { - min, - Math::Vector3(max.x, min.y, min.z), - Math::Vector3(min.x, max.y, min.z), - Math::Vector3(min.x, min.y, max.z), - max, - Math::Vector3(min.x, max.y, max.z), - Math::Vector3(max.x, min.y, max.z), - Math::Vector3(max.x, max.y, min.z) - }; -} - -Math::Bounds TransformBoundsToWorldSpace( - const Math::Bounds& localBounds, - const Math::Matrix4x4& localToWorld, - std::array* outWorldCorners = nullptr) { - const std::array localCorners = BuildBoundsCorners(localBounds); - std::array worldCorners = {}; - for (size_t index = 0; index < localCorners.size(); ++index) { - worldCorners[index] = localToWorld.MultiplyPoint(localCorners[index]); - } - - Math::Bounds worldBounds(worldCorners[0], Math::Vector3::Zero()); - for (size_t index = 1; index < worldCorners.size(); ++index) { - worldBounds.Encapsulate(worldCorners[index]); - } - - if (outWorldCorners != nullptr) { - *outWorldCorners = worldCorners; - } - - return worldBounds; -} - -void ExpandLightSpaceBounds( - const Math::Matrix4x4& view, - const std::array& worldCorners, - float& minX, - float& maxX, - float& minY, - float& maxY, - float& minZ, - float& maxZ) { - for (const Math::Vector3& corner : worldCorners) { - const Math::Vector3 cornerLS = view.MultiplyPoint(corner); - minX = std::min(minX, cornerLS.x); - maxX = std::max(maxX, cornerLS.x); - minY = std::min(minY, cornerLS.y); - maxY = std::max(maxY, cornerLS.y); - minZ = std::min(minZ, cornerLS.z); - maxZ = std::max(maxZ, cornerLS.z); - } -} - -DirectionalShadowRenderPlan BuildDirectionalShadowRenderPlan( - const Components::Scene& scene, - const Components::CameraComponent& camera, - const Components::LightComponent& light, - const DirectionalShadowPlanningSettings& shadowSettings, - float viewportAspect) { - 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 viewRight = camera.transform().GetRight().SqrMagnitude() <= Math::EPSILON - ? Math::Vector3::Right() - : camera.transform().GetRight().Normalized(); - const Math::Vector3 viewUp = camera.transform().GetUp().SqrMagnitude() <= Math::EPSILON - ? Math::Vector3::Up() - : camera.transform().GetUp().Normalized(); - const Math::Vector3 cameraPosition = camera.transform().GetPosition(); - const float aspect = viewportAspect > Math::EPSILON ? viewportAspect : 1.0f; - - const float shadowDistance = camera.GetProjectionType() == Components::CameraProjectionType::Perspective - ? std::clamp( - camera.GetFarClipPlane() * shadowSettings.perspectiveFocusFactor, - shadowSettings.minFocusDistance, - shadowSettings.maxFocusDistance) - : std::clamp( - camera.GetOrthographicSize() * shadowSettings.orthographicFocusFactor, - shadowSettings.minFocusDistance, - shadowSettings.maxFocusDistance); - const float sliceNear = std::max(camera.GetNearClipPlane(), 0.1f); - const float sliceFar = std::max(sliceNear + 0.1f, shadowDistance); - - std::array frustumCorners = {}; - if (camera.GetProjectionType() == Components::CameraProjectionType::Perspective) { - const float tanHalfFov = std::tan(camera.GetFieldOfView() * Math::DEG_TO_RAD * 0.5f); - const float nearHalfHeight = tanHalfFov * sliceNear; - const float nearHalfWidth = nearHalfHeight * aspect; - const float farHalfHeight = tanHalfFov * sliceFar; - const float farHalfWidth = farHalfHeight * aspect; - - const Math::Vector3 nearCenter = cameraPosition + viewForward * sliceNear; - const Math::Vector3 farCenter = cameraPosition + viewForward * sliceFar; - - frustumCorners[0] = nearCenter - viewRight * nearHalfWidth - viewUp * nearHalfHeight; - frustumCorners[1] = nearCenter + viewRight * nearHalfWidth - viewUp * nearHalfHeight; - frustumCorners[2] = nearCenter - viewRight * nearHalfWidth + viewUp * nearHalfHeight; - frustumCorners[3] = nearCenter + viewRight * nearHalfWidth + viewUp * nearHalfHeight; - frustumCorners[4] = farCenter - viewRight * farHalfWidth - viewUp * farHalfHeight; - frustumCorners[5] = farCenter + viewRight * farHalfWidth - viewUp * farHalfHeight; - frustumCorners[6] = farCenter - viewRight * farHalfWidth + viewUp * farHalfHeight; - frustumCorners[7] = farCenter + viewRight * farHalfWidth + viewUp * farHalfHeight; - } else { - const float orthoHalfHeight = camera.GetOrthographicSize(); - const float orthoHalfWidth = orthoHalfHeight * aspect; - const Math::Vector3 nearCenter = cameraPosition + viewForward * sliceNear; - const Math::Vector3 farCenter = cameraPosition + viewForward * sliceFar; - - frustumCorners[0] = nearCenter - viewRight * orthoHalfWidth - viewUp * orthoHalfHeight; - frustumCorners[1] = nearCenter + viewRight * orthoHalfWidth - viewUp * orthoHalfHeight; - frustumCorners[2] = nearCenter - viewRight * orthoHalfWidth + viewUp * orthoHalfHeight; - frustumCorners[3] = nearCenter + viewRight * orthoHalfWidth + viewUp * orthoHalfHeight; - frustumCorners[4] = farCenter - viewRight * orthoHalfWidth - viewUp * orthoHalfHeight; - frustumCorners[5] = farCenter + viewRight * orthoHalfWidth - viewUp * orthoHalfHeight; - frustumCorners[6] = farCenter - viewRight * orthoHalfWidth + viewUp * orthoHalfHeight; - frustumCorners[7] = farCenter + viewRight * orthoHalfWidth + viewUp * orthoHalfHeight; - } - - Math::Vector3 focusPoint = Math::Vector3::Zero(); - for (const Math::Vector3& corner : frustumCorners) { - focusPoint += corner; - } - focusPoint /= static_cast(frustumCorners.size()); - - Math::Bounds frustumWorldBounds(frustumCorners[0], Math::Vector3::Zero()); - for (size_t index = 1; index < frustumCorners.size(); ++index) { - frustumWorldBounds.Encapsulate(frustumCorners[index]); - } - - const float shadowViewDistance = std::max(sliceFar, shadowSettings.minDepthRange * 0.5f); - const Math::Vector3 shadowWorldPosition = - focusPoint + lightDirection * shadowViewDistance; - - Math::Vector3 shadowUp = Math::Vector3::Up(); - if (std::abs(Math::Vector3::Dot(lightDirection, shadowUp)) > 0.98f) { - shadowUp = Math::Vector3::Forward(); - } - - const Math::Vector3 shadowForward = (focusPoint - shadowWorldPosition).SqrMagnitude() <= Math::EPSILON - ? (lightDirection * -1.0f) - : (focusPoint - shadowWorldPosition).Normalized(); - const Math::Quaternion shadowRotation = Math::Quaternion::LookRotation(shadowForward, shadowUp); - const Math::Matrix4x4 view = Math::Matrix4x4::TRS( - shadowWorldPosition, - shadowRotation, - Math::Vector3::One()).Inverse(); - - float minX = std::numeric_limits::max(); - float maxX = std::numeric_limits::lowest(); - float minY = std::numeric_limits::max(); - float maxY = std::numeric_limits::lowest(); - float minZ = std::numeric_limits::max(); - float maxZ = std::numeric_limits::lowest(); - ExpandLightSpaceBounds(view, frustumCorners, minX, maxX, minY, maxY, minZ, maxZ); - - const uint32_t cullingMask = camera.GetCullingMask(); - const std::vector meshFilters = - scene.FindObjectsOfType(); - for (Components::MeshFilterComponent* meshFilter : meshFilters) { - if (meshFilter == nullptr || - !meshFilter->IsEnabled() || - meshFilter->GetGameObject() == nullptr) { - continue; - } - - Components::GameObject* gameObject = meshFilter->GetGameObject(); - if (!gameObject->IsActiveInHierarchy()) { - continue; - } - - const uint32_t gameObjectLayerMask = 1u << gameObject->GetLayer(); - if ((cullingMask & gameObjectLayerMask) == 0u) { - continue; - } - - Components::MeshRendererComponent* meshRenderer = - gameObject->GetComponent(); - if (meshRenderer == nullptr || - !meshRenderer->IsEnabled() || - (!meshRenderer->GetCastShadows() && !meshRenderer->GetReceiveShadows())) { - continue; - } - - Resources::Mesh* mesh = meshFilter->GetMesh(); - if (mesh == nullptr || !mesh->IsValid()) { - continue; - } - - std::array worldCorners = {}; - const Math::Bounds worldBounds = TransformBoundsToWorldSpace( - mesh->GetBounds(), - gameObject->GetTransform()->GetLocalToWorldMatrix(), - &worldCorners); - if (!frustumWorldBounds.Intersects(worldBounds)) { - continue; - } - - ExpandLightSpaceBounds(view, worldCorners, minX, maxX, minY, maxY, minZ, maxZ); - } - - minX -= shadowSettings.boundsPadding; - maxX += shadowSettings.boundsPadding; - minY -= shadowSettings.boundsPadding; - maxY += shadowSettings.boundsPadding; - minZ -= shadowSettings.minDepthPadding; - maxZ += shadowSettings.minDepthPadding; - - const float shadowHalfExtent = std::max( - std::max(std::abs(minX), std::abs(maxX)), - std::max(std::abs(minY), std::abs(maxY))); - const float unclampedShadowDepthRange = maxZ - minZ; - if (unclampedShadowDepthRange < shadowSettings.minDepthRange) { - const float centerZ = (minZ + maxZ) * 0.5f; - const float halfDepthRange = shadowSettings.minDepthRange * 0.5f; - minZ = centerZ - halfDepthRange; - maxZ = centerZ + halfDepthRange; - } - - const Math::Matrix4x4 projection = Math::Matrix4x4::Orthographic( - minX, - maxX, - minY, - maxY, - minZ, - maxZ); - - plan.enabled = true; - plan.lightDirection = lightDirection; - plan.focusPoint = focusPoint; - plan.orthographicHalfExtent = shadowHalfExtent; - plan.nearClipPlane = minZ; - plan.farClipPlane = maxZ; - plan.mapWidth = shadowSettings.mapDimension; - plan.mapHeight = shadowSettings.mapDimension; - 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 - void SceneRenderRequestPlanner::SetDirectionalShadowPlanningSettings( const DirectionalShadowPlanningSettings& settings) { - m_directionalShadowPlanningSettings = SanitizeDirectionalShadowPlanningSettings(settings); + m_directionalShadowPlanningSettings = + Internal::SanitizeDirectionalShadowPlanningSettings(settings); } const DirectionalShadowPlanningSettings& SceneRenderRequestPlanner::GetDirectionalShadowPlanningSettings() const { @@ -387,7 +69,7 @@ std::vector SceneRenderRequestPlanner::BuildRequests( continue; } - if (ShouldPlanDirectionalShadowForCamera( + if (Internal::ShouldPlanDirectionalShadowForCamera( *camera, renderedBaseCameraCount, requests.size())) { @@ -399,7 +81,7 @@ std::vector SceneRenderRequestPlanner::BuildRequests( ? static_cast(surface.GetRenderAreaWidth()) / static_cast(surface.GetRenderAreaHeight()) : 1.0f; - request.directionalShadow = BuildDirectionalShadowRenderPlan( + request.directionalShadow = Internal::BuildDirectionalShadowRenderPlan( scene, *camera, *mainDirectionalLight,