feat: 双视图视锥剔除(相机/光源各自剔除)
- Frustum(Gribb-Hartmann):从 view-proj 行组合提取 6 平面, 包围球相交测试;按 Vulkan [0,1] 深度约定取 near=row2 - Mesh 构造时缓存局部空间包围球(顶点包围盒 → 中心+半径) - 实例收集重写:单趟遍历,每个 (实体, mesh 部件) 对相机视锥与 光源视锥分别做剔除——相机外实体仍可投影子进视野,Shadow 必须用 光源视锥独立剔除,不能用相机结果 - 实例矩阵数组 = [主视图段 | 阴影视图段],batch.first 全局下标; 两视图共享同一份 ObjectBuffer SSBO - 聚合改哈希(unordered_map),单趟填充替代原两趟遍历 - 面板显示 instances main/shadow 计数(剔除效果实时可见) 实测:1 背包 80 main / 80 shadow(修复前 shadow=21——near 平面 按 [-1,1] 矩阵误用 [0,1] 公式,光锥近半段被误剔)
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
//
|
||||
// Created by atdunbg on 2026/9/23.
|
||||
//
|
||||
// Frustum:Gribb-Hartmann 视锥平面提取 + 包围球相交测试。
|
||||
//
|
||||
// 平面从 view-proj 矩阵的行组合中直接抽出(无需分解参数),
|
||||
// 用于按视图剔除(相机视图 / 光源视图各自独立一份)。
|
||||
// 注意:near/far 平面按 Vulkan [0,1] 深度约定提取
|
||||
//(z_clip ∈ [0,1]:near = row2,far = row3 - row2)。
|
||||
//
|
||||
|
||||
#ifndef HELLOVULKAN_FRUSTUM_H
|
||||
#define HELLOVULKAN_FRUSTUM_H
|
||||
|
||||
#include <Engine/Core/Types.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include <array>
|
||||
|
||||
namespace Engine {
|
||||
|
||||
struct Frustum {
|
||||
// 依次:left, right, bottom, top, near, far
|
||||
// xyz = 平面法线(未归一化,仅做符号/点积测试),w = 偏移
|
||||
std::array<glm::vec4, 6> planes{};
|
||||
|
||||
// 从 view-proj 矩阵提取 6 平面(列主序 glm 矩阵的"行"组合)。
|
||||
// 注意必须传未做 [0,1] 深度重映射的矩阵,提取公式才成立。
|
||||
static Frustum FromMatrix(const glm::mat4& m)
|
||||
{
|
||||
Frustum f;
|
||||
// 列主序下的"行 i": (m[0][i], m[1][i], m[2][i], m[3][i])
|
||||
const auto row = [&m](u32 i) {
|
||||
return glm::vec4(m[0][i], m[1][i], m[2][i], m[3][i]);
|
||||
};
|
||||
const glm::vec4 r0 = row(0);
|
||||
const glm::vec4 r1 = row(1);
|
||||
const glm::vec4 r2 = row(2);
|
||||
const glm::vec4 r3 = row(3);
|
||||
|
||||
f.planes[0] = r3 + r0; // left: x' >= -w
|
||||
f.planes[1] = r3 - r0; // right: x' <= w
|
||||
f.planes[2] = r3 + r1; // bottom: y' >= -w
|
||||
f.planes[3] = r3 - r1; // top: y' <= w
|
||||
f.planes[4] = r2; // near: z' >= 0(Vulkan [0,1])
|
||||
f.planes[5] = r3 - r2; // far: z' <= w
|
||||
return f;
|
||||
}
|
||||
|
||||
// 包围球与视锥相交测试(外接球近似,略保守——宁多画不漏画)。
|
||||
// 对每个平面:球心在平面负侧且距离超过 radius 才算外。
|
||||
bool IntersectsSphere(const glm::vec3& center, f32 radius) const
|
||||
{
|
||||
for (const auto& p : planes) {
|
||||
if (glm::dot(glm::vec3(p), center) + p.w < -radius) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Engine
|
||||
|
||||
#endif //HELLOVULKAN_FRUSTUM_H
|
||||
@@ -37,11 +37,19 @@ namespace Engine
|
||||
Material* GetMaterial() const { return m_Material.get(); }
|
||||
u32 GetIndexCount() const { return m_IndexCount; }
|
||||
|
||||
// 局部空间包围球(构造时从顶点算出),视锥剔除用
|
||||
struct Bounds {
|
||||
glm::vec3 center{0.0f};
|
||||
f32 radius = 0.0f;
|
||||
};
|
||||
const Bounds& GetBounds() const { return m_Bounds; }
|
||||
|
||||
private:
|
||||
Scope<Buffer> m_VertexBuffer;
|
||||
Scope<Buffer> m_IndexBuffer;
|
||||
Scope<Material> m_Material;
|
||||
u32 m_IndexCount = 0;
|
||||
Bounds m_Bounds;
|
||||
|
||||
u64 m_VertexBytes = 0;
|
||||
u64 m_IndexBytes = 0;
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <Engine/Core/FrameContext.h>
|
||||
#include <Engine/RenderGraph/RenderGraph.h>
|
||||
#include <vulkan/vulkan.hpp>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
@@ -138,8 +139,17 @@ namespace Engine {
|
||||
glm::mat4 m_LightSpace{1.0f}; // 阴影光源 VP(每帧由 Render 算好)
|
||||
|
||||
// GPUDriven:当帧实例数据(Render 期收集,Forward pass 消费)
|
||||
std::vector<InstanceBatch> m_InstanceBatches;
|
||||
std::vector<glm::mat4> m_InstanceModels;
|
||||
// 双视图剔除:相机视锥与光源视锥各出一份可见列表——
|
||||
// 相机外的实体仍可能投影子进视野,所以 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;
|
||||
|
||||
// 可选叠加层(不拥有;Application 持有)
|
||||
@@ -174,6 +184,10 @@ namespace Engine {
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user