Files
Seele/src/Editor/Asset/EnvironmentLoader.cpp
T

394 lines
19 KiB
C++
Raw Normal View History

2025-04-04 14:44:53 +02:00
#include "EnvironmentLoader.h"
#include "Asset/AssetRegistry.h"
#include "Asset/EnvironmentMapAsset.h"
#include "Graphics/Descriptor.h"
2025-07-09 23:25:03 +02:00
#include "Graphics/Enums.h"
#include "Graphics/Graphics.h"
#include "Graphics/Initializer.h"
#include "Graphics/RenderTarget.h"
2025-04-04 14:44:53 +02:00
#include "stb_image.h"
using namespace Seele;
2025-04-08 17:21:23 +02:00
static constexpr uint32 SOURCE_RESOLUTION = 2048;
static constexpr uint32 CONVOLUTED_RESOLUTION = 64;
2025-04-04 14:44:53 +02:00
EnvironmentLoader::EnvironmentLoader(Gfx::PGraphics graphics) : graphics(graphics) {
cubeRenderViewport = graphics->createViewport(nullptr, ViewportCreateInfo{
.dimensions =
{
2025-04-08 17:21:23 +02:00
.size = {SOURCE_RESOLUTION, SOURCE_RESOLUTION},
2025-04-04 14:44:53 +02:00
.offset = {0, 0},
},
2025-05-12 22:48:01 +02:00
.fieldOfView = glm::radians(90.0f),
2025-04-04 14:44:53 +02:00
});
2025-04-06 09:57:47 +02:00
convolutionViewport = graphics->createViewport(nullptr, ViewportCreateInfo{
2025-04-08 17:21:23 +02:00
.dimensions =
{
.size = {CONVOLUTED_RESOLUTION, CONVOLUTED_RESOLUTION},
.offset = {0, 0},
},
2025-05-12 22:48:01 +02:00
.fieldOfView = glm::radians(90.0f),
2025-04-08 17:21:23 +02:00
});
for (uint32 i = 0; i < prefilterViewports.size(); ++i) {
prefilterViewports[i] = graphics->createViewport(nullptr, ViewportCreateInfo{
.dimensions =
{
.size = {128 * std::pow(0.5, i), 128 * std::pow(0.5, i)},
.offset = {0, 0},
},
.fieldOfView = glm::radians(90.0f),
});
}
2025-04-06 09:57:47 +02:00
cubeSampler = graphics->createSampler({
.magFilter = Gfx::SE_FILTER_LINEAR,
.minFilter = Gfx::SE_FILTER_LINEAR,
.addressModeU = Gfx::SE_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
.addressModeV = Gfx::SE_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
.addressModeW = Gfx::SE_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
});
2025-04-04 14:44:53 +02:00
cubeRenderLayout = graphics->createDescriptorLayout("pViewParams");
cubeRenderLayout->addDescriptorBinding(Gfx::DescriptorBinding{
.name = "view",
.uniformLength = sizeof(Matrix4) * 6,
2025-06-20 13:06:57 +02:00
.descriptorCount = 24,
2025-04-04 14:44:53 +02:00
});
cubeRenderLayout->addDescriptorBinding(Gfx::DescriptorBinding{
.name = "projection",
.uniformLength = sizeof(Matrix4),
2025-06-20 13:06:57 +02:00
.descriptorCount = 4,
2025-04-04 14:44:53 +02:00
});
cubeRenderLayout->addDescriptorBinding(Gfx::DescriptorBinding{
.name = "equirectangularMap",
.descriptorType = Gfx::SE_DESCRIPTOR_TYPE_SAMPLED_IMAGE,
});
cubeRenderLayout->addDescriptorBinding(Gfx::DescriptorBinding{
.name = "sampler",
.descriptorType = Gfx::SE_DESCRIPTOR_TYPE_SAMPLER,
});
2025-04-06 09:57:47 +02:00
cubeRenderLayout->addDescriptorBinding(Gfx::DescriptorBinding{
.name = "cubeMap",
.descriptorType = Gfx::SE_DESCRIPTOR_TYPE_SAMPLED_IMAGE,
});
2025-04-04 14:44:53 +02:00
cubeRenderLayout->create();
cubePipelineLayout = graphics->createPipelineLayout("CubeRenderLayout");
cubePipelineLayout->addDescriptorLayout(cubeRenderLayout);
cubePipelineLayout->addPushConstants(Gfx::SePushConstantRange{
.stageFlags = Gfx::SE_SHADER_STAGE_FRAGMENT_BIT,
.offset = 0,
.size = sizeof(float),
.name = "pRoughness",
});
2025-04-04 14:44:53 +02:00
graphics->beginShaderCompilation(ShaderCompilationInfo{
.name = "CubeRenderPipeline",
2025-07-09 23:25:03 +02:00
.modules = {"FullScreenQuad", "EnvironmentMapping"},
.entryPoints =
{
{"vertMain", "EnvironmentMapping"},
{"computeCubemap", "EnvironmentMapping"},
{"convolveCubemap", "EnvironmentMapping"},
{"computePrefilteredCubemap", "EnvironmentMapping"},
{"quadMain", "FullScreenQuad"},
{"precomputeBRDF", "EnvironmentMapping"},
},
2025-04-04 14:44:53 +02:00
.rootSignature = cubePipelineLayout,
});
cubePipelineLayout->create();
cubeRenderVertex = graphics->createVertexShader({0});
cubeRenderFrag = graphics->createFragmentShader({1});
2025-04-06 09:57:47 +02:00
convolutionFrag = graphics->createFragmentShader({2});
prefilterFrag = graphics->createFragmentShader({3});
2025-07-09 23:25:03 +02:00
lutVert = graphics->createVertexShader({4});
lutFrag = graphics->createFragmentShader({5});
Gfx::OPipelineLayout emptyLayout = graphics->createPipelineLayout("LUTlayout");
{
graphics->beginDebugRegion("PrecomputeBRDF");
lutTexture = graphics->createTexture2D(TextureCreateInfo{
.format = Gfx::SE_FORMAT_R16G16_SFLOAT,
.width = 512,
.height = 512,
.usage = Gfx::SE_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | Gfx::SE_IMAGE_USAGE_SAMPLED_BIT,
});
lutSampler = graphics->createSampler(SamplerCreateInfo{
.magFilter = Gfx::SE_FILTER_LINEAR,
.minFilter = Gfx::SE_FILTER_LINEAR,
.addressModeU = Gfx::SE_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
.addressModeV = Gfx::SE_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
});
lutViewport = graphics->createViewport(nullptr, ViewportCreateInfo{
.dimensions =
{
.size = {512, 512},
.offset = {0, 0},
},
.fieldOfView = glm::radians(90.0f),
});
Gfx::ORenderPass lutPass = graphics->createRenderPass(
Gfx::RenderTargetLayout{
.colorAttachments = {Gfx::RenderTargetAttachment(lutTexture->getDefaultView(), Gfx::SE_IMAGE_LAYOUT_UNDEFINED,
Gfx::SE_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
Gfx::SE_ATTACHMENT_LOAD_OP_DONT_CARE, Gfx::SE_ATTACHMENT_STORE_OP_STORE)},
},
{Gfx::SubPassDependency{
.srcSubpass = 0,
.dstSubpass = ~0U,
.srcStage = Gfx::SE_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
.dstStage = Gfx::SE_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
.srcAccess = Gfx::SE_ACCESS_NONE,
.dstAccess = Gfx::SE_ACCESS_SHADER_READ_BIT,
}},
URect{{512, 512}, {0, 0}}, "LUTGeneration");
Gfx::PGraphicsPipeline pipeline = graphics->createGraphicsPipeline(Gfx::LegacyPipelineCreateInfo{
.vertexShader = lutVert,
.fragmentShader = lutFrag,
.renderPass = lutPass,
.pipelineLayout = emptyLayout,
.colorBlend =
{
.attachmentCount = 1,
},
});
graphics->beginRenderPass(lutPass);
Gfx::ORenderCommand cmd = graphics->createRenderCommand("LUT");
cmd->setViewport(lutViewport);
cmd->bindPipeline(pipeline);
cmd->draw(3, 1, 0, 0);
graphics->executeCommands(std::move(cmd));
graphics->endRenderPass();
graphics->endDebugRegion();
}
2025-04-04 14:44:53 +02:00
}
EnvironmentLoader::~EnvironmentLoader() {}
void EnvironmentLoader::importAsset(EnvironmentImportArgs args) {
std::filesystem::path assetPath = args.filePath.filename();
assetPath.replace_extension("asset");
OEnvironmentMapAsset asset = new EnvironmentMapAsset(args.importPath, assetPath.stem().string());
asset->setStatus(Asset::Status::Loading);
// the registry takes ownership, but we need to edit the reference
PEnvironmentMapAsset ref = asset;
AssetRegistry::get().registerEnvironmentMap(std::move(asset));
import(args, ref);
}
void EnvironmentLoader::import(EnvironmentImportArgs args, PEnvironmentMapAsset asset) {
stbi_set_flip_vertically_on_load(true);
int width, height, nrComponents;
float* data = stbi_loadf(args.filePath.string().c_str(), &width, &height, &nrComponents, 4);
stbi_set_flip_vertically_on_load(false);
assert(data);
Gfx::OTexture2D hdrTexture = graphics->createTexture2D(TextureCreateInfo{
.sourceData =
{
.size = width * height * 4 * sizeof(float),
.data = (uint8*)data,
},
.format = Gfx::SE_FORMAT_R32G32B32A32_SFLOAT,
.width = (uint32)width,
.height = (uint32)height,
.name = "HDRRaw",
});
2025-05-12 22:48:01 +02:00
Matrix4 captureProjection = cubeRenderViewport->getProjectionMatrix(0.1f, 10.0f);
2025-04-04 14:44:53 +02:00
Matrix4 captureViews[] = {
2025-04-06 09:57:47 +02:00
glm::lookAt(Vector(0.0f, 0.0f, 0.0f), Vector(1.0f, 0.0f, 0.0f), Vector(0.0f, 1.0f, 0.0f)),
glm::lookAt(Vector(0.0f, 0.0f, 0.0f), Vector(-1.0f, 0.0f, 0.0f), Vector(0.0f, 1.0f, 0.0f)),
glm::lookAt(Vector(0.0f, 0.0f, 0.0f), Vector(0.0f, 1.0f, 0.0f), Vector(0.0f, 0.0f, 1.0f)),
glm::lookAt(Vector(0.0f, 0.0f, 0.0f), Vector(0.0f, -1.0f, 0.0f), Vector(0.0f, 0.0f, -1.0f)),
2025-04-04 14:44:53 +02:00
glm::lookAt(Vector(0.0f, 0.0f, 0.0f), Vector(0.0f, 0.0f, -1.0f), Vector(0.0f, 1.0f, 0.0f)),
2025-05-23 05:07:03 +02:00
glm::lookAt(Vector(0.0f, 0.0f, 0.0f), Vector(0.0f, 0.0f, 1.0f), Vector(0.0f, 1.0f, 0.0f)),
2025-04-04 14:44:53 +02:00
};
Gfx::PDescriptorSet set = cubeRenderLayout->allocateDescriptorSet();
set->updateConstants("view", 0, captureViews);
set->updateConstants("projection", 0, &captureProjection);
set->updateTexture("equirectangularMap", 0, hdrTexture->getDefaultView());
2025-04-04 14:44:53 +02:00
set->updateSampler("sampler", 0, cubeSampler);
set->writeChanges();
Gfx::OTextureCube cubeMap = graphics->createTextureCube(TextureCreateInfo{
.format = Gfx::SE_FORMAT_R32G32B32A32_SFLOAT,
2025-04-08 17:21:23 +02:00
.width = SOURCE_RESOLUTION,
.height = SOURCE_RESOLUTION,
2025-04-06 09:57:47 +02:00
.usage = Gfx::SE_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | Gfx::SE_IMAGE_USAGE_SAMPLED_BIT,
2025-04-04 14:44:53 +02:00
});
2025-07-09 23:25:03 +02:00
{
graphics->beginDebugRegion("CubemapGeneration");
Array<Gfx::OTextureView> cubeViews;
cubeViews.add(cubeMap->createTextureView(0, 1, 0, 1));
cubeViews.add(cubeMap->createTextureView(0, 1, 1, 1));
cubeViews.add(cubeMap->createTextureView(0, 1, 2, 1));
cubeViews.add(cubeMap->createTextureView(0, 1, 3, 1));
cubeViews.add(cubeMap->createTextureView(0, 1, 4, 1));
cubeViews.add(cubeMap->createTextureView(0, 1, 5, 1));
for (uint32 i = 0; i < 6; ++i) {
Gfx::ORenderPass cubeRenderPass = graphics->createRenderPass(
Gfx::RenderTargetLayout{
.colorAttachments =
{
Gfx::RenderTargetAttachment(cubeViews[i], Gfx::SE_IMAGE_LAYOUT_UNDEFINED,
Gfx::SE_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, Gfx::SE_ATTACHMENT_LOAD_OP_DONT_CARE,
Gfx::SE_ATTACHMENT_STORE_OP_STORE),
},
2025-05-06 19:36:43 +02:00
},
2025-07-04 23:19:10 +02:00
{
2025-07-09 23:25:03 +02:00
Gfx::SubPassDependency{
.srcSubpass = 0,
.dstSubpass = ~0U,
.srcStage = Gfx::SE_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
.dstStage = Gfx::SE_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
.srcAccess = Gfx::SE_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
.dstAccess = Gfx::SE_ACCESS_SHADER_READ_BIT,
},
2025-07-04 23:19:10 +02:00
},
2025-07-09 23:25:03 +02:00
{
.size = {SOURCE_RESOLUTION, SOURCE_RESOLUTION},
.offset = {0, 0},
},
"EnvironmentRenderPass");
Gfx::PGraphicsPipeline cubeRenderPipeline = graphics->createGraphicsPipeline(Gfx::LegacyPipelineCreateInfo{
.vertexShader = cubeRenderVertex,
.fragmentShader = cubeRenderFrag,
.renderPass = cubeRenderPass,
.pipelineLayout = cubePipelineLayout,
.colorBlend =
{
.attachmentCount = 1,
},
});
graphics->beginRenderPass(cubeRenderPass);
Gfx::ORenderCommand renderCommand = graphics->createRenderCommand("CubeMapRender");
renderCommand->bindPipeline(cubeRenderPipeline);
renderCommand->bindDescriptor({set});
renderCommand->setViewport(cubeRenderViewport);
renderCommand->draw(6, 1, i * 6, 0);
graphics->executeCommands(std::move(renderCommand));
graphics->endRenderPass();
}
graphics->endDebugRegion();
2025-07-04 23:19:10 +02:00
}
2025-04-06 09:57:47 +02:00
set = cubeRenderLayout->allocateDescriptorSet();
set->updateConstants("view", 0, captureViews);
set->updateConstants("projection", 0, &captureProjection);
set->updateSampler("sampler", 0, cubeSampler);
set->updateTexture("cubeMap", 0, cubeMap->getDefaultView());
2025-04-06 09:57:47 +02:00
set->writeChanges();
2025-07-04 23:19:10 +02:00
Gfx::OTextureCube convolutedMap = graphics->createTextureCube(TextureCreateInfo{
.format = Gfx::SE_FORMAT_R32G32B32A32_SFLOAT,
.width = CONVOLUTED_RESOLUTION,
.height = CONVOLUTED_RESOLUTION,
.usage = Gfx::SE_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | Gfx::SE_IMAGE_USAGE_SAMPLED_BIT,
2025-07-04 23:19:10 +02:00
});
2025-07-09 23:25:03 +02:00
{
graphics->beginDebugRegion("Convolution");
Array<Gfx::OTextureView> convolutedViews;
convolutedViews.add(convolutedMap->createTextureView(0, 1, 0, 1));
convolutedViews.add(convolutedMap->createTextureView(0, 1, 1, 1));
convolutedViews.add(convolutedMap->createTextureView(0, 1, 2, 1));
convolutedViews.add(convolutedMap->createTextureView(0, 1, 3, 1));
convolutedViews.add(convolutedMap->createTextureView(0, 1, 4, 1));
convolutedViews.add(convolutedMap->createTextureView(0, 1, 5, 1));
for (uint32 i = 0; i < 6; ++i) {
Gfx::ORenderPass convolutionPass = graphics->createRenderPass(
Gfx::RenderTargetLayout{
.colorAttachments = {Gfx::RenderTargetAttachment(
convolutedViews[i], Gfx::SE_IMAGE_LAYOUT_UNDEFINED, Gfx::SE_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
Gfx::SE_ATTACHMENT_LOAD_OP_DONT_CARE, Gfx::SE_ATTACHMENT_STORE_OP_STORE)},
2025-07-04 23:19:10 +02:00
},
2025-07-09 23:25:03 +02:00
{},
{
.size = {CONVOLUTED_RESOLUTION, CONVOLUTED_RESOLUTION},
.offset = {0, 0},
},
"EnvironmentRenderPass");
Gfx::PGraphicsPipeline convolutionPipeline = graphics->createGraphicsPipeline(Gfx::LegacyPipelineCreateInfo{
.vertexShader = cubeRenderVertex,
.fragmentShader = convolutionFrag,
.renderPass = convolutionPass,
.pipelineLayout = cubePipelineLayout,
.colorBlend =
{
.attachmentCount = 1,
},
});
2025-04-06 09:57:47 +02:00
2025-07-09 23:25:03 +02:00
graphics->beginRenderPass(convolutionPass);
Gfx::ORenderCommand cmd = graphics->createRenderCommand("ConvolutionPass");
cmd->bindPipeline(convolutionPipeline);
cmd->bindDescriptor({set});
cmd->setViewport(convolutionViewport);
cmd->draw(6, 1, i * 6, 0);
graphics->executeCommands(std::move(cmd));
graphics->endRenderPass();
}
graphics->endDebugRegion();
2025-07-04 23:19:10 +02:00
}
Gfx::OTextureCube prefilteredCubeMap = graphics->createTextureCube(TextureCreateInfo{
.format = Gfx::SE_FORMAT_R32G32B32A32_SFLOAT,
.width = 128,
.height = 128,
.useMip = true,
.usage = Gfx::SE_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | Gfx::SE_IMAGE_USAGE_SAMPLED_BIT,
});
2025-07-09 23:25:03 +02:00
{
graphics->beginDebugRegion("Prefiltering");
for (uint32 mip = 0; mip < prefilteredCubeMap->getMipLevels(); ++mip) {
Array<Gfx::OTextureView> views;
views.add(prefilteredCubeMap->createTextureView(mip, 1, 0, 1));
views.add(prefilteredCubeMap->createTextureView(mip, 1, 1, 1));
views.add(prefilteredCubeMap->createTextureView(mip, 1, 2, 1));
views.add(prefilteredCubeMap->createTextureView(mip, 1, 3, 1));
views.add(prefilteredCubeMap->createTextureView(mip, 1, 4, 1));
views.add(prefilteredCubeMap->createTextureView(mip, 1, 5, 1));
for (uint32 i = 0; i < 6; ++i) {
Gfx::ORenderPass prefilterPass = graphics->createRenderPass(
Gfx::RenderTargetLayout{
.colorAttachments = {Gfx::RenderTargetAttachment(
views[i], Gfx::SE_IMAGE_LAYOUT_UNDEFINED, Gfx::SE_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
Gfx::SE_ATTACHMENT_LOAD_OP_DONT_CARE, Gfx::SE_ATTACHMENT_STORE_OP_STORE)},
},
2025-07-09 23:25:03 +02:00
{},
{
.size = {prefilterViewports[mip]->getWidth(), prefilterViewports[mip]->getHeight()},
.offset = {0, 0},
},
"PrefilterPass");
Gfx::PGraphicsPipeline prefilterPipeline = graphics->createGraphicsPipeline(Gfx::LegacyPipelineCreateInfo{
.vertexShader = cubeRenderVertex,
.fragmentShader = prefilterFrag,
.renderPass = prefilterPass,
.pipelineLayout = cubePipelineLayout,
.colorBlend =
{
.attachmentCount = 1,
},
});
2025-07-09 23:25:03 +02:00
graphics->beginRenderPass(prefilterPass);
Gfx::ORenderCommand cmd = graphics->createRenderCommand("PrefilterPass");
cmd->bindPipeline(prefilterPipeline);
cmd->bindDescriptor({set});
float roughness = (float)mip / (float)(prefilteredCubeMap->getMipLevels() - 1);
cmd->pushConstants(Gfx::SE_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(float), &roughness);
cmd->setViewport(prefilterViewports[mip]);
cmd->draw(6, 1, i * 6, 0);
graphics->executeCommands(std::move(cmd));
graphics->endRenderPass();
}
}
2025-07-09 23:25:03 +02:00
graphics->endDebugRegion();
}
2025-04-06 09:57:47 +02:00
asset->skybox = std::move(cubeMap);
asset->irradianceMap = std::move(convolutedMap);
asset->prefilteredMap = std::move(prefilteredCubeMap);
2025-07-09 23:25:03 +02:00
asset->brdfLUT = lutTexture;
asset->lutSampler = lutSampler;
2025-04-04 14:44:53 +02:00
graphics->waitDeviceIdle();
}