Add Deferred rendering
This commit is contained in:
parent
206862167c
commit
ec84f9c08d
Binary file not shown.
|
|
@ -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.
|
|
@ -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.
|
|
@ -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.
|
|
@ -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.
|
|
@ -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}
|
||||
)
|
||||
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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); //第四位 深度信息此处不用
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 175 KiB |
|
|
@ -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)**
|
||||
|
||||
|
|
|
|||
|
|
@ -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` 字段
|
||||
|
||||
运行程序并移动摄像机,你将看到以下场景:
|
||||
|
||||

|
||||
|
||||
## **输入附件**
|
||||
|
||||
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();
|
||||
```
|
||||
|
||||
## **最后**
|
||||
|
||||
现在再次运行程序,您应该可以看到延迟渲染的效果,且窗口大小可以调整:
|
||||
|
||||

|
||||
|
||||
作为进阶章节,您还可以尝试以下内容:
|
||||
|
||||
- 添加材质信息到 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)**
|
||||
|
||||
---
|
||||
|
|
@ -94,6 +94,7 @@ nav:
|
|||
- TODO: todo.md # 新版同步
|
||||
- 多管线渲染:
|
||||
- 阴影映射: md/04/20_shadowmap.md
|
||||
- 延迟渲染: md/04/21_deferred.md
|
||||
- TODO: todo.md # 模板测试
|
||||
- TODO: todo.md # 延迟渲染
|
||||
- 多线程渲染:
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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); //第四位 深度信息此处不用
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
27
src/App.cppm
27
src/App.cppm
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 // 依然作为色彩
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -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 );
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 );
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -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 );
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
|
@ -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 ));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue