We got a skybox

This commit is contained in:
2026-01-21 06:05:35 +01:00
parent b9878f2a06
commit a5b26f3bdd
27 changed files with 843 additions and 42 deletions
+286
View File
@@ -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
);
}