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:
@@ -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
|
||||
Reference in New Issue
Block a user