Program Listing for File SceneUboMarshal.hpp

Program Listing for File SceneUboMarshal.hpp#

↰ Return to documentation for file (Src/GraphicsEngineVulkan/common/SceneUboMarshal.hpp)

#pragma once

#include <algorithm>
#include <cassert>
#include <cstdint>
#include <span>

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>

#include "common/LightDirection.hpp"
#include "common/host_device_shared_vars.hpp"
#include "renderer/SceneUBO.hpp"
#include "scene/atmospheric_effects/clouds/CloudDispatch.hpp"

namespace Kataglyphis {

// Guards the zero-height extent a minimized or resizing window reports, which would put NaN/Inf in the projection.
constexpr auto aspectRatioOf(uint32_t width, uint32_t height) -> float
{ return (height > 0) ? static_cast<float>(width) / static_cast<float>(height) : 1.0F; }

// Flips Y for Vulkan clip space; never flip the cascade matrices to match (the shadow pass's culling relies on it).
inline auto makeVulkanProjection(float fovDegrees, float aspect, float nearPlane, float farPlane) -> glm::mat4
{
    glm::mat4 projection = glm::perspective(glm::radians(fovDegrees), aspect, nearPlane, farPlane);
    projection[1][1] *= -1;
    return projection;
}

// A negative value cast to uint32_t would wrap, skip the tap loop and read as fully shadowed.
constexpr auto clampPcfRadius(int guiValue) -> uint32_t
{ return static_cast<uint32_t>(std::clamp(guiValue, 0, MAX_PCF_RADIUS)); }

// clouds.slang divides by mesh scale * density multiplier, so a zero in either gives inf/NaN box hits.
constexpr float kMinCloudMeshExtent = 1e-3F;
constexpr float kMinCloudDensityMultiplier = 1e-3F;

constexpr auto clampCloudMeshScale(glm::vec3 meshScale, float densityMultiplier) -> glm::vec4
{
    return { std::max(meshScale.x, kMinCloudMeshExtent),
        std::max(meshScale.y, kMinCloudMeshExtent),
        std::max(meshScale.z, kMinCloudMeshExtent),
        std::max(densityMultiplier, kMinCloudDensityMultiplier) };
}

// Clamped here because the GUI vars can also come from a test or a config load.
constexpr auto clampCloudMarchSteps(int numMarchSteps) -> int
{ return std::clamp(numMarchSteps, kMinCloudMarchSteps, kMaxCloudMarchSteps); }

constexpr auto clampCloudLightMarchSteps(int numMarchStepsToLight) -> int
{ return std::clamp(numMarchStepsToLight, kMinCloudLightMarchSteps, kMaxCloudLightMarchSteps); }

// Scalars: the global module fragment cannot name GUISceneSharedVars. See docs/clouds.md § UBO packing.
inline void fillSceneUboClouds(VulkanRendererInternals::SceneUBO &ubo,
  glm::vec3 meshScale,
  float densityMultiplier,
  glm::vec3 meshOffset,
  float coverageThreshold,
  int numMarchSteps,
  int numMarchStepsToLight,
  float pillowness,
  float cirrusEffect,
  bool powderEffect)
{
    ubo.cloudLightMarch =
      glm::vec4(static_cast<float>(clampCloudLightMarchSteps(numMarchStepsToLight)), 0.0F, 0.0F, 0.0F);
    ubo.cloudMeshScale = clampCloudMeshScale(meshScale, densityMultiplier);
    ubo.cloudMeshOffset = glm::vec4(meshOffset.x, meshOffset.y, meshOffset.z, coverageThreshold);
    ubo.cloudParameters = glm::vec4(
      pillowness, cirrusEffect, powderEffect ? 1.0F : 0.0F, static_cast<float>(clampCloudMarchSteps(numMarchSteps)));
}

// Both .w are filler: shaders read only .xyz.
inline void fillSceneUboCamera(VulkanRendererInternals::SceneUBO &ubo, glm::vec3 position, glm::vec3 direction)
{
    ubo.view_dir = glm::vec4(direction, 1.0F);
    ubo.cam_pos = glm::vec4(position, 1.0F);
}

// color.w carries radiance, the one SceneUBO .w slot a shader must read; direction.w is filler.
inline void fillSceneUboDirectionalLight(VulkanRendererInternals::SceneUBO &ubo,
  glm::vec3 rawDirection,
  glm::vec3 color,
  float radiance)
{
    ubo.dirLight.direction = glm::vec4(normalizedLightDirection(rawDirection), 1.0F);
    ubo.dirLight.color = glm::vec4(color, radiance);
}

// Truncates to the shorter span, since NDEBUG drops the assert; disabled shadows only zero numCascades.
inline auto fillSceneUboCascades(VulkanRendererInternals::SceneUBO &ubo,
  std::span<const float> splitDepths,
  std::span<const glm::mat4> viewProjMatrices,
  bool shadowsEnabled) -> uint32_t
{
    assert(splitDepths.size() == viewProjMatrices.size());

    const size_t activeCascades =
      std::min({ splitDepths.size(), viewProjMatrices.size(), static_cast<size_t>(MAX_CASCADES) });
    for (size_t i = 0; i < activeCascades; ++i) {
        ubo.cascadeSplits[static_cast<int>(i)] = splitDepths[i];
        ubo.cascadeLightSpaceMatrices[i] = viewProjMatrices[i];
    }

    const auto numCascades = shadowsEnabled ? static_cast<uint32_t>(activeCascades) : 0U;
    ubo.numCascades = numCascades;
    return numCascades;
}

}// namespace Kataglyphis