feat: IBL 阶段②——漫反射辐照度卷积,PBR ambient 接入环境光

- Cubemap 构造期同步 CPU 半球 Lambert 积分(64×32 网格 ≈2048 采样/像素)
  生成 32² irradiance cube(漫反射是超低频信号,分辨率无需高)
- shader:kD 用粗糙度感知 FresnelSchlickRoughness + (1-metallic),
  N 采样 irradianceMap(Set2 binding 3)替换常数 ambient;
  ambientColor 保留为强度/色调乘子
- CreateNeutral 兜底:无 HDR 资产时 1×1 常数 cube,描述符恒有效
- FaceDir 改为返回单位方向(equirect 查表要求,修掉未归一化的畸变)
This commit is contained in:
2026-09-23 18:50:34 +08:00
parent 4b5990e6c8
commit fa25cb3348
6 changed files with 325 additions and 141 deletions
+40 -12
View File
@@ -1,11 +1,19 @@
//
// Created by atdunbg on 2026/9/23.
//
// Cubemap:环境立方体贴图。
// Cubemap:环境立方体贴图 + 漫反射辐照度图(IBL 阶段②)。
//
// 从等距柱状 HDR(.hdr,polyhaven 等)生成:CPU 重采样到 6 面
//(一次性加载成本,亚秒级),转 fp16 后整块上传,硬件 Cube 采样。
// 后续 IBL 的 irradiance/prefilter 卷积也基于本类的数据形态。
// 同一次加载内完成 irradiance 卷积(半球积分,源数据在手零额外 IO)。
//
// 双图契约:
// Get()/GetView() —— 环境 cube(Skybox 背景)
// GetIrradianceView() —— 漫反射辐照度 cube(PBR ambient)
// 镜面 IBL(pre-filter + BRDF LUT)后续接入,形态一致。
//
// 后续演进:CPU 卷积 → compute pass(动态环境零卡顿);本类的
// 方向表/上传路径届时整体迁移,接口不变。
//
#ifndef HELLOVULKAN_CUBEMAP_H
@@ -13,6 +21,7 @@
#include <Engine/Core/Types.h>
#include <Engine/Vulkan/MemoryAllocator.h>
#include <glm/glm.hpp>
#include <vulkan/vulkan.hpp>
namespace Engine {
@@ -21,36 +30,55 @@ namespace Engine {
class Cubemap {
public:
// 从等距柱状 HDR 生成(CPU 重采样到 6 面,faceSize 为每面边长)
// 从等距柱状 HDR 生成(CPU 重采样 6 面 + irradiance 卷积)
static Scope<Cubemap> CreateFromHdrEquirect(Device& device,
MemoryAllocator& allocator,
const std::string& path,
u32 faceSize);
// 中性兜底(无 HDR 资产时):env/irradiance 均为常数色。
// 保证引用侧描述符恒有效(validation),渲染退化为常数环境
static Scope<Cubemap> CreateNeutral(Device& device,
MemoryAllocator& allocator,
const glm::vec3& rgb);
~Cubemap();
Cubemap(const Cubemap&) = delete;
Cubemap& operator=(const Cubemap&) = delete;
vk::Image Get() const { return m_Image; }
vk::ImageView GetView() const { return m_View; }
// ---- 环境 cube(Skybox 背景)----
vk::Image Get() const { return m_EnvImage; }
vk::ImageView GetView() const { return m_EnvView; }
// ---- 漫反射辐照度 cube(PBR ambient)----
vk::ImageView GetIrradianceView() const { return m_IrrView; }
vk::Sampler GetSampler() const { return m_Sampler; }
u32 GetFaceSize() const { return m_FaceSize; }
private:
Cubemap(Device& device, MemoryAllocator& allocator,
vk::Image image, vk::ImageView view, vk::Sampler sampler,
MemoryAllocator::Allocation allocation, u32 faceSize)
vk::Image envImage, vk::ImageView envView,
vk::Image irrImage, vk::ImageView irrView, vk::Sampler sampler,
MemoryAllocator::Allocation envAlloc,
MemoryAllocator::Allocation irrAlloc, u32 faceSize)
: m_Device(device), m_Allocator(allocator),
m_Image(image), m_View(view), m_Sampler(sampler),
m_Allocation(allocation), m_FaceSize(faceSize) {}
m_EnvImage(envImage), m_EnvView(envView),
m_IrrImage(irrImage), m_IrrView(irrView),
m_Sampler(sampler), m_EnvAllocation(envAlloc),
m_IrrAllocation(irrAlloc), m_FaceSize(faceSize) {}
Device& m_Device;
MemoryAllocator& m_Allocator;
vk::Image m_Image;
vk::ImageView m_View;
vk::Image m_EnvImage;
vk::ImageView m_EnvView;
vk::Image m_IrrImage;
vk::ImageView m_IrrView;
vk::Sampler m_Sampler;
MemoryAllocator::Allocation m_Allocation;
MemoryAllocator::Allocation m_EnvAllocation;
MemoryAllocator::Allocation m_IrrAllocation;
u32 m_FaceSize;
};
@@ -24,6 +24,7 @@ namespace Engine {
class DescriptorManager;
class ShaderManager;
class UniformBuffer;
class Cubemap;
namespace Passes {
@@ -40,8 +41,8 @@ namespace Engine {
// ---- set 布局(shader 契约,改绑定必须同步 shaders/)----
vk::DescriptorSetLayout uboSetLayout; // Set0 = {UBO, ObjectBuffer, PrevObjectBuffer}
vk::DescriptorSetLayout textureSetLayout; // Set1 = {albedo}
vk::DescriptorSetLayout shadowSetLayout; // Set2 = {级联 0/1/2 阴影图}
vk::DescriptorSetLayout textureSetLayout; // Set1 = {材质 4 槽 + 参数 UBO}
vk::DescriptorSetLayout shadowSetLayout; // Set2 = {级联 0/1/2 阴影图, irradiance cube}
vk::PipelineLayout forwardLayout; // 3 sets,无 push(GPUDriven 经 SSBO)
vk::PipelineLayout shadowLayout; // Set0,push = lightVP 64B
@@ -60,6 +61,9 @@ namespace Engine {
void BindShadowMaps(RG::RenderGraph& rg,
const std::array<RG::TextureHandle, kMaxCascades>& maps);
// 环境辐照度(Set2 binding 3):view 全程稳定,绑一次即可
void BindEnvironment(const Cubemap& env);
private:
Device& m_Device;
DescriptorManager& m_Descriptors;