feat: 环境背景管线(IBL 阶段①)——HDR 等距柱状转 cube + Skybox pass

- Cubemap:stbi_loadf 加载 .hdr,CPU 双线性重采样 6 面(GL 规范方向表),
  f32→fp16 转换后整块上传 eCubeCompatible image(R16G16B16A16Sfloat)
- SkyboxPass:HDRColor 的 first writer,承担清屏职责;无环境贴图自动
  退化为纯清屏(Forward 不受影响)
- frag 逐像素 NDC 反投影取世界方向(invVP push,投影变换不可线性插值)
- Forward color 附件 Clear→Load:几何合成在天空之上
- SceneRenderer 产出 skyInvVP = inverse(proj × mat4(mat3(view)))
This commit is contained in:
2026-09-23 18:37:01 +08:00
parent 89bf4256e8
commit 4b5990e6c8
11 changed files with 662 additions and 1 deletions
+177
View File
@@ -0,0 +1,177 @@
//
// Created by atdunbg on 2026/9/23.
//
#include <Engine/Renderer/Passes/SkyboxPass.h>
#include <Engine/Renderer/Cubemap.h>
#include <Engine/Vulkan/Device.h>
#include <Engine/Vulkan/DescriptorManager.h>
#include <Engine/Vulkan/ShaderManager.h>
#include <Engine/Core/Log.h>
#include <glm/glm.hpp>
#include <tracy/Tracy.hpp>
#include <array>
namespace Engine::Passes
{
SkyboxPass::SkyboxPass(Device& device, ShaderManager& shaders,
DescriptorManager& descriptors, FrameSceneData& frame,
RG::TextureHandle hdrColor, const Cubemap* env)
: m_Device(device), m_Descriptors(descriptors), m_Frame(frame),
m_HDRColor(hdrColor), m_Env(env)
{
// Set0 = {samplerCube}(fragment 采样)
std::vector<DescriptorBinding> bindings = {
{0, vk::DescriptorType::eCombinedImageSampler,
vk::ShaderStageFlagBits::eFragment},
};
m_SetLayout = descriptors.CreateLayout(bindings);
// push = invVP 64B(skybox.vert/.frag 声明一致,VUID-08878)
m_Layout = descriptors.CreatePipelineLayout(
std::vector<vk::DescriptorSetLayout>{m_SetLayout},
sizeof(glm::mat4));
const std::string shaderDir = "shaders/";
auto vert = shaders.CreateFromFile(
shaderDir + "skybox.vert.spv",
vk::ShaderStageFlagBits::eVertex,
std::vector<vk::DescriptorSetLayout>{m_SetLayout},
sizeof(glm::mat4));
auto frag = shaders.CreateFromFile(
shaderDir + "skybox.frag.spv",
vk::ShaderStageFlagBits::eFragment,
std::vector<vk::DescriptorSetLayout>{m_SetLayout},
sizeof(glm::mat4));
m_Vert = vert.Handle;
m_Frag = frag.Handle;
if (!m_Vert || !m_Frag) {
ENGINE_FATAL("SkyboxPass: failed to create shader objects");
}
// per-slot set:cube view 全程稳定,构造期绑定一次即可
for (u32 slot = 0; slot < kMaxFramesInFlight; ++slot) {
m_Sets[slot] = descriptors.AllocateDescriptorSet(m_SetLayout);
if (m_Env) {
descriptors.UpdateTexture(m_Sets[slot], 0,
m_Env->GetSampler(),
m_Env->GetView());
}
}
}
SkyboxPass::~SkyboxPass()
{
const auto& vkDevice = m_Device.GetDevice();
if (m_Vert) vkDevice.destroyShaderEXT(m_Vert);
if (m_Frag) vkDevice.destroyShaderEXT(m_Frag);
vkDevice.destroyPipelineLayout(m_Layout);
vkDevice.destroyDescriptorSetLayout(m_SetLayout);
}
void SkyboxPass::Register(RG::RenderGraph& rg)
{
rg.AddPass("Skybox", [this](RG::RenderGraphContext& ctx) {
ZoneScopedN("Skybox");
const vk::CommandBuffer cmd = ctx.cmd;
vk::ClearValue clear;
clear.color = vk::ClearColorValue(
std::array<float, 4>{0.1f, 0.1f, 0.1f, 1.0f});
vk::RenderingAttachmentInfo color;
color.setImageView(ctx.View(m_HDRColor))
.setImageLayout(vk::ImageLayout::eColorAttachmentOptimal)
.setLoadOp(vk::AttachmentLoadOp::eClear) // 清屏职责在本 pass
.setStoreOp(vk::AttachmentStoreOp::eStore)
.setClearValue(clear);
vk::RenderingInfo renderingInfo;
renderingInfo.setRenderArea({{0, 0}, ctx.extent})
.setLayerCount(1)
.setColorAttachments(color); // 无深度附件
cmd.beginRendering(renderingInfo);
if (m_Env) {
// 无环境贴图:只清屏(Forward loadOp=Load 依赖这里先写)
std::array<vk::ShaderStageFlagBits, 2> stages = {
vk::ShaderStageFlagBits::eVertex,
vk::ShaderStageFlagBits::eFragment
};
std::array<vk::ShaderEXT, 2> shaders = {m_Vert, m_Frag};
cmd.bindShadersEXT(stages, shaders);
cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics,
m_Layout, 0,
m_Sets[ctx.frame.frameSlot], {});
cmd.pushConstants<glm::mat4>(
m_Layout,
vk::ShaderStageFlagBits::eVertex |
vk::ShaderStageFlagBits::eFragment,
0, m_Frame.skyInvVP);
// ---- 动态状态 ----
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({}, {});
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));
// 无混合直写(天空不透明)
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);
// ArrayProxyNoTemporaries 拒绝临时量:具名局部变量传入
vk::Bool32 blendEnable = VK_FALSE;
vk::ColorComponentFlags colorWriteMask =
vk::ColorComponentFlagBits::eR |
vk::ColorComponentFlagBits::eG |
vk::ColorComponentFlagBits::eB |
vk::ColorComponentFlagBits::eA;
cmd.setColorBlendEnableEXT(0, blendEnable);
cmd.setColorBlendEquationEXT(0, blendEquation);
cmd.setColorWriteMaskEXT(0, colorWriteMask);
cmd.draw(3, 1, 0, 0);
}
cmd.endRendering();
})
.Write(m_HDRColor, RG::ResourceUse::ColorAttachment);
}
} // namespace Engine::Passes