2025-04-04 14:44:53 +02:00
|
|
|
#include "EnvironmentLoader.h"
|
|
|
|
|
#include "Asset/AssetRegistry.h"
|
|
|
|
|
#include "Asset/EnvironmentMapAsset.h"
|
|
|
|
|
#include "Graphics/Descriptor.h"
|
|
|
|
|
#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
|
|
|
});
|
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);
|
|
|
|
|
|
|
|
|
|
graphics->beginShaderCompilation(ShaderCompilationInfo{
|
|
|
|
|
.name = "CubeRenderPipeline",
|
|
|
|
|
.modules = {"EnvironmentMapping"},
|
|
|
|
|
.entryPoints =
|
|
|
|
|
{
|
|
|
|
|
{"vertMain", "EnvironmentMapping"},
|
2025-04-06 09:57:47 +02:00
|
|
|
{"computeCubemap", "EnvironmentMapping"},
|
|
|
|
|
{"convolveCubemap", "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});
|
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);
|
2025-07-04 17:36:40 +02:00
|
|
|
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-04-06 09:57:47 +02:00
|
|
|
Gfx::ORenderPass cubeRenderPass = graphics->createRenderPass(
|
2025-04-04 14:44:53 +02:00
|
|
|
Gfx::RenderTargetLayout{
|
2025-05-06 19:36:43 +02:00
|
|
|
.colorAttachments =
|
|
|
|
|
{
|
2025-07-04 17:36:40 +02:00
|
|
|
Gfx::RenderTargetAttachment(cubeMap->getDefaultView(), Gfx::SE_IMAGE_LAYOUT_UNDEFINED, Gfx::SE_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
2025-05-06 19:36:43 +02:00
|
|
|
Gfx::SE_ATTACHMENT_LOAD_OP_DONT_CARE, Gfx::SE_ATTACHMENT_STORE_OP_STORE),
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
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-04-04 14:44:53 +02:00
|
|
|
},
|
2025-04-12 17:40:14 +02:00
|
|
|
{
|
|
|
|
|
.size = {SOURCE_RESOLUTION, SOURCE_RESOLUTION},
|
2025-04-12 17:43:47 +02:00
|
|
|
.offset = {0, 0},
|
2025-04-11 09:54:09 +02:00
|
|
|
},
|
2025-04-04 14:44:53 +02:00
|
|
|
"EnvironmentRenderPass", {0b111111}, {0b111111});
|
|
|
|
|
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);
|
2025-04-06 09:57:47 +02:00
|
|
|
renderCommand->draw(36, 1, 0, 0);
|
2025-04-04 14:44:53 +02:00
|
|
|
graphics->executeCommands(std::move(renderCommand));
|
|
|
|
|
graphics->endRenderPass();
|
2025-04-06 09:57:47 +02:00
|
|
|
|
|
|
|
|
Gfx::OTextureCube convolutedMap = graphics->createTextureCube(TextureCreateInfo{
|
|
|
|
|
.format = Gfx::SE_FORMAT_R32G32B32A32_SFLOAT,
|
2025-04-08 17:21:23 +02:00
|
|
|
.width = CONVOLUTED_RESOLUTION,
|
|
|
|
|
.height = CONVOLUTED_RESOLUTION,
|
2025-04-06 09:57:47 +02:00
|
|
|
.usage = Gfx::SE_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
|
|
|
|
|
});
|
|
|
|
|
Gfx::ORenderPass convolutionPass = graphics->createRenderPass(
|
|
|
|
|
Gfx::RenderTargetLayout{
|
2025-07-04 17:36:40 +02:00
|
|
|
.colorAttachments = {Gfx::RenderTargetAttachment(convolutedMap->getDefaultView(), Gfx::SE_IMAGE_LAYOUT_UNDEFINED,
|
2025-04-06 09:57:47 +02:00
|
|
|
Gfx::SE_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
|
|
|
|
Gfx::SE_ATTACHMENT_LOAD_OP_DONT_CARE, Gfx::SE_ATTACHMENT_STORE_OP_STORE)},
|
|
|
|
|
},
|
|
|
|
|
{},
|
2025-04-12 17:40:14 +02:00
|
|
|
{
|
|
|
|
|
.size = {CONVOLUTED_RESOLUTION, CONVOLUTED_RESOLUTION},
|
2025-04-12 17:43:47 +02:00
|
|
|
.offset = {0, 0},
|
2025-04-12 17:40:14 +02:00
|
|
|
},
|
2025-04-06 09:57:47 +02:00
|
|
|
"EnvironmentRenderPass", {0b111111}, {0b111111});
|
|
|
|
|
convolutionPipeline = graphics->createGraphicsPipeline(Gfx::LegacyPipelineCreateInfo{
|
|
|
|
|
.vertexShader = cubeRenderVertex,
|
|
|
|
|
.fragmentShader = convolutionFrag,
|
|
|
|
|
.renderPass = convolutionPass,
|
|
|
|
|
.pipelineLayout = cubePipelineLayout,
|
|
|
|
|
.colorBlend =
|
|
|
|
|
{
|
|
|
|
|
.attachmentCount = 1,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
set = cubeRenderLayout->allocateDescriptorSet();
|
|
|
|
|
set->updateConstants("view", 0, captureViews);
|
|
|
|
|
set->updateConstants("projection", 0, &captureProjection);
|
|
|
|
|
set->updateSampler("sampler", 0, cubeSampler);
|
2025-07-04 17:36:40 +02:00
|
|
|
set->updateTexture("cubeMap", 0, cubeMap->getDefaultView());
|
2025-04-06 09:57:47 +02:00
|
|
|
set->writeChanges();
|
|
|
|
|
|
|
|
|
|
graphics->beginRenderPass(convolutionPass);
|
|
|
|
|
Gfx::ORenderCommand cmd = graphics->createRenderCommand("ConvolutionPass");
|
|
|
|
|
cmd->bindPipeline(convolutionPipeline);
|
|
|
|
|
cmd->bindDescriptor({set});
|
|
|
|
|
cmd->setViewport(convolutionViewport);
|
|
|
|
|
cmd->draw(36, 1, 0, 0);
|
|
|
|
|
graphics->executeCommands(std::move(cmd));
|
|
|
|
|
graphics->endRenderPass();
|
|
|
|
|
|
|
|
|
|
asset->skybox = std::move(cubeMap);
|
|
|
|
|
asset->irradianceMap = std::move(convolutedMap);
|
2025-04-04 14:44:53 +02:00
|
|
|
graphics->waitDeviceIdle();
|
|
|
|
|
}
|