#include "Rendering/SceneRenderRequestPlanner.h" #include "Components/LightComponent.h" #include "Core/Math/Matrix4.h" #include "Rendering/SceneRenderRequestUtils.h" #include "Scene/Scene.h" #include 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 lights = scene.FindObjectsOfType(); 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 SceneRenderRequestPlanner::CollectCameras( const Components::Scene& scene, Components::CameraComponent* overrideCamera) const { std::vector cameras; if (SceneRenderRequestUtils::IsUsableCamera(overrideCamera)) { cameras.push_back(overrideCamera); return cameras; } cameras = scene.FindObjectsOfType(); cameras.erase( std::remove_if( cameras.begin(), cameras.end(), [](const Components::CameraComponent* camera) { return !SceneRenderRequestUtils::IsUsableCamera(camera); }), cameras.end()); SceneRenderRequestUtils::SortSceneCamerasForRendering(cameras); return cameras; } std::vector SceneRenderRequestPlanner::BuildRequests( const Components::Scene& scene, Components::CameraComponent* overrideCamera, const RenderContext& context, const RenderSurface& surface) const { std::vector requests; const std::vector cameras = CollectCameras(scene, overrideCamera); size_t renderedBaseCameraCount = 0; for (Components::CameraComponent* camera : cameras) { CameraRenderRequest request; if (!SceneRenderRequestUtils::BuildCameraRenderRequest( scene, *camera, context, surface, renderedBaseCameraCount, requests.size(), request)) { 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; } } return requests; } } // namespace Rendering } // namespace XCEngine