Add Deferred rendering

This commit is contained in:
Mysvac 2025-07-01 13:18:48 +08:00
parent 206862167c
commit ec84f9c08d
35 changed files with 2407 additions and 574 deletions

Binary file not shown.

View File

@ -0,0 +1,171 @@
module;
#include <memory>
#include <array>
#include <vector>
export module Descriptor;
import vulkan_hpp;
import Config;
import Device;
import GBuffer;
import GraphicsPipeline;
import UniformBuffer;
import TextureSampler;
import LightUniformBuffer;
export namespace vht {
/**
* @brief 描述符集管理类
* @details
* - 依赖:
* - m_device: 逻辑设备
* - m_graphics_pipeline: 图形管线
* - m_uniform_buffer: Uniform Buffer对象
* - m_texture_sampler: 纹理采样器对象
* - 工作:
* - 创建描述符池和描述符集
* - 可访问成员:
* - pool(): 获取描述符池
* - sets(): 获取描述符集列表
*/
class Descriptor {
std::shared_ptr<vht::Device> m_device;
std::shared_ptr<vht::GBuffer> m_g_buffer;
std::shared_ptr<vht::GraphicsPipeline> m_graphics_pipeline;
std::shared_ptr<vht::UniformBuffer> m_uniform_buffer;
std::shared_ptr<vht::TextureSampler> m_texture_sampler;
std::shared_ptr<vht::LightUniformBuffer> m_light_uniform_buffer;
vk::raii::DescriptorPool m_pool{ nullptr };
std::vector<vk::raii::DescriptorSet> m_sets;
std::vector<vk::raii::DescriptorSet> m_second_sets;
public:
explicit Descriptor(
std::shared_ptr<vht::Device> device,
std::shared_ptr<vht::GBuffer> g_buffer,
std::shared_ptr<vht::GraphicsPipeline> m_graphics_pipeline,
std::shared_ptr<vht::UniformBuffer> m_uniform_buffer,
std::shared_ptr<vht::TextureSampler> m_texture_sampler,
std::shared_ptr<vht::LightUniformBuffer> m_light_uniform_buffer
): m_device(std::move(device)),
m_g_buffer(std::move(g_buffer)),
m_graphics_pipeline(std::move(m_graphics_pipeline)),
m_uniform_buffer(std::move(m_uniform_buffer)),
m_texture_sampler(std::move(m_texture_sampler)),
m_light_uniform_buffer(std::move(m_light_uniform_buffer)) {
init();
}
[[nodiscard]]
const vk::raii::DescriptorPool& pool() const { return m_pool; }
[[nodiscard]]
const std::vector<vk::raii::DescriptorSet>& sets() const { return m_sets; }
[[nodiscard]]
const std::vector<vk::raii::DescriptorSet>& second_sets() const { return m_second_sets; }
void recreate() {
m_sets.clear();
m_second_sets.clear();
create_descriptor_sets();
}
private:
void init() {
create_descriptor_pool();
create_descriptor_sets();
}
// 创建描述符池
void create_descriptor_pool() {
std::array<vk::DescriptorPoolSize, 3> pool_sizes;
pool_sizes[0].type = vk::DescriptorType::eUniformBuffer;
pool_sizes[0].descriptorCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT * 2); // 包括 Light UBO
pool_sizes[1].type = vk::DescriptorType::eCombinedImageSampler;
pool_sizes[1].descriptorCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);
pool_sizes[2].type = vk::DescriptorType::eInputAttachment;
pool_sizes[2].descriptorCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT * 3);
vk::DescriptorPoolCreateInfo poolInfo;
poolInfo.flags = vk::DescriptorPoolCreateFlagBits::eFreeDescriptorSet;
poolInfo.setPoolSizes( pool_sizes );
poolInfo.maxSets = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT * 2);
m_pool = m_device->device().createDescriptorPool(poolInfo);
}
// 创建描述符集
void create_descriptor_sets() {
std::vector<vk::DescriptorSetLayout> layouts(MAX_FRAMES_IN_FLIGHT, *m_graphics_pipeline->descriptor_set_layout());
vk::DescriptorSetAllocateInfo alloc_info;
alloc_info.descriptorPool = m_pool;
alloc_info.setSetLayouts( layouts );
m_sets = m_device->device().allocateDescriptorSets(alloc_info);
for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i) {
vk::DescriptorBufferInfo buffer_info;
buffer_info.buffer = m_uniform_buffer->buffers()[i];
buffer_info.offset = 0;
buffer_info.range = sizeof(UBO);
vk::DescriptorImageInfo image_info;
image_info.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
image_info.imageView = m_texture_sampler->image_view();
image_info.sampler = m_texture_sampler->sampler();
std::array<vk::WriteDescriptorSet, 2> writes;
writes[0].dstSet = m_sets[i];
writes[0].dstBinding = 0;
writes[0].dstArrayElement = 0;
writes[0].descriptorType = vk::DescriptorType::eUniformBuffer;
writes[0].setBufferInfo(buffer_info);
writes[1].dstSet = m_sets[i];
writes[1].dstBinding = 1;
writes[1].dstArrayElement = 0;
writes[1].descriptorType = vk::DescriptorType::eCombinedImageSampler;
writes[1].setImageInfo(image_info);
m_device->device().updateDescriptorSets(writes, nullptr);
}
std::vector<vk::DescriptorSetLayout> second_layouts(MAX_FRAMES_IN_FLIGHT, *m_graphics_pipeline->second_descriptor_set_layout());
vk::DescriptorSetAllocateInfo second_alloc_info;
second_alloc_info.descriptorPool = m_pool;
second_alloc_info.setSetLayouts( second_layouts );
m_second_sets = m_device->device().allocateDescriptorSets(second_alloc_info);
for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i) {
vk::DescriptorBufferInfo light_buffer_info;
light_buffer_info.buffer = m_light_uniform_buffer->buffers()[i];
light_buffer_info.offset = 0;
light_buffer_info.range = sizeof(LightUBO);
std::array<vk::DescriptorImageInfo, 3> input_attachments;
input_attachments[0].imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
input_attachments[0].imageView = m_g_buffer->pos_views();
input_attachments[1].imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
input_attachments[1].imageView = m_g_buffer->color_views();
input_attachments[2].imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
input_attachments[2].imageView = m_g_buffer->normal_depth_views();
std::array<vk::WriteDescriptorSet, 2> writes;
writes[0].dstSet = m_second_sets[i];
writes[0].dstBinding = 0;
writes[0].dstArrayElement = 0;
writes[0].descriptorType = vk::DescriptorType::eUniformBuffer;
writes[0].setBufferInfo(light_buffer_info);
writes[1].dstSet = m_second_sets[i];
writes[1].dstBinding = 1;
writes[1].dstArrayElement = 0;
writes[1].descriptorType = vk::DescriptorType::eInputAttachment;
writes[1].setImageInfo(input_attachments);
m_device->device().updateDescriptorSets(writes, nullptr);
}
}
};
} // namespace vht

Binary file not shown.

View File

@ -0,0 +1,267 @@
module;
#include <memory>
#include <array>
#include <vector>
#include <stdexcept>
export module Drawer;
import vulkan_hpp;
import Config;
import DataLoader;
import Window;
import Device;
import Swapchain;
import RenderPass;
import GraphicsPipeline;
import CommandPool;
import InputAssembly;
import UniformBuffer;
import LightUniformBuffer;
import Descriptor;
export namespace vht {
/**
* @brief 绘制相关
* @details
* - 依赖:
* - m_data_loader: 数据加载器
* - m_window: 窗口与表面
* - m_device: 物理/逻辑设备与队列
* - m_swapchain: 交换链
* - m_render_pass: 渲染通道与帧缓冲
* - m_graphics_pipeline: 图形管线与描述布局
* - m_command_pool: 命令池
* - m_input_assembly: 输入装配(顶点缓冲和索引缓冲)
* - m_uniform_buffer: uniform 缓冲区
* - m_descriptor: 描述符集与池
* - 工作:
* - 创建同步对象(信号量和栅栏)
* - 创建命令缓冲区
* - 绘制函数 draw()
*/
class Drawer {
std::shared_ptr<vht::DataLoader> m_data_loader{ nullptr };
std::shared_ptr<vht::Window> m_window{ nullptr };
std::shared_ptr<vht::Device> m_device{ nullptr };
std::shared_ptr<vht::Swapchain> m_swapchain{ nullptr };
std::shared_ptr<vht::RenderPass> m_render_pass{ nullptr };
std::shared_ptr<vht::GraphicsPipeline> m_graphics_pipeline{ nullptr };
std::shared_ptr<vht::CommandPool> m_command_pool{ nullptr };
std::shared_ptr<vht::InputAssembly> m_input_assembly{ nullptr };
std::shared_ptr<vht::UniformBuffer> m_uniform_buffer{ nullptr };
std::shared_ptr<vht::LightUniformBuffer> m_light_uniform_buffer{ nullptr };
std::shared_ptr<vht::Descriptor> m_descriptor{ nullptr };
std::vector<vk::raii::Semaphore> m_image_available_semaphores;
std::vector<vk::raii::Semaphore> m_render_finished_semaphores;
std::vector<vk::raii::Fence> m_in_flight_fences;
std::vector<vk::raii::CommandBuffer> m_command_buffers;
int m_current_frame = 0;
public:
explicit Drawer(
std::shared_ptr<vht::DataLoader> data_loader,
std::shared_ptr<vht::Window> window,
std::shared_ptr<vht::Device> device,
std::shared_ptr<vht::Swapchain> swapchain,
std::shared_ptr<vht::RenderPass> render_pass,
std::shared_ptr<vht::GraphicsPipeline> graphics_pipeline,
std::shared_ptr<vht::CommandPool> command_pool,
std::shared_ptr<vht::InputAssembly> input_assembly,
std::shared_ptr<vht::UniformBuffer> uniform_buffer,
std::shared_ptr<vht::LightUniformBuffer> light_uniform_buffer,
std::shared_ptr<vht::Descriptor> descriptor
): m_data_loader(std::move(data_loader)),
m_window(std::move(window)),
m_device(std::move(device)),
m_swapchain(std::move(swapchain)),
m_render_pass(std::move(render_pass)),
m_graphics_pipeline(std::move(graphics_pipeline)),
m_command_pool(std::move(command_pool)),
m_input_assembly(std::move(input_assembly)),
m_uniform_buffer(std::move(uniform_buffer)),
m_light_uniform_buffer(std::move(light_uniform_buffer)),
m_descriptor(std::move(descriptor)) {
init();
}
void draw() {
// 等待当前帧的栅栏,即确保上一个帧的绘制完成
if( const auto res = m_device->device().waitForFences( *m_in_flight_fences[m_current_frame], true, UINT64_MAX );
res != vk::Result::eSuccess
) throw std::runtime_error{ "waitForFences in drawFrame was failed" };
// 获取交换链的下一个图像索引
uint32_t image_index;
try{
auto [res, idx] = m_swapchain->swapchain().acquireNextImage(UINT64_MAX, m_image_available_semaphores[m_current_frame]);
image_index = idx;
} catch (const vk::OutOfDateKHRError&){
m_render_pass->recreate();
m_descriptor->recreate();
return;
}
// 重置当前帧的栅栏,延迟到此处等待,防止上方 return 导致死锁
m_device->device().resetFences( *m_in_flight_fences[m_current_frame] );
// 更新 uniform 缓冲区
m_uniform_buffer->update(m_current_frame);
m_light_uniform_buffer->update(m_current_frame, m_uniform_buffer->view_pos());
// 重置当前帧的命令缓冲区,并记录新的命令
m_command_buffers[m_current_frame].reset();
record_command_buffer(m_command_buffers[m_current_frame], image_index);
// 设置绘制命令的提交信息
vk::SubmitInfo submit_info;
submit_info.setWaitSemaphores( *m_image_available_semaphores[m_current_frame] );
std::array<vk::PipelineStageFlags,1> waitStages = { vk::PipelineStageFlagBits::eColorAttachmentOutput };
submit_info.setWaitDstStageMask( waitStages );
submit_info.setCommandBuffers( *m_command_buffers[m_current_frame] );
submit_info.setSignalSemaphores( *m_render_finished_semaphores[m_current_frame] );
// 提交命令缓冲区到图形队列
m_device->graphics_queue().submit(submit_info, m_in_flight_fences[m_current_frame]);
// 设置呈现信息
vk::PresentInfoKHR present_info;
present_info.setWaitSemaphores( *m_render_finished_semaphores[m_current_frame] );
present_info.setSwapchains( *m_swapchain->swapchain() );
present_info.pImageIndices = &image_index;
// 提交呈现之类
try{
if( m_device->present_queue().presentKHR(present_info) == vk::Result::eSuboptimalKHR ) {
m_render_pass->recreate();
m_descriptor->recreate();
}
} catch (const vk::OutOfDateKHRError&){
m_render_pass->recreate();
m_descriptor->recreate();
}
// 检查窗口是否被调整大小
if( m_window->framebuffer_resized() ){
m_render_pass->recreate();
m_descriptor->recreate();
}
// 更新飞行中的帧索引
m_current_frame = (m_current_frame + 1) % MAX_FRAMES_IN_FLIGHT;
}
private:
void init() {
create_sync_object();
create_command_buffers();
}
// 创建命令缓冲区
void create_command_buffers() {
vk::CommandBufferAllocateInfo alloc_info;
alloc_info.commandPool = m_command_pool->pool();
alloc_info.level = vk::CommandBufferLevel::ePrimary;
alloc_info.commandBufferCount = MAX_FRAMES_IN_FLIGHT;
m_command_buffers = m_device->device().allocateCommandBuffers(alloc_info);
}
// 创建同步对象(信号量和栅栏)
void create_sync_object() {
vk::SemaphoreCreateInfo semaphore_create_info;
vk::FenceCreateInfo fence_create_info{ vk::FenceCreateFlagBits::eSignaled };
m_image_available_semaphores.reserve( MAX_FRAMES_IN_FLIGHT );
m_render_finished_semaphores.reserve( MAX_FRAMES_IN_FLIGHT );
m_in_flight_fences.reserve( MAX_FRAMES_IN_FLIGHT );
for(size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i){
m_image_available_semaphores.emplace_back( m_device->device(), semaphore_create_info );
m_render_finished_semaphores.emplace_back( m_device->device(), semaphore_create_info );
m_in_flight_fences.emplace_back( m_device->device() , fence_create_info );
}
}
// 记录命令缓冲区
void record_command_buffer(const vk::raii::CommandBuffer& command_buffer, const uint32_t image_index) const {
command_buffer.begin( vk::CommandBufferBeginInfo{} );
vk::RenderPassBeginInfo render_pass_begin_info;
render_pass_begin_info.renderPass = m_render_pass->render_pass();
render_pass_begin_info.framebuffer = m_render_pass->framebuffers()[image_index];
render_pass_begin_info.renderArea.offset = vk::Offset2D{0, 0};
render_pass_begin_info.renderArea.extent = m_swapchain->extent();
std::array<vk::ClearValue, 5> clear_values; // 增加 3 个子元素
clear_values[0] = vk::ClearColorValue{ 0.0f, 0.0f, 0.0f, 1.0f };
clear_values[1] = vk::ClearDepthStencilValue{ 1.0f ,0 };
clear_values[2] = clear_values[0];
clear_values[3] = clear_values[0];
clear_values[4] = clear_values[0];
render_pass_begin_info.setClearValues( clear_values );
command_buffer.beginRenderPass( render_pass_begin_info, vk::SubpassContents::eInline);
command_buffer.bindPipeline( vk::PipelineBindPoint::eGraphics, m_graphics_pipeline->pipeline() );
const vk::Viewport viewport(
0.0f, 0.0f, // x, y
static_cast<float>(m_swapchain->extent().width), // width
static_cast<float>(m_swapchain->extent().height), // height
0.0f, 1.0f // minDepth maxDepth
);
command_buffer.setViewport(0, viewport);
const vk::Rect2D scissor(
vk::Offset2D{0, 0}, // offset
m_swapchain->extent() // extent
);
command_buffer.setScissor(0, scissor);
const std::vector<vk::Buffer> vertex_buffers = { *m_input_assembly->vertex_buffer(), * m_input_assembly->instance_buffer() };
std::array<vk::DeviceSize,2> offsets = { 0, 0 };
command_buffer.bindVertexBuffers( 0, vertex_buffers, offsets );
command_buffer.bindIndexBuffer( m_input_assembly->index_buffer(), 0, vk::IndexType::eUint32 );
command_buffer.bindDescriptorSets(
vk::PipelineBindPoint::eGraphics,
m_graphics_pipeline->pipeline_layout(),
0,
*m_descriptor->sets()[m_current_frame],
nullptr
);
int32_t enable_texture = 1;
command_buffer.pushConstants<int32_t>(
m_graphics_pipeline->pipeline_layout(),
vk::ShaderStageFlagBits::eFragment,
0,
enable_texture
);
command_buffer.drawIndexed(m_data_loader->index_counts()[0], 1, m_data_loader->index_offsets()[0], 0, 0);
enable_texture = 0;
command_buffer.pushConstants<int32_t>(
m_graphics_pipeline->pipeline_layout(),
vk::ShaderStageFlagBits::eFragment,
0,
enable_texture
);
command_buffer.drawIndexed(m_data_loader->index_counts()[1], 1, m_data_loader->index_offsets()[1], 0, 1);
// ↓ 第二个管线的命令
// --- 切换到第二个子通道 ---
command_buffer.nextSubpass(vk::SubpassContents::eInline);
// --- 绑定第二个管线 ---
command_buffer.bindPipeline( vk::PipelineBindPoint::eGraphics, m_graphics_pipeline->second_pipeline() );
// 视口与裁剪共用上方的设置,此处无需再次设置
// --- 绑定描述符集合 ---
command_buffer.bindDescriptorSets(
vk::PipelineBindPoint::eGraphics,
m_graphics_pipeline->second_pipeline_layout(),
0,
*m_descriptor->second_sets()[m_current_frame],
nullptr
);
// 绘制 6 个点,对应着色器中的两个三角形
command_buffer.draw(6, 1, 0, 0);
command_buffer.endRenderPass();
command_buffer.end();
}
};
}

Binary file not shown.

View File

@ -0,0 +1,285 @@
module;
#include <memory>
#include <vector>
export module GraphicsPipeline;
import vulkan_hpp;
import DataLoader;
import Utility;
import Device;
import RenderPass;
export namespace vht {
/**
* @brief 图形管线相关
* @details
* - 依赖:
* - m_device: 逻辑设备与队列
* - m_render_pass: 渲染通道
* - 工作:
* - 创建描述符集布局
* - 创建图形管线布局和图形管线
* - 可访问成员:
* - descriptor_set_layout(): 描述符集布局
* - pipeline_layout(): 管线布局
* - pipeline(): 图形管线
*/
class GraphicsPipeline {
std::shared_ptr<vht::Device> m_device;
std::shared_ptr<vht::RenderPass> m_render_pass;
vk::raii::DescriptorSetLayout m_descriptor_set_layout{ nullptr };
vk::raii::PipelineLayout m_pipeline_layout{ nullptr };
vk::raii::Pipeline m_pipeline{ nullptr };
vk::raii::DescriptorSetLayout m_second_descriptor_set_layout{ nullptr };
vk::raii::PipelineLayout m_second_pipeline_layout{ nullptr };
vk::raii::Pipeline m_second_pipeline{ nullptr };
public:
explicit GraphicsPipeline(std::shared_ptr<vht::Device> device, std::shared_ptr<vht::RenderPass> render_pass)
: m_device(std::move(device)),
m_render_pass(std::move(render_pass)) {
init();
}
[[nodiscard]]
const vk::raii::DescriptorSetLayout& descriptor_set_layout() const { return m_descriptor_set_layout; }
[[nodiscard]]
const vk::raii::PipelineLayout& pipeline_layout() const { return m_pipeline_layout; }
[[nodiscard]]
const vk::raii::Pipeline& pipeline() const { return m_pipeline; }
[[nodiscard]]
const vk::raii::DescriptorSetLayout& second_descriptor_set_layout() const { return m_second_descriptor_set_layout; }
[[nodiscard]]
const vk::raii::PipelineLayout& second_pipeline_layout() const { return m_second_pipeline_layout; }
[[nodiscard]]
const vk::raii::Pipeline& second_pipeline() const { return m_second_pipeline; }
private:
void init() {
create_descriptor_set_layout();
create_graphics_pipeline();
create_second_descriptor_set_layout();
create_second_graphics_pipeline();
}
// 创建描述符集布局
void create_descriptor_set_layout() {
vk::DescriptorSetLayoutBinding ubo_layout_binging;
ubo_layout_binging.binding = 0;
ubo_layout_binging.descriptorType = vk::DescriptorType::eUniformBuffer;
ubo_layout_binging.descriptorCount = 1;
ubo_layout_binging.stageFlags = vk::ShaderStageFlagBits::eVertex;
vk::DescriptorSetLayoutBinding sampler_layout_binding;
sampler_layout_binding.binding = 1;
sampler_layout_binding.descriptorType = vk::DescriptorType::eCombinedImageSampler;
sampler_layout_binding.descriptorCount = 1;
sampler_layout_binding.stageFlags = vk::ShaderStageFlagBits::eFragment;
const auto bindings = { ubo_layout_binging, sampler_layout_binding };
vk::DescriptorSetLayoutCreateInfo layoutInfo;
layoutInfo.setBindings( bindings );
m_descriptor_set_layout = m_device->device().createDescriptorSetLayout( layoutInfo );
}
void create_second_descriptor_set_layout() {
vk::DescriptorSetLayoutBinding light_ubo_layout_binging;
light_ubo_layout_binging.binding = 0;
light_ubo_layout_binging.descriptorType = vk::DescriptorType::eUniformBuffer;
light_ubo_layout_binging.descriptorCount = 1;
light_ubo_layout_binging.stageFlags = vk::ShaderStageFlagBits::eFragment;
vk::DescriptorSetLayoutBinding input_layout_binging; // 输入附件绑定
input_layout_binging.binding = 1;
input_layout_binging.descriptorType = vk::DescriptorType::eInputAttachment;
input_layout_binging.descriptorCount = 3; // 有三个输入附件
input_layout_binging.stageFlags = vk::ShaderStageFlagBits::eFragment;
const auto bindings = { light_ubo_layout_binging, input_layout_binging };
vk::DescriptorSetLayoutCreateInfo layoutInfo;
layoutInfo.setBindings( bindings );
m_second_descriptor_set_layout = m_device->device().createDescriptorSetLayout( layoutInfo );
}
// 创建图形管线
void create_graphics_pipeline() {
const auto vertex_shader_code = vht::read_shader("shaders/vert.spv");
const auto fragment_shader_code = vht::read_shader("shaders/frag.spv");
const auto vertex_shader_module = vht::create_shader_module(m_device->device(), vertex_shader_code);
const auto fragment_shader_module = vht::create_shader_module(m_device->device(), fragment_shader_code);
vk::PipelineShaderStageCreateInfo vertex_shader_create_info;
vertex_shader_create_info.stage = vk::ShaderStageFlagBits::eVertex;
vertex_shader_create_info.module = vertex_shader_module;
vertex_shader_create_info.pName = "main";
vk::PipelineShaderStageCreateInfo fragment_shader_create_info;
fragment_shader_create_info.stage = vk::ShaderStageFlagBits::eFragment;
fragment_shader_create_info.module = fragment_shader_module;
fragment_shader_create_info.pName = "main";
const auto shader_stages = { vertex_shader_create_info, fragment_shader_create_info };
const auto dynamic_states = { vk::DynamicState::eViewport, vk::DynamicState::eScissor };
vk::PipelineDynamicStateCreateInfo dynamic_state;
dynamic_state.setDynamicStates(dynamic_states);
auto binding_description = vht::Vertex::get_binding_description();
auto attribute_description = vht::Vertex::get_attribute_description();
auto instance_binding_description = vht::InstanceData::get_binding_description();
auto instance_attribute_description = vht::InstanceData::get_attribute_description();
auto binding_descriptions = { binding_description, instance_binding_description };
std::vector<vk::VertexInputAttributeDescription> attribute_descriptions;
attribute_descriptions.insert(attribute_descriptions.end(), attribute_description.begin(), attribute_description.end());
attribute_descriptions.insert(attribute_descriptions.end(), instance_attribute_description.begin(), instance_attribute_description.end());
vk::PipelineVertexInputStateCreateInfo vertex_input;
vertex_input.setVertexBindingDescriptions(binding_descriptions);
vertex_input.setVertexAttributeDescriptions(attribute_descriptions);
vk::PipelineInputAssemblyStateCreateInfo input_assembly;
input_assembly.topology = vk::PrimitiveTopology::eTriangleList;
vk::PipelineViewportStateCreateInfo viewport_state;
viewport_state.viewportCount = 1;
viewport_state.scissorCount = 1;
vk::PipelineDepthStencilStateCreateInfo depth_stencil;
depth_stencil.depthTestEnable = true;
depth_stencil.depthWriteEnable = true;
depth_stencil.depthCompareOp = vk::CompareOp::eLess;
depth_stencil.depthBoundsTestEnable = false; // Optional
depth_stencil.stencilTestEnable = false; // Optional
vk::PipelineRasterizationStateCreateInfo rasterizer;
rasterizer.depthClampEnable = false;
rasterizer.rasterizerDiscardEnable = false;
rasterizer.polygonMode = vk::PolygonMode::eFill;
rasterizer.lineWidth = 1.0f;
rasterizer.cullMode = vk::CullModeFlagBits::eBack;
rasterizer.frontFace = vk::FrontFace::eCounterClockwise;
rasterizer.depthBiasEnable = false;
vk::PipelineMultisampleStateCreateInfo multisampling;
multisampling.rasterizationSamples = vk::SampleCountFlagBits::e1;
multisampling.sampleShadingEnable = false; // default
std::array<vk::PipelineColorBlendAttachmentState, 3> color_blend_attachments;
for (auto& att : color_blend_attachments) {
att.blendEnable = false;
att.colorWriteMask = vk::FlagTraits<vk::ColorComponentFlagBits>::allFlags;
}
vk::PipelineColorBlendStateCreateInfo color_blend;
color_blend.logicOpEnable = false;
color_blend.logicOp = vk::LogicOp::eCopy;
color_blend.setAttachments( color_blend_attachments );
vk::PushConstantRange pushConstantRange;
pushConstantRange.stageFlags = vk::ShaderStageFlagBits::eFragment;
pushConstantRange.offset = 0;
pushConstantRange.size = sizeof(int32_t);
vk::PipelineLayoutCreateInfo layout_create_info;
layout_create_info.setPushConstantRanges( pushConstantRange );
layout_create_info.setSetLayouts( *m_descriptor_set_layout );
m_pipeline_layout = m_device->device().createPipelineLayout( layout_create_info );
vk::GraphicsPipelineCreateInfo create_info;
create_info.layout = m_pipeline_layout;
create_info.setStages( shader_stages );
create_info.pVertexInputState = &vertex_input;
create_info.pInputAssemblyState = &input_assembly;
create_info.pDynamicState = &dynamic_state;
create_info.pViewportState = &viewport_state;
create_info.pDepthStencilState = &depth_stencil;
create_info.pRasterizationState = &rasterizer;
create_info.pMultisampleState = &multisampling;
create_info.pColorBlendState = &color_blend;
create_info.renderPass = m_render_pass->render_pass();
create_info.subpass = 0;
m_pipeline = m_device->device().createGraphicsPipeline( nullptr, create_info );
}
void create_second_graphics_pipeline() {
// 修改着色器名
const auto vertex_shader_code = vht::read_shader("shaders/second_vert.spv");
const auto fragment_shader_code = vht::read_shader("shaders/second_frag.spv");
const auto vertex_shader_module = vht::create_shader_module(m_device->device(), vertex_shader_code);
const auto fragment_shader_module = vht::create_shader_module(m_device->device(), fragment_shader_code);
vk::PipelineShaderStageCreateInfo vertex_shader_create_info;
vertex_shader_create_info.stage = vk::ShaderStageFlagBits::eVertex;
vertex_shader_create_info.module = vertex_shader_module;
vertex_shader_create_info.pName = "main";
vk::PipelineShaderStageCreateInfo fragment_shader_create_info;
fragment_shader_create_info.stage = vk::ShaderStageFlagBits::eFragment;
fragment_shader_create_info.module = fragment_shader_module;
fragment_shader_create_info.pName = "main";
const auto shader_stages = { vertex_shader_create_info, fragment_shader_create_info };
const auto dynamic_states = { vk::DynamicState::eViewport, vk::DynamicState::eScissor };
vk::PipelineDynamicStateCreateInfo dynamic_state;
dynamic_state.setDynamicStates(dynamic_states);
// 不需要顶点输入数据
vk::PipelineVertexInputStateCreateInfo vertex_input;
vk::PipelineInputAssemblyStateCreateInfo input_assembly;
input_assembly.topology = vk::PrimitiveTopology::eTriangleList;
vk::PipelineViewportStateCreateInfo viewport_state;
viewport_state.viewportCount = 1;
viewport_state.scissorCount = 1;
// 不需要深度测试
// vk::PipelineDepthStencilStateCreateInfo depth_stencil;
vk::PipelineRasterizationStateCreateInfo rasterizer;
rasterizer.depthClampEnable = false;
rasterizer.rasterizerDiscardEnable = false;
rasterizer.polygonMode = vk::PolygonMode::eFill;
rasterizer.lineWidth = 1.0f;
rasterizer.cullMode = vk::CullModeFlagBits::eBack;
rasterizer.frontFace = vk::FrontFace::eCounterClockwise;
rasterizer.depthBiasEnable = false;
vk::PipelineMultisampleStateCreateInfo multisampling;
multisampling.rasterizationSamples = vk::SampleCountFlagBits::e1;
multisampling.sampleShadingEnable = false; // default
vk::PipelineColorBlendAttachmentState color_blend_attachment;
color_blend_attachment.blendEnable = false; // default
color_blend_attachment.colorWriteMask = vk::FlagTraits<vk::ColorComponentFlagBits>::allFlags;
vk::PipelineColorBlendStateCreateInfo color_blend;
color_blend.logicOpEnable = false;
color_blend.logicOp = vk::LogicOp::eCopy;
color_blend.setAttachments( color_blend_attachment );
vk::PipelineLayoutCreateInfo layout_create_info;
layout_create_info.setSetLayouts( *m_second_descriptor_set_layout );
m_second_pipeline_layout = m_device->device().createPipelineLayout( layout_create_info );
vk::GraphicsPipelineCreateInfo create_info;
create_info.layout = m_second_pipeline_layout;
create_info.setStages( shader_stages );
create_info.pVertexInputState = &vertex_input;
create_info.pInputAssemblyState = &input_assembly;
create_info.pDynamicState = &dynamic_state;
create_info.pViewportState = &viewport_state;
create_info.pRasterizationState = &rasterizer;
create_info.pMultisampleState = &multisampling;
create_info.pColorBlendState = &color_blend;
create_info.renderPass = m_render_pass->render_pass();
create_info.subpass = 1; // 绑定第二个子通道
m_second_pipeline = m_device->device().createGraphicsPipeline( nullptr, create_info );
}
};
}

Binary file not shown.

View File

@ -0,0 +1,244 @@
module;
#include <array>
#include <vector>
#include <memory>
#include <GLFW/glfw3.h>
export module RenderPass;
import vulkan_hpp;
import Window;
import Device;
import Swapchain;
import DepthImage;
import GBuffer;
export namespace vht {
/**
* @brief 渲染通道相关
* @details
* - 依赖:
* - m_window: 窗口
* - m_device: 逻辑设备与队列
* - m_swapchain: 交换链
* - m_depth_image: 深度图像
* - 工作:
* - 创建渲染通道
* - 创建帧缓冲区
* - 支持交换链重建
* - 可访问成员:
* - render_pass(): 渲染通道
* - framebuffers(): 帧缓冲区列表
*/
class RenderPass {
std::shared_ptr<vht::Window> m_window{ nullptr };
std::shared_ptr<vht::Device> m_device{ nullptr };
std::shared_ptr<vht::Swapchain> m_swapchain{ nullptr };
std::shared_ptr<vht::DepthImage> m_depth_image{ nullptr };
std::shared_ptr<vht::GBuffer> m_g_buffer{ nullptr };
vk::raii::RenderPass m_render_pass{ nullptr };
std::vector<vk::raii::Framebuffer> m_framebuffers;
public:
explicit RenderPass(
std::shared_ptr<vht::Window> window,
std::shared_ptr<vht::Device> device,
std::shared_ptr<vht::Swapchain> swapchain,
std::shared_ptr<vht::DepthImage> depth_image,
std::shared_ptr<vht::GBuffer> g_buffer
): m_window(std::move(window)),
m_device(std::move(device)),
m_swapchain(std::move(swapchain)),
m_depth_image(std::move(depth_image)),
m_g_buffer(std::move(g_buffer)) {
init();
}
/**
* @brief 重建交换链与帧缓冲区
* @details
* 在窗口大小改变时调用,重新创建交换链和帧缓冲区。
* 注意 m_swapchain 的 recreate 仅重置交换链和图像视图,不重置帧缓冲区。
* 此函数调用了它,并额外重置了帧缓冲区。
*/
void recreate() {
int width = 0, height = 0;
glfwGetFramebufferSize(m_window->ptr(), &width, &height);
while (width == 0 || height == 0) {
glfwGetFramebufferSize(m_window->ptr(), &width, &height);
glfwWaitEvents();
}
m_device->device().waitIdle();
m_framebuffers.clear();
m_swapchain->recreate();
m_depth_image->recreate();
m_g_buffer->recreate();
create_framebuffers();
m_window->reset_framebuffer_resized();
}
[[nodiscard]]
const vk::raii::RenderPass& render_pass() const { return m_render_pass; }
[[nodiscard]]
const std::vector<vk::raii::Framebuffer>& framebuffers() const { return m_framebuffers; }
private:
void init() {
create_render_pass();
create_framebuffers();
}
// 创建渲染通道
void create_render_pass() {
vk::AttachmentDescription color_attachment;
color_attachment.format = m_swapchain->format();
color_attachment.samples = vk::SampleCountFlagBits::e1;
color_attachment.loadOp = vk::AttachmentLoadOp::eClear;
color_attachment.storeOp = vk::AttachmentStoreOp::eStore;
color_attachment.stencilLoadOp = vk::AttachmentLoadOp::eDontCare;
color_attachment.stencilStoreOp = vk::AttachmentStoreOp::eDontCare;
color_attachment.initialLayout = vk::ImageLayout::eUndefined;
color_attachment.finalLayout = vk::ImageLayout::ePresentSrcKHR;
vk::AttachmentReference color_attachment_ref;
color_attachment_ref.attachment = 0;
color_attachment_ref.layout = vk::ImageLayout::eColorAttachmentOptimal;
vk::AttachmentDescription depth_attachment;
depth_attachment.format = m_depth_image->format();
depth_attachment.samples = vk::SampleCountFlagBits::e1;
depth_attachment.loadOp = vk::AttachmentLoadOp::eClear;
depth_attachment.storeOp = vk::AttachmentStoreOp::eDontCare;
depth_attachment.stencilLoadOp = vk::AttachmentLoadOp::eDontCare;
depth_attachment.stencilStoreOp = vk::AttachmentStoreOp::eDontCare;
depth_attachment.initialLayout = vk::ImageLayout::eUndefined;
depth_attachment.finalLayout = vk::ImageLayout::eDepthStencilAttachmentOptimal;
vk::AttachmentReference depth_attachment_ref;
depth_attachment_ref.attachment = 1;
depth_attachment_ref.layout = vk::ImageLayout::eDepthStencilAttachmentOptimal;
vk::AttachmentDescription g_pos_attachment;
g_pos_attachment.format = m_g_buffer->pos_format();
g_pos_attachment.samples = vk::SampleCountFlagBits::e1;
g_pos_attachment.loadOp = vk::AttachmentLoadOp::eClear;
g_pos_attachment.storeOp = vk::AttachmentStoreOp::eDontCare; // 渲染通道内部使用,不需要 Store
g_pos_attachment.stencilLoadOp = vk::AttachmentLoadOp::eDontCare;
g_pos_attachment.stencilStoreOp = vk::AttachmentStoreOp::eDontCare;
g_pos_attachment.initialLayout = vk::ImageLayout::eUndefined;
// 最终布局设为最后一个子通道使用时的布局,减少转换开销
g_pos_attachment.finalLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
vk::AttachmentDescription g_color_attachment;
g_color_attachment.format = m_g_buffer->color_format();
g_color_attachment.samples = vk::SampleCountFlagBits::e1;
g_color_attachment.loadOp = vk::AttachmentLoadOp::eClear;
g_color_attachment.storeOp = vk::AttachmentStoreOp::eDontCare;
g_color_attachment.stencilLoadOp = vk::AttachmentLoadOp::eDontCare;
g_color_attachment.stencilStoreOp = vk::AttachmentStoreOp::eDontCare;
g_color_attachment.initialLayout = vk::ImageLayout::eUndefined;
g_color_attachment.finalLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
vk::AttachmentDescription g_normal_depth_attachment;
g_normal_depth_attachment.format = m_g_buffer->normal_depth_format();
g_normal_depth_attachment.samples = vk::SampleCountFlagBits::e1;
g_normal_depth_attachment.loadOp = vk::AttachmentLoadOp::eClear;
g_normal_depth_attachment.storeOp = vk::AttachmentStoreOp::eDontCare;
g_normal_depth_attachment.stencilLoadOp = vk::AttachmentLoadOp::eDontCare;
g_normal_depth_attachment.stencilStoreOp = vk::AttachmentStoreOp::eDontCare;
g_normal_depth_attachment.initialLayout = vk::ImageLayout::eUndefined;
g_normal_depth_attachment.finalLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
// 用于第一个子通道的附件引用
vk::AttachmentReference g_pos_out_ref;
g_pos_out_ref.attachment = 2; // 后续附件绑定到帧缓冲的实际索引
g_pos_out_ref.layout = vk::ImageLayout::eColorAttachmentOptimal;
vk::AttachmentReference g_color_out_ref;
g_color_out_ref.attachment = 3;
g_color_out_ref.layout = vk::ImageLayout::eColorAttachmentOptimal;
vk::AttachmentReference g_normal_depth_out_ref;
g_normal_depth_out_ref.attachment = 4;
g_normal_depth_out_ref.layout = vk::ImageLayout::eColorAttachmentOptimal;
// 用于第二个子通道的附件引用
vk::AttachmentReference g_pos_input_ref;
g_pos_input_ref.attachment = 2;
g_pos_input_ref.layout = vk::ImageLayout::eShaderReadOnlyOptimal;
vk::AttachmentReference g_color_input_ref;
g_color_input_ref.attachment = 3;
g_color_input_ref.layout = vk::ImageLayout::eShaderReadOnlyOptimal;
vk::AttachmentReference g_normal_depth_input_ref;
g_normal_depth_input_ref.attachment = 4;
g_normal_depth_input_ref.layout = vk::ImageLayout::eShaderReadOnlyOptimal;
std::array<vk::SubpassDescription,2> subpasses;
subpasses[0].pipelineBindPoint = vk::PipelineBindPoint::eGraphics;
const auto first_attachments = { g_pos_out_ref, g_color_out_ref, g_normal_depth_out_ref };
subpasses[0].setColorAttachments( first_attachments );
subpasses[0].setPDepthStencilAttachment( &depth_attachment_ref );
subpasses[1].pipelineBindPoint = vk::PipelineBindPoint::eGraphics;
const auto second_attachments = { g_pos_input_ref, g_color_input_ref, g_normal_depth_input_ref };
subpasses[1].setInputAttachments( second_attachments );
subpasses[1].setColorAttachments( color_attachment_ref );
std::array<vk::SubpassDependency,2> dependencies;
dependencies[0].srcSubpass = vk::SubpassExternal;
dependencies[0].srcStageMask = vk::PipelineStageFlagBits::eFragmentShader;
dependencies[0].srcAccessMask = {};
dependencies[0].dstSubpass = 0;
dependencies[0].dstStageMask = vk::PipelineStageFlagBits::eEarlyFragmentTests;
dependencies[0].dstAccessMask = vk::AccessFlagBits::eDepthStencilAttachmentWrite;
dependencies[1].srcSubpass = 0;
dependencies[1].srcStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput;
dependencies[1].srcAccessMask = vk::AccessFlagBits::eColorAttachmentWrite;
dependencies[1].dstSubpass = 1;
dependencies[1].dstStageMask = vk::PipelineStageFlagBits::eFragmentShader;
dependencies[1].dstAccessMask = vk::AccessFlagBits::eInputAttachmentRead;
const auto attachments = {
color_attachment,
depth_attachment,
g_pos_attachment,
g_color_attachment,
g_normal_depth_attachment
};
vk::RenderPassCreateInfo create_info;
create_info.setAttachments( attachments );
create_info.setSubpasses( subpasses );
create_info.setDependencies( dependencies );
m_render_pass = m_device->device().createRenderPass( create_info );
}
// 创建帧缓冲区
void create_framebuffers() {
m_framebuffers.clear();
m_framebuffers.reserve( m_swapchain->size() );
vk::FramebufferCreateInfo create_info;
create_info.renderPass = m_render_pass;
create_info.width = m_swapchain->extent().width;
create_info.height = m_swapchain->extent().height;
create_info.layers = 1;
for (size_t i = 0; const auto& image_view : m_swapchain->image_views()) {
std::array<vk::ImageView, 5> attachments {
image_view,
m_depth_image->image_view(),
m_g_buffer->pos_views(),
m_g_buffer->color_views(),
m_g_buffer->normal_depth_views()
};
create_info.setAttachments( attachments );
m_framebuffers.emplace_back( m_device->device().createFramebuffer(create_info) );
++i;
}
}
};
} // namespace vht

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,49 @@
cmake_minimum_required(VERSION 3.30)
find_package(Vulkan REQUIRED)
set(SHADER_DIR ${CMAKE_CURRENT_SOURCE_DIR})
set(VERT_SHADER ${SHADER_DIR}/shader.vert)
set(FRAG_SHADER ${SHADER_DIR}/shader.frag)
set(VERT_SECOND ${SHADER_DIR}/second.vert)
set(FRAG_SECOND ${SHADER_DIR}/second.frag)
set(SPIRV_VERT ${SHADER_DIR}/vert.spv)
set(SPIRV_FRAG ${SHADER_DIR}/frag.spv)
set(SPIRV_SECOND_VERT ${SHADER_DIR}/second_vert.spv)
set(SPIRV_SECOND_FRAG ${SHADER_DIR}/second_frag.spv)
add_custom_command(
OUTPUT ${SPIRV_VERT}
COMMAND ${Vulkan_GLSLC_EXECUTABLE} ${VERT_SHADER} -o ${SPIRV_VERT}
COMMENT "Compiling shader.vert to vert.spv"
DEPENDS ${VERT_SHADER}
)
add_custom_command(
OUTPUT ${SPIRV_FRAG}
COMMAND ${Vulkan_GLSLC_EXECUTABLE} ${FRAG_SHADER} -o ${SPIRV_FRAG}
COMMENT "Compiling shader.frag to frag.spv"
DEPENDS ${FRAG_SHADER}
)
add_custom_command(
OUTPUT ${SPIRV_SECOND_VERT}
COMMAND ${Vulkan_GLSLC_EXECUTABLE} ${VERT_SECOND} -o ${SPIRV_SECOND_VERT}
COMMENT "Compiling shader.vert to vert.spv"
DEPENDS ${VERT_SECOND}
)
add_custom_command(
OUTPUT ${SPIRV_SECOND_FRAG}
COMMAND ${Vulkan_GLSLC_EXECUTABLE} ${FRAG_SECOND} -o ${SPIRV_SECOND_FRAG}
COMMENT "Compiling shader.frag to frag.spv"
DEPENDS ${FRAG_SECOND}
)
add_custom_target(CompileShaders ALL
DEPENDS ${SPIRV_VERT} ${SPIRV_FRAG} ${SPIRV_SECOND_VERT} ${SPIRV_SECOND_FRAG}
)

View File

@ -0,0 +1,38 @@
#version 450
layout(std140, binding = 0) uniform LightUBO {
vec3 lightPos;
vec3 lightColor;
vec3 viewPos;
} ubo;
layout(input_attachment_index = 0, binding = 1) uniform subpassInput g_buffer[3];
layout(location = 0) out vec4 outColor;
void main() {
vec3 pos = subpassLoad(g_buffer[0]).xyz; // 片段位置
vec3 color = subpassLoad(g_buffer[1]).rgb; // 片段颜色
vec3 normal = subpassLoad(g_buffer[2]).xyz; // 法线
// 视角方向
vec3 viewDir = normalize(ubo.viewPos - pos);
// 环境光强
vec3 ambient = 0.15 * ubo.lightColor;
// 漫反射
vec3 lightDir = normalize(ubo.lightPos - pos);
float diff = max(dot(normal, lightDir), 0.0);
vec3 diffuse = diff * 0.8 * ubo.lightColor;
// 镜面反射
vec3 reflectDir = reflect(-lightDir, normal);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), 512);
vec3 specular = spec * 0.3 * ubo.lightColor;
// 最终色彩
vec3 result = (ambient + diffuse + specular) * color;
result = min(result, vec3(1.0));
outColor = vec4(result, 1.0);
}

View File

@ -0,0 +1,16 @@
#version 450
// 通过两个三角形,绘制整个屏幕
// 注意顺序
vec2 output_position[6] = vec2[](
vec2(-1.0, -1.0),
vec2(1.0, 1.0),
vec2(1.0, -1.0),
vec2(-1.0, -1.0),
vec2(-1.0, 1.0),
vec2(1.0, 1.0)
);
void main() {
gl_Position =vec4(output_position[gl_VertexIndex], 0.5, 1.0);
}

View File

@ -0,0 +1,29 @@
#version 450
layout(push_constant) uniform PushConstants {
int enableTexture;
} pc;
layout(binding = 1) uniform sampler2D texSampler;
// layout(std140, binding = 2) uniform LightUBO 移除光源内容
layout(location = 0) in vec3 fragPos;
layout(location = 1) in vec3 fragNormal;
layout(location = 2) in vec2 fragTexCoord;
layout(location = 3) in float fragNa; // 无用
layout(location = 4) in vec3 fragKa; // 无用
layout(location = 5) in vec3 fragKd; // 无用
layout(location = 6) in vec3 fragKs; // 无用
layout(location = 0) out vec4 outPosition;
layout(location = 1) out vec4 outColor;
layout(location = 2) out vec4 outNormalDepth;
void main() {
// 根据推送常量决定是否采样纹理
vec3 objectColor = pc.enableTexture == 1 ? texture(texSampler, fragTexCoord).rgb : vec3(0.5, 0.5, 0.5);
outColor = vec4(objectColor, 1.0);
outPosition = vec4(fragPos, 1.0);
outNormalDepth = vec4(fragNormal, 1.0); //第四位 深度信息此处不用
}

View File

@ -0,0 +1,35 @@
#version 450
layout(binding = 0) uniform UniformBufferObject {
mat4 model;
mat4 view;
mat4 proj;
} ubo;
layout(location = 0) in vec3 inPosition;
layout(location = 1) in vec3 inNormal;
layout(location = 2) in vec2 inTexCoord;
layout(location = 3) in float inNa;
layout(location = 4) in vec3 inKa;
layout(location = 5) in vec3 inKd;
layout(location = 6) in vec3 inKs;
layout(location = 0) out vec3 fragPos;
layout(location = 1) out vec3 fragNormal;
layout(location = 2) out vec2 fragTexCoord;
layout(location = 3) out float fragNa;
layout(location = 4) out vec3 fragKa;
layout(location = 5) out vec3 fragKd;
layout(location = 6) out vec3 fragKs;
void main() {
gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 1.0);
// 计算片元对应的世界坐标位置、法线和纹理坐标,并传递给着色器以进行 phong 光照计算
fragPos = (ubo.model * vec4(inPosition, 1.0)).xyz;
fragNormal = mat3(ubo.model) * inNormal;
fragTexCoord = inTexCoord;
fragNa = inNa;
fragKa = inKa;
fragKd = inKd;
fragKs = inKs;
}

BIN
docs/images/0421/defer.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 KiB

View File

@ -803,7 +803,7 @@ vec3 objectColor = pc.enableTexture == 1 ? texture(texSampler, fragTexCoord).rgb
---
**[基础代码](../../codes/04/20_shadowmap/0420_base_code.zip)**
**[初始代码集](../../codes/04/20_shadowmap/0420_base_code.zip)**
**[shadow.vert新增](../../codes/04/20_shadowmap/shaders/shadow.vert)**

729
docs/md/04/21_deferred.md Normal file
View File

@ -0,0 +1,729 @@
---
title: 延迟渲染
comments: true
---
# **延迟渲染**
## **前言**
在绘制每个物体(的片段)时完成对应的光照计算,这种方法称为 **前向渲染\(forward rendering\)** ,也就是我们上一节的做法。
此方法有个问题,片段着色器后存在深度测试,很多片段会被丢弃。光照计算的消耗通常较高,对这些会被丢弃的片段进行光照计算是很浪费性能的。
**延迟渲染\(deferred rendering\)**的做法是进行两次渲染,第一次将片段的基础色彩、法线和坐标等信息渲染(存储)到输出的颜色附件中,第二次渲染直接读取附件内容进行光照计算。
第一次渲染的结果图像们被称为 **G-Buffer\(几何缓冲区\)** ,仅包含通过深度测试的片段的信息,从而减少光照计算的次数。
第一个管线的输出图像将直接作为第二个管线的输入附件(下面会为您介绍),因此我们可以使用单个渲染通道、两个子通道。
## **基础代码**
请下载并阅读下面的基础代码,此基础代码和上一节的基础代码相似,但提供了 G-Buffer
**[点击下载](../../codes/04/21_deferred/0421_base_code.zip)**
G-Buffer 是延迟渲染的核心,它并非实际的缓冲区对象,而是多个图像附件的集合。
第二次渲染不依赖顶点数据,因此我们需要在 G-Buffer 中存储每个片段对应的世界坐标、法线、颜色和材质等信息。
本章为了简化内容,不记录材质信息(你可以自行添加),因此需要三个图像分别存储其他三种数据。
基础代码中已经提供了 `GBuffer.cppm` 模块,请你自行阅读并重点关注以下内容:
- 图像格式
- 图像的 `usage` 字段
- 图像视图的 `AspectFlag` 字段
运行程序并移动摄像机,你将看到以下场景:
![Marry](../../images/0420/Marry.png)
## **输入附件**
Vulkan 渲染通道中的“**输入附件\(Input Attachment\)**“是一种特殊的附件类型,允许在同一个渲染通道的不同子通道之间高效传递图像数据。比如:
- 在第一个子通道中将渲染结果写入附件(如颜色或深度)。
- 在后续子通道中,将该附件作为“输入附件”读取,直接在着色器中访问,无需显式采样。
着色器中可以通过 `input_attachment_index` 关键字指定输入附件的索引,像这样使用:
```glsl
layout(input_attachment_index = 0, set = 0, binding = 0) uniform subpassInput gBuffer;
void main() {
vec4 data = subpassLoad(gBuffer);
// 使用 data 进行后续处理
}
```
注意到它直接使用 `subpassLoad` 读取而没有任何索引,这就是输入附件的特性,它读取的直接就是附件中对应当前片段位置的数据。
我们需要在渲染通道中指定附件的类型是输入附件,但这还不够,后续还需要为其分配描述符集。
## **渲染通道**
我们需要在渲染通道中创建两个子通道,第一个子通道用于渲染 G-Buffer第二个子通道用于光照计算。
然后设置渲染通道的附件,并为两个子通道指定需要的附件和子通道依赖。
### 1. 添加附件描述
基础代码已为渲染通道注入了 GBuffer 的依赖,可以直接修改 `create_render_pass` 函数。
首先创建三个新的附件描述,分别对应、法线和世界坐标:
```cpp
vk::AttachmentDescription g_pos_attachment;
g_pos_attachment.format = m_g_buffer->pos_format();
g_pos_attachment.samples = vk::SampleCountFlagBits::e1;
g_pos_attachment.loadOp = vk::AttachmentLoadOp::eClear;
g_pos_attachment.storeOp = vk::AttachmentStoreOp::eDontCare; // 渲染通道内部使用,不需要 Store
g_pos_attachment.stencilLoadOp = vk::AttachmentLoadOp::eDontCare;
g_pos_attachment.stencilStoreOp = vk::AttachmentStoreOp::eDontCare;
g_pos_attachment.initialLayout = vk::ImageLayout::eUndefined;
// 最终布局设为最后一个子通道使用时的布局,减少转换开销
g_pos_attachment.finalLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
vk::AttachmentDescription g_color_attachment;
g_color_attachment.format = m_g_buffer->color_format();
g_color_attachment.samples = vk::SampleCountFlagBits::e1;
g_color_attachment.loadOp = vk::AttachmentLoadOp::eClear;
g_color_attachment.storeOp = vk::AttachmentStoreOp::eDontCare;
g_color_attachment.stencilLoadOp = vk::AttachmentLoadOp::eDontCare;
g_color_attachment.stencilStoreOp = vk::AttachmentStoreOp::eDontCare;
g_color_attachment.initialLayout = vk::ImageLayout::eUndefined;
g_color_attachment.finalLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
vk::AttachmentDescription g_normal_depth_attachment;
g_normal_depth_attachment.format = m_g_buffer->normal_depth_format();
g_normal_depth_attachment.samples = vk::SampleCountFlagBits::e1;
g_normal_depth_attachment.loadOp = vk::AttachmentLoadOp::eClear;
g_normal_depth_attachment.storeOp = vk::AttachmentStoreOp::eDontCare;
g_normal_depth_attachment.stencilLoadOp = vk::AttachmentLoadOp::eDontCare;
g_normal_depth_attachment.stencilStoreOp = vk::AttachmentStoreOp::eDontCare;
g_normal_depth_attachment.initialLayout = vk::ImageLayout::eUndefined;
g_normal_depth_attachment.finalLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
```
三个附件描述的内容几乎一样,只有格式不同。
因为附件只在渲染通道内部的两个子通道中使用,我们不关心渲染通道外如何,所以 `storeOp` 都设置为 `DontCare`
最终布局同样不影响效果,我们使用 `ShaderReadOnlyOptimal` ,因为后面的第二个子通道会将布局转换成它,设为同样的布局可以减少转换开销。
### 2. 子通道附件引用
现在添加子通道的附件引用,分别对应三个附件:
```cpp
// 用于第一个子通道的附件引用
vk::AttachmentReference g_pos_out_ref;
g_pos_out_ref.attachment = 2; // 后续附件绑定到帧缓冲的实际索引
g_pos_out_ref.layout = vk::ImageLayout::eColorAttachmentOptimal;
vk::AttachmentReference g_color_out_ref;
g_color_out_ref.attachment = 3;
g_color_out_ref.layout = vk::ImageLayout::eColorAttachmentOptimal;
vk::AttachmentReference g_normal_depth_out_ref;
g_normal_depth_out_ref.attachment = 4;
g_normal_depth_out_ref.layout = vk::ImageLayout::eColorAttachmentOptimal;
// 用于第二个子通道的附件引用
vk::AttachmentReference g_pos_input_ref;
g_pos_input_ref.attachment = 2;
g_pos_input_ref.layout = vk::ImageLayout::eShaderReadOnlyOptimal;
vk::AttachmentReference g_color_input_ref;
g_color_input_ref.attachment = 3;
g_color_input_ref.layout = vk::ImageLayout::eShaderReadOnlyOptimal;
vk::AttachmentReference g_normal_depth_input_ref;
g_normal_depth_input_ref.attachment = 4;
g_normal_depth_input_ref.layout = vk::ImageLayout::eShaderReadOnlyOptimal;
```
注意到第一个子通道的附件引用使用 `ColorAttachmentOptimal` 布局,而第二个子通道的附件引用使用 `ShaderReadOnlyOptimal` 布局,且它们实际引用同一组附件。
### 3. 绑定帧缓冲
现在需要把实际的图像资源绑定到帧缓冲区,修改 `create_framebuffers` 函数中的 `attachments`
```cpp
std::array<vk::ImageView, 5> attachments {
image_view,
m_depth_image->image_view(),
m_g_buffer->pos_views(),
m_g_buffer->color_views(),
m_g_buffer->normal_depth_views()
};
```
这里的资源顺序需要严格对应附件引用的 `attachment` 字段。
我们的呈现保证同一时间只有一组绘制命令在执行,因此只需要一组 G-Buffer 资源。
### 4. 创建子通道
删除原先的子通道创建代码,添加两个新的子通道:
```cpp
std::array<vk::SubpassDescription,2> subpasses;
// 第一个子通道 生成 G-Buffer
subpasses[0].pipelineBindPoint = vk::PipelineBindPoint::eGraphics;
const auto first_attachments = { g_pos_out_ref, g_color_out_ref, g_normal_depth_out_ref };
subpasses[0].setColorAttachments( first_attachments );
subpasses[0].setPDepthStencilAttachment( &depth_attachment_ref );
// 第二个子通道 进行光照计算
subpasses[1].pipelineBindPoint = vk::PipelineBindPoint::eGraphics;
const auto second_attachments = { g_pos_input_ref, g_color_input_ref, g_normal_depth_input_ref };
subpasses[1].setInputAttachments( second_attachments );
subpasses[1].setColorAttachments( color_attachment_ref );
```
第一个子通道需要使用深度缓冲区过滤无效片段,然后将信息写入三个色彩附件。
而第二个子通道使用输入附件接受三个附件数据,并将光照结果写入交换链图像对应的颜色附件。
然后设置子通道依赖:
```cpp
std::array<vk::SubpassDependency,2> dependencies;
dependencies[0].srcSubpass = vk::SubpassExternal;
dependencies[0].srcStageMask = vk::PipelineStageFlagBits::eFragmentShader;
dependencies[0].srcAccessMask = {};
dependencies[0].dstSubpass = 0;
dependencies[0].dstStageMask = vk::PipelineStageFlagBits::eEarlyFragmentTests;
dependencies[0].dstAccessMask = vk::AccessFlagBits::eDepthStencilAttachmentWrite;
dependencies[1].srcSubpass = 0;
dependencies[1].srcStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput;
dependencies[1].srcAccessMask = vk::AccessFlagBits::eColorAttachmentWrite;
dependencies[1].dstSubpass = 1;
dependencies[1].dstStageMask = vk::PipelineStageFlagBits::eFragmentShader;
dependencies[1].dstAccessMask = vk::AccessFlagBits::eInputAttachmentRead;
```
需要等待外部子通道(上一次渲染)的片段着色器执行完成,即附件使用完毕,才可转换它们的布局。
第一个子通道写入颜色后,才能开始第二个子通道的光照计算(读取输入附件)。
然后可以创建渲染通道:
```cpp
const auto attachments = {
color_attachment,
depth_attachment,
g_pos_attachment,
g_color_attachment,
g_normal_depth_attachment
};
vk::RenderPassCreateInfo create_info;
create_info.setAttachments( attachments );
create_info.setSubpasses( subpasses );
create_info.setDependencies( dependencies );
```
`attachments` 内的附件描述顺序同样要与帧缓冲区的 `attachments` 顺序严格对应。
### 5. 图像重建
还需要修改 `recreate` 函数,只需在深度图像重建后重建 GBuffer 资源:
```cpp
m_framebuffers.clear();
m_swapchain->recreate();
m_depth_image->recreate();
m_g_buffer->recreate(); // 在深度图像重建语句的下方
create_framebuffers();
```
## **着色器**
现在需要修改原有的着色器代码,并添加新管线的着色器代码。
### 1. 修改原有着色器
首先修改 `shader.frag` 片段着色器,需要向颜色混合附件输出世界坐标等消息:
```glsl
#version 450
layout(push_constant) uniform PushConstants {
int enableTexture;
} pc;
layout(binding = 1) uniform sampler2D texSampler;
// layout(std140, binding = 2) uniform LightUBO 移除光源内容
layout(location = 0) in vec3 fragPos;
layout(location = 1) in vec3 fragNormal;
layout(location = 2) in vec2 fragTexCoord;
layout(location = 3) in float fragNa; // 无用
layout(location = 4) in vec3 fragKa; // 无用
layout(location = 5) in vec3 fragKd; // 无用
layout(location = 6) in vec3 fragKs; // 无用
layout(location = 0) out vec4 outPosition;
layout(location = 1) out vec4 outColor;
layout(location = 2) out vec4 outNormalDepth;
void main() {
// 根据推送常量决定是否采样纹理
vec3 objectColor = pc.enableTexture == 1 ? texture(texSampler, fragTexCoord).rgb : vec3(0.5, 0.5, 0.5);
outColor = vec4(objectColor, 1.0);
outPosition = vec4(fragPos, 1.0);
outNormalDepth = vec4(fragNormal, 1.0); //第四位 深度信息此处不用
}
```
可以看到第一个管线所做的事情非常简单,它将片段的世界坐标、颜色和法线等信息输出到三个颜色附件中。
我们没有用到材质信息,你可以自行扩展。
### 2. 添加新顶点着色器
然后添加第二个管线的顶点着色器 `second.vert` 。我们不需要任何输入,因此直接选择屏幕的端点,后续通过六个顶点绘制整个屏幕:
```glsl
#version 450
// 通过两个三角形,绘制整个屏幕
// 注意顶点顺序
vec2 output_position[6] = vec2[](
vec2(-1.0, -1.0),
vec2(1.0, 1.0),
vec2(1.0, -1.0),
vec2(-1.0, -1.0),
vec2(-1.0, 1.0),
vec2(1.0, 1.0)
);
void main() {
gl_Position =vec4(output_position[gl_VertexIndex], 0.5, 1.0);
}
```
### 3. 添加新片段着色器
然后添加第二个片段着色器 `second.frag` ,它将读取 G-Buffer 中的内容进行光照计算:
```glsl
#version 450
layout(std140, binding = 0) uniform LightUBO {
vec3 lightPos;
vec3 lightColor;
vec3 viewPos;
} ubo;
layout(input_attachment_index = 0, binding = 1) uniform subpassInput g_buffer[3];
layout(location = 0) out vec4 outColor;
void main() {
vec3 pos = subpassLoad(g_buffer[0]).xyz; // 片段位置
vec3 color = subpassLoad(g_buffer[1]).rgb; // 片段颜色
vec3 normal = subpassLoad(g_buffer[2]).xyz; // 法线
// 视角方向
vec3 viewDir = normalize(ubo.viewPos - pos);
// 环境光强
vec3 ambient = 0.15 * ubo.lightColor;
// 漫反射
vec3 lightDir = normalize(ubo.lightPos - pos);
float diff = max(dot(normal, lightDir), 0.0);
vec3 diffuse = diff * 0.8 * ubo.lightColor;
// 镜面反射
vec3 reflectDir = reflect(-lightDir, normal);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), 512);
vec3 specular = spec * 0.3 * ubo.lightColor;
// 最终色彩
vec3 result = (ambient + diffuse + specular) * color;
result = min(result, vec3(1.0));
outColor = vec4(result, 1.0);
}
```
光照计算此处不再赘述,注意我们将光源信息放在了此着色器,后续需要修改描述符布局。
### 4. CMake脚本
最后修改 `shaders` 目录下的 `CMakeLists.txt` ,添加着色器的自动编译:
```cmake
# ...
set(VERT_SECOND ${SHADER_DIR}/second.vert)
set(FRAG_SECOND ${SHADER_DIR}/second.frag)
# ...
set(SPIRV_SECOND_VERT ${SHADER_DIR}/second_vert.spv)
set(SPIRV_SECOND_FRAG ${SHADER_DIR}/second_frag.spv)
# ......
add_custom_command(
OUTPUT ${SPIRV_SECOND_VERT}
COMMAND ${Vulkan_GLSLC_EXECUTABLE} ${VERT_SECOND} -o ${SPIRV_SECOND_VERT}
COMMENT "Compiling shader.vert to vert.spv"
DEPENDS ${VERT_SECOND}
)
add_custom_command(
OUTPUT ${SPIRV_SECOND_FRAG}
COMMAND ${Vulkan_GLSLC_EXECUTABLE} ${FRAG_SECOND} -o ${SPIRV_SECOND_FRAG}
COMMENT "Compiling shader.frag to frag.spv"
DEPENDS ${FRAG_SECOND}
)
add_custom_target(CompileShaders ALL
DEPENDS ${SPIRV_VERT} ${SPIRV_FRAG} ${SPIRV_SECOND_VERT} ${SPIRV_SECOND_FRAG}
)
```
## **修改图形管线**
回到 `GraphicsPipeline.cppm` 模块,修改原有的管线创建代码。
### 1. 修改旧管线
首先修改描述符布局 `create_descriptor_set_layout` 函数,删除光源的描述符:
```cpp
// 删除 light_ubo_layout_binging
// vk::DescriptorSetLayoutBinding light_ubo_layout_binging;
const auto bindings = { ubo_layout_binging, sampler_layout_binding };
```
还需要修改管线创建代码,增加颜色混合阶段的附件数量:
```cpp
std::array<vk::PipelineColorBlendAttachmentState, 3> color_blend_attachments;
for (auto& att : color_blend_attachments) {
att.blendEnable = false;
att.colorWriteMask = vk::FlagTraits<vk::ColorComponentFlagBits>::allFlags;
}
vk::PipelineColorBlendStateCreateInfo color_blend;
color_blend.logicOpEnable = false;
color_blend.logicOp = vk::LogicOp::eCopy;
color_blend.setAttachments( color_blend_attachments );
```
### 2. 添加新管线
首先添加新成员变量:
```cpp
vk::raii::DescriptorSetLayout m_second_descriptor_set_layout{ nullptr };
vk::raii::PipelineLayout m_second_pipeline_layout{ nullptr };
vk::raii::Pipeline m_second_pipeline{ nullptr };
...
[[nodiscard]]
const vk::raii::DescriptorSetLayout& second_descriptor_set_layout() const { return m_second_descriptor_set_layout; }
[[nodiscard]]
const vk::raii::PipelineLayout& second_pipeline_layout() const { return m_second_pipeline_layout; }
[[nodiscard]]
const vk::raii::Pipeline& second_pipeline() const { return m_second_pipeline; }
```
添加新的描述符布局创建函数:
```cpp
void init() {
create_descriptor_set_layout();
create_graphics_pipeline();
create_second_descriptor_set_layout();
}
void create_second_descriptor_set_layout() {
vk::DescriptorSetLayoutBinding light_ubo_layout_binging;
light_ubo_layout_binging.binding = 0;
light_ubo_layout_binging.descriptorType = vk::DescriptorType::eUniformBuffer;
light_ubo_layout_binging.descriptorCount = 1;
light_ubo_layout_binging.stageFlags = vk::ShaderStageFlagBits::eFragment;
vk::DescriptorSetLayoutBinding input_layout_binging; // 输入附件绑定
input_layout_binging.binding = 1;
input_layout_binging.descriptorType = vk::DescriptorType::eInputAttachment;
input_layout_binging.descriptorCount = 3; // 有三个输入附件
input_layout_binging.stageFlags = vk::ShaderStageFlagBits::eFragment;
const auto bindings = { light_ubo_layout_binging, input_layout_binging };
vk::DescriptorSetLayoutCreateInfo layoutInfo;
layoutInfo.setBindings( bindings );
m_second_descriptor_set_layout = m_device->device().createDescriptorSetLayout( layoutInfo );
}
```
第一个描述符集用于光源信息,第二个描述符集用于输入附件。
我们有三个输入附件,因此需要三个输入附件绑定描述符。
然后可以创建新管线,管线创建代码与原有管线类似:但不需要顶点输入和深度测试:
```cpp
void init() {
create_descriptor_set_layout();
create_graphics_pipeline();
create_second_descriptor_set_layout();
create_second_graphics_pipeline();
}
void create_second_graphics_pipeline() {
// 着色器阶段
const auto vertex_shader_code = vht::read_shader("shaders/second_vert.spv");
const auto fragment_shader_code = vht::read_shader("shaders/second_frag.spv");
const auto vertex_shader_module = vht::create_shader_module(m_device->device(), vertex_shader_code);
const auto fragment_shader_module = vht::create_shader_module(m_device->device(), fragment_shader_code);
vk::PipelineShaderStageCreateInfo vertex_shader_create_info;
vertex_shader_create_info.stage = vk::ShaderStageFlagBits::eVertex;
vertex_shader_create_info.module = vertex_shader_module;
vertex_shader_create_info.pName = "main";
vk::PipelineShaderStageCreateInfo fragment_shader_create_info;
fragment_shader_create_info.stage = vk::ShaderStageFlagBits::eFragment;
fragment_shader_create_info.module = fragment_shader_module;
fragment_shader_create_info.pName = "main";
const auto shader_stages = { vertex_shader_create_info, fragment_shader_create_info };
const auto dynamic_states = { vk::DynamicState::eViewport, vk::DynamicState::eScissor };
vk::PipelineDynamicStateCreateInfo dynamic_state;
dynamic_state.setDynamicStates(dynamic_states);
// 不需要顶点输入数据
vk::PipelineVertexInputStateCreateInfo vertex_input;
vk::PipelineInputAssemblyStateCreateInfo input_assembly;
input_assembly.topology = vk::PrimitiveTopology::eTriangleList;
vk::PipelineViewportStateCreateInfo viewport_state;
viewport_state.viewportCount = 1;
viewport_state.scissorCount = 1;
// 不需要深度测试
// vk::PipelineDepthStencilStateCreateInfo depth_stencil;
// 光栅化器不需要变
vk::PipelineRasterizationStateCreateInfo rasterizer;
rasterizer.depthClampEnable = false;
rasterizer.rasterizerDiscardEnable = false;
rasterizer.polygonMode = vk::PolygonMode::eFill;
rasterizer.lineWidth = 1.0f;
rasterizer.cullMode = vk::CullModeFlagBits::eBack;
rasterizer.frontFace = vk::FrontFace::eCounterClockwise;
rasterizer.depthBiasEnable = false;
// 多次采样不变
vk::PipelineMultisampleStateCreateInfo multisampling;
multisampling.rasterizationSamples = vk::SampleCountFlagBits::e1;
multisampling.sampleShadingEnable = false; // default
// 颜色混合附件只需要一个,对应交换链图像
vk::PipelineColorBlendAttachmentState color_blend_attachment;
color_blend_attachment.blendEnable = false; // default
color_blend_attachment.colorWriteMask = vk::FlagTraits<vk::ColorComponentFlagBits>::allFlags;
vk::PipelineColorBlendStateCreateInfo color_blend;
color_blend.logicOpEnable = false;
color_blend.logicOp = vk::LogicOp::eCopy;
color_blend.setAttachments( color_blend_attachment );
// 创建管线布局
vk::PipelineLayoutCreateInfo layout_create_info;
layout_create_info.setSetLayouts( *m_second_descriptor_set_layout );
m_second_pipeline_layout = m_device->device().createPipelineLayout( layout_create_info );
// 管线创建
vk::GraphicsPipelineCreateInfo create_info;
create_info.layout = m_second_pipeline_layout;
create_info.setStages( shader_stages );
create_info.pVertexInputState = &vertex_input;
create_info.pInputAssemblyState = &input_assembly;
create_info.pDynamicState = &dynamic_state;
create_info.pViewportState = &viewport_state;
create_info.pRasterizationState = &rasterizer;
create_info.pMultisampleState = &multisampling;
create_info.pColorBlendState = &color_blend;
create_info.renderPass = m_render_pass->render_pass();
create_info.subpass = 1; // 绑定第二个子通道
m_second_pipeline = m_device->device().createGraphicsPipeline( nullptr, create_info );
}
```
## **描述符集**
现在需要实际分配描述符集,回到 `Descriptor.cppm` 模块,修改 `create_descriptor_pool` 函数:
```cpp
std::array<vk::DescriptorPoolSize, 3> pool_sizes; // 修改为三个
...
pool_sizes[2].type = vk::DescriptorType::eInputAttachment;
pool_sizes[2].descriptorCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT * 3);
...
poolInfo.maxSets = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT * 2); // 增加为2倍
```
> 这里选择创建飞行帧个数的描述符集,但实际上我们只有一组附件,你完全可以只创建一组描述符集。
然后需要修改 `create_descriptor_sets` 函数首先删除原有管线中光源UBO的内容
```cpp
// 删除
// vk::DescriptorBufferInfo light_buffer_info;
// light_buffer_info.buffer = m_light_uniform_buffer->buffers()[i];
// light_buffer_info.offset = 0;
// light_buffer_info.range = sizeof(LightUBO);
...
// 减小数组大小
std::array<vk::WriteDescriptorSet, 2> writes;
...
// 删除
// writes[2].dstSet = m_sets[i];
// writes[2].dstBinding = 2;
// writes[2].dstArrayElement = 0;
// writes[2].descriptorType = vk::DescriptorType::eUniformBuffer;
// writes[2].setBufferInfo(light_buffer_info);
```
然后添加第二个管线的描述符集分配:
```cpp
...
// 添加成员变量
std::vector<vk::raii::DescriptorSet> m_second_sets;
....
// 提供对外接口
[[nodiscard]]
const std::vector<vk::raii::DescriptorSet>& second_sets() const { return m_second_sets; }
...
// create_descriptor_sets 函数,分配描述符集
std::vector<vk::DescriptorSetLayout> second_layouts(MAX_FRAMES_IN_FLIGHT, *m_graphics_pipeline->second_descriptor_set_layout());
vk::DescriptorSetAllocateInfo second_alloc_info;
second_alloc_info.descriptorPool = m_pool;
second_alloc_info.setSetLayouts( second_layouts );
m_second_sets = m_device->device().allocateDescriptorSets(second_alloc_info);
for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i) {
vk::DescriptorBufferInfo light_buffer_info;
light_buffer_info.buffer = m_light_uniform_buffer->buffers()[i];
light_buffer_info.offset = 0;
light_buffer_info.range = sizeof(LightUBO);
std::array<vk::DescriptorImageInfo, 3> input_attachments;
input_attachments[0].imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
input_attachments[0].imageView = m_g_buffer->pos_views();
input_attachments[1].imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
input_attachments[1].imageView = m_g_buffer->color_views();
input_attachments[2].imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
input_attachments[2].imageView = m_g_buffer->normal_depth_views();
std::array<vk::WriteDescriptorSet, 2> writes;
writes[0].dstSet = m_second_sets[i];
writes[0].dstBinding = 0;
writes[0].dstArrayElement = 0;
writes[0].descriptorType = vk::DescriptorType::eUniformBuffer;
writes[0].setBufferInfo(light_buffer_info);
writes[1].dstSet = m_second_sets[i];
writes[1].dstBinding = 1;
writes[1].dstArrayElement = 0;
writes[1].descriptorType = vk::DescriptorType::eInputAttachment;
writes[1].setImageInfo(input_attachments);
m_device->device().updateDescriptorSets(writes, nullptr);
}
```
## **命令录制**
回到 `Drawer.cppm` 模块,修改 `record_command_buffer` 函数。
首先需要为新的附件添加 `clear_values`
```cpp
std::array<vk::ClearValue, 5> clear_values; // 增加 3 个子元素
clear_values[0] = vk::ClearColorValue{ 0.0f, 0.0f, 0.0f, 1.0f };
clear_values[1] = vk::ClearDepthStencilValue{ 1.0f ,0 };
clear_values[2] = clear_values[0];
clear_values[3] = clear_values[0];
clear_values[4] = clear_values[0];
render_pass_begin_info.setClearValues( clear_values );
```
第一个图形管线的绘制代码无需调整,现在在下方添加第二个管线的绘制代码:
```cpp
// ...... 无需调整
command_buffer.drawIndexed(m_data_loader->index_counts()[1], 1, m_data_loader->index_offsets()[1], 0, 1);
// ↑ 第一个管线的命令
// ↓ 第二个管线的命令
// --- 切换到第二个子通道 ---
command_buffer.nextSubpass(vk::SubpassContents::eInline);
// --- 绑定第二个管线 ---
command_buffer.bindPipeline( vk::PipelineBindPoint::eGraphics, m_graphics_pipeline->second_pipeline() );
// 视口与裁剪共用上方的设置,此处无需再次设置
// --- 绑定描述符集合 ---
command_buffer.bindDescriptorSets(
vk::PipelineBindPoint::eGraphics,
m_graphics_pipeline->second_pipeline_layout(),
0,
*m_descriptor->second_sets()[m_current_frame],
nullptr
);
// 绘制 6 个点,对应顶点着色器中的两个三角形
command_buffer.draw(6, 1, 0, 0);
command_buffer.endRenderPass();
command_buffer.end();
```
## **重建描述符**
现在已经可以运行程序,但它会在窗口大小改变时崩溃,因为我们虽然有 G-Buffer 的重置,但没有重建描述符集。
当窗口尺寸变化时G-Buffer 的图像资源会被重建,但描述符集仍然指向旧的图像资源,导致访问错误。
这里用一种最简单的方式重建,现在回到 `Descriptor.cppm` 模块,添加公开的 `recreate` 函数:
```cpp
void recreate() {
m_sets.clear();
m_second_sets.clear();
create_descriptor_sets();
}
```
然后回到 `Drawer.cppm` 模块,在**每次**交换链重置后调用它:
```cpp
m_render_pass->recreate();
m_descriptor->recreate();
```
## **最后**
现在再次运行程序,您应该可以看到延迟渲染的效果,且窗口大小可以调整:
![deferred_rendering](../../images/0421/defer.png)
作为进阶章节,您还可以尝试以下内容:
- 添加材质信息到 G-Buffer 中,使用更多的附件。
- 尝试将上一章的阴影映射与本章内容结合。
---
**[初始代码集](../../codes/04/21_deferred/0421_base_code.zip)**
**[second.vert新增](../../codes/04/21_deferred/shaders/second.vert)**
**[second.frag新增](../../codes/04/21_deferred/shaders/second.frag)**
**[shaders/CMakeLists.txt修改](../../codes/04/21_deferred/shaders/CMakeLists.txt)**
**[shaders/CMakeLists.diff差异文件](../../codes/04/21_deferred/shaders/CMakeLists.diff)**
**[GraphicsPipeline.cppm修改](../../codes/04/21_deferred/GraphicsPipeline.cppm)**
**[GraphicsPipeline.diff差异文件](../../codes/04/21_deferred/GraphicsPipeline.diff)**
**[Descriptor.cppm修改](../../codes/04/21_deferred/Descriptor.cppm)**
**[Descriptor.diff差异文件](../../codes/04/21_deferred/Descriptor.diff)**
**[Drawer.cppm修改](../../codes/04/21_deferred/Drawer.cppm)**
**[Drawer.diff差异文件](../../codes/04/21_deferred/Drawer.diff)**
**[RenderPass.cppm修改](../../codes/04/21_deferred/RenderPass.cppm)**
**[RenderPass.diff差异文件](../../codes/04/21_deferred/RenderPass.diff)**
---

View File

@ -94,6 +94,7 @@ nav:
- TODO: todo.md # 新版同步
- 多管线渲染:
- 阴影映射: md/04/20_shadowmap.md
- 延迟渲染: md/04/21_deferred.md
- TODO: todo.md # 模板测试
- TODO: todo.md # 延迟渲染
- 多线程渲染:

View File

@ -6,12 +6,14 @@ set(SHADER_DIR ${CMAKE_CURRENT_SOURCE_DIR})
set(VERT_SHADER ${SHADER_DIR}/shader.vert)
set(FRAG_SHADER ${SHADER_DIR}/shader.frag)
set(SHADOW_SHADER ${SHADER_DIR}/shadow.vert)
set(VERT_SECOND ${SHADER_DIR}/second.vert)
set(FRAG_SECOND ${SHADER_DIR}/second.frag)
set(SPIRV_VERT ${SHADER_DIR}/vert.spv)
set(SPIRV_FRAG ${SHADER_DIR}/frag.spv)
set(SPIRV_SHADOW ${SHADER_DIR}/shadow.spv)
set(SPIRV_SECOND_VERT ${SHADER_DIR}/second_vert.spv)
set(SPIRV_SECOND_FRAG ${SHADER_DIR}/second_frag.spv)
add_custom_command(
OUTPUT ${SPIRV_VERT}
@ -28,12 +30,20 @@ add_custom_command(
)
add_custom_command(
OUTPUT ${SPIRV_SHADOW}
COMMAND ${Vulkan_GLSLC_EXECUTABLE} ${SHADOW_SHADER} -o ${SPIRV_SHADOW}
COMMENT "Compiling shadow.vert vertex shadow.spv"
DEPENDS ${SHADOW_SHADER}
OUTPUT ${SPIRV_SECOND_VERT}
COMMAND ${Vulkan_GLSLC_EXECUTABLE} ${VERT_SECOND} -o ${SPIRV_SECOND_VERT}
COMMENT "Compiling shader.vert to vert.spv"
DEPENDS ${VERT_SECOND}
)
add_custom_command(
OUTPUT ${SPIRV_SECOND_FRAG}
COMMAND ${Vulkan_GLSLC_EXECUTABLE} ${FRAG_SECOND} -o ${SPIRV_SECOND_FRAG}
COMMENT "Compiling shader.frag to frag.spv"
DEPENDS ${FRAG_SECOND}
)
add_custom_target(CompileShaders ALL
DEPENDS ${SPIRV_VERT} ${SPIRV_FRAG} ${SPIRV_SHADOW}
DEPENDS ${SPIRV_VERT} ${SPIRV_FRAG} ${SPIRV_SECOND_VERT} ${SPIRV_SECOND_FRAG}
)

38
shaders/second.frag Normal file
View File

@ -0,0 +1,38 @@
#version 450
layout(std140, binding = 0) uniform LightUBO {
vec3 lightPos;
vec3 lightColor;
vec3 viewPos;
} ubo;
layout(input_attachment_index = 0, binding = 1) uniform subpassInput g_buffer[3];
layout(location = 0) out vec4 outColor;
void main() {
vec3 pos = subpassLoad(g_buffer[0]).xyz; // 片段位置
vec3 color = subpassLoad(g_buffer[1]).rgb; // 片段颜色
vec3 normal = subpassLoad(g_buffer[2]).xyz; // 法线
// 视角方向
vec3 viewDir = normalize(ubo.viewPos - pos);
// 环境光强
vec3 ambient = 0.15 * ubo.lightColor;
// 漫反射
vec3 lightDir = normalize(ubo.lightPos - pos);
float diff = max(dot(normal, lightDir), 0.0);
vec3 diffuse = diff * 0.8 * ubo.lightColor;
// 镜面反射
vec3 reflectDir = reflect(-lightDir, normal);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), 512);
vec3 specular = spec * 0.3 * ubo.lightColor;
// 最终色彩
vec3 result = (ambient + diffuse + specular) * color;
result = min(result, vec3(1.0));
outColor = vec4(result, 1.0);
}

16
shaders/second.vert Normal file
View File

@ -0,0 +1,16 @@
#version 450
// 通过两个三角形,绘制整个屏幕
// 注意顺序
vec2 output_position[6] = vec2[](
vec2(-1.0, -1.0),
vec2(1.0, 1.0),
vec2(1.0, -1.0),
vec2(-1.0, -1.0),
vec2(-1.0, 1.0),
vec2(1.0, 1.0)
);
void main() {
gl_Position =vec4(output_position[gl_VertexIndex], 0.5, 1.0);
}

View File

@ -6,69 +6,24 @@ layout(push_constant) uniform PushConstants {
layout(binding = 1) uniform sampler2D texSampler;
layout(std140, binding = 2) uniform LightUBO {
mat4 model;
mat4 view;
mat4 proj;
vec3 lightPos;
vec3 lightColor;
vec3 viewPos;
} ubo;
layout(binding = 3) uniform sampler2D depthSampler;
// layout(std140, binding = 2) uniform LightUBO 移除光源内容
layout(location = 0) in vec3 fragPos;
layout(location = 1) in vec3 fragNormal;
layout(location = 2) in vec2 fragTexCoord;
layout(location = 3) in float fragNa;
layout(location = 4) in vec3 fragKa;
layout(location = 5) in vec3 fragKd;
layout(location = 6) in vec3 fragKs;
layout(location = 3) in float fragNa; // 无用
layout(location = 4) in vec3 fragKa; // 无用
layout(location = 5) in vec3 fragKd; // 无用
layout(location = 6) in vec3 fragKs; // 无用
layout(location = 0) out vec4 outColor;
layout(location = 0) out vec4 outPosition;
layout(location = 1) out vec4 outColor;
layout(location = 2) out vec4 outNormalDepth;
void main() {
// 计算光源空间坐标
vec4 lightSpacePos = ubo.proj * ubo.view * ubo.model * vec4(fragPos, 1.0);
vec3 projCoords = lightSpacePos.xyz / lightSpacePos.w;
projCoords.xy = projCoords.xy * 0.5 + 0.5; // 将 x,y 转换到[0,1]区间
float shadow = 0.0; // 光照范围之外
if(projCoords.x > 0.0 && projCoords.x < 1.0 && projCoords.y > 0.0 && projCoords.y < 1.0) {
float closestDepth = texture(depthSampler, projCoords.xy).r;
float currentDepth = projCoords.z;
// 阴影判断
float bias = 0.001;
shadow = currentDepth - bias > closestDepth ? 0.5 : 1.0; // 0.5表示阴影1.0表示光照
}
// 根据推送常量决定是否采样纹理
float depth = texture(depthSampler, fragTexCoord.xy).r;
depth = (depth - 0.90) * 10.0;
depth = pow(depth, 6.0);
vec3 objectColor = pc.enableTexture == 1 ? texture(texSampler, fragTexCoord).rgb : vec3(depth, depth, depth);
// 法线
vec3 normal = normalize(fragNormal);
// 视角方向
vec3 viewDir = normalize(ubo.viewPos - fragPos);
// 环境光强
vec3 ambient = 0.2 * fragKa * ubo.lightColor;
// 漫反射
vec3 lightDir = normalize(ubo.lightPos - fragPos);
float diff = max(dot(normal, lightDir), 0.0);
vec3 diffuse = diff * fragKd * ubo.lightColor;
// 镜面反射
vec3 reflectDir = reflect(-lightDir, normal);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), fragNa);
vec3 specular = 0.6 * spec * fragKs * ubo.lightColor;
// 最终色彩
vec3 result = (ambient + shadow * (diffuse + specular)) * objectColor;
result = min(result, vec3(1.0));
outColor = vec4(result, 1.0);
}
vec3 objectColor = pc.enableTexture == 1 ? texture(texSampler, fragTexCoord).rgb : vec3(0.5, 0.5, 0.5);
outColor = vec4(objectColor, 1.0);
outPosition = vec4(fragPos, 1.0);
outNormalDepth = vec4(fragNormal, 1.0); //第四位 深度信息此处不用
}

View File

@ -1,18 +0,0 @@
#version 450
layout(location = 0) in vec3 inPosition;
layout(location = 1) in vec3 inNormal;
layout(location = 2) in vec2 inTexCoord;
layout(std140, binding = 0) uniform UBO {
mat4 model;
mat4 view;
mat4 proj;
vec3 lightPos;
vec3 lightColor;
vec3 viewPos;
} ubo;
void main() {
gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 1.0);
}

View File

@ -16,9 +16,7 @@ import Window;
import Device;
import Swapchain;
import DepthImage;
import ShadowDepthImage;
import ShadowRenderPass;
import ShadowPipeline;
import GBuffer;
import RenderPass;
import GraphicsPipeline;
import CommandPool;
@ -37,9 +35,7 @@ export namespace vht {
std::shared_ptr<vht::Device> m_device{ nullptr };
std::shared_ptr<vht::Swapchain> m_swapchain{ nullptr };
std::shared_ptr<vht::DepthImage> m_depth_image{ nullptr };
std::shared_ptr<vht::ShadowDepthImage> m_shadow_depth_image{ nullptr };
std::shared_ptr<vht::ShadowRenderPass> m_shadow_render_pass{ nullptr };
std::shared_ptr<vht::ShadowPipeline> m_shadow_pipeline{ nullptr };
std::shared_ptr<vht::GBuffer> m_g_buffer{ nullptr };
std::shared_ptr<vht::RenderPass> m_render_pass{ nullptr };
std::shared_ptr<vht::GraphicsPipeline> m_graphics_pipeline{ nullptr };
std::shared_ptr<vht::CommandPool> m_command_pool{ nullptr };
@ -73,12 +69,8 @@ export namespace vht {
std::cout << "swapchain created" << std::endl;
init_depth_image();
std::cout << "depth image created" << std::endl;
init_shadow_depth_image();
std::cout << "shadow depth image created" << std::endl;
init_shadow_render_pass();
std::cout << "shadow render pass created" << std::endl;
init_shadow_pipeline();
std::cout << "shadow pipeline created" << std::endl;
init_g_buffer();
std::cout << "g buffer created" << std::endl;
init_render_pass();
std::cout << "render pass created" << std::endl;
init_graphics_pipeline();
@ -104,10 +96,8 @@ export namespace vht {
void init_device() { m_device = std::make_shared<vht::Device>( m_context, m_window ); }
void init_swapchain() { m_swapchain = std::make_shared<vht::Swapchain>( m_window, m_device ); }
void init_depth_image() { m_depth_image = std::make_shared<vht::DepthImage>( m_device, m_swapchain ); }
void init_shadow_depth_image() { m_shadow_depth_image = std::make_shared<vht::ShadowDepthImage>( m_device ); }
void init_shadow_render_pass() { m_shadow_render_pass = std::make_shared<vht::ShadowRenderPass>( m_device, m_shadow_depth_image ); }
void init_shadow_pipeline() { m_shadow_pipeline = std::make_shared<vht::ShadowPipeline>(m_device, m_shadow_render_pass); }
void init_render_pass() { m_render_pass = std::make_shared<vht::RenderPass>( m_window, m_device, m_swapchain, m_depth_image ); }
void init_g_buffer() { m_g_buffer = std::make_shared<vht::GBuffer>( m_device, m_swapchain ); }
void init_render_pass() { m_render_pass = std::make_shared<vht::RenderPass>( m_window, m_device, m_swapchain, m_depth_image, m_g_buffer ); }
void init_graphics_pipeline() { m_graphics_pipeline = std::make_shared<vht::GraphicsPipeline>( m_device, m_render_pass ); }
void init_command_pool() { m_command_pool = std::make_shared<vht::CommandPool>( m_device ); }
void init_input_assembly() { m_input_assembly = std::make_shared<vht::InputAssembly>( m_data_loader, m_device, m_command_pool ); }
@ -117,8 +107,7 @@ export namespace vht {
void init_descriptor() {
m_descriptor = std::make_shared<vht::Descriptor>(
m_device,
m_shadow_depth_image,
m_shadow_pipeline,
m_g_buffer,
m_graphics_pipeline,
m_uniform_buffer,
m_texture_sampler,
@ -131,8 +120,6 @@ export namespace vht {
m_window,
m_device,
m_swapchain,
m_shadow_render_pass,
m_shadow_pipeline,
m_render_pass,
m_graphics_pipeline,
m_command_pool,

View File

@ -10,8 +10,7 @@ import vulkan_hpp;
import Config;
import Device;
import ShadowDepthImage;
import ShadowPipeline;
import GBuffer;
import GraphicsPipeline;
import UniformBuffer;
import TextureSampler;
@ -35,27 +34,24 @@ export namespace vht {
*/
class Descriptor {
std::shared_ptr<vht::Device> m_device;
std::shared_ptr<vht::ShadowDepthImage> m_shadow_depth_image{ nullptr };
std::shared_ptr<vht::ShadowPipeline> m_shadow_pipeline{ nullptr };
std::shared_ptr<vht::GBuffer> m_g_buffer;
std::shared_ptr<vht::GraphicsPipeline> m_graphics_pipeline;
std::shared_ptr<vht::UniformBuffer> m_uniform_buffer;
std::shared_ptr<vht::TextureSampler> m_texture_sampler;
std::shared_ptr<vht::LightUniformBuffer> m_light_uniform_buffer;
vk::raii::DescriptorPool m_pool{ nullptr };
std::vector<vk::raii::DescriptorSet> m_sets;
std::vector<vk::raii::DescriptorSet> m_shadow_sets;
std::vector<vk::raii::DescriptorSet> m_second_sets;
public:
explicit Descriptor(
std::shared_ptr<vht::Device> device,
std::shared_ptr<vht::ShadowDepthImage> shadow_depth_image,
std::shared_ptr<vht::ShadowPipeline> shadow_pipeline,
std::shared_ptr<vht::GBuffer> g_buffer,
std::shared_ptr<vht::GraphicsPipeline> m_graphics_pipeline,
std::shared_ptr<vht::UniformBuffer> m_uniform_buffer,
std::shared_ptr<vht::TextureSampler> m_texture_sampler,
std::shared_ptr<vht::LightUniformBuffer> m_light_uniform_buffer
): m_device(std::move(device)),
m_shadow_depth_image(std::move(shadow_depth_image)),
m_shadow_pipeline(std::move(shadow_pipeline)),
m_g_buffer(std::move(g_buffer)),
m_graphics_pipeline(std::move(m_graphics_pipeline)),
m_uniform_buffer(std::move(m_uniform_buffer)),
m_texture_sampler(std::move(m_texture_sampler)),
@ -68,7 +64,13 @@ export namespace vht {
[[nodiscard]]
const std::vector<vk::raii::DescriptorSet>& sets() const { return m_sets; }
[[nodiscard]]
const std::vector<vk::raii::DescriptorSet>& shadow_sets() const { return m_shadow_sets; }
const std::vector<vk::raii::DescriptorSet>& second_sets() const { return m_second_sets; }
void recreate() {
m_sets.clear();
m_second_sets.clear();
create_descriptor_sets();
}
private:
void init() {
@ -77,45 +79,23 @@ export namespace vht {
}
// 创建描述符池
void create_descriptor_pool() {
std::array<vk::DescriptorPoolSize, 2> pool_sizes;
std::array<vk::DescriptorPoolSize, 3> pool_sizes;
pool_sizes[0].type = vk::DescriptorType::eUniformBuffer;
pool_sizes[0].descriptorCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT * 3); // UBO + 2 份 Light UBO
pool_sizes[0].descriptorCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT * 2); // 包括 Light UBO
pool_sizes[1].type = vk::DescriptorType::eCombinedImageSampler;
pool_sizes[1].descriptorCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT * 2); // 纹理采样器 + 阴影贴图
pool_sizes[1].descriptorCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);
pool_sizes[2].type = vk::DescriptorType::eInputAttachment;
pool_sizes[2].descriptorCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT * 3);
vk::DescriptorPoolCreateInfo poolInfo;
poolInfo.flags = vk::DescriptorPoolCreateFlagBits::eFreeDescriptorSet;
poolInfo.setPoolSizes( pool_sizes );
poolInfo.maxSets = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT * 2); // 两个管线都需要 MAX_FRAMES_IN_FLIGHT
poolInfo.maxSets = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT * 2);
m_pool = m_device->device().createDescriptorPool(poolInfo);
}
// 创建描述符集
void create_descriptor_sets() {
// 为阴影管线创建描述符集布局
std::vector<vk::DescriptorSetLayout> shadow_layouts(MAX_FRAMES_IN_FLIGHT, *m_shadow_pipeline->descriptor_set_layout());
vk::DescriptorSetAllocateInfo shadow_alloc_info;
shadow_alloc_info.descriptorPool = m_pool;
shadow_alloc_info.setSetLayouts( shadow_layouts );
m_shadow_sets = m_device->device().allocateDescriptorSets(shadow_alloc_info);
for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i) {
vk::DescriptorBufferInfo shadow_light_buffer_info;
shadow_light_buffer_info.buffer = m_light_uniform_buffer->buffers()[i];
shadow_light_buffer_info.offset = 0;
shadow_light_buffer_info.range = sizeof(LightUBO);
vk::WriteDescriptorSet write;
write.dstSet = m_shadow_sets[i];
write.dstBinding = 0;
write.dstArrayElement = 0;
write.descriptorType = vk::DescriptorType::eUniformBuffer;
write.setBufferInfo(shadow_light_buffer_info);
m_device->device().updateDescriptorSets(write, nullptr);
}
std::vector<vk::DescriptorSetLayout> layouts(MAX_FRAMES_IN_FLIGHT, *m_graphics_pipeline->descriptor_set_layout());
vk::DescriptorSetAllocateInfo alloc_info;
alloc_info.descriptorPool = m_pool;
@ -134,17 +114,7 @@ export namespace vht {
image_info.imageView = m_texture_sampler->image_view();
image_info.sampler = m_texture_sampler->sampler();
vk::DescriptorBufferInfo light_buffer_info;
light_buffer_info.buffer = m_light_uniform_buffer->buffers()[i];
light_buffer_info.offset = 0;
light_buffer_info.range = sizeof(LightUBO);
vk::DescriptorImageInfo depth_map_info;
depth_map_info.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
depth_map_info.imageView = m_shadow_depth_image->image_views()[i];
depth_map_info.sampler = m_shadow_depth_image->sampler();
std::array<vk::WriteDescriptorSet, 4> writes;
std::array<vk::WriteDescriptorSet, 2> writes;
writes[0].dstSet = m_sets[i];
writes[0].dstBinding = 0;
writes[0].dstArrayElement = 0;
@ -155,16 +125,42 @@ export namespace vht {
writes[1].dstArrayElement = 0;
writes[1].descriptorType = vk::DescriptorType::eCombinedImageSampler;
writes[1].setImageInfo(image_info);
writes[2].dstSet = m_sets[i];
writes[2].dstBinding = 2;
writes[2].dstArrayElement = 0;
writes[2].descriptorType = vk::DescriptorType::eUniformBuffer;
writes[2].setBufferInfo(light_buffer_info);
writes[3].dstSet = m_sets[i];
writes[3].dstBinding = 3;
writes[3].dstArrayElement = 0;
writes[3].descriptorType = vk::DescriptorType::eCombinedImageSampler;
writes[3].setImageInfo(depth_map_info);
m_device->device().updateDescriptorSets(writes, nullptr);
}
std::vector<vk::DescriptorSetLayout> second_layouts(MAX_FRAMES_IN_FLIGHT, *m_graphics_pipeline->second_descriptor_set_layout());
vk::DescriptorSetAllocateInfo second_alloc_info;
second_alloc_info.descriptorPool = m_pool;
second_alloc_info.setSetLayouts( second_layouts );
m_second_sets = m_device->device().allocateDescriptorSets(second_alloc_info);
for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i) {
vk::DescriptorBufferInfo light_buffer_info;
light_buffer_info.buffer = m_light_uniform_buffer->buffers()[i];
light_buffer_info.offset = 0;
light_buffer_info.range = sizeof(LightUBO);
std::array<vk::DescriptorImageInfo, 3> input_attachments;
input_attachments[0].imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
input_attachments[0].imageView = m_g_buffer->pos_views();
input_attachments[1].imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
input_attachments[1].imageView = m_g_buffer->color_views();
input_attachments[2].imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
input_attachments[2].imageView = m_g_buffer->normal_depth_views();
std::array<vk::WriteDescriptorSet, 2> writes;
writes[0].dstSet = m_second_sets[i];
writes[0].dstBinding = 0;
writes[0].dstArrayElement = 0;
writes[0].descriptorType = vk::DescriptorType::eUniformBuffer;
writes[0].setBufferInfo(light_buffer_info);
writes[1].dstSet = m_second_sets[i];
writes[1].dstBinding = 1;
writes[1].dstArrayElement = 0;
writes[1].descriptorType = vk::DescriptorType::eInputAttachment;
writes[1].setImageInfo(input_attachments);
m_device->device().updateDescriptorSets(writes, nullptr);
}

View File

@ -14,8 +14,6 @@ import DataLoader;
import Window;
import Device;
import Swapchain;
import ShadowRenderPass;
import ShadowPipeline;
import RenderPass;
import GraphicsPipeline;
import CommandPool;
@ -50,8 +48,6 @@ export namespace vht {
std::shared_ptr<vht::Window> m_window{ nullptr };
std::shared_ptr<vht::Device> m_device{ nullptr };
std::shared_ptr<vht::Swapchain> m_swapchain{ nullptr };
std::shared_ptr<vht::ShadowRenderPass> m_shadow_render_pass{ nullptr };
std::shared_ptr<vht::ShadowPipeline> m_shadow_pipeline{ nullptr };
std::shared_ptr<vht::RenderPass> m_render_pass{ nullptr };
std::shared_ptr<vht::GraphicsPipeline> m_graphics_pipeline{ nullptr };
std::shared_ptr<vht::CommandPool> m_command_pool{ nullptr };
@ -70,8 +66,6 @@ export namespace vht {
std::shared_ptr<vht::Window> window,
std::shared_ptr<vht::Device> device,
std::shared_ptr<vht::Swapchain> swapchain,
std::shared_ptr<vht::ShadowRenderPass> shadow_render_pass,
std::shared_ptr<vht::ShadowPipeline> shadow_pipeline,
std::shared_ptr<vht::RenderPass> render_pass,
std::shared_ptr<vht::GraphicsPipeline> graphics_pipeline,
std::shared_ptr<vht::CommandPool> command_pool,
@ -83,8 +77,6 @@ export namespace vht {
m_window(std::move(window)),
m_device(std::move(device)),
m_swapchain(std::move(swapchain)),
m_shadow_render_pass(std::move(shadow_render_pass)),
m_shadow_pipeline(std::move(shadow_pipeline)),
m_render_pass(std::move(render_pass)),
m_graphics_pipeline(std::move(graphics_pipeline)),
m_command_pool(std::move(command_pool)),
@ -109,6 +101,7 @@ export namespace vht {
image_index = idx;
} catch (const vk::OutOfDateKHRError&){
m_render_pass->recreate();
m_descriptor->recreate();
return;
}
// 重置当前帧的栅栏,延迟到此处等待,防止上方 return 导致死锁
@ -139,13 +132,16 @@ export namespace vht {
try{
if( m_device->present_queue().presentKHR(present_info) == vk::Result::eSuboptimalKHR ) {
m_render_pass->recreate();
m_descriptor->recreate();
}
} catch (const vk::OutOfDateKHRError&){
m_render_pass->recreate();
m_descriptor->recreate();
}
// 检查窗口是否被调整大小
if( m_window->framebuffer_resized() ){
m_render_pass->recreate();
m_descriptor->recreate();
}
// 更新飞行中的帧索引
m_current_frame = (m_current_frame + 1) % MAX_FRAMES_IN_FLIGHT;
@ -180,38 +176,8 @@ export namespace vht {
}
// 记录命令缓冲区
void record_command_buffer(const vk::raii::CommandBuffer& command_buffer, const uint32_t image_index) const {
// 开始记录命令缓冲区
command_buffer.begin( vk::CommandBufferBeginInfo{} );
// 记录阴影渲染通道的命令
vk::RenderPassBeginInfo shadow_pass_begin_info;
shadow_pass_begin_info.renderPass = m_shadow_render_pass->render_pass();
// 注意这里的帧缓冲区是阴影渲染通道的帧缓冲,使用 current_frame 索引而不是 image_index
shadow_pass_begin_info.framebuffer = m_shadow_render_pass->framebuffers()[m_current_frame];
shadow_pass_begin_info.renderArea.offset = vk::Offset2D{0, 0};
shadow_pass_begin_info.renderArea.extent = m_shadow_render_pass->extent(); // 注意这里使用阴影渲染通道的尺寸
constexpr vk::ClearValue shadow_clear_color = vk::ClearDepthStencilValue{ 1.0f ,0 };
shadow_pass_begin_info.setClearValues( shadow_clear_color );
command_buffer.beginRenderPass( shadow_pass_begin_info, vk::SubpassContents::eInline );
command_buffer.bindPipeline( vk::PipelineBindPoint::eGraphics, m_shadow_pipeline->pipeline() );
// 我们使用了静态视口与裁剪,因此此处无需设置
const std::vector<vk::Buffer> shadow_vertex_buffers = { *m_input_assembly->vertex_buffer() };
constexpr std::array<vk::DeviceSize,1> shadow_offsets = { 0 };
command_buffer.bindVertexBuffers( 0, shadow_vertex_buffers, shadow_offsets );
command_buffer.bindIndexBuffer( m_input_assembly->index_buffer(), 0, vk::IndexType::eUint32 );
command_buffer.bindDescriptorSets(
vk::PipelineBindPoint::eGraphics,
m_shadow_pipeline->pipeline_layout(),
0,
*m_descriptor->shadow_sets()[m_current_frame],
nullptr
);
command_buffer.drawIndexed(static_cast<uint32_t>(m_data_loader->indices().size()), 1, 0, 0, 0);
command_buffer.endRenderPass();
vk::RenderPassBeginInfo render_pass_begin_info;
render_pass_begin_info.renderPass = m_render_pass->render_pass();
render_pass_begin_info.framebuffer = m_render_pass->framebuffers()[image_index];
@ -219,9 +185,12 @@ export namespace vht {
render_pass_begin_info.renderArea.offset = vk::Offset2D{0, 0};
render_pass_begin_info.renderArea.extent = m_swapchain->extent();
std::array<vk::ClearValue, 2> clear_values;
std::array<vk::ClearValue, 5> clear_values; // 增加 3 个子元素
clear_values[0] = vk::ClearColorValue{ 0.0f, 0.0f, 0.0f, 1.0f };
clear_values[1] = vk::ClearDepthStencilValue{ 1.0f ,0 };
clear_values[2] = clear_values[0];
clear_values[3] = clear_values[0];
clear_values[4] = clear_values[0];
render_pass_begin_info.setClearValues( clear_values );
command_buffer.beginRenderPass( render_pass_begin_info, vk::SubpassContents::eInline);
@ -272,6 +241,25 @@ export namespace vht {
);
command_buffer.drawIndexed(m_data_loader->index_counts()[1], 1, m_data_loader->index_offsets()[1], 0, 1);
// ↓ 第二个管线的命令
// --- 切换到第二个子通道 ---
command_buffer.nextSubpass(vk::SubpassContents::eInline);
// --- 绑定第二个管线 ---
command_buffer.bindPipeline( vk::PipelineBindPoint::eGraphics, m_graphics_pipeline->second_pipeline() );
// 视口与裁剪共用上方的设置,此处无需再次设置
// --- 绑定描述符集合 ---
command_buffer.bindDescriptorSets(
vk::PipelineBindPoint::eGraphics,
m_graphics_pipeline->second_pipeline_layout(),
0,
*m_descriptor->second_sets()[m_current_frame],
nullptr
);
// 绘制 6 个点,对应着色器中的两个三角形
command_buffer.draw(6, 1, 0, 0);
command_buffer.endRenderPass();
command_buffer.end();
}

154
src/utils/GBuffer.cppm Normal file
View File

@ -0,0 +1,154 @@
module;
#include <memory>
#include <vector>
export module GBuffer;
import vulkan_hpp;
import Config;
import Utility;
import Device;
import Swapchain;
export namespace vht {
/**
* @brief GBuffer 类
* @details
* - pos 顶点位置缓冲区
* - color 原色彩缓冲区
* - normal_depth 法线和深度缓冲区
* - recreate(): 重新创建 GBuffer 资源
*/
class GBuffer {
std::shared_ptr<vht::Device> m_device{ nullptr };
std::shared_ptr<vht::Swapchain> m_swapchain{ nullptr };
// 用于记录顶点位置的资源
vk::raii::DeviceMemory m_pos_memory{ nullptr };
vk::raii::Image m_pos_image{ nullptr };
vk::raii::ImageView m_pos_view{ nullptr };
vk::Format m_pos_format{ vk::Format::eR32G32B32A32Sfloat }; // 四通道保证支持,三通道可能不被所有 GPU 支持
// 用于记录原色彩的资源
vk::raii::DeviceMemory m_color_memory{ nullptr };
vk::raii::Image m_color_image{ nullptr };
vk::raii::ImageView m_color_view{ nullptr };
vk::Format m_color_format{ vk::Format::eR8G8B8A8Srgb }; // 色彩格式,使用 sRGB 格式以便于显示
// 用于记录法线和深度的资源
vk::raii::DeviceMemory m_normal_depth_memory{ nullptr };
vk::raii::Image m_normal_depth_image{ nullptr };
vk::raii::ImageView m_normal_depth_view{ nullptr };
vk::Format m_normal_depth_format{ vk::Format::eR32G32B32A32Sfloat }; // 四通道保证支持,三通道可能不被所有 GPU 支持
public:
explicit GBuffer(std::shared_ptr<vht::Device> device, std::shared_ptr<vht::Swapchain> swapchain)
: m_device(std::move(device)),
m_swapchain(std::move(swapchain)) {
create_pos_images();
create_color_images();
create_normal_depth_images();
}
void recreate() {
m_pos_view.clear();
m_pos_image.clear();
m_pos_memory.clear();
m_color_view.clear();
m_color_image.clear();
m_color_memory.clear();
m_normal_depth_view.clear();
m_normal_depth_image.clear();
m_normal_depth_memory.clear();
create_pos_images();
create_color_images();
create_normal_depth_images();
}
[[nodiscard]]
const vk::raii::Image& pos_images() const { return m_pos_image; }
[[nodiscard]]
const vk::raii::ImageView& pos_views() const { return m_pos_view; }
[[nodiscard]]
vk::Format pos_format() const { return m_pos_format; }
[[nodiscard]]
const vk::raii::Image& color_images() const { return m_color_image; }
[[nodiscard]]
const vk::raii::ImageView& color_views() const { return m_color_view; }
[[nodiscard]]
vk::Format color_format() const { return m_color_format; }
[[nodiscard]]
const vk::raii::Image& normal_depth_images() const { return m_normal_depth_image; }
[[nodiscard]]
const vk::raii::ImageView& normal_depth_views() const { return m_normal_depth_view; }
[[nodiscard]]
vk::Format normal_depth_format() const { return m_normal_depth_format; }
private:
void create_pos_images() {
create_image(
m_pos_image,
m_pos_memory,
m_device->device(),
m_device->physical_device(),
m_swapchain->extent().width,
m_swapchain->extent().height,
m_pos_format,
vk::ImageTiling::eOptimal,
vk::ImageUsageFlagBits::eColorAttachment | // 需要作为色彩写入附件
vk::ImageUsageFlagBits::eInputAttachment, // 需要作为输入附件
vk::MemoryPropertyFlagBits::eDeviceLocal
);
m_pos_view = create_image_view(
m_device->device(),
m_pos_image,
m_pos_format,
vk::ImageAspectFlagBits::eColor // 依然作为色彩
);
}
void create_color_images() {
create_image(
m_color_image,
m_color_memory,
m_device->device(),
m_device->physical_device(),
m_swapchain->extent().width,
m_swapchain->extent().height,
m_color_format,
vk::ImageTiling::eOptimal,
vk::ImageUsageFlagBits::eColorAttachment | // 需要作为色彩写入附件
vk::ImageUsageFlagBits::eInputAttachment, // 需要作为输入附件
vk::MemoryPropertyFlagBits::eDeviceLocal
);
m_color_view = create_image_view(
m_device->device(),
m_color_image,
m_color_format,
vk::ImageAspectFlagBits::eColor // 依然作为色彩
);
}
void create_normal_depth_images() {
create_image(
m_normal_depth_image,
m_normal_depth_memory,
m_device->device(),
m_device->physical_device(),
m_swapchain->extent().width,
m_swapchain->extent().height,
m_normal_depth_format,
vk::ImageTiling::eOptimal,
vk::ImageUsageFlagBits::eColorAttachment | // 需要作为色彩写入附件
vk::ImageUsageFlagBits::eInputAttachment, // 需要作为输入附件
vk::MemoryPropertyFlagBits::eDeviceLocal
);
m_normal_depth_view = create_image_view(
m_device->device(),
m_normal_depth_image,
m_normal_depth_format,
vk::ImageAspectFlagBits::eColor // 依然作为色彩
);
}
};
}

View File

@ -34,6 +34,9 @@ export namespace vht {
vk::raii::DescriptorSetLayout m_descriptor_set_layout{ nullptr };
vk::raii::PipelineLayout m_pipeline_layout{ nullptr };
vk::raii::Pipeline m_pipeline{ nullptr };
vk::raii::DescriptorSetLayout m_second_descriptor_set_layout{ nullptr };
vk::raii::PipelineLayout m_second_pipeline_layout{ nullptr };
vk::raii::Pipeline m_second_pipeline{ nullptr };
public:
explicit GraphicsPipeline(std::shared_ptr<vht::Device> device, std::shared_ptr<vht::RenderPass> render_pass)
: m_device(std::move(device)),
@ -47,11 +50,19 @@ export namespace vht {
const vk::raii::PipelineLayout& pipeline_layout() const { return m_pipeline_layout; }
[[nodiscard]]
const vk::raii::Pipeline& pipeline() const { return m_pipeline; }
[[nodiscard]]
const vk::raii::DescriptorSetLayout& second_descriptor_set_layout() const { return m_second_descriptor_set_layout; }
[[nodiscard]]
const vk::raii::PipelineLayout& second_pipeline_layout() const { return m_second_pipeline_layout; }
[[nodiscard]]
const vk::raii::Pipeline& second_pipeline() const { return m_second_pipeline; }
private:
void init() {
create_descriptor_set_layout();
create_graphics_pipeline();
create_second_descriptor_set_layout();
create_second_graphics_pipeline();
}
// 创建描述符集布局
void create_descriptor_set_layout() {
@ -67,29 +78,28 @@ export namespace vht {
sampler_layout_binding.descriptorCount = 1;
sampler_layout_binding.stageFlags = vk::ShaderStageFlagBits::eFragment;
vk::DescriptorSetLayoutBinding light_ubo_layout_binging;
light_ubo_layout_binging.binding = 2;
light_ubo_layout_binging.descriptorType = vk::DescriptorType::eUniformBuffer;
light_ubo_layout_binging.descriptorCount = 1;
light_ubo_layout_binging.stageFlags = vk::ShaderStageFlagBits::eFragment;
vk::DescriptorSetLayoutBinding depth_map_layout_binding;
depth_map_layout_binding.binding = 3;
depth_map_layout_binding.descriptorType = vk::DescriptorType::eCombinedImageSampler;
depth_map_layout_binding.descriptorCount = 1;
depth_map_layout_binding.stageFlags = vk::ShaderStageFlagBits::eFragment;
auto bindings = {
ubo_layout_binging,
sampler_layout_binding,
light_ubo_layout_binging,
depth_map_layout_binding // 添加深度图像采样器描述符
};
const auto bindings = { ubo_layout_binging, sampler_layout_binding };
vk::DescriptorSetLayoutCreateInfo layoutInfo;
layoutInfo.setBindings( bindings );
m_descriptor_set_layout = m_device->device().createDescriptorSetLayout( layoutInfo );
}
void create_second_descriptor_set_layout() {
vk::DescriptorSetLayoutBinding light_ubo_layout_binging;
light_ubo_layout_binging.binding = 0;
light_ubo_layout_binging.descriptorType = vk::DescriptorType::eUniformBuffer;
light_ubo_layout_binging.descriptorCount = 1;
light_ubo_layout_binging.stageFlags = vk::ShaderStageFlagBits::eFragment;
vk::DescriptorSetLayoutBinding input_layout_binging; // 输入附件绑定
input_layout_binging.binding = 1;
input_layout_binging.descriptorType = vk::DescriptorType::eInputAttachment;
input_layout_binging.descriptorCount = 3; // 有三个输入附件
input_layout_binging.stageFlags = vk::ShaderStageFlagBits::eFragment;
const auto bindings = { light_ubo_layout_binging, input_layout_binging };
vk::DescriptorSetLayoutCreateInfo layoutInfo;
layoutInfo.setBindings( bindings );
m_second_descriptor_set_layout = m_device->device().createDescriptorSetLayout( layoutInfo );
}
// 创建图形管线
void create_graphics_pipeline() {
const auto vertex_shader_code = vht::read_shader("shaders/vert.spv");
@ -152,14 +162,16 @@ export namespace vht {
multisampling.rasterizationSamples = vk::SampleCountFlagBits::e1;
multisampling.sampleShadingEnable = false; // default
vk::PipelineColorBlendAttachmentState color_blend_attachment;
color_blend_attachment.blendEnable = false; // default
color_blend_attachment.colorWriteMask = vk::FlagTraits<vk::ColorComponentFlagBits>::allFlags;
std::array<vk::PipelineColorBlendAttachmentState, 3> color_blend_attachments;
for (auto& att : color_blend_attachments) {
att.blendEnable = false;
att.colorWriteMask = vk::FlagTraits<vk::ColorComponentFlagBits>::allFlags;
}
vk::PipelineColorBlendStateCreateInfo color_blend;
color_blend.logicOpEnable = false;
color_blend.logicOp = vk::LogicOp::eCopy;
color_blend.setAttachments( color_blend_attachment );
color_blend.setAttachments( color_blend_attachments );
vk::PushConstantRange pushConstantRange;
pushConstantRange.stageFlags = vk::ShaderStageFlagBits::eFragment;
@ -189,6 +201,84 @@ export namespace vht {
m_pipeline = m_device->device().createGraphicsPipeline( nullptr, create_info );
}
void create_second_graphics_pipeline() {
// 修改着色器名
const auto vertex_shader_code = vht::read_shader("shaders/second_vert.spv");
const auto fragment_shader_code = vht::read_shader("shaders/second_frag.spv");
const auto vertex_shader_module = vht::create_shader_module(m_device->device(), vertex_shader_code);
const auto fragment_shader_module = vht::create_shader_module(m_device->device(), fragment_shader_code);
vk::PipelineShaderStageCreateInfo vertex_shader_create_info;
vertex_shader_create_info.stage = vk::ShaderStageFlagBits::eVertex;
vertex_shader_create_info.module = vertex_shader_module;
vertex_shader_create_info.pName = "main";
vk::PipelineShaderStageCreateInfo fragment_shader_create_info;
fragment_shader_create_info.stage = vk::ShaderStageFlagBits::eFragment;
fragment_shader_create_info.module = fragment_shader_module;
fragment_shader_create_info.pName = "main";
const auto shader_stages = { vertex_shader_create_info, fragment_shader_create_info };
const auto dynamic_states = { vk::DynamicState::eViewport, vk::DynamicState::eScissor };
vk::PipelineDynamicStateCreateInfo dynamic_state;
dynamic_state.setDynamicStates(dynamic_states);
// 不需要顶点输入数据
vk::PipelineVertexInputStateCreateInfo vertex_input;
vk::PipelineInputAssemblyStateCreateInfo input_assembly;
input_assembly.topology = vk::PrimitiveTopology::eTriangleList;
vk::PipelineViewportStateCreateInfo viewport_state;
viewport_state.viewportCount = 1;
viewport_state.scissorCount = 1;
// 不需要深度测试
// vk::PipelineDepthStencilStateCreateInfo depth_stencil;
vk::PipelineRasterizationStateCreateInfo rasterizer;
rasterizer.depthClampEnable = false;
rasterizer.rasterizerDiscardEnable = false;
rasterizer.polygonMode = vk::PolygonMode::eFill;
rasterizer.lineWidth = 1.0f;
rasterizer.cullMode = vk::CullModeFlagBits::eBack;
rasterizer.frontFace = vk::FrontFace::eCounterClockwise;
rasterizer.depthBiasEnable = false;
vk::PipelineMultisampleStateCreateInfo multisampling;
multisampling.rasterizationSamples = vk::SampleCountFlagBits::e1;
multisampling.sampleShadingEnable = false; // default
vk::PipelineColorBlendAttachmentState color_blend_attachment;
color_blend_attachment.blendEnable = false; // default
color_blend_attachment.colorWriteMask = vk::FlagTraits<vk::ColorComponentFlagBits>::allFlags;
vk::PipelineColorBlendStateCreateInfo color_blend;
color_blend.logicOpEnable = false;
color_blend.logicOp = vk::LogicOp::eCopy;
color_blend.setAttachments( color_blend_attachment );
vk::PipelineLayoutCreateInfo layout_create_info;
layout_create_info.setSetLayouts( *m_second_descriptor_set_layout );
m_second_pipeline_layout = m_device->device().createPipelineLayout( layout_create_info );
vk::GraphicsPipelineCreateInfo create_info;
create_info.layout = m_second_pipeline_layout;
create_info.setStages( shader_stages );
create_info.pVertexInputState = &vertex_input;
create_info.pInputAssemblyState = &input_assembly;
create_info.pDynamicState = &dynamic_state;
create_info.pViewportState = &viewport_state;
create_info.pRasterizationState = &rasterizer;
create_info.pMultisampleState = &multisampling;
create_info.pColorBlendState = &color_blend;
create_info.renderPass = m_render_pass->render_pass();
create_info.subpass = 1; // 绑定第二个子通道
m_second_pipeline = m_device->device().createGraphicsPipeline( nullptr, create_info );
}
};
}

View File

@ -20,9 +20,6 @@ import Device;
export namespace vht {
struct LightUBO {
glm::mat4 model{ glm::mat4(1.0f) }; // 光源模型矩阵
glm::mat4 view{}; // 视图矩阵
glm::mat4 proj{}; // 投影矩阵
alignas(16) glm::vec3 light_pos{ 2.0f, 4.2f, 3.2f }; // 光源位置
alignas(16) glm::vec3 light_color{ 1.0f, 1.0f, 1.0f }; // 光源颜色
alignas(16) glm::vec3 view_pos{}; // 摄像机位置
@ -56,15 +53,8 @@ export namespace vht {
// 更新摄像机位置,在 Drawer 类的 draw 函数中调用,计算机位置来自 UniformBuffer 类
void update(const int current_frame, const glm::vec3 view_pos) const {
LightUBO light_ubp;
light_ubp.view_pos = view_pos;
light_ubp.view = glm::lookAt(
light_ubp.light_pos,
glm::vec3(0.0f, 0.0f, 0.0f),
glm::vec3(0.0f, 1.0f, 0.0f)
);
light_ubp.proj = glm::perspective(glm::radians(90.0f), 1.0f, 0.1f, 60.0f);
light_ubp.proj[1][1] *= -1;
constexpr LightUBO light_ubp;
// 如果需要更新光源,可自行修改此处代码
memcpy(m_mapped[current_frame], &light_ubp, sizeof(LightUBO));
}
private:

View File

@ -14,6 +14,7 @@ import Window;
import Device;
import Swapchain;
import DepthImage;
import GBuffer;
export namespace vht {
@ -38,6 +39,7 @@ export namespace vht {
std::shared_ptr<vht::Device> m_device{ nullptr };
std::shared_ptr<vht::Swapchain> m_swapchain{ nullptr };
std::shared_ptr<vht::DepthImage> m_depth_image{ nullptr };
std::shared_ptr<vht::GBuffer> m_g_buffer{ nullptr };
vk::raii::RenderPass m_render_pass{ nullptr };
std::vector<vk::raii::Framebuffer> m_framebuffers;
public:
@ -45,11 +47,13 @@ export namespace vht {
std::shared_ptr<vht::Window> window,
std::shared_ptr<vht::Device> device,
std::shared_ptr<vht::Swapchain> swapchain,
std::shared_ptr<vht::DepthImage> depth_image
std::shared_ptr<vht::DepthImage> depth_image,
std::shared_ptr<vht::GBuffer> g_buffer
): m_window(std::move(window)),
m_device(std::move(device)),
m_swapchain(std::move(swapchain)),
m_depth_image(std::move(depth_image)){
m_depth_image(std::move(depth_image)),
m_g_buffer(std::move(g_buffer)) {
init();
}
@ -72,6 +76,7 @@ export namespace vht {
m_framebuffers.clear();
m_swapchain->recreate();
m_depth_image->recreate();
m_g_buffer->recreate();
create_framebuffers();
m_window->reset_framebuffer_resized();
@ -117,24 +122,96 @@ export namespace vht {
depth_attachment_ref.attachment = 1;
depth_attachment_ref.layout = vk::ImageLayout::eDepthStencilAttachmentOptimal;
vk::SubpassDescription subpass;
subpass.pipelineBindPoint = vk::PipelineBindPoint::eGraphics;
subpass.setColorAttachments( color_attachment_ref );
subpass.setPDepthStencilAttachment( &depth_attachment_ref );
vk::AttachmentDescription g_pos_attachment;
g_pos_attachment.format = m_g_buffer->pos_format();
g_pos_attachment.samples = vk::SampleCountFlagBits::e1;
g_pos_attachment.loadOp = vk::AttachmentLoadOp::eClear;
g_pos_attachment.storeOp = vk::AttachmentStoreOp::eDontCare; // 渲染通道内部使用,不需要 Store
g_pos_attachment.stencilLoadOp = vk::AttachmentLoadOp::eDontCare;
g_pos_attachment.stencilStoreOp = vk::AttachmentStoreOp::eDontCare;
g_pos_attachment.initialLayout = vk::ImageLayout::eUndefined;
// 最终布局设为最后一个子通道使用时的布局,减少转换开销
g_pos_attachment.finalLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
vk::SubpassDependency dependency;
dependency.srcSubpass = vk::SubpassExternal;
dependency.srcStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput | vk::PipelineStageFlagBits::eEarlyFragmentTests;
dependency.srcAccessMask = {};
dependency.dstSubpass = 0;
dependency.dstStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput | vk::PipelineStageFlagBits::eEarlyFragmentTests;
dependency.dstAccessMask = vk::AccessFlagBits::eColorAttachmentWrite | vk::AccessFlagBits::eDepthStencilAttachmentWrite;
vk::AttachmentDescription g_color_attachment;
g_color_attachment.format = m_g_buffer->color_format();
g_color_attachment.samples = vk::SampleCountFlagBits::e1;
g_color_attachment.loadOp = vk::AttachmentLoadOp::eClear;
g_color_attachment.storeOp = vk::AttachmentStoreOp::eDontCare;
g_color_attachment.stencilLoadOp = vk::AttachmentLoadOp::eDontCare;
g_color_attachment.stencilStoreOp = vk::AttachmentStoreOp::eDontCare;
g_color_attachment.initialLayout = vk::ImageLayout::eUndefined;
g_color_attachment.finalLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
const auto attachments = { color_attachment, depth_attachment };
vk::AttachmentDescription g_normal_depth_attachment;
g_normal_depth_attachment.format = m_g_buffer->normal_depth_format();
g_normal_depth_attachment.samples = vk::SampleCountFlagBits::e1;
g_normal_depth_attachment.loadOp = vk::AttachmentLoadOp::eClear;
g_normal_depth_attachment.storeOp = vk::AttachmentStoreOp::eDontCare;
g_normal_depth_attachment.stencilLoadOp = vk::AttachmentLoadOp::eDontCare;
g_normal_depth_attachment.stencilStoreOp = vk::AttachmentStoreOp::eDontCare;
g_normal_depth_attachment.initialLayout = vk::ImageLayout::eUndefined;
g_normal_depth_attachment.finalLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
// 用于第一个子通道的附件引用
vk::AttachmentReference g_pos_out_ref;
g_pos_out_ref.attachment = 2; // 后续附件绑定到帧缓冲的实际索引
g_pos_out_ref.layout = vk::ImageLayout::eColorAttachmentOptimal;
vk::AttachmentReference g_color_out_ref;
g_color_out_ref.attachment = 3;
g_color_out_ref.layout = vk::ImageLayout::eColorAttachmentOptimal;
vk::AttachmentReference g_normal_depth_out_ref;
g_normal_depth_out_ref.attachment = 4;
g_normal_depth_out_ref.layout = vk::ImageLayout::eColorAttachmentOptimal;
// 用于第二个子通道的附件引用
vk::AttachmentReference g_pos_input_ref;
g_pos_input_ref.attachment = 2;
g_pos_input_ref.layout = vk::ImageLayout::eShaderReadOnlyOptimal;
vk::AttachmentReference g_color_input_ref;
g_color_input_ref.attachment = 3;
g_color_input_ref.layout = vk::ImageLayout::eShaderReadOnlyOptimal;
vk::AttachmentReference g_normal_depth_input_ref;
g_normal_depth_input_ref.attachment = 4;
g_normal_depth_input_ref.layout = vk::ImageLayout::eShaderReadOnlyOptimal;
std::array<vk::SubpassDescription,2> subpasses;
subpasses[0].pipelineBindPoint = vk::PipelineBindPoint::eGraphics;
const auto first_attachments = { g_pos_out_ref, g_color_out_ref, g_normal_depth_out_ref };
subpasses[0].setColorAttachments( first_attachments );
subpasses[0].setPDepthStencilAttachment( &depth_attachment_ref );
subpasses[1].pipelineBindPoint = vk::PipelineBindPoint::eGraphics;
const auto second_attachments = { g_pos_input_ref, g_color_input_ref, g_normal_depth_input_ref };
subpasses[1].setInputAttachments( second_attachments );
subpasses[1].setColorAttachments( color_attachment_ref );
std::array<vk::SubpassDependency,2> dependencies;
dependencies[0].srcSubpass = vk::SubpassExternal;
dependencies[0].srcStageMask = vk::PipelineStageFlagBits::eFragmentShader;
dependencies[0].srcAccessMask = {};
dependencies[0].dstSubpass = 0;
dependencies[0].dstStageMask = vk::PipelineStageFlagBits::eEarlyFragmentTests;
dependencies[0].dstAccessMask = vk::AccessFlagBits::eDepthStencilAttachmentWrite;
dependencies[1].srcSubpass = 0;
dependencies[1].srcStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput;
dependencies[1].srcAccessMask = vk::AccessFlagBits::eColorAttachmentWrite;
dependencies[1].dstSubpass = 1;
dependencies[1].dstStageMask = vk::PipelineStageFlagBits::eFragmentShader;
dependencies[1].dstAccessMask = vk::AccessFlagBits::eInputAttachmentRead;
const auto attachments = {
color_attachment,
depth_attachment,
g_pos_attachment,
g_color_attachment,
g_normal_depth_attachment
};
vk::RenderPassCreateInfo create_info;
create_info.setAttachments( attachments );
create_info.setSubpasses( subpass );
create_info.setDependencies( dependency );
create_info.setSubpasses( subpasses );
create_info.setDependencies( dependencies );
m_render_pass = m_device->device().createRenderPass( create_info );
}
@ -148,7 +225,13 @@ export namespace vht {
create_info.height = m_swapchain->extent().height;
create_info.layers = 1;
for (size_t i = 0; const auto& image_view : m_swapchain->image_views()) {
std::array<vk::ImageView, 2> attachments { image_view, m_depth_image->image_view() };
std::array<vk::ImageView, 5> attachments {
image_view,
m_depth_image->image_view(),
m_g_buffer->pos_views(),
m_g_buffer->color_views(),
m_g_buffer->normal_depth_views()
};
create_info.setAttachments( attachments );
m_framebuffers.emplace_back( m_device->device().createFramebuffer(create_info) );
++i;

View File

@ -1,115 +0,0 @@
module;
#include <memory>
#include <vector>
export module ShadowDepthImage;
import vulkan_hpp;
import Config;
import Utility;
import Device;
export namespace vht {
/**
* @brief 阴影深度图像相关
* @details
* - 依赖:
* - m_device: 物理/逻辑设备与队列
* - m_swapchain: 交换链
* - 工作:
* - 创建阴影深度图像
* - 创建阴影深度图像视图
* - 可访问成员:
* - image(): 阴影深度图像
* - image_view(): 阴影深度图像视图
* - format(): 阴影深度图像格式
* - width(): 阴影深度图像宽度
* - height(): 阴影深度图像高度
*/
class ShadowDepthImage {
std::shared_ptr<vht::Device> m_device{ nullptr };
std::vector<vk::raii::DeviceMemory> m_memories;
std::vector<vk::raii::Image> m_images;
std::vector<vk::raii::ImageView> m_image_views;
vk::raii::Sampler m_sampler{ nullptr };
vk::Format m_format{ vk::Format::eD32Sfloat }; // 默认格式为 D32Sfloat
uint32_t m_width{ 2000 }; // 深度图的大小,越大效果越好
uint32_t m_height{ 1600 };
public:
explicit ShadowDepthImage(std::shared_ptr<vht::Device> device)
: m_device(std::move(device)) {
init();
}
[[nodiscard]]
const std::vector<vk::raii::Image>& images() const { return m_images; }
[[nodiscard]]
const std::vector<vk::raii::ImageView>& image_views() const { return m_image_views; }
[[nodiscard]]
const vk::raii::Sampler& sampler() const { return m_sampler; }
[[nodiscard]]
vk::Format format() const { return m_format; }
[[nodiscard]]
uint32_t width() const { return m_width; }
[[nodiscard]]
uint32_t height() const { return m_height; }
private:
void init() {
create_depth_resources();
create_sampler();
}
// 创建深度图像和视图
void create_depth_resources() {
m_images.reserve(MAX_FRAMES_IN_FLIGHT);
m_image_views.reserve(MAX_FRAMES_IN_FLIGHT);
m_memories.reserve(MAX_FRAMES_IN_FLIGHT);
for (int i=0; i<vht::MAX_FRAMES_IN_FLIGHT ;++i) {
m_memories.emplace_back(nullptr);
m_images.emplace_back(nullptr);
m_image_views.emplace_back(nullptr);
create_image(
m_images.back(),
m_memories.back(),
m_device->device(),
m_device->physical_device(),
m_width,
m_height,
m_format,
vk::ImageTiling::eOptimal,
vk::ImageUsageFlagBits::eDepthStencilAttachment |
vk::ImageUsageFlagBits::eSampled,
vk::MemoryPropertyFlagBits::eDeviceLocal
);
m_image_views.back() = create_image_view(
m_device->device(),
m_images.back(),
m_format,
vk::ImageAspectFlagBits::eDepth
);
}
}
// 创建采样器
void create_sampler() {
vk::SamplerCreateInfo create_info;
create_info.magFilter = vk::Filter::eLinear;
create_info.minFilter = vk::Filter::eLinear;
create_info.mipmapMode = vk::SamplerMipmapMode::eNearest;
create_info.addressModeU = vk::SamplerAddressMode::eClampToEdge;
create_info.addressModeV = vk::SamplerAddressMode::eClampToEdge;
create_info.addressModeW = vk::SamplerAddressMode::eClampToEdge;
create_info.compareEnable = true;
create_info.compareOp = vk::CompareOp::eLess;
create_info.borderColor = vk::BorderColor::eFloatOpaqueWhite;
m_sampler = m_device->device().createSampler( create_info );
}
};
}

View File

@ -1,127 +0,0 @@
module;
#include <memory>
#include <vector>
export module ShadowPipeline;
import vulkan_hpp;
import DataLoader;
import Utility;
import Device;
import ShadowRenderPass;
export namespace vht {
class ShadowPipeline {
std::shared_ptr<vht::Device> m_device;
std::shared_ptr<vht::ShadowRenderPass> m_shadow_render_pass;
vk::raii::DescriptorSetLayout m_descriptor_set_layout{ nullptr };
vk::raii::PipelineLayout m_pipeline_layout{ nullptr };
vk::raii::Pipeline m_pipeline{ nullptr };
public:
explicit ShadowPipeline(std::shared_ptr<vht::Device> device, std::shared_ptr<vht::ShadowRenderPass> shadow_render_pass)
: m_device(std::move(device)),
m_shadow_render_pass(std::move(shadow_render_pass)) {
create_descriptor_set_layout();
create_graphics_pipeline();
}
[[nodiscard]]
const vk::raii::DescriptorSetLayout& descriptor_set_layout() const { return m_descriptor_set_layout; }
[[nodiscard]]
const vk::raii::PipelineLayout& pipeline_layout() const { return m_pipeline_layout; }
[[nodiscard]]
const vk::raii::Pipeline& pipeline() const { return m_pipeline; }
private:
void create_descriptor_set_layout() {
vk::DescriptorSetLayoutBinding light_ubo_layout_binging;
light_ubo_layout_binging.binding = 0;
light_ubo_layout_binging.descriptorType = vk::DescriptorType::eUniformBuffer;
light_ubo_layout_binging.descriptorCount = 1;
light_ubo_layout_binging.stageFlags = vk::ShaderStageFlagBits::eVertex;
vk::DescriptorSetLayoutCreateInfo layoutInfo;
layoutInfo.setBindings( light_ubo_layout_binging );
m_descriptor_set_layout = m_device->device().createDescriptorSetLayout( layoutInfo );
}
// 创建图形管线
void create_graphics_pipeline() {
const auto vertex_shader_code = vht::read_shader("shaders/shadow.spv");
const auto vertex_shader_module = vht::create_shader_module(m_device->device(), vertex_shader_code);
// 只需要顶点着色器
vk::PipelineShaderStageCreateInfo vertex_shader_create_info;
vertex_shader_create_info.stage = vk::ShaderStageFlagBits::eVertex;
vertex_shader_create_info.module = vertex_shader_module;
vertex_shader_create_info.pName = "main";
const std::vector<vk::PipelineShaderStageCreateInfo> shader_stages = { vertex_shader_create_info };
// 顶点输入状态(只需要顶点位置,不需要实例材质数据)
auto binding_description = vht::Vertex::get_binding_description();
auto attribute_description = vht::Vertex::get_attribute_description();
vk::PipelineVertexInputStateCreateInfo vertex_input;
vertex_input.setVertexBindingDescriptions(binding_description);
vertex_input.setVertexAttributeDescriptions(attribute_description);
// 输入装配状态
vk::PipelineInputAssemblyStateCreateInfo input_assembly;
input_assembly.topology = vk::PrimitiveTopology::eTriangleList;
// 视口与裁剪
// 直接使用静态状态,因为我们的深度图像大小不会变化
const vk::Viewport viewport(
0.0f, 0.0f, // x, y
static_cast<float>(m_shadow_render_pass->extent().width), // width
static_cast<float>(m_shadow_render_pass->extent().height), // height
0.0f, 1.0f // minDepth maxDepth
);
const vk::Rect2D scissor(
vk::Offset2D{0, 0}, // offset
m_shadow_render_pass->extent() // extent
);
vk::PipelineViewportStateCreateInfo viewport_state;
viewport_state.setViewports(viewport);
viewport_state.setScissors(scissor);
// 深度与模板测试
vk::PipelineDepthStencilStateCreateInfo depth_stencil;
depth_stencil.depthTestEnable = true;
depth_stencil.depthWriteEnable = true;
depth_stencil.depthCompareOp = vk::CompareOp::eLess;
// 光栅化器
vk::PipelineRasterizationStateCreateInfo rasterizer;
rasterizer.depthClampEnable = false;
rasterizer.rasterizerDiscardEnable = false;
rasterizer.polygonMode = vk::PolygonMode::eFill;
rasterizer.lineWidth = 1.0f;
rasterizer.cullMode = vk::CullModeFlagBits::eBack;
rasterizer.frontFace = vk::FrontFace::eCounterClockwise;
rasterizer.depthBiasEnable = false;
// 多重采样
vk::PipelineMultisampleStateCreateInfo multisampling;
multisampling.rasterizationSamples = vk::SampleCountFlagBits::e1;
multisampling.sampleShadingEnable = false; // default
// 管线布局,引用描述符集布局
vk::PipelineLayoutCreateInfo layout_create_info;
layout_create_info.setSetLayouts( *m_descriptor_set_layout );
m_pipeline_layout = m_device->device().createPipelineLayout( layout_create_info );
// 创建图形管线
vk::GraphicsPipelineCreateInfo create_info;
create_info.layout = m_pipeline_layout;
create_info.setStages( shader_stages );
create_info.pVertexInputState = &vertex_input;
create_info.pInputAssemblyState = &input_assembly;
create_info.pViewportState = &viewport_state;
create_info.pDepthStencilState = &depth_stencil;
create_info.pRasterizationState = &rasterizer;
create_info.pMultisampleState = &multisampling;
// 不需要颜色混合阶段
// 不需要管线动态状态
create_info.renderPass = m_shadow_render_pass->render_pass();
create_info.subpass = 0;
m_pipeline = m_device->device().createGraphicsPipeline( nullptr, create_info );
}
};
}

View File

@ -1,78 +0,0 @@
module;
#include <memory>
#include <vector>
export module ShadowRenderPass;
import vulkan_hpp;
import Device;
import ShadowDepthImage;
export namespace vht {
class ShadowRenderPass {
std::shared_ptr<vht::Device> m_device{ nullptr };
std::shared_ptr<vht::ShadowDepthImage> m_shadow_depth_image{ nullptr };
vk::raii::RenderPass m_render_pass{ nullptr };
std::vector<vk::raii::Framebuffer> m_framebuffers;
vk::Extent2D m_extent{}; // 使用阴影深度图的宽度和高度
public:
explicit ShadowRenderPass(
std::shared_ptr<vht::Device> device,
std::shared_ptr<vht::ShadowDepthImage> shadow_depth_image)
: m_device(std::move(device)),
m_shadow_depth_image(std::move(shadow_depth_image)) {
m_extent.width = m_shadow_depth_image->width();
m_extent.height = m_shadow_depth_image->height();
create_render_pass();
create_framebuffers();
}
[[nodiscard]]
const vk::raii::RenderPass& render_pass() const { return m_render_pass; }
[[nodiscard]]
const std::vector<vk::raii::Framebuffer>& framebuffers() const { return m_framebuffers; }
[[nodiscard]]
const vk::Extent2D& extent() const { return m_extent; } // 命令缓冲录制时需要使用
private:
// 创建渲染通道
void create_render_pass() {
vk::AttachmentDescription depth_attachment;
depth_attachment.format = m_shadow_depth_image->format();
depth_attachment.samples = vk::SampleCountFlagBits::e1;
depth_attachment.loadOp = vk::AttachmentLoadOp::eClear;
depth_attachment.storeOp = vk::AttachmentStoreOp::eStore; // 阴影深度图像需要存储
depth_attachment.stencilLoadOp = vk::AttachmentLoadOp::eDontCare;
depth_attachment.stencilStoreOp = vk::AttachmentStoreOp::eDontCare;
depth_attachment.initialLayout = vk::ImageLayout::eUndefined;
depth_attachment.finalLayout = vk::ImageLayout::eShaderReadOnlyOptimal; // 后续需要让着色器读取
vk::AttachmentReference depth_attachment_ref;
depth_attachment_ref.attachment = 0;
depth_attachment_ref.layout = vk::ImageLayout::eDepthStencilAttachmentOptimal;
vk::SubpassDescription subpass;
subpass.pipelineBindPoint = vk::PipelineBindPoint::eGraphics;
subpass.setPDepthStencilAttachment( &depth_attachment_ref );
vk::RenderPassCreateInfo create_info;
create_info.setAttachments( depth_attachment );
create_info.setSubpasses( subpass );
m_render_pass = m_device->device().createRenderPass( create_info );
}
// 创建帧缓冲
void create_framebuffers() {
vk::FramebufferCreateInfo create_info;
create_info.renderPass = m_render_pass;
create_info.width = m_extent.width; // 使用阴影深度图的宽度和高度
create_info.height = m_extent.height;
create_info.layers = 1;
for (const auto& image_view : m_shadow_depth_image->image_views()) {
create_info.setAttachments( *image_view ); // 记得加 * 操作符
m_framebuffers.emplace_back(m_device->device().createFramebuffer( create_info ));
}
}
};
}