refactor: 解耦重构——还清四处架构债(对齐 UE/Unity 分层)
- SceneRenderer:场景→渲染数据翻译层(UE FSceneRenderer / Unity CullingResults
模式),收口相机/灯光/光空间矩阵/双视图剔除,Renderer 只编排
- pass 对象化(Unity ScriptableRenderPass 模式):8 个 pass lambda 拆为
Passes::{Shadow,Forward,Bloom,Tonemap,ImGui,Present},BuildGraph 超级函数消失;
SceneGpu/FullscreenGpu 承接共享装配
- 中性 AssetRef(Core 层):ECS 组件不再依赖 Assets 模块(Unity InstanceID 思路)
- IAssetUploader 接口:AssetManager 不感知 Vulkan 具体类,上传服务由渲染层
VulkanAssetUploader 提供
- Renderer 公共 API 不变(App 层零改动),编译零错误 validation 零警告
This commit is contained in:
@@ -1,37 +0,0 @@
|
||||
//
|
||||
// Created by atdunbg on 2026/9/22.
|
||||
//
|
||||
// AssetHandle:资产句柄 = (池内索引, 生成号),与 Entity/RG 句柄同一哲学。
|
||||
//
|
||||
// 组件、场景数据只存句柄;所有权归 AssetManager。
|
||||
// 悬空句柄(generation 不匹配)会被 Get 显式拒绝,杜绝 use-after-free。
|
||||
//
|
||||
|
||||
#ifndef HELLOVULKAN_ASSETHANDLE_H
|
||||
#define HELLOVULKAN_ASSETHANDLE_H
|
||||
|
||||
#include <Engine/Core/Types.h>
|
||||
|
||||
// Model 前向声明(句柄不依赖完整类型)
|
||||
namespace Engine { class Model; }
|
||||
|
||||
namespace Engine::Assets {
|
||||
|
||||
inline constexpr u32 kInvalidAssetIndex = 0xFFFFFFFF;
|
||||
|
||||
template<typename T>
|
||||
struct AssetHandle {
|
||||
u32 index = kInvalidAssetIndex;
|
||||
u32 generation = 0;
|
||||
|
||||
bool Valid() const { return index != kInvalidAssetIndex; }
|
||||
explicit operator bool() const { return Valid(); }
|
||||
bool operator==(const AssetHandle& o) const = default;
|
||||
};
|
||||
|
||||
// 常用别名
|
||||
using ModelHandle = AssetHandle<Engine::Model>;
|
||||
|
||||
} // namespace Engine::Assets
|
||||
|
||||
#endif //HELLOVULKAN_ASSETHANDLE_H
|
||||
@@ -3,14 +3,9 @@
|
||||
//
|
||||
// AssetManager:引擎资产层。
|
||||
//
|
||||
// 职责划分(对照 Renderer):
|
||||
// - 资产层:路径 → 解析/加载 → 缓存去重 → 句柄分发 + 生命周期管理
|
||||
// - 渲染层:拿已解析资产创建/绑定 GPU 资源
|
||||
//
|
||||
// Model 的构造仍需要 GPU 上下文(buffer/image upload),这是现阶段的
|
||||
// 折中:资产层"使用"GPU 层的接口,但 Renderer 不再知道磁盘和缓存。
|
||||
// 后续演进:把 Model 拆成 CPU 解析数据 + GPU 上传两段,实现真正的
|
||||
// 异步加载(IO 线程解析 → 主线程/上传队列提交),本文件结构已为此留好位置。
|
||||
// 职责:路径 → 解析/加载 → 缓存去重 → 句柄分发 + 生命周期管理。
|
||||
// GPU 创建不在这里:经 IAssetUploader 接口(渲染层提供实现)完成,
|
||||
// 本类不感知 Vulkan——依赖方向 Assets → 接口,实现由组合根注入。
|
||||
//
|
||||
// 组件存句柄不存指针的理由:
|
||||
// shared_ptr 会引入共享所有权 → 原子引用计数(swap-remove 拷贝时)
|
||||
@@ -23,7 +18,8 @@
|
||||
|
||||
#include <Engine/Core/Types.h>
|
||||
#include <Engine/Core/Log.h>
|
||||
#include <Engine/Assets/AssetHandle.h>
|
||||
#include <Engine/Core/AssetRef.h>
|
||||
#include <Engine/Assets/IAssetUploader.h>
|
||||
#include <Engine/Renderer/Model.h>
|
||||
|
||||
#include <functional>
|
||||
@@ -32,11 +28,6 @@
|
||||
#include <vector>
|
||||
|
||||
namespace Engine {
|
||||
|
||||
class Device;
|
||||
class MemoryAllocator;
|
||||
class DescriptorManager;
|
||||
|
||||
namespace Assets {
|
||||
|
||||
// ---------- 通用资产缓存(不感知 Vulkan / 磁盘细节由 loader 决定) ----------
|
||||
@@ -48,7 +39,7 @@ namespace Engine {
|
||||
using LoaderFn = std::function<Scope<T>(const std::string&)>;
|
||||
|
||||
// 加载(带路径去重)。失败返回无效句柄。
|
||||
AssetHandle<T> Load(const std::string& path, const LoaderFn& loader)
|
||||
AssetRef<T> Load(const std::string& path, const LoaderFn& loader)
|
||||
{
|
||||
if (auto it = m_ByPath.find(path); it != m_ByPath.end()) {
|
||||
return it->second;
|
||||
@@ -60,7 +51,7 @@ namespace Engine {
|
||||
return {};
|
||||
}
|
||||
|
||||
AssetHandle<T> h;
|
||||
AssetRef<T> h;
|
||||
if (!m_Free.empty()) {
|
||||
h.index = m_Free.back();
|
||||
m_Free.pop_back();
|
||||
@@ -77,7 +68,7 @@ namespace Engine {
|
||||
}
|
||||
|
||||
// 句柄 → 资产指针。悬空句柄 FATAL(bug 信号);已卸载返回 nullptr。
|
||||
T* Get(AssetHandle<T> h) const
|
||||
T* Get(AssetRef<T> h) const
|
||||
{
|
||||
if (!h.Valid() || h.index >= m_Entries.size()) {
|
||||
ENGINE_FATAL("AssetCache::Get: invalid handle (index={}, gen={})",
|
||||
@@ -94,7 +85,7 @@ namespace Engine {
|
||||
// 卸载:句柄即刻失效(generation 递增),路径缓存清除。
|
||||
// 注意:GPU 侧何时安全释放由上层(Renderer fence)保证——
|
||||
// 本类只管所有权,不管 GPU 生命周期。
|
||||
void Unload(AssetHandle<T> h)
|
||||
void Unload(AssetRef<T> h)
|
||||
{
|
||||
if (!h.Valid() || h.index >= m_Entries.size()) return;
|
||||
|
||||
@@ -109,7 +100,7 @@ namespace Engine {
|
||||
m_Free.push_back(h.index);
|
||||
}
|
||||
|
||||
const std::string& Path(AssetHandle<T> h) const
|
||||
const std::string& Path(AssetRef<T> h) const
|
||||
{
|
||||
static const std::string kEmpty;
|
||||
if (!h.Valid() || h.index >= m_Entries.size()) return kEmpty;
|
||||
@@ -132,29 +123,27 @@ namespace Engine {
|
||||
|
||||
std::vector<Entry> m_Entries;
|
||||
std::vector<u32> m_Free;
|
||||
std::unordered_map<std::string, AssetHandle<T>> m_ByPath;
|
||||
std::unordered_map<std::string, AssetRef<T>> m_ByPath;
|
||||
};
|
||||
|
||||
// ---------- AssetManager:全引擎资产入口 ----------
|
||||
// (ModelHandle 别名来自 AssetHandle.h)
|
||||
|
||||
class AssetManager {
|
||||
public:
|
||||
// GPU 上下文由组合根(Application)在 Renderer 装配完成后注入
|
||||
AssetManager(Device& device, MemoryAllocator& allocator,
|
||||
DescriptorManager& descriptors,
|
||||
vk::DescriptorSetLayout textureLayout);
|
||||
// 上传服务由组合根(Application)注入:渲染层实现接口,
|
||||
// 资产层不感知 Vulkan 具体类
|
||||
explicit AssetManager(IAssetUploader& uploader);
|
||||
~AssetManager() = default;
|
||||
|
||||
AssetManager(const AssetManager&) = delete;
|
||||
AssetManager& operator=(const AssetManager&) = delete;
|
||||
|
||||
ModelHandle LoadModel(const std::string& path);
|
||||
Model* GetModel(ModelHandle h) const { return m_Models.Get(h); }
|
||||
void UnloadModel(ModelHandle h) { m_Models.Unload(h); }
|
||||
ModelRef LoadModel(const std::string& path);
|
||||
Model* GetModel(ModelRef h) const { return m_Models.Get(h); }
|
||||
void UnloadModel(ModelRef h) { m_Models.Unload(h); }
|
||||
|
||||
// 程序化平面(纯色贴图):size 为半边长。带缓存,同参数共享一份。
|
||||
ModelHandle CreatePlane(f32 halfSize, u8 r, u8 g, u8 b);
|
||||
ModelRef CreatePlane(f32 halfSize, u8 r, u8 g, u8 b);
|
||||
|
||||
AssetCache<Model>& Models() { return m_Models; }
|
||||
|
||||
@@ -162,10 +151,7 @@ namespace Engine {
|
||||
// 各自一个 AssetCache<T> + 便捷方法,模式与 Model 完全一致。
|
||||
|
||||
private:
|
||||
Device& m_Device;
|
||||
MemoryAllocator& m_Allocator;
|
||||
DescriptorManager& m_Descriptors;
|
||||
vk::DescriptorSetLayout m_TextureLayout;
|
||||
IAssetUploader& m_Uploader;
|
||||
|
||||
AssetCache<Model> m_Models;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
//
|
||||
// Created by atdunbg on 2026/9/23.
|
||||
//
|
||||
// IAssetUploader:资产层 → GPU 的上传接口。
|
||||
//
|
||||
// 资产层(AssetManager/AssetCache)只认这个接口,不感知 Vulkan 具体类
|
||||
// (Device/Allocator/DescriptorManager)。实现由渲染层提供
|
||||
// (VulkanAssetUploader),组合根(Renderer)装配后注入。
|
||||
//
|
||||
// 对齐 UE:资产系统(AssetManager/StreamableManager)与渲染资源创建
|
||||
// (rendering thread / RHI)分离,资产层只拿到"可用的渲染资源"。
|
||||
//
|
||||
// 后续演进点:接口加返回 future / 回调即为异步加载
|
||||
// (IO 线程解析 → 上传队列提交),本接口已是正确的接缝。
|
||||
//
|
||||
|
||||
#ifndef HELLOVULKAN_IASSETUPLOADER_H
|
||||
#define HELLOVULKAN_IASSETUPLOADER_H
|
||||
|
||||
#include <Engine/Core/Types.h>
|
||||
#include <Engine/Renderer/Model.h>
|
||||
|
||||
namespace Engine::Assets {
|
||||
|
||||
class IAssetUploader {
|
||||
public:
|
||||
virtual ~IAssetUploader() = default;
|
||||
|
||||
// 磁盘模型 → 解析 + GPU 上传(失败返回 nullptr)
|
||||
virtual Scope<Model> LoadModel(const std::string& path) = 0;
|
||||
|
||||
// 程序化纯色平面(size 为半边长)
|
||||
virtual Scope<Model> CreatePlane(f32 halfSize, u8 r, u8 g, u8 b) = 0;
|
||||
};
|
||||
|
||||
} // namespace Engine::Assets
|
||||
|
||||
#endif //HELLOVULKAN_IASSETUPLOADER_H
|
||||
@@ -0,0 +1,45 @@
|
||||
//
|
||||
// Created by atdunbg on 2026/9/23.
|
||||
//
|
||||
// AssetRef:中性资产引用 = (池内索引, 生成号),与 Entity/RG 句柄同一哲学。
|
||||
//
|
||||
// 放在 Core 层(不依赖 Assets/Vulkan):ECS 组件、序列化数据只存这个
|
||||
// 纯值类型。对齐 UE/Unity 的做法——组件持有"资产引用",但不感知资产
|
||||
// 系统内部:Unity 的 InstanceID、UE 的 FSoftObjectPath 本质都是中性引用
|
||||
// 值,解析归资产系统(AssetManager)。
|
||||
// 悬空引用(generation 不匹配)由 AssetCache::Get 显式拒绝。
|
||||
//
|
||||
|
||||
#ifndef HELLOVULKAN_ASSETREF_H
|
||||
#define HELLOVULKAN_ASSETREF_H
|
||||
|
||||
#include <Engine/Core/Types.h>
|
||||
|
||||
// 资产类型前向声明(必须放全局层:namespace 内前向声明会创建
|
||||
// Engine::Assets::Engine::Model 这类错误嵌套命名空间)
|
||||
namespace Engine {
|
||||
class Model;
|
||||
class Texture;
|
||||
class Material;
|
||||
}
|
||||
|
||||
namespace Engine {
|
||||
|
||||
inline constexpr u32 kInvalidAssetIndex = 0xFFFFFFFF;
|
||||
|
||||
template<typename T>
|
||||
struct AssetRef {
|
||||
u32 index = kInvalidAssetIndex;
|
||||
u32 generation = 0;
|
||||
|
||||
bool Valid() const { return index != kInvalidAssetIndex; }
|
||||
explicit operator bool() const { return Valid(); }
|
||||
bool operator==(const AssetRef& o) const = default;
|
||||
};
|
||||
|
||||
// 常用别名(新增资产类型时在这里加一行)
|
||||
using ModelRef = AssetRef<Model>;
|
||||
|
||||
} // namespace Engine
|
||||
|
||||
#endif //HELLOVULKAN_ASSETREF_H
|
||||
@@ -10,8 +10,8 @@
|
||||
|
||||
#include <Engine/Core/Types.h>
|
||||
#include <Engine/Core/UUID.h>
|
||||
#include <Engine/Core/AssetRef.h>
|
||||
#include <Engine/Renderer/Camera.h>
|
||||
#include <Engine/Assets/AssetHandle.h>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
@@ -62,9 +62,10 @@ namespace Engine::ECS {
|
||||
};
|
||||
|
||||
// 网格渲染体:引用共享的 Model 资产。
|
||||
// 存句柄不存指针/Ref:所有权归 AssetManager,悬空由句柄 generation 拦截。
|
||||
// 存中性 AssetRef(Core 层纯值类型,Unity InstanceID 思路)——
|
||||
// ECS 不感知资产系统内部;解析归 AssetManager,悬空由 generation 拦截。
|
||||
struct MeshRendererComponent {
|
||||
Assets::ModelHandle model;
|
||||
ModelRef model;
|
||||
};
|
||||
|
||||
// 相机。一个场景中 primary=true 的实体是当前渲染视角。
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
//
|
||||
// Created by atdunbg on 2026/9/23.
|
||||
//
|
||||
// BloomPass:Extract(HDR 软阈值)→ BlurH → BlurV,全部半分辨率。
|
||||
// 一个"效果"对应多个 RG pass 的例子(Unity 的一个 ScriptableRenderPass
|
||||
// 常含多个 PassEvent 阶段,同构)。
|
||||
//
|
||||
|
||||
#ifndef HELLOVULKAN_BLOOMPASS_H
|
||||
#define HELLOVULKAN_BLOOMPASS_H
|
||||
|
||||
#include <Engine/Renderer/Passes/RenderPass.h>
|
||||
#include <Engine/Renderer/Passes/FullscreenGpu.h>
|
||||
#include <vulkan/vulkan.hpp>
|
||||
#include <vector>
|
||||
|
||||
namespace Engine::Passes {
|
||||
|
||||
class BloomPass final : public RenderPass {
|
||||
public:
|
||||
BloomPass(FullscreenGpu& gpu, PostProcessParams& params,
|
||||
RG::TextureHandle hdrColor,
|
||||
RG::TextureHandle bloomA, RG::TextureHandle bloomB);
|
||||
~BloomPass() override = default;
|
||||
|
||||
void Register(RG::RenderGraph& rg) override;
|
||||
void OnTargetsRebuilt(RG::RenderGraph& rg) override;
|
||||
|
||||
private:
|
||||
FullscreenGpu& m_Gpu;
|
||||
PostProcessParams& m_Params;
|
||||
RG::TextureHandle m_HDR;
|
||||
RG::TextureHandle m_A; // 提取 + 垂直模糊结果
|
||||
RG::TextureHandle m_B; // 水平模糊中间结果
|
||||
|
||||
// per-slot 输入描述符
|
||||
std::vector<vk::DescriptorSet> m_ExtractSets;
|
||||
std::vector<vk::DescriptorSet> m_BlurHSets;
|
||||
std::vector<vk::DescriptorSet> m_BlurVSets;
|
||||
};
|
||||
|
||||
} // namespace Engine::Passes
|
||||
|
||||
#endif //HELLOVULKAN_BLOOMPASS_H
|
||||
@@ -0,0 +1,38 @@
|
||||
//
|
||||
// Created by atdunbg on 2026/9/23.
|
||||
//
|
||||
// ForwardPass:相机视角渲染到 HDR 颜色图(线性光照值)。
|
||||
// 显示变换不在本 pass——HDRColor 经 Tonemap 归一后才写 Backbuffer
|
||||
// (对齐 UE/Unity:SceneColor(HDR) → tonemapper → UI)。
|
||||
//
|
||||
|
||||
#ifndef HELLOVULKAN_FORWARDPASS_H
|
||||
#define HELLOVULKAN_FORWARDPASS_H
|
||||
|
||||
#include <Engine/Renderer/Passes/RenderPass.h>
|
||||
#include <Engine/Renderer/Passes/SceneGpu.h>
|
||||
#include <Engine/Renderer/SceneTypes.h>
|
||||
|
||||
namespace Engine::Passes {
|
||||
|
||||
class ForwardPass final : public RenderPass {
|
||||
public:
|
||||
ForwardPass(SceneGpu& gpu, FrameSceneData& frame,
|
||||
RG::TextureHandle hdrColor, RG::TextureHandle depth,
|
||||
RG::TextureHandle shadowMap)
|
||||
: m_Gpu(gpu), m_Frame(frame),
|
||||
m_HDRColor(hdrColor), m_Depth(depth), m_ShadowMap(shadowMap) {}
|
||||
|
||||
void Register(RG::RenderGraph& rg) override;
|
||||
|
||||
private:
|
||||
SceneGpu& m_Gpu;
|
||||
FrameSceneData& m_Frame;
|
||||
RG::TextureHandle m_HDRColor;
|
||||
RG::TextureHandle m_Depth;
|
||||
RG::TextureHandle m_ShadowMap;
|
||||
};
|
||||
|
||||
} // namespace Engine::Passes
|
||||
|
||||
#endif //HELLOVULKAN_FORWARDPASS_H
|
||||
@@ -0,0 +1,78 @@
|
||||
//
|
||||
// Created by atdunbg on 2026/9/23.
|
||||
//
|
||||
// FullscreenGpu:全屏后处理共享装配(Tonemap/Bloom 系 pass 共用)。
|
||||
//
|
||||
// 全屏 pass 的共性收口在这里:Set0 = {输入0, 输入1}、push 16B 通用结构
|
||||
// (规避 VUID-08878 的"range 定义必须一致")、无顶点输入全屏三角、
|
||||
// 公共录制函数。新增全屏效果 = 复用本类 + 新增一个 frag shader。
|
||||
//
|
||||
|
||||
#ifndef HELLOVULKAN_FULLSCREENGPU_H
|
||||
#define HELLOVULKAN_FULLSCREENGPU_H
|
||||
|
||||
#include <Engine/Core/Types.h>
|
||||
#include <Engine/RenderGraph/RenderGraph.h>
|
||||
#include <vulkan/vulkan.hpp>
|
||||
|
||||
namespace Engine {
|
||||
|
||||
class Device;
|
||||
class DescriptorManager;
|
||||
class ShaderManager;
|
||||
|
||||
namespace Passes {
|
||||
|
||||
// 全屏 pass 通用 push(16B):所有全屏 pass 统一声明与推送
|
||||
struct FullscreenPush {
|
||||
f32 exposure;
|
||||
f32 param0; // 各 pass 自定语义(tonemap: aces 开关)
|
||||
f32 param1;
|
||||
f32 param2;
|
||||
};
|
||||
|
||||
// 后处理链参数(ImGui 可调;Renderer 持有,pass 按引用读取)
|
||||
struct PostProcessParams {
|
||||
f32 exposure = 1.0f;
|
||||
bool useACES = true;
|
||||
f32 bloomThreshold = 1.0f; // HDR 域亮度阈值
|
||||
f32 bloomStrength = 0.8f; // 叠加强度
|
||||
};
|
||||
|
||||
class FullscreenGpu {
|
||||
public:
|
||||
FullscreenGpu(Device& device, DescriptorManager& descriptors,
|
||||
ShaderManager& shaders);
|
||||
~FullscreenGpu();
|
||||
|
||||
FullscreenGpu(const FullscreenGpu&) = delete;
|
||||
FullscreenGpu& operator=(const FullscreenGpu&) = delete;
|
||||
|
||||
vk::DescriptorSetLayout setLayout; // Set0 = {输入0, 输入1}
|
||||
vk::PipelineLayout layout; // push 16B(FullscreenPush)
|
||||
vk::Sampler sampler;
|
||||
vk::ShaderEXT vert{}, extractFrag{}, blurFrag{}, tonemapFrag{};
|
||||
|
||||
// 各 pass 自己持有 per-slot set;分配统一走这里
|
||||
vk::DescriptorSet AllocateSet();
|
||||
|
||||
// 输入图片绑定(RG 重编译后重绑;物理镜像按槽位各一份)
|
||||
void UpdateSetInput(vk::DescriptorSet set, u32 binding,
|
||||
RG::RenderGraph& rg, RG::TextureHandle input,
|
||||
u32 slot);
|
||||
|
||||
// 全屏 pass 公共录制:动态状态/draw 全一致,仅附件 view、
|
||||
// 输入 set、push 值不同
|
||||
void RecordFullscreen(RG::RenderGraphContext& ctx, vk::ImageView view,
|
||||
vk::ShaderEXT frag, const vk::DescriptorSet& set,
|
||||
const FullscreenPush& push);
|
||||
|
||||
private:
|
||||
Device& m_Device;
|
||||
DescriptorManager& m_Descriptors;
|
||||
};
|
||||
|
||||
} // namespace Passes
|
||||
} // namespace Engine
|
||||
|
||||
#endif //HELLOVULKAN_FULLSCREENGPU_H
|
||||
@@ -0,0 +1,38 @@
|
||||
//
|
||||
// Created by atdunbg on 2026/9/23.
|
||||
//
|
||||
// ImGuiPass:叠加层(loadOp=Load,不清屏)。
|
||||
// 框架只负责把 draw data 录进 cmd;UI 内容全部由派生类在
|
||||
// OnRenderOverlay() 里构建,框架自身不画任何 ImGui 控件。
|
||||
// 未启用叠加层时空转(begin/end 一次空 rendering region)。
|
||||
//
|
||||
|
||||
#ifndef HELLOVULKAN_IMGUIPASS_H
|
||||
#define HELLOVULKAN_IMGUIPASS_H
|
||||
|
||||
#include <Engine/Renderer/Passes/RenderPass.h>
|
||||
|
||||
namespace Engine {
|
||||
|
||||
class ImGuiLayer;
|
||||
|
||||
namespace Passes {
|
||||
|
||||
class ImGuiPass final : public RenderPass {
|
||||
public:
|
||||
// 二级指针:Renderer::SetImGuiLayer 在 Register 之后才可能被调
|
||||
//(EnableImGui 时机由派生类决定),执行期解引用取最新值
|
||||
ImGuiPass(ImGuiLayer** layer, RG::TextureHandle backbuffer)
|
||||
: m_Layer(layer), m_Backbuffer(backbuffer) {}
|
||||
|
||||
void Register(RG::RenderGraph& rg) override;
|
||||
|
||||
private:
|
||||
ImGuiLayer** m_Layer;
|
||||
RG::TextureHandle m_Backbuffer;
|
||||
};
|
||||
|
||||
} // namespace Passes
|
||||
} // namespace Engine
|
||||
|
||||
#endif //HELLOVULKAN_IMGUIPASS_H
|
||||
@@ -0,0 +1,32 @@
|
||||
//
|
||||
// Created by atdunbg on 2026/9/23.
|
||||
//
|
||||
// PresentPass:transition 到 ePresentSrcKHR。
|
||||
// layout 转换由 RG 的 barrier 完成,实际 vkQueuePresentKHR 由 Renderer 驱动。
|
||||
//
|
||||
|
||||
#ifndef HELLOVULKAN_PRESENTPASS_H
|
||||
#define HELLOVULKAN_PRESENTPASS_H
|
||||
|
||||
#include <Engine/Renderer/Passes/RenderPass.h>
|
||||
|
||||
namespace Engine::Passes {
|
||||
|
||||
class PresentPass final : public RenderPass {
|
||||
public:
|
||||
explicit PresentPass(RG::TextureHandle backbuffer)
|
||||
: m_Backbuffer(backbuffer) {}
|
||||
|
||||
void Register(RG::RenderGraph& rg) override
|
||||
{
|
||||
rg.AddPass("Present", [](RG::RenderGraphContext&) {})
|
||||
.Read(m_Backbuffer, RG::ResourceUse::Present);
|
||||
}
|
||||
|
||||
private:
|
||||
RG::TextureHandle m_Backbuffer;
|
||||
};
|
||||
|
||||
} // namespace Engine::Passes
|
||||
|
||||
#endif //HELLOVULKAN_PRESENTPASS_H
|
||||
@@ -0,0 +1,34 @@
|
||||
//
|
||||
// Created by atdunbg on 2026/9/23.
|
||||
//
|
||||
// RenderPass:渲染 pass 对象基类(Unity ScriptableRenderPass 思路)。
|
||||
//
|
||||
// pass 自己负责两件事,框架(Renderer)不再手写"超级函数":
|
||||
// Register —— 向 RenderGraph 声明资源使用 + 录制 lambda;
|
||||
// 执行序由 RG 拓扑排序决定(writer 必先于 reader),
|
||||
// 与注册顺序无关
|
||||
// OnTargetsRebuilt —— RG 重编译(resize / vsync 切换)后重绑描述符
|
||||
// (物理镜像按槽位可能全部换新)
|
||||
//
|
||||
// pass 之间不互相依赖:数据流只经 RG 资源句柄。新增效果 = 新增一个
|
||||
// RenderPass 子类 + Renderer::BuildGraph 里注册一行,其它代码不动。
|
||||
//
|
||||
|
||||
#ifndef HELLOVULKAN_RENDERPASS_H
|
||||
#define HELLOVULKAN_RENDERPASS_H
|
||||
|
||||
#include <Engine/RenderGraph/RenderGraph.h>
|
||||
|
||||
namespace Engine::Passes {
|
||||
|
||||
class RenderPass {
|
||||
public:
|
||||
virtual ~RenderPass() = default;
|
||||
|
||||
virtual void Register(RG::RenderGraph& rg) = 0;
|
||||
virtual void OnTargetsRebuilt(RG::RenderGraph& rg) { (void)rg; }
|
||||
};
|
||||
|
||||
} // namespace Engine::Passes
|
||||
|
||||
#endif //HELLOVULKAN_RENDERPASS_H
|
||||
@@ -0,0 +1,67 @@
|
||||
//
|
||||
// Created by atdunbg on 2026/9/23.
|
||||
//
|
||||
// SceneGpu:场景级 GPU 共享装配(Shadow/Forward 及未来几何类 pass 共用)。
|
||||
//
|
||||
// 从 Renderer 拆出的装配体:set 布局 / pipeline 布局 / shader object /
|
||||
// 采样器 / per-slot 描述符集合。新增几何 pass(延迟、调试线框等)直接
|
||||
// 复用本装配,不必碰 Renderer。
|
||||
//
|
||||
|
||||
#ifndef HELLOVULKAN_SCENEGPU_H
|
||||
#define HELLOVULKAN_SCENEGPU_H
|
||||
|
||||
#include <Engine/Core/FrameContext.h>
|
||||
#include <Engine/RenderGraph/RenderGraph.h>
|
||||
#include <vulkan/vulkan.hpp>
|
||||
#include <vector>
|
||||
|
||||
namespace Engine {
|
||||
|
||||
class Device;
|
||||
class DescriptorManager;
|
||||
class ShaderManager;
|
||||
class UniformBuffer;
|
||||
|
||||
namespace Passes {
|
||||
|
||||
class SceneGpu {
|
||||
public:
|
||||
SceneGpu(Device& device, DescriptorManager& descriptors,
|
||||
ShaderManager& shaders,
|
||||
UniformBuffer& ubo, UniformBuffer& objectBuffer);
|
||||
~SceneGpu();
|
||||
|
||||
SceneGpu(const SceneGpu&) = delete;
|
||||
SceneGpu& operator=(const SceneGpu&) = delete;
|
||||
|
||||
// ---- set 布局(shader 契约,改绑定必须同步 shaders/)----
|
||||
vk::DescriptorSetLayout uboSetLayout; // Set0 = {UBO, ObjectBuffer}
|
||||
vk::DescriptorSetLayout textureSetLayout; // Set1 = {albedo}
|
||||
vk::DescriptorSetLayout shadowSetLayout; // Set2 = {shadow map}
|
||||
vk::PipelineLayout forwardLayout; // 3 sets,无 push(GPUDriven 经 SSBO)
|
||||
vk::PipelineLayout shadowLayout; // Set0,push = lightVP 64B
|
||||
|
||||
// per-slot 描述符:Set0(UBO+实例 SSBO)与 Set2(阴影图采样)
|
||||
std::vector<vk::DescriptorSet> uboSets;
|
||||
std::vector<vk::DescriptorSet> shadowSets;
|
||||
|
||||
// 阴影采样器:clamp-to-border 白色 = 光视锥外自动无阴影
|
||||
//(省 shader 出界分支)
|
||||
vk::Sampler shadowSampler;
|
||||
|
||||
vk::ShaderEXT forwardVert{}, forwardFrag{};
|
||||
vk::ShaderEXT shadowVert{}, shadowFrag{};
|
||||
|
||||
// RG 重编译后重绑阴影图(物理镜像按槽位各一份)
|
||||
void BindShadowMap(RG::RenderGraph& rg, RG::TextureHandle shadowMap);
|
||||
|
||||
private:
|
||||
Device& m_Device;
|
||||
DescriptorManager& m_Descriptors;
|
||||
};
|
||||
|
||||
} // namespace Passes
|
||||
} // namespace Engine
|
||||
|
||||
#endif //HELLOVULKAN_SCENEGPU_H
|
||||
@@ -0,0 +1,33 @@
|
||||
//
|
||||
// Created by atdunbg on 2026/9/23.
|
||||
//
|
||||
// ShadowPass:光源视角写深度。GPUDriven batch 消费光源视锥剔除结果
|
||||
// (FrameSceneData::shadowView——相机外实体仍投影子)。
|
||||
//
|
||||
|
||||
#ifndef HELLOVULKAN_SHADOWPASS_H
|
||||
#define HELLOVULKAN_SHADOWPASS_H
|
||||
|
||||
#include <Engine/Renderer/Passes/RenderPass.h>
|
||||
#include <Engine/Renderer/Passes/SceneGpu.h>
|
||||
#include <Engine/Renderer/SceneTypes.h>
|
||||
|
||||
namespace Engine::Passes {
|
||||
|
||||
class ShadowPass final : public RenderPass {
|
||||
public:
|
||||
ShadowPass(SceneGpu& gpu, FrameSceneData& frame, RG::TextureHandle shadowMap)
|
||||
: m_Gpu(gpu), m_Frame(frame), m_ShadowMap(shadowMap) {}
|
||||
|
||||
void Register(RG::RenderGraph& rg) override;
|
||||
void OnTargetsRebuilt(RG::RenderGraph& rg) override;
|
||||
|
||||
private:
|
||||
SceneGpu& m_Gpu;
|
||||
FrameSceneData& m_Frame;
|
||||
RG::TextureHandle m_ShadowMap;
|
||||
};
|
||||
|
||||
} // namespace Engine::Passes
|
||||
|
||||
#endif //HELLOVULKAN_SHADOWPASS_H
|
||||
@@ -0,0 +1,41 @@
|
||||
//
|
||||
// Created by atdunbg on 2026/9/23.
|
||||
//
|
||||
// TonemapPass:HDR(+bloom)→ 显示空间,全屏三角写 Backbuffer。
|
||||
// 显示变换在此显式建模(对齐 UE/Unity:HDR SceneColor → tonemapper → UI)。
|
||||
// Backbuffer 从此只接收"显示空间"颜色:tonemap 直接写、ImGui 叠加。
|
||||
//
|
||||
|
||||
#ifndef HELLOVULKAN_TONEMAPPASS_H
|
||||
#define HELLOVULKAN_TONEMAPPASS_H
|
||||
|
||||
#include <Engine/Renderer/Passes/RenderPass.h>
|
||||
#include <Engine/Renderer/Passes/FullscreenGpu.h>
|
||||
#include <vulkan/vulkan.hpp>
|
||||
#include <vector>
|
||||
|
||||
namespace Engine::Passes {
|
||||
|
||||
class TonemapPass final : public RenderPass {
|
||||
public:
|
||||
TonemapPass(FullscreenGpu& gpu, PostProcessParams& params,
|
||||
RG::TextureHandle hdrColor, RG::TextureHandle bloomA,
|
||||
RG::TextureHandle backbuffer);
|
||||
~TonemapPass() override = default;
|
||||
|
||||
void Register(RG::RenderGraph& rg) override;
|
||||
void OnTargetsRebuilt(RG::RenderGraph& rg) override;
|
||||
|
||||
private:
|
||||
FullscreenGpu& m_Gpu;
|
||||
PostProcessParams& m_Params;
|
||||
RG::TextureHandle m_HDR;
|
||||
RG::TextureHandle m_A;
|
||||
RG::TextureHandle m_Backbuffer;
|
||||
|
||||
std::vector<vk::DescriptorSet> m_Sets; // per-slot
|
||||
};
|
||||
|
||||
} // namespace Engine::Passes
|
||||
|
||||
#endif //HELLOVULKAN_TONEMAPPASS_H
|
||||
@@ -1,6 +1,19 @@
|
||||
//
|
||||
// Created by atdunbg on 2026/9/20.
|
||||
//
|
||||
// Renderer:渲染门面 + 渲染侧组合根。
|
||||
//
|
||||
// 分工(对照 UE/Unity 的渲染架构):
|
||||
// Renderer —— 装配模块、声明 RG 资源、注册 pass 对象、
|
||||
// 每帧编排(翻译场景 → 写 buffer → 驱动 RG → 提交呈现)。
|
||||
// 不理解场景细节,不含 pass 实现。
|
||||
// SceneRenderer —— 场景 → FrameSceneData 的翻译层(相机/灯光/剔除,
|
||||
// 对应 UE FSceneRenderer / Unity CullingResults)。
|
||||
// Passes::* —— 各 pass 的声明与录制(对应 Unity ScriptableRenderPass)。
|
||||
// 新增效果 = 新增 pass 类 + BuildGraph 注册一行。
|
||||
//
|
||||
// 对外只暴露两个入口:Render(Scene&, Assets&) 和给 App 层调参的访问器。
|
||||
//
|
||||
|
||||
#ifndef HELLOVULKAN_RENDERER_H
|
||||
#define HELLOVULKAN_RENDERER_H
|
||||
@@ -9,13 +22,11 @@
|
||||
|
||||
#include <Engine/Core/Types.h>
|
||||
#include <Engine/Core/FrameContext.h>
|
||||
#include <Engine/Renderer/SceneTypes.h>
|
||||
#include <Engine/RenderGraph/RenderGraph.h>
|
||||
#include <vulkan/vulkan.hpp>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
namespace Engine {
|
||||
|
||||
class Window;
|
||||
@@ -27,26 +38,14 @@ namespace Engine {
|
||||
class SyncManager;
|
||||
class CommandManager;
|
||||
class UniformBuffer;
|
||||
class Model;
|
||||
class SceneRenderer;
|
||||
class VulkanAssetUploader;
|
||||
class ImGuiLayer;
|
||||
|
||||
namespace ECS { class Scene; }
|
||||
namespace Assets { class AssetManager; }
|
||||
class ImGuiLayer;
|
||||
class Mesh;
|
||||
namespace Assets { class AssetManager; class IAssetUploader; }
|
||||
namespace Passes { class RenderPass; class SceneGpu; class FullscreenGpu; class PostProcessParams; }
|
||||
|
||||
// GPUDriven:按 mesh 聚合的绘制批
|
||||
struct InstanceBatch {
|
||||
Mesh* mesh;
|
||||
u32 first; // 在实例矩阵数组中的起始下标
|
||||
u32 count; // 实例数
|
||||
};
|
||||
|
||||
// Renderer 职责(收敛后):
|
||||
// 1. 装配底层模块(Device/Swapchain/Sync/Command/Descriptor/Shader)
|
||||
// 2. 声明并驱动 RenderGraph(pass 结构在这里组装)
|
||||
// 3. Render(Scene&, AssetManager&):从 ECS 场景读取相机/灯光/渲染体,
|
||||
// 经资产层解析句柄后绘制
|
||||
// 不拥有场景,不加载资产(资产归 AssetManager,磁盘 IO 与这里无关)。
|
||||
class Renderer {
|
||||
public:
|
||||
Renderer(Window& window);
|
||||
@@ -61,69 +60,63 @@ namespace Engine {
|
||||
Device& GetDevice() const { return *m_Device; }
|
||||
MemoryAllocator& GetAllocator() const { return *m_Allocator; }
|
||||
DescriptorManager& GetDescriptorManager() const { return *m_DescriptorManager; }
|
||||
vk::DescriptorSetLayout GetTextureSetLayout() const { return m_TextureSetLayout; }
|
||||
vk::Format GetSwapchainFormat() const; // Swapchain 不完整,实现在 cpp
|
||||
vk::DescriptorSetLayout GetTextureSetLayout() const; // 实现在 cpp(SceneGpu 成员)
|
||||
vk::Format GetSwapchainFormat() const;
|
||||
u32 GetSwapchainImageCount() const;
|
||||
// 资产上传服务(渲染层对资产层提供的 IAssetUploader 实现)
|
||||
Assets::IAssetUploader& GetAssetUploader() const;
|
||||
|
||||
// 可选叠加层(ImGui):不设置则 RG 里的 ImGui pass 为空转。
|
||||
// 生命周期:层必须先于 Renderer 析构(Application 成员序保证)。
|
||||
void SetImGuiLayer(ImGuiLayer* layer) { m_ImGuiLayer = layer; }
|
||||
|
||||
// ---- 后处理参数(ImGui 可调,转发到 pass 共享的 PostProcessParams)----
|
||||
f32 GetExposure() const;
|
||||
void SetExposure(f32 e);
|
||||
bool GetUseACES() const;
|
||||
void SetUseACES(bool b);
|
||||
f32 GetBloomThreshold() const;
|
||||
void SetBloomThreshold(f32 v);
|
||||
f32 GetBloomStrength() const;
|
||||
void SetBloomStrength(f32 v);
|
||||
|
||||
// pass 级 GPU 耗时(最近完成帧,毫秒;与 GetPassGpuMs 下标对应)
|
||||
const std::vector<std::string>& GetPassNames() const { return m_PassNames; }
|
||||
const std::vector<f32>& GetPassGpuMs() const { return m_PassGpuMs; }
|
||||
|
||||
// 双视图剔除后的实例数(遥测)
|
||||
u32 GetMainInstanceCount() const;
|
||||
u32 GetShadowInstanceCount() const;
|
||||
|
||||
// 垂直同步开关(present mode 切换会重建 swapchain),
|
||||
// 只应在帧 UI 构建段调用(Render 之前)
|
||||
void SetVSync(bool vsync);
|
||||
|
||||
private:
|
||||
void BuildGraph(); // 声明 pass 结构 + 首次 Compile
|
||||
void BuildGraph(); // 声明 RG 资源 + 注册 pass 对象
|
||||
void HandleResize();
|
||||
void UpdateShadowDescriptors();
|
||||
void UpdateFullscreenDescriptors();
|
||||
void ReadGpuTimes(u32 frameSlot);
|
||||
|
||||
Window& m_Window;
|
||||
|
||||
// ---- 底层模块(析构序:后声明者先析构,RG 在 Device/Allocator 之前)----
|
||||
// ---- 底层模块(析构序:后声明者先析构;引用他人者先析构)----
|
||||
Scope<Device> m_Device;
|
||||
Scope<MemoryAllocator> m_Allocator;
|
||||
Scope<DescriptorManager> m_DescriptorManager;
|
||||
Scope<ShaderManager> m_ShaderManager;
|
||||
Scope<UniformBuffer> m_UniformBuffer; // per-slot UBO ring
|
||||
Scope<UniformBuffer> m_ObjectBuffer; // GPUDriven 实例矩阵 ring(SSBO)
|
||||
Scope<Passes::SceneGpu> m_SceneGpu; // 场景类 pass 共享装配
|
||||
Scope<Passes::FullscreenGpu> m_FullscreenGpu; // 全屏 pass 共享装配
|
||||
Scope<Swapchain> m_Swapchain;
|
||||
Scope<SyncManager> m_SyncManager;
|
||||
Scope<CommandManager> m_CommandManager;
|
||||
Scope<UniformBuffer> m_UniformBuffer;
|
||||
Scope<UniformBuffer> m_ObjectBuffer; // GPUDriven 实例矩阵 ring(SSBO)
|
||||
Scope<SceneRenderer> m_SceneRenderer; // 场景翻译层(先于 pass 构造)
|
||||
Scope<RG::RenderGraph> m_RenderGraph;
|
||||
Scope<VulkanAssetUploader> m_AssetUploader;
|
||||
|
||||
// ---- 管线装配(shader object 方案,无 vk::Pipeline)----
|
||||
// Forward layout:3 个 set(UBO/贴图/阴影图)+ 128B push(与 Shadow pass
|
||||
// 共用上限;forward 的 shader block 只有 64B,block ⊂ range 合法)
|
||||
vk::PipelineLayout m_PipelineLayout;
|
||||
vk::DescriptorSetLayout m_UBOSetLayout;
|
||||
vk::DescriptorSetLayout m_TextureSetLayout;
|
||||
vk::DescriptorSetLayout m_ShadowSetLayout;
|
||||
std::vector<vk::DescriptorSet> m_UBODescriptorSets; // 每帧槽位一个
|
||||
std::vector<vk::DescriptorSet> m_ShadowDescriptorSets; // 每帧槽位一个
|
||||
vk::Sampler m_ShadowSampler;
|
||||
vk::ShaderEXT m_VertShader{};
|
||||
vk::ShaderEXT m_FragShader{};
|
||||
|
||||
// Shadow pass 独立装配:无 set,push constant = {lightVP, model} 128B
|
||||
vk::PipelineLayout m_ShadowPipelineLayout;
|
||||
vk::ShaderEXT m_ShadowVertShader{};
|
||||
vk::ShaderEXT m_ShadowFragShader{};
|
||||
|
||||
// Tonemap pass 装配:Set0 = {HDR 颜色图, Bloom 图},push = 16B
|
||||
//(全屏 pass 通用:exposure/aces/bloomStrength/pad)
|
||||
vk::PipelineLayout m_TonemapPipelineLayout;
|
||||
vk::DescriptorSetLayout m_TonemapSetLayout;
|
||||
std::vector<vk::DescriptorSet> m_TonemapDescriptorSets; // 每帧槽位一个
|
||||
vk::Sampler m_TonemapSampler;
|
||||
vk::ShaderEXT m_TonemapVertShader{};
|
||||
vk::ShaderEXT m_TonemapFragShader{};
|
||||
|
||||
// Bloom:Extract(HDR→A) → BlurH(A→B) → BlurV(B→A),半分辨率
|
||||
// 与 Tonemap 共用 set layout/pipeline layout/vertex shader
|
||||
std::vector<vk::DescriptorSet> m_BloomExtractSets; // 每帧槽位
|
||||
std::vector<vk::DescriptorSet> m_BloomBlurHSets;
|
||||
std::vector<vk::DescriptorSet> m_BloomBlurVSets;
|
||||
vk::ShaderEXT m_BloomExtractFrag{};
|
||||
vk::ShaderEXT m_BloomBlurFrag{};
|
||||
// pass 对象(注册序;执行序由 RG 拓扑排序决定)
|
||||
std::vector<Scope<Passes::RenderPass>> m_Passes;
|
||||
|
||||
// ---- RenderGraph 资源句柄 ----
|
||||
RG::TextureHandle m_Backbuffer{};
|
||||
@@ -133,34 +126,12 @@ namespace Engine {
|
||||
RG::TextureHandle m_BloomA{}; // 半分辨率:提取 + 垂直模糊结果
|
||||
RG::TextureHandle m_BloomB{}; // 半分辨率:水平模糊中间结果
|
||||
|
||||
// ---- 当帧绑定(Render() 期间有效,供 pass 回调读取)----
|
||||
ECS::Scene* m_BoundScene = nullptr;
|
||||
Assets::AssetManager* m_BoundAssets = nullptr;
|
||||
glm::mat4 m_LightSpace{1.0f}; // 阴影光源 VP(每帧由 Render 算好)
|
||||
|
||||
// GPUDriven:当帧实例数据(Render 期收集,Forward pass 消费)
|
||||
// 双视图剔除:相机视锥与光源视锥各出一份可见列表——
|
||||
// 相机外的实体仍可能投影子进视野,所以 Shadow 用光源视锥单独剔除
|
||||
struct CullResult {
|
||||
std::vector<InstanceBatch> batches;
|
||||
u32 instanceCount = 0;
|
||||
};
|
||||
CullResult m_MainView; // 相机视锥
|
||||
CullResult m_ShadowView; // 光源视锥
|
||||
std::unordered_map<Mesh*, u32> m_MainBatchLookup;
|
||||
std::unordered_map<Mesh*, u32> m_ShadowBatchLookup;
|
||||
std::vector<glm::mat4> m_InstanceModels; // [主视图段 | 阴影视图段]
|
||||
static constexpr u32 kMaxInstances = 16384;
|
||||
// pass 共享的可调参数(Renderer 持有,App 层访问器转发)
|
||||
Scope<Passes::PostProcessParams> m_PostParams;
|
||||
|
||||
// 可选叠加层(不拥有;Application 持有)
|
||||
ImGuiLayer* m_ImGuiLayer = nullptr;
|
||||
|
||||
// ---- Tonemap/Bloom 参数(ImGui 可调)----
|
||||
f32 m_Exposure = 1.0f;
|
||||
bool m_UseACES = true;
|
||||
f32 m_BloomThreshold = 1.0f; // HDR 域亮度阈值
|
||||
f32 m_BloomStrength = 0.8f; // 叠加强度
|
||||
|
||||
// GPU timestamp(pass 级耗时,ImGui 可读)
|
||||
vk::QueryPool m_QueryPool;
|
||||
u32 m_QueriesPerFrame = 0; // 每帧槽位的查询数 = pass 数 × 2
|
||||
@@ -168,31 +139,6 @@ namespace Engine {
|
||||
std::vector<std::string> m_PassNames; // 执行序 pass 名
|
||||
std::vector<f32> m_PassGpuMs; // 最近一帧各 pass 耗时
|
||||
bool m_TimesPrimed[2] = {false, false}; // 每槽位首帧跳过读取
|
||||
f32 m_CollectMs = 0.0f; // 实例收集 CPU 耗时(遥测)
|
||||
|
||||
public:
|
||||
f32 GetExposure() const { return m_Exposure; }
|
||||
void SetExposure(f32 e) { m_Exposure = e; }
|
||||
bool GetUseACES() const { return m_UseACES; }
|
||||
void SetUseACES(bool b) { m_UseACES = b; }
|
||||
f32 GetBloomThreshold() const { return m_BloomThreshold; }
|
||||
void SetBloomThreshold(f32 v) { m_BloomThreshold = v; }
|
||||
f32 GetBloomStrength() const { return m_BloomStrength; }
|
||||
void SetBloomStrength(f32 v) { m_BloomStrength = v; }
|
||||
|
||||
// pass 级 GPU 耗时(最近完成帧,毫秒;与 GetPassGpuMs 下标对应)
|
||||
const std::vector<std::string>& GetPassNames() const { return m_PassNames; }
|
||||
const std::vector<f32>& GetPassGpuMs() const { return m_PassGpuMs; }
|
||||
|
||||
// 双视图剔除后的实例数(遥测)
|
||||
u32 GetMainInstanceCount() const { return m_MainView.instanceCount; }
|
||||
u32 GetShadowInstanceCount() const { return m_ShadowView.instanceCount; }
|
||||
|
||||
// 垂直同步开关(Mailbox ↔ Immediate):切换会重建 swapchain,
|
||||
// 只应在帧 UI 构建段调用(Render 之前)
|
||||
void SetVSync(bool vsync);
|
||||
|
||||
private:
|
||||
|
||||
vk::Extent2D m_Extent{};
|
||||
u32 m_CurrentFrame = 0;
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
//
|
||||
// Created by atdunbg on 2026/9/23.
|
||||
//
|
||||
// SceneRenderer:场景 → 渲染数据的每帧翻译层(UE FSceneRenderer /
|
||||
// Unity CullingResults 的对应物)。
|
||||
//
|
||||
// 职责(全部是"读场景、产数据",不碰 GPU、不碰 Vulkan):
|
||||
// 1. 相机/灯光/环境色 → UniformBufferObject 语义组装(含阴影光空间矩阵)
|
||||
// 2. 单趟实例收集 + 双视图剔除(相机视锥/光源视锥各自独立可见列表——
|
||||
// 相机外实体仍可能投影子进视野,shadow popping 的教训)
|
||||
// Renderer 拿 FrameSceneData 写 buffer、驱动 pass;场景理解全部收口在此。
|
||||
//
|
||||
// 扩展性:
|
||||
// - 多光源:lights 数组已就位(kMaxLights),每灯各自 lightSpace 即 CSM 前置
|
||||
// - 级联阴影:BuildFrame 内按级联多出几份 lightSpace + shadowView,
|
||||
// pass 侧按索引消费,本类结构不变
|
||||
// - 遮挡剔除/GPU 剔除:只改本类收集逻辑,下游无感知
|
||||
//
|
||||
|
||||
#ifndef HELLOVULKAN_SCENERENDERER_H
|
||||
#define HELLOVULKAN_SCENERENDERER_H
|
||||
|
||||
#include <Engine/Core/Types.h>
|
||||
#include <Engine/Renderer/SceneTypes.h>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace Engine {
|
||||
|
||||
namespace ECS { class Scene; }
|
||||
namespace Assets { class AssetManager; }
|
||||
|
||||
class SceneRenderer {
|
||||
public:
|
||||
// 从场景构建当帧渲染数据。无 primary 相机返回 false(渲染跳帧)。
|
||||
// aspect 由调用方传入(跟随 swapchain,本类不感知窗口)。
|
||||
bool BuildFrame(ECS::Scene& scene, Assets::AssetManager& assets, f32 aspect);
|
||||
|
||||
FrameSceneData& Frame() { return m_Frame; }
|
||||
|
||||
// ---- 遥测 ----
|
||||
u32 GetMainInstanceCount() const { return m_Frame.mainView.instanceCount; }
|
||||
u32 GetShadowInstanceCount() const { return m_Frame.shadowView.instanceCount; }
|
||||
f32 GetCollectMs() const { return m_Frame.collectMs; }
|
||||
|
||||
private:
|
||||
// 阴影光空间矩阵:光源正交投影(覆盖场景,[0,1] 深度重映射)。
|
||||
// 写入(Shadow pass push)与采样(Forward UBO)共用同一矩阵。
|
||||
void ComputeLightSpace(const glm::vec3& shadowDir);
|
||||
|
||||
FrameSceneData m_Frame;
|
||||
|
||||
// batch 聚合查找表(mesh → batch 下标),每帧 clear 复用桶
|
||||
std::unordered_map<Mesh*, u32> m_MainLookup;
|
||||
std::unordered_map<Mesh*, u32> m_ShadowLookup;
|
||||
|
||||
bool m_TruncationWarned = false; // 实例缓冲打满只告警一次
|
||||
};
|
||||
|
||||
} // namespace Engine
|
||||
|
||||
#endif //HELLOVULKAN_SCENERENDERER_H
|
||||
@@ -0,0 +1,69 @@
|
||||
//
|
||||
// Created by atdunbg on 2026/9/23.
|
||||
//
|
||||
// SceneTypes:场景 → GPU 的数据契约(CPU 结构镜像 shader 布局)。
|
||||
//
|
||||
// SceneRenderer 生产、Renderer/各 pass 消费;改字段必须同步 shaders/。
|
||||
// 常量也集中在这里:SSBO 容量、灯数、阴影图尺寸都是"渲染预算",
|
||||
// 改任何一个都要同步评估 buffer/池容量。
|
||||
//
|
||||
|
||||
#ifndef HELLOVULKAN_SCENETYPES_H
|
||||
#define HELLOVULKAN_SCENETYPES_H
|
||||
|
||||
#include <Engine/Core/Types.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include <vector>
|
||||
|
||||
namespace Engine {
|
||||
|
||||
class Mesh;
|
||||
|
||||
// GPUDriven:按 mesh 聚合的绘制批
|
||||
struct InstanceBatch {
|
||||
Mesh* mesh;
|
||||
u32 first; // 在实例矩阵数组中的起始下标
|
||||
u32 count; // 实例数
|
||||
};
|
||||
|
||||
// 一个渲染视图的剔除结果(相机视图 / 光源视图各一份)
|
||||
struct CullResult {
|
||||
std::vector<InstanceBatch> batches;
|
||||
u32 instanceCount = 0;
|
||||
};
|
||||
|
||||
constexpr u32 kMaxLights = 4;
|
||||
constexpr u32 kMaxInstances = 16384; // 实例 SSBO 容量(矩阵数)
|
||||
constexpr u32 kShadowMapSize = 2048; // 阴影图边长
|
||||
|
||||
// 镜像 shader 侧 UBO 布局的灯光项
|
||||
struct LightData {
|
||||
glm::vec4 direction; // xyz = 方向(已归一),w 未用
|
||||
glm::vec4 color; // rgb = 颜色,a = 强度
|
||||
};
|
||||
|
||||
// 镜像 simple_shader 的 UBO 布局(std140 语义由 shader 侧对齐)
|
||||
struct UniformBufferObject {
|
||||
glm::mat4 view;
|
||||
glm::mat4 proj;
|
||||
glm::mat4 lightSpace; // 阴影光源 VP(写入与采样共用同一矩阵)
|
||||
glm::vec4 ambientColor;
|
||||
LightData lights[kMaxLights];
|
||||
};
|
||||
|
||||
// 每帧从场景收集的渲染数据(SceneRenderer 生产,pass 消费)。
|
||||
// 复用成员容量:clear() 不缩容,稳态零每帧堆分配。
|
||||
struct FrameSceneData {
|
||||
UniformBufferObject ubo{};
|
||||
|
||||
// 实例矩阵数组 = [主视图段 | 阴影视图段](共享同一 per-slot SSBO)
|
||||
std::vector<glm::mat4> instanceModels;
|
||||
CullResult mainView; // 相机视锥 → Forward pass
|
||||
CullResult shadowView; // 光源视锥 → Shadow pass(相机外实体仍投影子)
|
||||
|
||||
f32 collectMs = 0.0f; // 收集+剔除 CPU 耗时(遥测)
|
||||
};
|
||||
|
||||
} // namespace Engine
|
||||
|
||||
#endif //HELLOVULKAN_SCENETYPES_H
|
||||
@@ -0,0 +1,43 @@
|
||||
//
|
||||
// Created by atdunbg on 2026/9/23.
|
||||
//
|
||||
// VulkanAssetUploader:IAssetUploader 的渲染层实现。
|
||||
//
|
||||
// 属于渲染层对资产层提供的服务:持有 GPU 上下文,负责把解析后的
|
||||
// 资产数据创建为 Vulkan 资源。资产层(AssetManager)只看到接口,
|
||||
// 不知道 Device/Allocator 的存在——依赖方向:Assets → IAssetUploader
|
||||
// ← VulkanAssetUploader(渲染层),永远不出现 Assets → Vulkan 具体。
|
||||
//
|
||||
|
||||
#ifndef HELLOVULKAN_VULKANASSETUPLOADER_H
|
||||
#define HELLOVULKAN_VULKANASSETUPLOADER_H
|
||||
|
||||
#include <Engine/Assets/IAssetUploader.h>
|
||||
|
||||
namespace Engine {
|
||||
|
||||
class Device;
|
||||
class MemoryAllocator;
|
||||
class DescriptorManager;
|
||||
|
||||
class VulkanAssetUploader final : public Assets::IAssetUploader {
|
||||
public:
|
||||
VulkanAssetUploader(Device& device, MemoryAllocator& allocator,
|
||||
DescriptorManager& descriptors,
|
||||
vk::DescriptorSetLayout textureLayout)
|
||||
: m_Device(device), m_Allocator(allocator),
|
||||
m_Descriptors(descriptors), m_TextureLayout(textureLayout) {}
|
||||
|
||||
Scope<Model> LoadModel(const std::string& path) override;
|
||||
Scope<Model> CreatePlane(f32 halfSize, u8 r, u8 g, u8 b) override;
|
||||
|
||||
private:
|
||||
Device& m_Device;
|
||||
MemoryAllocator& m_Allocator;
|
||||
DescriptorManager& m_Descriptors;
|
||||
vk::DescriptorSetLayout m_TextureLayout;
|
||||
};
|
||||
|
||||
} // namespace Engine
|
||||
|
||||
#endif //HELLOVULKAN_VULKANASSETUPLOADER_H
|
||||
@@ -3,41 +3,31 @@
|
||||
//
|
||||
|
||||
#include <Engine/Assets/AssetManager.h>
|
||||
#include <Engine/Vulkan/Device.h>
|
||||
#include <Engine/Vulkan/MemoryAllocator.h>
|
||||
#include <Engine/Vulkan/DescriptorManager.h>
|
||||
#include <Engine/Core/Log.h>
|
||||
|
||||
namespace Engine::Assets {
|
||||
|
||||
AssetManager::AssetManager(Device& device, MemoryAllocator& allocator,
|
||||
DescriptorManager& descriptors,
|
||||
vk::DescriptorSetLayout textureLayout)
|
||||
: m_Device(device), m_Allocator(allocator),
|
||||
m_Descriptors(descriptors), m_TextureLayout(textureLayout)
|
||||
AssetManager::AssetManager(IAssetUploader& uploader)
|
||||
: m_Uploader(uploader)
|
||||
{
|
||||
ENGINE_INFO("AssetManager created");
|
||||
}
|
||||
|
||||
ModelHandle AssetManager::LoadModel(const std::string& path)
|
||||
ModelRef AssetManager::LoadModel(const std::string& path)
|
||||
{
|
||||
return m_Models.Load(path, [this](const std::string& p) -> Scope<Model> {
|
||||
// GPU 上传在此发生:资产层调用渲染层接口完成 buffer/image 创建
|
||||
return CreateScope<Model>(
|
||||
m_Device, m_Allocator, m_Descriptors, m_TextureLayout, p);
|
||||
});
|
||||
// GPU 上传经接口发生在渲染层实现里,本层只管缓存与句柄
|
||||
return m_Models.Load(path,
|
||||
[this](const std::string& p) { return m_Uploader.LoadModel(p); });
|
||||
}
|
||||
|
||||
ModelHandle AssetManager::CreatePlane(f32 halfSize, u8 r, u8 g, u8 b)
|
||||
ModelRef AssetManager::CreatePlane(f32 halfSize, u8 r, u8 g, u8 b)
|
||||
{
|
||||
// 程序化资产复用同一缓存:键 = 伪路径 + 参数
|
||||
std::string key = "procedural://plane/" + std::to_string(halfSize) +
|
||||
"/" + std::to_string(r) + "," + std::to_string(g) +
|
||||
"," + std::to_string(b);
|
||||
return m_Models.Load(key, [&](const std::string&) -> Scope<Model> {
|
||||
return Model::CreatePlane(m_Device, m_Allocator, m_Descriptors,
|
||||
m_TextureLayout, halfSize, r, g, b);
|
||||
});
|
||||
return m_Models.Load(key,
|
||||
[&](const std::string&) { return m_Uploader.CreatePlane(halfSize, r, g, b); });
|
||||
}
|
||||
|
||||
} // namespace Engine::Assets
|
||||
|
||||
@@ -30,13 +30,10 @@ namespace Engine {
|
||||
m_Scene = CreateScope<ECS::Scene>();
|
||||
m_Renderer = CreateScope<Renderer>(*m_Window);
|
||||
|
||||
// 资产层:复用渲染器的 GPU 上下文;
|
||||
// 资产层:上传服务由渲染层提供(IAssetUploader 实现),
|
||||
// 资产层不感知 Vulkan 具体类。
|
||||
// 声明在 Renderer 之后 → 析构先于 Renderer(它引用其子模块)
|
||||
m_Assets = CreateScope<Assets::AssetManager>(
|
||||
m_Renderer->GetDevice(),
|
||||
m_Renderer->GetAllocator(),
|
||||
m_Renderer->GetDescriptorManager(),
|
||||
m_Renderer->GetTextureSetLayout());
|
||||
m_Assets = CreateScope<Assets::AssetManager>(m_Renderer->GetAssetUploader());
|
||||
}
|
||||
|
||||
Application::~Application()
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
//
|
||||
// Created by atdunbg on 2026/9/23.
|
||||
//
|
||||
|
||||
#include <Engine/Renderer/Passes/BloomPass.h>
|
||||
#include <Engine/Core/FrameContext.h>
|
||||
|
||||
namespace Engine::Passes
|
||||
{
|
||||
BloomPass::BloomPass(FullscreenGpu& gpu, PostProcessParams& params,
|
||||
RG::TextureHandle hdrColor,
|
||||
RG::TextureHandle bloomA, RG::TextureHandle bloomB)
|
||||
: m_Gpu(gpu), m_Params(params),
|
||||
m_HDR(hdrColor), m_A(bloomA), m_B(bloomB)
|
||||
{
|
||||
m_ExtractSets.resize(kMaxFramesInFlight);
|
||||
m_BlurHSets.resize(kMaxFramesInFlight);
|
||||
m_BlurVSets.resize(kMaxFramesInFlight);
|
||||
for (u32 slot = 0; slot < kMaxFramesInFlight; ++slot) {
|
||||
m_ExtractSets[slot] = m_Gpu.AllocateSet();
|
||||
m_BlurHSets[slot] = m_Gpu.AllocateSet();
|
||||
m_BlurVSets[slot] = m_Gpu.AllocateSet();
|
||||
}
|
||||
}
|
||||
|
||||
void BloomPass::Register(RG::RenderGraph& rg)
|
||||
{
|
||||
// Extract:HDR → 半分辨率 A(软阈值由 frag 的 param0 传入)
|
||||
rg.AddPass("BloomExtract",
|
||||
[this](RG::RenderGraphContext& ctx) {
|
||||
m_Gpu.RecordFullscreen(ctx, ctx.View(m_A), m_Gpu.extractFrag,
|
||||
m_ExtractSets[ctx.frame.frameSlot],
|
||||
FullscreenPush{0.0f, m_Params.bloomThreshold, 0.0f, 0.0f});
|
||||
})
|
||||
.Read(m_HDR, RG::ResourceUse::ShaderRead,
|
||||
vk::ShaderStageFlagBits::eFragment)
|
||||
.Write(m_A, RG::ResourceUse::ColorAttachment);
|
||||
|
||||
// BlurH:A → B(水平步长经 push 传入)
|
||||
rg.AddPass("BloomBlurH",
|
||||
[this](RG::RenderGraphContext& ctx) {
|
||||
m_Gpu.RecordFullscreen(ctx, ctx.View(m_B), m_Gpu.blurFrag,
|
||||
m_BlurHSets[ctx.frame.frameSlot],
|
||||
FullscreenPush{0.0f,
|
||||
1.0f / ctx.extent.width, 0.0f,
|
||||
0.0f});
|
||||
})
|
||||
.Read(m_A, RG::ResourceUse::ShaderRead,
|
||||
vk::ShaderStageFlagBits::eFragment)
|
||||
.Write(m_B, RG::ResourceUse::ColorAttachment);
|
||||
|
||||
// BlurV:B → A(垂直步长)
|
||||
rg.AddPass("BloomBlurV",
|
||||
[this](RG::RenderGraphContext& ctx) {
|
||||
m_Gpu.RecordFullscreen(ctx, ctx.View(m_A), m_Gpu.blurFrag,
|
||||
m_BlurVSets[ctx.frame.frameSlot],
|
||||
FullscreenPush{0.0f, 0.0f,
|
||||
1.0f / ctx.extent.height,
|
||||
0.0f});
|
||||
})
|
||||
.Read(m_B, RG::ResourceUse::ShaderRead,
|
||||
vk::ShaderStageFlagBits::eFragment)
|
||||
.Write(m_A, RG::ResourceUse::ColorAttachment);
|
||||
}
|
||||
|
||||
void BloomPass::OnTargetsRebuilt(RG::RenderGraph& rg)
|
||||
{
|
||||
for (u32 slot = 0; slot < kMaxFramesInFlight; ++slot) {
|
||||
m_Gpu.UpdateSetInput(m_ExtractSets[slot], 0, rg, m_HDR, slot);
|
||||
m_Gpu.UpdateSetInput(m_BlurHSets[slot], 0, rg, m_A, slot);
|
||||
m_Gpu.UpdateSetInput(m_BlurVSets[slot], 0, rg, m_B, slot);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Engine::Passes
|
||||
@@ -0,0 +1,135 @@
|
||||
//
|
||||
// Created by atdunbg on 2026/9/23.
|
||||
//
|
||||
|
||||
#include <Engine/Renderer/Passes/ForwardPass.h>
|
||||
#include <Engine/Renderer/Vertex.h>
|
||||
#include <Engine/Renderer/Mesh.h>
|
||||
#include <Engine/Renderer/Material.h>
|
||||
|
||||
#include <array>
|
||||
|
||||
namespace Engine::Passes
|
||||
{
|
||||
void ForwardPass::Register(RG::RenderGraph& rg)
|
||||
{
|
||||
rg.AddPass("Forward", [this](RG::RenderGraphContext& ctx) {
|
||||
const vk::CommandBuffer cmd = ctx.cmd;
|
||||
|
||||
vk::ClearValue clearValue;
|
||||
clearValue.color = vk::ClearColorValue(
|
||||
std::array<float, 4>{0.1f, 0.1f, 0.1f, 0.0f});
|
||||
|
||||
vk::RenderingAttachmentInfo colorAttachment;
|
||||
colorAttachment.setImageView(ctx.View(m_HDRColor))
|
||||
.setImageLayout(vk::ImageLayout::eColorAttachmentOptimal)
|
||||
.setLoadOp(vk::AttachmentLoadOp::eClear)
|
||||
.setStoreOp(vk::AttachmentStoreOp::eStore)
|
||||
.setClearValue(clearValue);
|
||||
|
||||
vk::RenderingAttachmentInfo depthAttachment;
|
||||
depthAttachment.setImageView(ctx.View(m_Depth))
|
||||
.setImageLayout(vk::ImageLayout::eDepthAttachmentOptimal)
|
||||
.setLoadOp(vk::AttachmentLoadOp::eClear)
|
||||
.setStoreOp(vk::AttachmentStoreOp::eDontCare)
|
||||
.setClearValue(vk::ClearValue(
|
||||
vk::ClearDepthStencilValue(1.0f, 0)));
|
||||
|
||||
vk::RenderingInfo renderingInfo;
|
||||
renderingInfo.setRenderArea({{0, 0}, ctx.extent})
|
||||
.setLayerCount(1)
|
||||
.setColorAttachments(colorAttachment)
|
||||
.setPDepthAttachment(&depthAttachment);
|
||||
|
||||
cmd.beginRendering(renderingInfo);
|
||||
|
||||
// 绑定 shader object
|
||||
std::array<vk::ShaderStageFlagBits, 2> stages = {
|
||||
vk::ShaderStageFlagBits::eVertex,
|
||||
vk::ShaderStageFlagBits::eFragment
|
||||
};
|
||||
std::array<vk::ShaderEXT, 2> shaders = {m_Gpu.forwardVert, m_Gpu.forwardFrag};
|
||||
cmd.bindShadersEXT(stages, shaders);
|
||||
|
||||
// Set 0:本帧槽位的 UBO;Set 2:本帧槽位的阴影图
|
||||
cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics,
|
||||
m_Gpu.forwardLayout, 0,
|
||||
m_Gpu.uboSets[ctx.frame.frameSlot], {});
|
||||
cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics,
|
||||
m_Gpu.forwardLayout, 2,
|
||||
m_Gpu.shadowSets[ctx.frame.frameSlot], {});
|
||||
|
||||
// ---- 动态状态 ----
|
||||
cmd.setRasterizerDiscardEnable(VK_FALSE);
|
||||
|
||||
cmd.setDepthTestEnable(VK_TRUE);
|
||||
cmd.setDepthWriteEnable(VK_TRUE);
|
||||
cmd.setDepthCompareOp(vk::CompareOp::eLess);
|
||||
cmd.setDepthBoundsTestEnable(VK_FALSE);
|
||||
|
||||
cmd.setStencilTestEnable(VK_FALSE);
|
||||
cmd.setDepthBiasEnable(VK_FALSE);
|
||||
cmd.setPolygonModeEXT(vk::PolygonMode::eFill);
|
||||
cmd.setCullMode(vk::CullModeFlagBits::eNone);
|
||||
cmd.setFrontFace(vk::FrontFace::eCounterClockwise);
|
||||
cmd.setPrimitiveTopology(vk::PrimitiveTopology::eTriangleList);
|
||||
cmd.setPrimitiveRestartEnable(VK_FALSE);
|
||||
|
||||
auto binding = Vertex::GetBindingDescription();
|
||||
auto attributes = Vertex::GetAttributeDescriptions();
|
||||
cmd.setVertexInputEXT(binding, attributes);
|
||||
|
||||
cmd.setRasterizationSamplesEXT(vk::SampleCountFlagBits::e1);
|
||||
vk::SampleMask sampleMask = 0xFFFFFFFF;
|
||||
cmd.setSampleMaskEXT(vk::SampleCountFlagBits::e1, sampleMask);
|
||||
cmd.setAlphaToCoverageEnableEXT(VK_FALSE);
|
||||
cmd.setAlphaToOneEnableEXT(VK_FALSE);
|
||||
|
||||
vk::Viewport viewport(0.0f, 0.0f,
|
||||
static_cast<float>(ctx.extent.width),
|
||||
static_cast<float>(ctx.extent.height),
|
||||
0.0f, 1.0f);
|
||||
cmd.setViewportWithCount(viewport);
|
||||
|
||||
vk::Rect2D scissor({0, 0}, ctx.extent);
|
||||
cmd.setScissorWithCount(scissor);
|
||||
|
||||
vk::Bool32 blendEnable = VK_FALSE;
|
||||
cmd.setColorBlendEnableEXT(0, blendEnable);
|
||||
|
||||
vk::ColorBlendEquationEXT blendEquation;
|
||||
blendEquation.setSrcColorBlendFactor(vk::BlendFactor::eOne)
|
||||
.setDstColorBlendFactor(vk::BlendFactor::eZero)
|
||||
.setColorBlendOp(vk::BlendOp::eAdd)
|
||||
.setSrcAlphaBlendFactor(vk::BlendFactor::eOne)
|
||||
.setDstAlphaBlendFactor(vk::BlendFactor::eZero)
|
||||
.setAlphaBlendOp(vk::BlendOp::eAdd);
|
||||
cmd.setColorBlendEquationEXT(0, blendEquation);
|
||||
|
||||
vk::ColorComponentFlags colorWriteMask =
|
||||
vk::ColorComponentFlagBits::eR |
|
||||
vk::ColorComponentFlagBits::eG |
|
||||
vk::ColorComponentFlagBits::eB |
|
||||
vk::ColorComponentFlagBits::eA;
|
||||
cmd.setColorWriteMaskEXT(0, colorWriteMask);
|
||||
|
||||
// ---- 绘制:GPUDriven 按 mesh 分 batch(相机视锥剔除后)----
|
||||
// 实例矩阵已由 Renderer 写入 ObjectBuffer(per-slot SSBO);
|
||||
// 同 mesh 的 N 个可见实体合并为一次 drawIndexed(instanceCount=N),
|
||||
// draw 数 = 可见唯一 mesh 数,与实体数量解耦
|
||||
for (const auto& batch : m_Frame.mainView.batches) {
|
||||
cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics,
|
||||
m_Gpu.forwardLayout, 1,
|
||||
batch.mesh->GetMaterial()->GetDescriptorSet(), {});
|
||||
batch.mesh->Draw(cmd, batch.count, batch.first);
|
||||
}
|
||||
|
||||
cmd.endRendering();
|
||||
})
|
||||
.Write(m_HDRColor, RG::ResourceUse::ColorAttachment)
|
||||
.Write(m_Depth, RG::ResourceUse::DepthAttachment)
|
||||
.Read(m_ShadowMap, RG::ResourceUse::ShaderRead,
|
||||
vk::ShaderStageFlagBits::eFragment);
|
||||
}
|
||||
|
||||
} // namespace Engine::Passes
|
||||
@@ -0,0 +1,174 @@
|
||||
//
|
||||
// Created by atdunbg on 2026/9/23.
|
||||
//
|
||||
|
||||
#include <Engine/Renderer/Passes/FullscreenGpu.h>
|
||||
#include <Engine/Vulkan/Device.h>
|
||||
#include <Engine/Vulkan/DescriptorManager.h>
|
||||
#include <Engine/Vulkan/ShaderManager.h>
|
||||
#include <Engine/Core/Log.h>
|
||||
|
||||
#include <array>
|
||||
|
||||
namespace Engine::Passes
|
||||
{
|
||||
FullscreenGpu::FullscreenGpu(Device& device, DescriptorManager& descriptors,
|
||||
ShaderManager& shaders)
|
||||
: m_Device(device), m_Descriptors(descriptors)
|
||||
{
|
||||
// Set0 = {HDR 颜色图, Bloom 图},push = 16B 通用结构
|
||||
std::vector<DescriptorBinding> bindings = {
|
||||
{0, vk::DescriptorType::eCombinedImageSampler,
|
||||
vk::ShaderStageFlagBits::eFragment},
|
||||
{1, vk::DescriptorType::eCombinedImageSampler,
|
||||
vk::ShaderStageFlagBits::eFragment},
|
||||
};
|
||||
setLayout = descriptors.CreateLayout(bindings);
|
||||
|
||||
layout = descriptors.CreatePipelineLayout(
|
||||
std::vector<vk::DescriptorSetLayout>{setLayout},
|
||||
static_cast<u32>(sizeof(FullscreenPush)));
|
||||
|
||||
sampler = device.GetDevice().createSampler(
|
||||
vk::SamplerCreateInfo{}
|
||||
.setMagFilter(vk::Filter::eLinear)
|
||||
.setMinFilter(vk::Filter::eLinear)
|
||||
.setMipmapMode(vk::SamplerMipmapMode::eNearest)
|
||||
.setAddressModeU(vk::SamplerAddressMode::eClampToEdge)
|
||||
.setAddressModeV(vk::SamplerAddressMode::eClampToEdge)
|
||||
.setAddressModeW(vk::SamplerAddressMode::eClampToEdge));
|
||||
|
||||
const std::string shaderDir = "shaders/";
|
||||
auto vertShader = shaders.CreateFromFile(
|
||||
shaderDir + "tonemap.vert.spv",
|
||||
vk::ShaderStageFlagBits::eVertex,
|
||||
std::vector<vk::DescriptorSetLayout>{setLayout},
|
||||
static_cast<u32>(sizeof(FullscreenPush)));
|
||||
auto tonemapF = shaders.CreateFromFile(
|
||||
shaderDir + "tonemap.frag.spv",
|
||||
vk::ShaderStageFlagBits::eFragment,
|
||||
std::vector<vk::DescriptorSetLayout>{setLayout},
|
||||
static_cast<u32>(sizeof(FullscreenPush)));
|
||||
auto extractF = shaders.CreateFromFile(
|
||||
shaderDir + "bloom_extract.frag.spv",
|
||||
vk::ShaderStageFlagBits::eFragment,
|
||||
std::vector<vk::DescriptorSetLayout>{setLayout},
|
||||
static_cast<u32>(sizeof(FullscreenPush)));
|
||||
auto blurF = shaders.CreateFromFile(
|
||||
shaderDir + "bloom_blur.frag.spv",
|
||||
vk::ShaderStageFlagBits::eFragment,
|
||||
std::vector<vk::DescriptorSetLayout>{setLayout},
|
||||
static_cast<u32>(sizeof(FullscreenPush)));
|
||||
|
||||
vert = vertShader.Handle;
|
||||
tonemapFrag = tonemapF.Handle;
|
||||
extractFrag = extractF.Handle;
|
||||
blurFrag = blurF.Handle;
|
||||
|
||||
if (!vert || !tonemapFrag || !extractFrag || !blurFrag) {
|
||||
ENGINE_FATAL("FullscreenGpu: failed to create shader objects");
|
||||
}
|
||||
}
|
||||
|
||||
FullscreenGpu::~FullscreenGpu()
|
||||
{
|
||||
const auto& vkDevice = m_Device.GetDevice();
|
||||
|
||||
if (vert) vkDevice.destroyShaderEXT(vert);
|
||||
if (tonemapFrag) vkDevice.destroyShaderEXT(tonemapFrag);
|
||||
if (extractFrag) vkDevice.destroyShaderEXT(extractFrag);
|
||||
if (blurFrag) vkDevice.destroyShaderEXT(blurFrag);
|
||||
|
||||
vkDevice.destroySampler(sampler);
|
||||
vkDevice.destroyPipelineLayout(layout);
|
||||
vkDevice.destroyDescriptorSetLayout(setLayout);
|
||||
}
|
||||
|
||||
vk::DescriptorSet FullscreenGpu::AllocateSet()
|
||||
{
|
||||
return m_Descriptors.AllocateDescriptorSet(setLayout);
|
||||
}
|
||||
|
||||
void FullscreenGpu::UpdateSetInput(vk::DescriptorSet set, u32 binding,
|
||||
RG::RenderGraph& rg, RG::TextureHandle input,
|
||||
u32 slot)
|
||||
{
|
||||
// 物理镜像按帧槽位各一份:每个槽位的描述符绑各自的 view
|
||||
m_Descriptors.UpdateTexture(set, binding, sampler,
|
||||
rg.GetPhysicalView(input, slot));
|
||||
}
|
||||
|
||||
void FullscreenGpu::RecordFullscreen(RG::RenderGraphContext& ctx,
|
||||
vk::ImageView view,
|
||||
vk::ShaderEXT frag,
|
||||
const vk::DescriptorSet& set,
|
||||
const FullscreenPush& push)
|
||||
{
|
||||
const vk::CommandBuffer cmd = ctx.cmd;
|
||||
|
||||
vk::ClearValue clearValue;
|
||||
clearValue.color = vk::ClearColorValue(
|
||||
std::array<float, 4>{0.0f, 0.0f, 0.0f, 1.0f});
|
||||
|
||||
vk::RenderingAttachmentInfo colorAttachment;
|
||||
colorAttachment.setImageView(view)
|
||||
.setImageLayout(vk::ImageLayout::eColorAttachmentOptimal)
|
||||
.setLoadOp(vk::AttachmentLoadOp::eClear)
|
||||
.setStoreOp(vk::AttachmentStoreOp::eStore)
|
||||
.setClearValue(clearValue);
|
||||
|
||||
vk::RenderingInfo renderingInfo;
|
||||
renderingInfo.setRenderArea({{0, 0}, ctx.extent})
|
||||
.setLayerCount(1)
|
||||
.setColorAttachments(colorAttachment);
|
||||
|
||||
cmd.beginRendering(renderingInfo);
|
||||
|
||||
std::array<vk::ShaderStageFlagBits, 2> stages = {
|
||||
vk::ShaderStageFlagBits::eVertex,
|
||||
vk::ShaderStageFlagBits::eFragment
|
||||
};
|
||||
std::array<vk::ShaderEXT, 2> shaders = {vert, frag};
|
||||
cmd.bindShadersEXT(stages, shaders);
|
||||
|
||||
cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics,
|
||||
layout, 0, set, {});
|
||||
|
||||
// ---- 动态状态(无深度附件、无顶点输入)----
|
||||
cmd.setRasterizerDiscardEnable(VK_FALSE);
|
||||
cmd.setDepthTestEnable(VK_FALSE);
|
||||
cmd.setDepthWriteEnable(VK_FALSE);
|
||||
cmd.setDepthBoundsTestEnable(VK_FALSE);
|
||||
cmd.setStencilTestEnable(VK_FALSE);
|
||||
cmd.setDepthBiasEnable(VK_FALSE);
|
||||
cmd.setPolygonModeEXT(vk::PolygonMode::eFill);
|
||||
cmd.setCullMode(vk::CullModeFlagBits::eNone);
|
||||
cmd.setFrontFace(vk::FrontFace::eCounterClockwise);
|
||||
cmd.setPrimitiveTopology(vk::PrimitiveTopology::eTriangleList);
|
||||
cmd.setPrimitiveRestartEnable(VK_FALSE);
|
||||
cmd.setVertexInputEXT(
|
||||
std::array<vk::VertexInputBindingDescription2EXT, 0>{},
|
||||
std::array<vk::VertexInputAttributeDescription2EXT, 0>{});
|
||||
cmd.setRasterizationSamplesEXT(vk::SampleCountFlagBits::e1);
|
||||
vk::SampleMask sampleMask = 0xFFFFFFFF;
|
||||
cmd.setSampleMaskEXT(vk::SampleCountFlagBits::e1, sampleMask);
|
||||
cmd.setAlphaToCoverageEnableEXT(VK_FALSE);
|
||||
cmd.setAlphaToOneEnableEXT(VK_FALSE);
|
||||
|
||||
vk::Viewport viewport(0.0f, 0.0f,
|
||||
static_cast<float>(ctx.extent.width),
|
||||
static_cast<float>(ctx.extent.height),
|
||||
0.0f, 1.0f);
|
||||
cmd.setViewportWithCount(viewport);
|
||||
cmd.setScissorWithCount(vk::Rect2D({0, 0}, ctx.extent));
|
||||
|
||||
cmd.pushConstants(layout,
|
||||
vk::ShaderStageFlagBits::eVertex |
|
||||
vk::ShaderStageFlagBits::eFragment,
|
||||
0, sizeof(FullscreenPush), &push);
|
||||
|
||||
cmd.draw(3, 1, 0, 0);
|
||||
cmd.endRendering();
|
||||
}
|
||||
|
||||
} // namespace Engine::Passes
|
||||
@@ -0,0 +1,36 @@
|
||||
//
|
||||
// Created by atdunbg on 2026/9/23.
|
||||
//
|
||||
|
||||
#include <Engine/Renderer/Passes/ImGuiPass.h>
|
||||
#include <Engine/ImGui/ImGuiLayer.h>
|
||||
|
||||
namespace Engine::Passes
|
||||
{
|
||||
void ImGuiPass::Register(RG::RenderGraph& rg)
|
||||
{
|
||||
rg.AddPass("ImGui", [this](RG::RenderGraphContext& ctx) {
|
||||
const vk::CommandBuffer cmd = ctx.cmd;
|
||||
|
||||
vk::RenderingAttachmentInfo colorAttachment;
|
||||
colorAttachment.setImageView(ctx.View(m_Backbuffer))
|
||||
.setImageLayout(vk::ImageLayout::eColorAttachmentOptimal)
|
||||
.setLoadOp(vk::AttachmentLoadOp::eLoad) // 关键:叠加
|
||||
.setStoreOp(vk::AttachmentStoreOp::eStore);
|
||||
|
||||
vk::RenderingInfo renderingInfo;
|
||||
renderingInfo.setRenderArea({{0, 0}, ctx.extent})
|
||||
.setLayerCount(1)
|
||||
.setColorAttachments(colorAttachment);
|
||||
|
||||
cmd.beginRendering(renderingInfo);
|
||||
|
||||
if (*m_Layer) {
|
||||
(*m_Layer)->RenderDrawData(cmd);
|
||||
}
|
||||
|
||||
cmd.endRendering();
|
||||
}).Write(m_Backbuffer, RG::ResourceUse::ColorAttachment);
|
||||
}
|
||||
|
||||
} // namespace Engine::Passes
|
||||
@@ -0,0 +1,139 @@
|
||||
//
|
||||
// Created by atdunbg on 2026/9/23.
|
||||
//
|
||||
|
||||
#include <Engine/Renderer/Passes/SceneGpu.h>
|
||||
#include <Engine/Vulkan/Device.h>
|
||||
#include <Engine/Vulkan/DescriptorManager.h>
|
||||
#include <Engine/Vulkan/ShaderManager.h>
|
||||
#include <Engine/Vulkan/UniformBuffer.h>
|
||||
#include <Engine/Core/Log.h>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
namespace Engine::Passes
|
||||
{
|
||||
SceneGpu::SceneGpu(Device& device, DescriptorManager& descriptors,
|
||||
ShaderManager& shaders,
|
||||
UniformBuffer& ubo, UniformBuffer& objectBuffer)
|
||||
: m_Device(device), m_Descriptors(descriptors)
|
||||
{
|
||||
// Set 0: UBO + GPUDriven 实例数据(SSBO,vertex 读取)
|
||||
std::vector<DescriptorBinding> uboBindings = {
|
||||
{0, vk::DescriptorType::eUniformBuffer,
|
||||
vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment},
|
||||
{1, vk::DescriptorType::eStorageBuffer,
|
||||
vk::ShaderStageFlagBits::eVertex},
|
||||
};
|
||||
uboSetLayout = descriptors.CreateLayout(uboBindings);
|
||||
|
||||
// Set 1: texture
|
||||
std::vector<DescriptorBinding> texBindings = {
|
||||
{0, vk::DescriptorType::eCombinedImageSampler, vk::ShaderStageFlagBits::eFragment},
|
||||
};
|
||||
textureSetLayout = descriptors.CreateLayout(texBindings);
|
||||
|
||||
// Set 2: shadow map(fragment 采样)
|
||||
std::vector<DescriptorBinding> shadowBindings = {
|
||||
{0, vk::DescriptorType::eCombinedImageSampler, vk::ShaderStageFlagBits::eFragment},
|
||||
};
|
||||
shadowSetLayout = descriptors.CreateLayout(shadowBindings);
|
||||
|
||||
// Forward pipeline layout:三个 set;无 push(GPUDriven 经 SSBO)
|
||||
forwardLayout = descriptors.CreatePipelineLayout(
|
||||
{uboSetLayout, textureSetLayout, shadowSetLayout}, 0);
|
||||
|
||||
// Shadow pipeline layout:Set0 = UBO/ObjectBuffer(实例矩阵),
|
||||
// push = lightVP 64B
|
||||
shadowLayout = descriptors.CreatePipelineLayout(
|
||||
std::vector<vk::DescriptorSetLayout>{uboSetLayout},
|
||||
sizeof(glm::mat4));
|
||||
|
||||
const std::string shaderDir = "shaders/";
|
||||
// forward vert/frag 均无 push(range 定义一致:都没有,VUID-08878)
|
||||
auto vert = shaders.CreateFromFile(
|
||||
shaderDir + "simple_shader.vert.spv",
|
||||
vk::ShaderStageFlagBits::eVertex,
|
||||
{uboSetLayout, textureSetLayout, shadowSetLayout}, 0);
|
||||
auto frag = shaders.CreateFromFile(
|
||||
shaderDir + "simple_shader.frag.spv",
|
||||
vk::ShaderStageFlagBits::eFragment,
|
||||
{uboSetLayout, textureSetLayout, shadowSetLayout}, 0);
|
||||
|
||||
// shadow shader:depth-only,push = lightVP
|
||||
auto shadowVertShader = shaders.CreateFromFile(
|
||||
shaderDir + "shadow.vert.spv",
|
||||
vk::ShaderStageFlagBits::eVertex,
|
||||
std::vector<vk::DescriptorSetLayout>{uboSetLayout},
|
||||
sizeof(glm::mat4));
|
||||
auto shadowFragShader = shaders.CreateFromFile(
|
||||
shaderDir + "shadow.frag.spv",
|
||||
vk::ShaderStageFlagBits::eFragment,
|
||||
std::vector<vk::DescriptorSetLayout>{uboSetLayout},
|
||||
sizeof(glm::mat4));
|
||||
|
||||
forwardVert = vert.Handle;
|
||||
forwardFrag = frag.Handle;
|
||||
shadowVert = shadowVertShader.Handle;
|
||||
shadowFrag = shadowFragShader.Handle;
|
||||
|
||||
if (!forwardVert || !forwardFrag || !shadowVert || !shadowFrag) {
|
||||
ENGINE_FATAL("SceneGpu: failed to create shader objects");
|
||||
}
|
||||
|
||||
// 阴影采样器:clamp-to-border 白色 = 光视锥之外无阴影
|
||||
vk::SamplerCreateInfo shadowSamplerInfo;
|
||||
shadowSamplerInfo.setMagFilter(vk::Filter::eLinear)
|
||||
.setMinFilter(vk::Filter::eLinear)
|
||||
.setMipmapMode(vk::SamplerMipmapMode::eNearest)
|
||||
.setAddressModeU(vk::SamplerAddressMode::eClampToBorder)
|
||||
.setAddressModeV(vk::SamplerAddressMode::eClampToBorder)
|
||||
.setAddressModeW(vk::SamplerAddressMode::eClampToBorder)
|
||||
.setBorderColor(vk::BorderColor::eFloatOpaqueWhite)
|
||||
.setUnnormalizedCoordinates(VK_FALSE)
|
||||
.setCompareEnable(VK_FALSE);
|
||||
shadowSampler = device.GetDevice().createSampler(shadowSamplerInfo);
|
||||
|
||||
// per-slot 描述符分配 + Set0 绑定(buffer 句柄全程稳定,不用重绑)
|
||||
uboSets.resize(kMaxFramesInFlight);
|
||||
shadowSets.resize(kMaxFramesInFlight);
|
||||
for (u32 slot = 0; slot < kMaxFramesInFlight; ++slot) {
|
||||
uboSets[slot] = descriptors.AllocateDescriptorSet(uboSetLayout);
|
||||
descriptors.UpdateUniformBuffer(
|
||||
uboSets[slot], 0, ubo.Get(slot), ubo.GetSize());
|
||||
descriptors.UpdateStorageBuffer(
|
||||
uboSets[slot], 1, objectBuffer.Get(slot), objectBuffer.GetSize());
|
||||
|
||||
shadowSets[slot] = descriptors.AllocateDescriptorSet(shadowSetLayout);
|
||||
}
|
||||
// shadowSets 的图片绑定在 Compile 后由 BindShadowMap 完成
|
||||
}
|
||||
|
||||
SceneGpu::~SceneGpu()
|
||||
{
|
||||
const auto& vkDevice = m_Device.GetDevice();
|
||||
|
||||
if (forwardVert) vkDevice.destroyShaderEXT(forwardVert);
|
||||
if (forwardFrag) vkDevice.destroyShaderEXT(forwardFrag);
|
||||
if (shadowVert) vkDevice.destroyShaderEXT(shadowVert);
|
||||
if (shadowFrag) vkDevice.destroyShaderEXT(shadowFrag);
|
||||
|
||||
vkDevice.destroySampler(shadowSampler);
|
||||
vkDevice.destroyPipelineLayout(forwardLayout);
|
||||
vkDevice.destroyPipelineLayout(shadowLayout);
|
||||
vkDevice.destroyDescriptorSetLayout(uboSetLayout);
|
||||
vkDevice.destroyDescriptorSetLayout(textureSetLayout);
|
||||
vkDevice.destroyDescriptorSetLayout(shadowSetLayout);
|
||||
}
|
||||
|
||||
void SceneGpu::BindShadowMap(RG::RenderGraph& rg, RG::TextureHandle shadowMap)
|
||||
{
|
||||
// 物理镜像按帧槽位各一份:每个槽位的描述符绑各自的 view
|
||||
for (u32 slot = 0; slot < kMaxFramesInFlight; ++slot) {
|
||||
m_Descriptors.UpdateTexture(
|
||||
shadowSets[slot], 0, shadowSampler,
|
||||
rg.GetPhysicalView(shadowMap, slot));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Engine::Passes
|
||||
@@ -0,0 +1,104 @@
|
||||
//
|
||||
// Created by atdunbg on 2026/9/23.
|
||||
//
|
||||
|
||||
#include <Engine/Renderer/Passes/ShadowPass.h>
|
||||
#include <Engine/Renderer/Vertex.h>
|
||||
#include <Engine/Renderer/Mesh.h>
|
||||
|
||||
#include <array>
|
||||
|
||||
namespace Engine::Passes
|
||||
{
|
||||
void ShadowPass::Register(RG::RenderGraph& rg)
|
||||
{
|
||||
// 所有渲染体都写入,不做实体级开关——castShadow 应由材质/实体
|
||||
// 数据驱动剔除,而非 pass 逻辑特判
|
||||
rg.AddPass("Shadow", [this](RG::RenderGraphContext& ctx) {
|
||||
const vk::CommandBuffer cmd = ctx.cmd;
|
||||
|
||||
vk::RenderingAttachmentInfo depthAttachment;
|
||||
depthAttachment.setImageView(ctx.View(m_ShadowMap))
|
||||
.setImageLayout(vk::ImageLayout::eDepthAttachmentOptimal)
|
||||
.setLoadOp(vk::AttachmentLoadOp::eClear)
|
||||
.setStoreOp(vk::AttachmentStoreOp::eStore)
|
||||
.setClearValue(vk::ClearValue(
|
||||
vk::ClearDepthStencilValue(1.0f, 0)));
|
||||
|
||||
vk::RenderingInfo renderingInfo;
|
||||
renderingInfo.setRenderArea({{0, 0}, {kShadowMapSize, kShadowMapSize}})
|
||||
.setLayerCount(1)
|
||||
.setPDepthAttachment(&depthAttachment);
|
||||
|
||||
cmd.beginRendering(renderingInfo);
|
||||
|
||||
std::array<vk::ShaderStageFlagBits, 2> stages = {
|
||||
vk::ShaderStageFlagBits::eVertex,
|
||||
vk::ShaderStageFlagBits::eFragment
|
||||
};
|
||||
std::array<vk::ShaderEXT, 2> shaders = {m_Gpu.shadowVert, m_Gpu.shadowFrag};
|
||||
cmd.bindShadersEXT(stages, shaders);
|
||||
|
||||
// ---- 动态状态(depth-only)----
|
||||
cmd.setRasterizerDiscardEnable(VK_FALSE);
|
||||
|
||||
cmd.setDepthTestEnable(VK_TRUE);
|
||||
cmd.setDepthWriteEnable(VK_TRUE);
|
||||
cmd.setDepthCompareOp(vk::CompareOp::eLess);
|
||||
cmd.setDepthBoundsTestEnable(VK_FALSE);
|
||||
|
||||
cmd.setStencilTestEnable(VK_FALSE);
|
||||
// 深度 bias 压平 shadow acne(NDC 量级)
|
||||
cmd.setDepthBiasEnable(VK_TRUE);
|
||||
cmd.setDepthBias(0.0025f, 0.0f, 0.003f);
|
||||
cmd.setPolygonModeEXT(vk::PolygonMode::eFill);
|
||||
cmd.setCullMode(vk::CullModeFlagBits::eNone);
|
||||
cmd.setFrontFace(vk::FrontFace::eCounterClockwise);
|
||||
cmd.setPrimitiveTopology(vk::PrimitiveTopology::eTriangleList);
|
||||
cmd.setPrimitiveRestartEnable(VK_FALSE);
|
||||
|
||||
auto binding = Vertex::GetBindingDescription();
|
||||
auto attributes = Vertex::GetAttributeDescriptions();
|
||||
cmd.setVertexInputEXT(binding, attributes);
|
||||
|
||||
cmd.setRasterizationSamplesEXT(vk::SampleCountFlagBits::e1);
|
||||
vk::SampleMask sampleMask = 0xFFFFFFFF;
|
||||
cmd.setSampleMaskEXT(vk::SampleCountFlagBits::e1, sampleMask);
|
||||
cmd.setAlphaToCoverageEnableEXT(VK_FALSE);
|
||||
cmd.setAlphaToOneEnableEXT(VK_FALSE);
|
||||
|
||||
vk::Viewport viewport(0.0f, 0.0f,
|
||||
static_cast<float>(kShadowMapSize),
|
||||
static_cast<float>(kShadowMapSize),
|
||||
0.0f, 1.0f);
|
||||
cmd.setViewportWithCount(viewport);
|
||||
|
||||
vk::Rect2D scissor({0, 0}, {kShadowMapSize, kShadowMapSize});
|
||||
cmd.setScissorWithCount(scissor);
|
||||
|
||||
// ---- 绘制:GPUDriven——与 Forward 共用实例数据,但用
|
||||
// 光源视锥剔除后的 batch(相机外实体仍投影子)----
|
||||
cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics,
|
||||
m_Gpu.shadowLayout, 0,
|
||||
m_Gpu.uboSets[ctx.frame.frameSlot], {});
|
||||
|
||||
// stage 必须覆盖 layout range 声明的全部 stage(VUID-01796)
|
||||
cmd.pushConstants(m_Gpu.shadowLayout,
|
||||
vk::ShaderStageFlagBits::eVertex |
|
||||
vk::ShaderStageFlagBits::eFragment,
|
||||
0, sizeof(glm::mat4), &m_Frame.ubo.lightSpace);
|
||||
|
||||
for (const auto& batch : m_Frame.shadowView.batches) {
|
||||
batch.mesh->Draw(cmd, batch.count, batch.first);
|
||||
}
|
||||
|
||||
cmd.endRendering();
|
||||
}).Write(m_ShadowMap, RG::ResourceUse::DepthAttachment);
|
||||
}
|
||||
|
||||
void ShadowPass::OnTargetsRebuilt(RG::RenderGraph& rg)
|
||||
{
|
||||
m_Gpu.BindShadowMap(rg, m_ShadowMap);
|
||||
}
|
||||
|
||||
} // namespace Engine::Passes
|
||||
@@ -0,0 +1,48 @@
|
||||
//
|
||||
// Created by atdunbg on 2026/9/23.
|
||||
//
|
||||
|
||||
#include <Engine/Renderer/Passes/TonemapPass.h>
|
||||
#include <Engine/Core/FrameContext.h>
|
||||
|
||||
namespace Engine::Passes
|
||||
{
|
||||
TonemapPass::TonemapPass(FullscreenGpu& gpu, PostProcessParams& params,
|
||||
RG::TextureHandle hdrColor, RG::TextureHandle bloomA,
|
||||
RG::TextureHandle backbuffer)
|
||||
: m_Gpu(gpu), m_Params(params),
|
||||
m_HDR(hdrColor), m_A(bloomA), m_Backbuffer(backbuffer)
|
||||
{
|
||||
m_Sets.resize(kMaxFramesInFlight);
|
||||
for (u32 slot = 0; slot < kMaxFramesInFlight; ++slot) {
|
||||
m_Sets[slot] = m_Gpu.AllocateSet();
|
||||
}
|
||||
}
|
||||
|
||||
void TonemapPass::Register(RG::RenderGraph& rg)
|
||||
{
|
||||
rg.AddPass("Tonemap",
|
||||
[this](RG::RenderGraphContext& ctx) {
|
||||
m_Gpu.RecordFullscreen(ctx, ctx.View(m_Backbuffer),
|
||||
m_Gpu.tonemapFrag,
|
||||
m_Sets[ctx.frame.frameSlot],
|
||||
FullscreenPush{m_Params.exposure,
|
||||
m_Params.useACES ? 1.0f : 0.0f,
|
||||
m_Params.bloomStrength, 0.0f});
|
||||
})
|
||||
.Write(m_Backbuffer, RG::ResourceUse::ColorAttachment)
|
||||
.Read(m_HDR, RG::ResourceUse::ShaderRead,
|
||||
vk::ShaderStageFlagBits::eFragment)
|
||||
.Read(m_A, RG::ResourceUse::ShaderRead,
|
||||
vk::ShaderStageFlagBits::eFragment);
|
||||
}
|
||||
|
||||
void TonemapPass::OnTargetsRebuilt(RG::RenderGraph& rg)
|
||||
{
|
||||
for (u32 slot = 0; slot < kMaxFramesInFlight; ++slot) {
|
||||
m_Gpu.UpdateSetInput(m_Sets[slot], 0, rg, m_HDR, slot);
|
||||
m_Gpu.UpdateSetInput(m_Sets[slot], 1, rg, m_A, slot);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Engine::Passes
|
||||
+120
-843
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,168 @@
|
||||
//
|
||||
// Created by atdunbg on 2026/9/23.
|
||||
//
|
||||
|
||||
#include <Engine/Renderer/SceneRenderer.h>
|
||||
#include <Engine/ECS/Scene.h>
|
||||
#include <Engine/ECS/Components.h>
|
||||
#include <Engine/Renderer/Camera.h>
|
||||
#include <Engine/Renderer/Frustum.h>
|
||||
#include <Engine/Renderer/Model.h>
|
||||
#include <Engine/Renderer/Mesh.h>
|
||||
#include <Engine/Assets/AssetManager.h>
|
||||
#include <Engine/Core/Log.h>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
#include <chrono>
|
||||
|
||||
namespace Engine
|
||||
{
|
||||
using namespace ECS; // TransformComponent / Entity 等直接使用
|
||||
|
||||
namespace
|
||||
{
|
||||
// 光源正交包围盒(覆盖当前场景;后续按场景 AABB 自适应)
|
||||
constexpr f32 kLightOrthoExtent = 15.0f;
|
||||
constexpr f32 kLightDistance = 30.0f;
|
||||
constexpr f32 kLightNear = 1.0f;
|
||||
constexpr f32 kLightFar = 80.0f;
|
||||
}
|
||||
|
||||
void SceneRenderer::ComputeLightSpace(const glm::vec3& shadowDir)
|
||||
{
|
||||
// 光源位置在光方向的反方向远处,看向场景中心
|
||||
const glm::vec3 lightPos = -shadowDir * kLightDistance;
|
||||
// up 与视线平行时叉积退化(NaN),切换 up
|
||||
const glm::vec3 up = glm::abs(shadowDir.y) > 0.999f
|
||||
? glm::vec3(0.0f, 0.0f, 1.0f) : glm::vec3(0.0f, 1.0f, 0.0f);
|
||||
const glm::mat4 lightView = glm::lookAt(
|
||||
lightPos, glm::vec3(0.0f), up);
|
||||
|
||||
glm::mat4 lightProj = glm::ortho(
|
||||
-kLightOrthoExtent, kLightOrthoExtent,
|
||||
-kLightOrthoExtent, kLightOrthoExtent,
|
||||
kLightNear, kLightFar);
|
||||
// Vulkan clip 深度 [0,1]:z' = 0.5*z + 0.5*w。
|
||||
// ortho 的 w_clip = 1,偏移折算进 z 的平移分量(与 perspective 公式不同!)
|
||||
lightProj[2][2] *= 0.5f;
|
||||
lightProj[3][2] = lightProj[3][2] * 0.5f + 0.5f;
|
||||
|
||||
m_Frame.ubo.lightSpace = lightProj * lightView;
|
||||
}
|
||||
|
||||
bool SceneRenderer::BuildFrame(ECS::Scene& scene, Assets::AssetManager& assets,
|
||||
f32 aspect)
|
||||
{
|
||||
const auto collectStart = std::chrono::steady_clock::now();
|
||||
|
||||
// ---- 1. 相机 ----
|
||||
auto* cameraComp = scene.GetPrimaryCamera();
|
||||
if (!cameraComp) {
|
||||
// 无相机:本帧跳过(正常场景必有相机,安全网)
|
||||
return false;
|
||||
}
|
||||
const Camera& camera = cameraComp->camera;
|
||||
|
||||
UniformBufferObject& ubo = m_Frame.ubo;
|
||||
ubo.view = camera.GetView();
|
||||
ubo.proj = camera.GetProjection(aspect, 0.1f, 100.0f);
|
||||
ubo.ambientColor = scene.GetSettings().ambientColor;
|
||||
|
||||
// ---- 2. 灯光(第一个灯作为阴影光源)----
|
||||
u32 lightCount = 0;
|
||||
glm::vec3 shadowDir{0.0f, -1.0f, 0.0f}; // 无灯兜底:头顶正射
|
||||
scene.Each<LightComponent>([&](Entity, LightComponent& light) {
|
||||
if (lightCount >= kMaxLights) return;
|
||||
|
||||
glm::vec3 dir = glm::length(light.direction) > 0.0f
|
||||
? glm::normalize(light.direction)
|
||||
: glm::vec3(0.0f);
|
||||
ubo.lights[lightCount].direction = glm::vec4(dir, 0.0f);
|
||||
ubo.lights[lightCount].color = light.color;
|
||||
|
||||
if (lightCount == 0) shadowDir = dir;
|
||||
++lightCount;
|
||||
});
|
||||
for (u32 i = lightCount; i < kMaxLights; ++i) {
|
||||
ubo.lights[i].direction = glm::vec4(0.0f);
|
||||
ubo.lights[i].color = glm::vec4(0.0f);
|
||||
}
|
||||
ComputeLightSpace(shadowDir);
|
||||
|
||||
// ---- 3. GPUDriven:单趟收集 + 双视图剔除 ----
|
||||
// 每个实例对相机视锥、光源视锥分别做包围球测试:
|
||||
// 进相机视锥 → 主视图 batch(Forward 画)
|
||||
// 进光源视锥 → 阴影 batch(Shadow 画)——相机外实体仍可投影子
|
||||
// 实例矩阵数组 = [主视图段 | 阴影视图段],batch.first 为全局下标。
|
||||
// 哈希聚合保证各视图内 batch 区间连续。
|
||||
m_Frame.instanceModels.clear();
|
||||
m_Frame.mainView.batches.clear();
|
||||
m_Frame.shadowView.batches.clear();
|
||||
m_Frame.mainView.instanceCount = 0;
|
||||
m_Frame.shadowView.instanceCount = 0;
|
||||
m_MainLookup.clear();
|
||||
m_ShadowLookup.clear();
|
||||
bool instanceTruncated = false;
|
||||
|
||||
// 剔除平面提取按 [0,1] 深度约定——相机 proj 与 lightSpace 都已
|
||||
// 做 [0,1] 重映射;原始 [-1,1] 矩阵的 near 平面公式不同,勿用
|
||||
const Frustum mainFrustum = Frustum::FromMatrix(ubo.proj * ubo.view);
|
||||
const Frustum shadowFrustum = Frustum::FromMatrix(ubo.lightSpace);
|
||||
|
||||
scene.Each<TransformComponent, MeshRendererComponent>(
|
||||
[&](Entity, TransformComponent& t, MeshRendererComponent& mr) {
|
||||
Model* model = assets.GetModel(mr.model);
|
||||
if (!model) return;
|
||||
|
||||
// 实例世界缩放(矩阵列向量最大长度,包围球半径随缩放)
|
||||
const f32 worldScale = glm::max(
|
||||
glm::length(glm::vec3(t.world[0])),
|
||||
glm::max(glm::length(glm::vec3(t.world[1])),
|
||||
glm::length(glm::vec3(t.world[2]))));
|
||||
|
||||
for (auto& mesh : model->GetMeshes()) {
|
||||
const auto& bounds = mesh->GetBounds();
|
||||
const glm::vec3 worldCenter =
|
||||
glm::vec3(t.world * glm::vec4(bounds.center, 1.0f));
|
||||
const f32 worldRadius = bounds.radius * worldScale;
|
||||
|
||||
auto tryAppend = [&](CullResult& view, const Frustum& frustum,
|
||||
std::unordered_map<Mesh*, u32>& lookup) {
|
||||
if (!frustum.IntersectsSphere(worldCenter, worldRadius)) {
|
||||
return;
|
||||
}
|
||||
if (m_Frame.instanceModels.size() >= kMaxInstances) {
|
||||
instanceTruncated = true;
|
||||
return;
|
||||
}
|
||||
const auto [it, inserted] = lookup.emplace(
|
||||
mesh.get(), static_cast<u32>(view.batches.size()));
|
||||
if (inserted) {
|
||||
view.batches.push_back({mesh.get(),
|
||||
static_cast<u32>(m_Frame.instanceModels.size()), 0});
|
||||
}
|
||||
view.batches[it->second].count++;
|
||||
view.instanceCount++;
|
||||
m_Frame.instanceModels.push_back(t.world);
|
||||
};
|
||||
|
||||
tryAppend(m_Frame.mainView, mainFrustum, m_MainLookup);
|
||||
tryAppend(m_Frame.shadowView, shadowFrustum, m_ShadowLookup);
|
||||
}
|
||||
});
|
||||
|
||||
if (instanceTruncated && !m_TruncationWarned) {
|
||||
// 只告警一次(每帧告警会刷屏)
|
||||
m_TruncationWarned = true;
|
||||
ENGINE_WARN("GPUDriven: instance buffer full ({}), entities truncated",
|
||||
kMaxInstances);
|
||||
}
|
||||
|
||||
m_Frame.collectMs = std::chrono::duration<f32, std::milli>(
|
||||
std::chrono::steady_clock::now() - collectStart).count();
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Engine
|
||||
@@ -0,0 +1,23 @@
|
||||
//
|
||||
// Created by atdunbg on 2026/9/23.
|
||||
//
|
||||
|
||||
#include <Engine/Renderer/VulkanAssetUploader.h>
|
||||
#include <Engine/Renderer/Model.h>
|
||||
|
||||
namespace Engine {
|
||||
|
||||
Scope<Model> VulkanAssetUploader::LoadModel(const std::string& path)
|
||||
{
|
||||
// GPU 上传在此发生:解析(assimp)+ buffer/image 创建一步完成
|
||||
return CreateScope<Model>(
|
||||
m_Device, m_Allocator, m_Descriptors, m_TextureLayout, path);
|
||||
}
|
||||
|
||||
Scope<Model> VulkanAssetUploader::CreatePlane(f32 halfSize, u8 r, u8 g, u8 b)
|
||||
{
|
||||
return Model::CreatePlane(m_Device, m_Allocator, m_Descriptors,
|
||||
m_TextureLayout, halfSize, r, g, b);
|
||||
}
|
||||
|
||||
} // namespace Engine
|
||||
Reference in New Issue
Block a user