feat: add spot light shadows with a tilled buffer
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
#ifndef FRUSTUM_H
|
||||
#define FRUSTUM_H
|
||||
|
||||
#include <array>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include "Camera.h"
|
||||
@@ -71,7 +73,12 @@ namespace edge
|
||||
std::array<glm::vec3, 8> calculateFrustumCornersWorldSpace(const Camera& camera);
|
||||
|
||||
Frustum createFrustumFromCamera(const Camera& camera);
|
||||
Frustum createFrustumFromViewProj(const glm::mat4& viewProj);
|
||||
std::array<glm::vec3, 8> calculateFrustumCorners(const glm::mat4& viewProj);
|
||||
|
||||
bool isInFrustum(const Frustum& frustum, const Sphere& s);
|
||||
// Conservative: frustums that only come close can still count as overlapping
|
||||
bool frustumsOverlap(const Frustum& frustum, const glm::mat4& otherViewProj);
|
||||
bool isInFrustum(const Frustum& frustum, const AABB& aabb);
|
||||
Sphere calculateBoundingSphereWorld(const glm::mat4& transform, const Sphere& s, bool hasSkeleton);
|
||||
}
|
||||
|
||||
@@ -3,9 +3,12 @@
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include <glm/mat4x4.hpp>
|
||||
#include <glm/vec2.hpp>
|
||||
#include <glm/vec3.hpp>
|
||||
|
||||
inline constexpr std::uint32_t NO_SHADOW = 0xFFFFFFFF;
|
||||
|
||||
enum class LightType : std::uint32_t {
|
||||
Directional = 0,
|
||||
Point = 1,
|
||||
@@ -26,7 +29,7 @@ struct Light {
|
||||
float innerConeAngle{0.35f};
|
||||
float outerConeAngle{0.5f};
|
||||
|
||||
// directional lights only, the first one submitted each frame gets the shadow map
|
||||
// directional and spot lights, point lights don't have shadows yet
|
||||
bool castShadows{false};
|
||||
};
|
||||
|
||||
@@ -42,10 +45,15 @@ struct GPULight {
|
||||
|
||||
float spotInnerCos;
|
||||
float spotOuterCos;
|
||||
std::uint32_t castShadows;
|
||||
std::uint32_t shadowIndex;
|
||||
float padding;
|
||||
};
|
||||
|
||||
struct GPUSpotShadow {
|
||||
glm::mat4 lightViewProj;
|
||||
float normalOffsetPerMeter;
|
||||
};
|
||||
|
||||
static_assert(sizeof(GPULight) == 64, "GPULight must match the struct in lights.glsl");
|
||||
|
||||
#endif //LIGHT_H
|
||||
|
||||
@@ -30,7 +30,7 @@ public:
|
||||
void resize(GfxDevice& gfxDevice, glm::ivec2 drawImageSize);
|
||||
|
||||
void beginDrawing(std::size_t frameIndex);
|
||||
void appendLight(const Light& light);
|
||||
void appendLight(const Light& light, std::uint32_t shadowIndex);
|
||||
|
||||
void flushCurrentFrame(VkCommandBuffer cmd, GfxDevice& gfxDevice);
|
||||
|
||||
|
||||
@@ -8,33 +8,46 @@
|
||||
#include <glm/glm.hpp>
|
||||
#include <vulkan/vulkan.h>
|
||||
|
||||
#include <destrum/Graphics/Light.h>
|
||||
#include <destrum/Graphics/Pipeline.h>
|
||||
#include <destrum/Graphics/ids.h>
|
||||
#include <destrum/Graphics/MeshDrawCommand.h>
|
||||
#include <destrum/Graphics/Resources/Buffer.h>
|
||||
|
||||
class GfxDevice;
|
||||
struct GPUImage;
|
||||
class RenderResources;
|
||||
|
||||
// Renders the depth of the scene as seen from a directional light, centered around the camera
|
||||
// Renders scene depth from the lights: one map for the directional light, one atlas tile per spot light
|
||||
class ShadowMapPipeline final
|
||||
{
|
||||
public:
|
||||
static constexpr std::uint32_t SHADOW_MAP_SIZE = 2048;
|
||||
static constexpr std::uint32_t SPOT_SHADOW_TILE_SIZE = 1024;
|
||||
static constexpr std::uint32_t SPOT_SHADOW_TILES_PER_ROW = 4;
|
||||
static constexpr std::uint32_t MAX_SPOT_SHADOWS = SPOT_SHADOW_TILES_PER_ROW * SPOT_SHADOW_TILES_PER_ROW;
|
||||
|
||||
void init(GfxDevice& gfxDevice, RenderResources& resources);
|
||||
void cleanup(VkDevice device);
|
||||
|
||||
void draw(
|
||||
void drawDirectionalShadow(
|
||||
VkCommandBuffer cmd,
|
||||
const RenderResources& resources,
|
||||
const GPUBuffer& sceneDataBuffer,
|
||||
const std::vector<MeshDrawCommand>& drawCommands,
|
||||
const std::vector<std::size_t>& sortedDrawCommands);
|
||||
const std::vector<std::size_t>& sortedDrawCommands,
|
||||
const glm::mat4& lightViewProj);
|
||||
|
||||
[[nodiscard]] glm::mat4 calculateLightViewProj(const glm::vec3& lightDirection, const glm::vec3& center) const;
|
||||
void drawSpotShadows(
|
||||
VkCommandBuffer cmd,
|
||||
const RenderResources& resources,
|
||||
const std::vector<MeshDrawCommand>& drawCommands,
|
||||
const std::vector<std::size_t>& sortedDrawCommands,
|
||||
const std::vector<glm::mat4>& lightViewProjs);
|
||||
|
||||
[[nodiscard]] glm::mat4 calculateDirectionalViewProj(const glm::vec3& lightDirection, const glm::vec3& center) const;
|
||||
[[nodiscard]] static GPUSpotShadow calculateSpotShadow(const Light& spotLight);
|
||||
|
||||
[[nodiscard]] ImageID getShadowMapId() const { return shadowMapId; }
|
||||
[[nodiscard]] ImageID getSpotShadowAtlasId() const { return spotShadowAtlasId; }
|
||||
[[nodiscard]] float getNormalOffset() const;
|
||||
|
||||
// half the width of the area around the camera that receives shadows
|
||||
@@ -44,18 +57,26 @@ public:
|
||||
private:
|
||||
struct PushConstants
|
||||
{
|
||||
glm::mat4 transform;
|
||||
VkDeviceAddress sceneDataBuffer;
|
||||
glm::mat4 modelToLightClip;
|
||||
VkDeviceAddress vertexBuffer;
|
||||
std::uint32_t materialId;
|
||||
std::uint32_t padding;
|
||||
};
|
||||
|
||||
void beginDepthRendering(VkCommandBuffer cmd, const RenderResources& resources, const GPUImage& depthImage);
|
||||
void endDepthRendering(VkCommandBuffer cmd, const GPUImage& depthImage);
|
||||
void setRenderRegion(VkCommandBuffer cmd, const VkRect2D& region);
|
||||
void drawShadowCasters(
|
||||
VkCommandBuffer cmd,
|
||||
const RenderResources& resources,
|
||||
const std::vector<MeshDrawCommand>& drawCommands,
|
||||
const std::vector<std::size_t>& sortedDrawCommands,
|
||||
const glm::mat4& lightViewProj);
|
||||
|
||||
VkPipelineLayout m_pipelineLayout{VK_NULL_HANDLE};
|
||||
std::unique_ptr<Pipeline> m_pipeline;
|
||||
|
||||
VkFormat shadowMapFormat{VK_FORMAT_D32_SFLOAT};
|
||||
ImageID shadowMapId{NULL_IMAGE_ID};
|
||||
ImageID spotShadowAtlasId{NULL_IMAGE_ID};
|
||||
|
||||
float shadowDistance{25.0f};
|
||||
// how far in front of / behind the camera (along the light) casters are still captured
|
||||
|
||||
@@ -17,8 +17,10 @@
|
||||
#include <destrum/Graphics/RenderResources.h>
|
||||
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include <destrum/Graphics/Pipelines/SkinningPipeline.h>
|
||||
|
||||
@@ -112,6 +114,8 @@ public:
|
||||
|
||||
private:
|
||||
void createDrawImage(GfxDevice& gfxDevice, const glm::ivec2& drawImageSize, bool firstCreate);
|
||||
void uploadLights(const Camera& camera);
|
||||
[[nodiscard]] std::uint32_t assignShadowIndex(const Light& light, const Frustum& cameraFrustum);
|
||||
|
||||
RenderResources* resources = nullptr;
|
||||
|
||||
@@ -156,6 +160,10 @@ private:
|
||||
float shadowNormalOffset;
|
||||
std::uint32_t padding;
|
||||
glm::mat4 lightViewProj;
|
||||
|
||||
std::uint32_t spotShadowAtlasId;
|
||||
std::uint32_t spotShadowTilesPerRow;
|
||||
std::array<GPUSpotShadow, ShadowMapPipeline::MAX_SPOT_SHADOWS> spotShadows;
|
||||
};
|
||||
|
||||
NBuffer sceneDataBuffer;
|
||||
@@ -170,6 +178,8 @@ private:
|
||||
std::unique_ptr<ShadowMapPipeline> shadowMapPipeline;
|
||||
|
||||
std::optional<glm::vec3> shadowLightDirection;
|
||||
std::vector<Light> submittedLights;
|
||||
std::vector<GPUSpotShadow> spotShadows;
|
||||
|
||||
bool renderLightHeatmap{false};
|
||||
bool initialized{false};
|
||||
|
||||
Reference in New Issue
Block a user