We got a skybox
This commit is contained in:
@@ -0,0 +1,286 @@
|
||||
#include <destrum/Graphics/Resources/Cubemap.h>
|
||||
|
||||
#include "destrum/FS/AssetFS.h"
|
||||
#include "destrum/Graphics/GfxDevice.h"
|
||||
#include "destrum/Graphics/Pipeline.h"
|
||||
#include "destrum/Util/GameState.h"
|
||||
#include <destrum/Graphics/Util.h>
|
||||
|
||||
#include "glm/ext/matrix_clip_space.hpp"
|
||||
#include "spdlog/spdlog.h"
|
||||
|
||||
|
||||
CubeMap::CubeMap() {
|
||||
m_projection = glm::perspective(glm::radians(90.0f), 1.0f, 0.1f, 10.0f);;
|
||||
}
|
||||
|
||||
CubeMap::~CubeMap() {
|
||||
auto& gfx = GameState::GetInstance().Gfx();
|
||||
VkDevice device = gfx.getDevice();
|
||||
|
||||
if (m_skyboxView) {
|
||||
vkDestroyImageView(device, m_skyboxView, nullptr);
|
||||
m_skyboxView = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
if (m_cubemapPipelineLayout) {
|
||||
vkDestroyPipelineLayout(device, m_cubemapPipelineLayout, nullptr);
|
||||
m_cubemapPipelineLayout = VK_NULL_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
void CubeMap::LoadCubeMap(const std::filesystem::path& directoryPath) {
|
||||
m_hdrImage = GameState::GetInstance().Gfx().loadImageFromFile(directoryPath, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, false);
|
||||
}
|
||||
|
||||
void CubeMap::RenderToCubemap(ImageID inputImage,
|
||||
VkImage outputImage,
|
||||
std::array<VkImageView, 6> faceViews,
|
||||
uint32_t size)
|
||||
{
|
||||
// Ensure pipeline exists
|
||||
if (!m_cubemapPipeline || m_cubemapPipelineLayout == VK_NULL_HANDLE) {
|
||||
throw std::runtime_error("Cubemap pipeline not initialized. Call InitCubemapPipeline first.");
|
||||
}
|
||||
|
||||
auto& gfx = GameState::GetInstance().Gfx();
|
||||
|
||||
gfx.GetImmediateExecuter().immediateSubmit([&](VkCommandBuffer cmd) {
|
||||
|
||||
VkImageMemoryBarrier barrier{ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER };
|
||||
barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
barrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
barrier.image = outputImage;
|
||||
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
barrier.subresourceRange.baseMipLevel = 0;
|
||||
barrier.subresourceRange.levelCount = 1;
|
||||
barrier.subresourceRange.baseArrayLayer = 0;
|
||||
barrier.subresourceRange.layerCount = 6;
|
||||
barrier.srcAccessMask = 0;
|
||||
barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
|
||||
vkCmdPipelineBarrier(
|
||||
cmd,
|
||||
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
|
||||
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||
0,
|
||||
0, nullptr,
|
||||
0, nullptr,
|
||||
1, &barrier
|
||||
);
|
||||
|
||||
VkViewport viewport{};
|
||||
viewport.x = 0.0f;
|
||||
viewport.y = 0.0f;
|
||||
viewport.width = static_cast<float>(size);
|
||||
viewport.height = static_cast<float>(size);
|
||||
viewport.minDepth = 0.0f;
|
||||
viewport.maxDepth = 1.0f;
|
||||
|
||||
VkRect2D scissor{};
|
||||
scissor.offset = {0, 0};
|
||||
scissor.extent = {size, size};
|
||||
|
||||
for (uint32_t face = 0; face < 6; ++face) {
|
||||
PC pc{};
|
||||
pc.viewMtx = viewMatrices[face];
|
||||
pc.projMtx = m_projection;
|
||||
pc.inputImageId = m_hdrImage;
|
||||
|
||||
VkRenderingAttachmentInfoKHR colorAttachment{ VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO_KHR };
|
||||
colorAttachment.imageView = faceViews[face];
|
||||
colorAttachment.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
||||
colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
|
||||
colorAttachment.clearValue.color = {0.f, 0.f, 0.f, 1.f};
|
||||
|
||||
VkRenderingInfoKHR renderingInfo{ VK_STRUCTURE_TYPE_RENDERING_INFO_KHR };
|
||||
renderingInfo.renderArea.offset = {0, 0};
|
||||
renderingInfo.renderArea.extent = {size, size};
|
||||
renderingInfo.layerCount = 1;
|
||||
renderingInfo.colorAttachmentCount = 1;
|
||||
renderingInfo.pColorAttachments = &colorAttachment;
|
||||
|
||||
vkCmdBeginRendering(cmd, &renderingInfo);
|
||||
|
||||
vkCmdSetViewport(cmd, 0, 1, &viewport);
|
||||
vkCmdSetScissor(cmd, 0, 1, &scissor);
|
||||
|
||||
m_cubemapPipeline->bind(cmd);
|
||||
gfx.bindBindlessDescSet(cmd, m_cubemapPipelineLayout);
|
||||
|
||||
vkCmdPushConstants(
|
||||
cmd,
|
||||
m_cubemapPipelineLayout,
|
||||
VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT,
|
||||
0,
|
||||
sizeof(PC),
|
||||
&pc
|
||||
);
|
||||
|
||||
vkCmdDraw(cmd, 36, 1, 0, 0);
|
||||
|
||||
vkCmdEndRendering(cmd);
|
||||
}
|
||||
|
||||
// Transition to shader read
|
||||
barrier.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
||||
|
||||
vkCmdPipelineBarrier(
|
||||
cmd,
|
||||
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
|
||||
0,
|
||||
0, nullptr,
|
||||
0, nullptr,
|
||||
1, &barrier
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
void CubeMap::CreateCubeMap() {
|
||||
const uint32_t mipLevels = static_cast<uint32_t>(std::floor(std::log2(m_cubeMapSize))) + 1;
|
||||
|
||||
VkImageCreateInfo imageInfo{};
|
||||
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
||||
imageInfo.imageType = VK_IMAGE_TYPE_2D;
|
||||
imageInfo.extent.height = m_cubeMapSize;
|
||||
imageInfo.extent.width = m_cubeMapSize;
|
||||
imageInfo.extent.depth = 1;
|
||||
imageInfo.mipLevels = mipLevels;
|
||||
imageInfo.arrayLayers = 6; // 6 faces for cubemap
|
||||
imageInfo.format = VK_FORMAT_R32G32B32A32_SFLOAT;
|
||||
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
|
||||
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
imageInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
||||
imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
imageInfo.flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT; // Create a cubemap
|
||||
|
||||
VmaAllocationCreateInfo allocInfo{};
|
||||
allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
|
||||
|
||||
auto& device = GameState::GetInstance().Gfx();
|
||||
GPUImage cubeMapID = device.createImageRaw({
|
||||
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
|
||||
.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT,
|
||||
.flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT,
|
||||
.extent =
|
||||
VkExtent3D{
|
||||
.width = m_cubeMapSize,
|
||||
.height = m_cubeMapSize,
|
||||
.depth = 1,
|
||||
},
|
||||
.numLayers = 6,
|
||||
.mipMap = true,
|
||||
.isCubemap = true
|
||||
});
|
||||
|
||||
// if (vmaCreateImage(device.getAllocator(), &imageInfo, &allocInfo, &cubeMapID.image, &cubeMapID.allocation, nullptr) != VK_SUCCESS) {
|
||||
// throw std::runtime_error("Failed to create image with VMA!");
|
||||
// }
|
||||
|
||||
|
||||
std::array<VkImageView, 6> faceViews{};
|
||||
for (uint32_t face = 0; face < 6; ++face) {
|
||||
VkImageViewCreateInfo viewInfo{};
|
||||
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||
viewInfo.image = cubeMapID.image;
|
||||
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||
viewInfo.format = VK_FORMAT_R32G32B32A32_SFLOAT;
|
||||
viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
viewInfo.subresourceRange.baseMipLevel = 0;
|
||||
viewInfo.subresourceRange.levelCount = 1;
|
||||
viewInfo.subresourceRange.baseArrayLayer = face;
|
||||
viewInfo.subresourceRange.layerCount = 1;
|
||||
|
||||
if (vkCreateImageView(device.getDevice(), &viewInfo, nullptr, &faceViews[face]) != VK_SUCCESS) {
|
||||
throw std::runtime_error("Failed to create image view!");
|
||||
}
|
||||
}
|
||||
|
||||
VkImageViewCreateInfo viewInfo{};
|
||||
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||
viewInfo.image = cubeMapID.image;
|
||||
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_CUBE;
|
||||
viewInfo.format = VK_FORMAT_R32G32B32A32_SFLOAT;
|
||||
viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
viewInfo.subresourceRange.baseMipLevel = 0;
|
||||
viewInfo.subresourceRange.levelCount = 1;
|
||||
viewInfo.subresourceRange.baseArrayLayer = 0;
|
||||
viewInfo.subresourceRange.layerCount = 6;
|
||||
|
||||
|
||||
if (vkCreateImageView(device.getDevice(), &viewInfo, nullptr, &m_skyboxView) != VK_SUCCESS) {
|
||||
throw std::runtime_error("Failed to create image view!");
|
||||
}
|
||||
const auto vertPath = AssetFS::GetInstance().GetCookedPathForFile("engine://shaders/cubemap.vert");
|
||||
const auto fragPath = AssetFS::GetInstance().GetCookedPathForFile("engine://shaders/cubemap.frag");
|
||||
spdlog::info("hdriImage id = {}", m_hdrImage);
|
||||
RenderToCubemap(m_hdrImage, cubeMapID.image, faceViews, m_cubeMapSize);
|
||||
|
||||
m_cubemapImageID = GameState::GetInstance().Gfx().addImageToCache(cubeMapID);
|
||||
|
||||
for (auto v : faceViews) {
|
||||
vkDestroyImageView(device.getDevice(), v, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
ImageID CubeMap::GetCubeMapImageID() {
|
||||
return m_cubemapImageID;
|
||||
}
|
||||
|
||||
void CubeMap::InitCubemapPipeline(const std::string& vertPath, const std::string& fragPath)
|
||||
{
|
||||
auto& gfx = GameState::GetInstance().Gfx();
|
||||
VkDevice device = gfx.getDevice();
|
||||
|
||||
if (m_cubemapPipeline) return; // already created
|
||||
|
||||
// Save paths if you want
|
||||
m_cubemapVert = vertPath;
|
||||
m_cubemapFrag = fragPath;
|
||||
|
||||
|
||||
|
||||
VkPushConstantRange pushConstantRange{};
|
||||
pushConstantRange.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
pushConstantRange.offset = 0;
|
||||
pushConstantRange.size = sizeof(PC);
|
||||
|
||||
const auto layouts = std::array{ gfx.getBindlessDescSetLayout() };
|
||||
|
||||
VkPipelineLayoutCreateInfo pipelineLayoutInfo{ VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO };
|
||||
pipelineLayoutInfo.setLayoutCount = static_cast<uint32_t>(layouts.size());
|
||||
pipelineLayoutInfo.pSetLayouts = layouts.data();
|
||||
pipelineLayoutInfo.pushConstantRangeCount = 1;
|
||||
pipelineLayoutInfo.pPushConstantRanges = &pushConstantRange;
|
||||
|
||||
if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &m_cubemapPipelineLayout) != VK_SUCCESS) {
|
||||
throw std::runtime_error("Failed to create cubemap pipeline layout!");
|
||||
}
|
||||
|
||||
PipelineConfigInfo pipelineConfig{};
|
||||
Pipeline::DefaultPipelineConfigInfo(pipelineConfig);
|
||||
|
||||
pipelineConfig.vertexAttributeDescriptions = {};
|
||||
pipelineConfig.vertexBindingDescriptions = {};
|
||||
pipelineConfig.pipelineLayout = m_cubemapPipelineLayout;
|
||||
pipelineConfig.colorAttachments = { VK_FORMAT_R32G32B32A32_SFLOAT }; // must match cubemap image view format
|
||||
pipelineConfig.depthAttachment = VK_FORMAT_UNDEFINED;
|
||||
pipelineConfig.depthStencilInfo.depthTestEnable = VK_FALSE;
|
||||
pipelineConfig.depthStencilInfo.depthWriteEnable = VK_FALSE;
|
||||
pipelineConfig.rasterizationInfo.cullMode = VK_CULL_MODE_NONE;
|
||||
|
||||
m_cubemapPipeline = std::make_unique<Pipeline>(
|
||||
gfx,
|
||||
vertPath,
|
||||
fragPath,
|
||||
pipelineConfig
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user