feat: add pbr support
This commit is contained in:
@@ -147,6 +147,8 @@ void BindlessSetManager::initDefaultSamplers(VkDevice device, float maxAnisotrop
|
||||
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
|
||||
.magFilter = VK_FILTER_NEAREST,
|
||||
.minFilter = VK_FILTER_NEAREST,
|
||||
.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST,
|
||||
.maxLod = VK_LOD_CLAMP_NONE,
|
||||
};
|
||||
VK_CHECK(vkCreateSampler(device, &samplerCreateInfo, nullptr, &nearestSampler));
|
||||
vkutil::addDebugLabel(device, nearestSampler, "nearest");
|
||||
@@ -159,6 +161,7 @@ void BindlessSetManager::initDefaultSamplers(VkDevice device, float maxAnisotrop
|
||||
.magFilter = VK_FILTER_LINEAR,
|
||||
.minFilter = VK_FILTER_LINEAR,
|
||||
.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR,
|
||||
.maxLod = VK_LOD_CLAMP_NONE,
|
||||
};
|
||||
VK_CHECK(vkCreateSampler(device, &samplerCreateInfo, nullptr, &linearSampler));
|
||||
vkutil::addDebugLabel(device, linearSampler, "linear");
|
||||
@@ -173,6 +176,7 @@ void BindlessSetManager::initDefaultSamplers(VkDevice device, float maxAnisotrop
|
||||
.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR,
|
||||
.anisotropyEnable = VK_TRUE,
|
||||
.maxAnisotropy = maxAnisotropy,
|
||||
.maxLod = VK_LOD_CLAMP_NONE,
|
||||
};
|
||||
VK_CHECK(vkCreateSampler(device, &samplerCreateInfo, nullptr, &anisotropicSampler));
|
||||
vkutil::addDebugLabel(device, anisotropicSampler, "anisotropic");
|
||||
|
||||
@@ -57,11 +57,6 @@ MaterialID MaterialCache::addMaterial(Material material)
|
||||
device->waitIdle();
|
||||
}
|
||||
|
||||
const auto getTextureOrElse = [](ImageID imageId, ImageID placeholder)
|
||||
{
|
||||
return imageId != NULL_IMAGE_ID ? imageId : placeholder;
|
||||
};
|
||||
|
||||
MaterialData* data = static_cast<MaterialData*>(materialDataBuffer.info.pMappedData);
|
||||
|
||||
const auto id = getFreeMaterialId();
|
||||
@@ -72,28 +67,7 @@ MaterialID MaterialCache::addMaterial(Material material)
|
||||
throw std::runtime_error("MaterialCache material buffer is not mapped");
|
||||
}
|
||||
|
||||
data[id] = MaterialData{
|
||||
.baseColor = glm::vec4(material.baseColor, 1.0f),
|
||||
|
||||
.metalRoughnessEmissive = glm::vec4{
|
||||
material.metallicFactor,
|
||||
material.roughnessFactor,
|
||||
material.emissiveFactor,
|
||||
0.f
|
||||
},
|
||||
|
||||
.textureFilteringMode = static_cast<std::uint32_t>(material.textureFilteringMode),
|
||||
|
||||
.diffuseTex = getTextureOrElse(
|
||||
material.diffuseTexture,
|
||||
defaultTextures.white),
|
||||
|
||||
.normalTex = defaultTextures.normal,
|
||||
|
||||
.metallicRoughnessTex = defaultTextures.metallicRoughness,
|
||||
|
||||
.emissiveTex = defaultTextures.emissive,
|
||||
};
|
||||
data[id] = toMaterialData(material);
|
||||
device->getMemoryManager().flushAllocation(
|
||||
materialDataBuffer,
|
||||
static_cast<VkDeviceSize>(id) * sizeof(MaterialData),
|
||||
@@ -190,6 +164,35 @@ std::optional<MaterialID> MaterialCache::findMaterialByName(
|
||||
return fallback;
|
||||
}
|
||||
|
||||
MaterialData MaterialCache::toMaterialData(const Material& material) const
|
||||
{
|
||||
const auto getTextureOrElse = [](ImageID imageId, ImageID placeholder)
|
||||
{
|
||||
return imageId != NULL_IMAGE_ID ? imageId : placeholder;
|
||||
};
|
||||
|
||||
return MaterialData{
|
||||
.baseColor = glm::vec4(material.baseColor, 1.0f),
|
||||
.emissiveColor = glm::vec4(material.emissiveColor, 0.0f),
|
||||
|
||||
.metallicRoughnessNormalOcclusion = glm::vec4{
|
||||
material.metallicFactor,
|
||||
material.roughnessFactor,
|
||||
material.normalScale,
|
||||
material.occlusionStrength
|
||||
},
|
||||
|
||||
.textureFilteringMode = static_cast<std::uint32_t>(material.textureFilteringMode),
|
||||
.diffuseTex = getTextureOrElse(material.diffuseTexture, defaultTextures.white),
|
||||
.normalTex = getTextureOrElse(material.normalTexture, defaultTextures.normal),
|
||||
.metallicTex = getTextureOrElse(material.metallicTexture, defaultTextures.metallicRoughness),
|
||||
.roughnessTex = getTextureOrElse(material.roughnessTexture, defaultTextures.metallicRoughness),
|
||||
.occlusionTex = getTextureOrElse(material.occlusionTexture, defaultTextures.white),
|
||||
.emissiveTex = getTextureOrElse(material.emissiveTexture, defaultTextures.emissive),
|
||||
._pad0 = 0,
|
||||
};
|
||||
}
|
||||
|
||||
MaterialID MaterialCache::getFreeMaterialId() const
|
||||
{
|
||||
return static_cast<MaterialID>(materials.size());
|
||||
@@ -212,32 +215,10 @@ void MaterialCache::updateMaterialGPU(MaterialID id)
|
||||
assert(id < materials.size());
|
||||
assert(materialDataBuffer.info.pMappedData && "materialDataBuffer must be mapped");
|
||||
|
||||
const auto getTextureOrElse = [](ImageID imageId, ImageID placeholder)
|
||||
{
|
||||
return imageId != NULL_IMAGE_ID ? imageId : placeholder;
|
||||
};
|
||||
|
||||
Material& material = materials[id];
|
||||
|
||||
MaterialData* data =
|
||||
reinterpret_cast<MaterialData*>(materialDataBuffer.info.pMappedData);
|
||||
|
||||
data[id] = MaterialData{
|
||||
.baseColor = glm::vec4(material.baseColor, 1.0f),
|
||||
|
||||
.metalRoughnessEmissive = glm::vec4{
|
||||
material.metallicFactor,
|
||||
material.roughnessFactor,
|
||||
material.emissiveFactor,
|
||||
0.f
|
||||
},
|
||||
|
||||
.textureFilteringMode = static_cast<std::uint32_t>(material.textureFilteringMode),
|
||||
.diffuseTex = getTextureOrElse(material.diffuseTexture, defaultTextures.white),
|
||||
.normalTex = defaultTextures.normal,
|
||||
.metallicRoughnessTex = defaultTextures.metallicRoughness,
|
||||
.emissiveTex = defaultTextures.emissive,
|
||||
};
|
||||
data[id] = toMaterialData(materials[id]);
|
||||
device->getMemoryManager().flushAllocation(
|
||||
materialDataBuffer,
|
||||
static_cast<VkDeviceSize>(id) * sizeof(MaterialData),
|
||||
|
||||
@@ -148,10 +148,11 @@ std::optional<GPUImage> ImageManager::loadImageFromFile(
|
||||
VK_FORMAT_FEATURE_BLIT_DST_BIT |
|
||||
VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT;
|
||||
if ((formatProperties.optimalTilingFeatures & requiredFeatures) != requiredFeatures) {
|
||||
spdlog::error(
|
||||
"Format {} does not support linear mip generation",
|
||||
static_cast<int>(data.vkFormat));
|
||||
return std::nullopt;
|
||||
spdlog::warn(
|
||||
"Format {} does not support linear mip generation, loading '{}' without mips",
|
||||
static_cast<int>(data.vkFormat),
|
||||
path.string());
|
||||
mipMap = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,18 +9,18 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <initializer_list>
|
||||
#include <utility>
|
||||
|
||||
namespace {
|
||||
const ModelDoc::TextureInfo* FindDiffuseTexture(
|
||||
const ModelDoc::MaterialInfo& material)
|
||||
// Returns the first loadable texture, trying the semantics in order
|
||||
const ModelDoc::TextureInfo* FindTexture(
|
||||
const ModelDoc::MaterialInfo& material,
|
||||
std::initializer_list<ModelDoc::TextureSemantic> semantics)
|
||||
{
|
||||
for (const auto semantic : std::array{
|
||||
ModelDoc::TextureSemantic::BaseColor,
|
||||
ModelDoc::TextureSemantic::Diffuse,
|
||||
}) {
|
||||
for (const auto semantic : semantics) {
|
||||
for (const auto& texture : material.textures) {
|
||||
if (texture.semantic == semantic) {
|
||||
if (texture.semantic == semantic && !texture.embedded && !texture.path.empty()) {
|
||||
return &texture;
|
||||
}
|
||||
}
|
||||
@@ -230,32 +230,56 @@ void RenderResources::loadModelAsset(
|
||||
continue;
|
||||
}
|
||||
|
||||
Material material = Material::SimpleColor(
|
||||
glm::vec3{sourceMaterial.baseColor},
|
||||
sourceMaterial.name);
|
||||
material.assetReference = sourceMaterial.assetReference;
|
||||
material.metallicFactor = sourceMaterial.metallicFactor;
|
||||
material.roughnessFactor = sourceMaterial.roughnessFactor;
|
||||
material.emissiveFactor = std::max({
|
||||
sourceMaterial.emissiveColor.x,
|
||||
sourceMaterial.emissiveColor.y,
|
||||
sourceMaterial.emissiveColor.z,
|
||||
});
|
||||
|
||||
if (const auto* texture = FindDiffuseTexture(sourceMaterial);
|
||||
texture != nullptr && !texture->embedded && !texture->path.empty()) {
|
||||
material.diffuseTexture = loadImageFromFile(
|
||||
gfxDevice,
|
||||
std::filesystem::path{texture->path},
|
||||
VK_IMAGE_USAGE_SAMPLED_BIT,
|
||||
false,
|
||||
TextureIntent::ColorSrgb);
|
||||
}
|
||||
|
||||
materials().addMaterial(std::move(material));
|
||||
addModelMaterial(gfxDevice, sourceMaterial);
|
||||
}
|
||||
}
|
||||
|
||||
MaterialID RenderResources::addModelMaterial(
|
||||
GfxDevice& gfxDevice,
|
||||
const ModelDoc::MaterialInfo& sourceMaterial)
|
||||
{
|
||||
using Semantic = ModelDoc::TextureSemantic;
|
||||
|
||||
const auto loadTexture = [&](std::initializer_list<Semantic> semantics, TextureIntent intent)
|
||||
{
|
||||
const auto* texture = FindTexture(sourceMaterial, semantics);
|
||||
if (texture == nullptr) {
|
||||
return NULL_IMAGE_ID;
|
||||
}
|
||||
return loadImageFromFile(
|
||||
gfxDevice,
|
||||
std::filesystem::path{texture->path},
|
||||
VK_IMAGE_USAGE_SAMPLED_BIT,
|
||||
true,
|
||||
intent);
|
||||
};
|
||||
|
||||
Material material = Material::SimpleColor(
|
||||
glm::vec3{sourceMaterial.baseColor},
|
||||
sourceMaterial.name);
|
||||
material.assetReference = sourceMaterial.assetReference;
|
||||
material.metallicFactor = sourceMaterial.metallicFactor;
|
||||
material.roughnessFactor = sourceMaterial.roughnessFactor;
|
||||
material.emissiveColor = sourceMaterial.emissiveColor;
|
||||
|
||||
material.diffuseTexture = loadTexture({Semantic::BaseColor, Semantic::Diffuse}, TextureIntent::ColorSrgb);
|
||||
material.normalTexture = loadTexture({Semantic::Normal}, TextureIntent::DataLinear);
|
||||
material.metallicTexture = loadTexture({Semantic::MetallicRoughness, Semantic::Metallic}, TextureIntent::DataLinear);
|
||||
material.roughnessTexture = loadTexture({Semantic::MetallicRoughness, Semantic::Roughness}, TextureIntent::DataLinear);
|
||||
material.occlusionTexture = loadTexture({Semantic::AmbientOcclusion}, TextureIntent::DataLinear);
|
||||
material.emissiveTexture = loadTexture({Semantic::Emissive}, TextureIntent::ColorSrgb);
|
||||
|
||||
// Formats without PBR factors (FBX, OBJ) leave them at 0, which would hide the map completely
|
||||
if (material.metallicTexture != NULL_IMAGE_ID && material.metallicFactor == 0.0f) {
|
||||
material.metallicFactor = 1.0f;
|
||||
}
|
||||
if (material.emissiveTexture != NULL_IMAGE_ID && material.emissiveColor == glm::vec3{0.0f}) {
|
||||
material.emissiveColor = glm::vec3{1.0f};
|
||||
}
|
||||
|
||||
return materials().addMaterial(std::move(material));
|
||||
}
|
||||
|
||||
BindlessSetManager& RenderResources::getBindlessSetManager() {
|
||||
return imageCache->bindlessSetManager;
|
||||
}
|
||||
|
||||
@@ -223,7 +223,7 @@ void ResourcesInspector::drawImageDetails(ImageID id, const RenderResources& res
|
||||
std::vector<std::size_t> users;
|
||||
for (std::size_t materialId = 0; materialId < materials.materials.size(); ++materialId)
|
||||
{
|
||||
if (materials.materials[materialId].diffuseTexture == id)
|
||||
if (materials.materials[materialId].UsesTexture(id))
|
||||
{
|
||||
users.push_back(materialId);
|
||||
}
|
||||
@@ -232,7 +232,7 @@ void ResourcesInspector::drawImageDetails(ImageID id, const RenderResources& res
|
||||
if (!users.empty())
|
||||
{
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Diffuse texture of %zu material(s):", users.size());
|
||||
ImGui::Text("Used by %zu material(s):", users.size());
|
||||
|
||||
constexpr std::size_t maxListed = 8;
|
||||
for (std::size_t i = 0; i < std::min(users.size(), maxListed); ++i)
|
||||
|
||||
Reference in New Issue
Block a user