ADD: emissive, occlusion
This commit is contained in:
@@ -11,6 +11,7 @@ layout(location=0) out vec4 outColor;
|
|||||||
layout(set=1, binding=0) uniform sampler2D posTex;
|
layout(set=1, binding=0) uniform sampler2D posTex;
|
||||||
layout(set=1, binding=1) uniform sampler2D normalTex;
|
layout(set=1, binding=1) uniform sampler2D normalTex;
|
||||||
layout(set=1, binding=2) uniform sampler2D albedoTex;
|
layout(set=1, binding=2) uniform sampler2D albedoTex;
|
||||||
|
layout(set=1, binding=3) uniform sampler2D extraTex;
|
||||||
layout(set=2, binding=0) uniform sampler2D shadowTex[4];
|
layout(set=2, binding=0) uniform sampler2D shadowTex[4];
|
||||||
// TLAS for ray query (optional, guarded by sceneData.rtOptions.x)
|
// TLAS for ray query (optional, guarded by sceneData.rtOptions.x)
|
||||||
#ifdef GL_EXT_ray_query
|
#ifdef GL_EXT_ray_query
|
||||||
@@ -279,6 +280,10 @@ void main(){
|
|||||||
vec3 albedo = albedoSample.rgb;
|
vec3 albedo = albedoSample.rgb;
|
||||||
float metallic = clamp(albedoSample.a, 0.0, 1.0);
|
float metallic = clamp(albedoSample.a, 0.0, 1.0);
|
||||||
|
|
||||||
|
vec4 extraSample = texture(extraTex, inUV);
|
||||||
|
float ao = extraSample.x;
|
||||||
|
vec3 emissive = extraSample.yzw;
|
||||||
|
|
||||||
vec3 camPos = vec3(inverse(sceneData.view)[3]);
|
vec3 camPos = vec3(inverse(sceneData.view)[3]);
|
||||||
vec3 V = normalize(camPos - pos);
|
vec3 V = normalize(camPos - pos);
|
||||||
|
|
||||||
@@ -340,7 +345,8 @@ void main(){
|
|||||||
vec3 specIBL = prefiltered * (F0 * brdf.x + brdf.y);
|
vec3 specIBL = prefiltered * (F0 * brdf.x + brdf.y);
|
||||||
vec3 diffIBL = (1.0 - metallic) * albedo * sh_eval_irradiance(N);
|
vec3 diffIBL = (1.0 - metallic) * albedo * sh_eval_irradiance(N);
|
||||||
|
|
||||||
vec3 color = direct + diffIBL + specIBL;
|
vec3 indirect = diffIBL + specIBL;
|
||||||
|
vec3 color = direct + indirect * ao + emissive;
|
||||||
|
|
||||||
outColor = vec4(color, 1.0);
|
outColor = vec4(color, 1.0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ layout(location=0) out vec4 outColor;
|
|||||||
layout(set=1, binding=0) uniform sampler2D posTex;
|
layout(set=1, binding=0) uniform sampler2D posTex;
|
||||||
layout(set=1, binding=1) uniform sampler2D normalTex;
|
layout(set=1, binding=1) uniform sampler2D normalTex;
|
||||||
layout(set=1, binding=2) uniform sampler2D albedoTex;
|
layout(set=1, binding=2) uniform sampler2D albedoTex;
|
||||||
|
layout(set=1, binding=3) uniform sampler2D extraTex;
|
||||||
layout(set=2, binding=0) uniform sampler2D shadowTex[4];
|
layout(set=2, binding=0) uniform sampler2D shadowTex[4];
|
||||||
|
|
||||||
// Tunables for shadow quality and blending
|
// Tunables for shadow quality and blending
|
||||||
@@ -208,6 +209,10 @@ void main(){
|
|||||||
vec3 albedo = albedoSample.rgb;
|
vec3 albedo = albedoSample.rgb;
|
||||||
float metallic = clamp(albedoSample.a, 0.0, 1.0);
|
float metallic = clamp(albedoSample.a, 0.0, 1.0);
|
||||||
|
|
||||||
|
vec4 extraSample = texture(extraTex, inUV);
|
||||||
|
float ao = extraSample.x;
|
||||||
|
vec3 emissive = extraSample.yzw;
|
||||||
|
|
||||||
vec3 camPos = vec3(inverse(sceneData.view)[3]);
|
vec3 camPos = vec3(inverse(sceneData.view)[3]);
|
||||||
vec3 V = normalize(camPos - pos);
|
vec3 V = normalize(camPos - pos);
|
||||||
|
|
||||||
@@ -235,7 +240,8 @@ void main(){
|
|||||||
vec3 specIBL = prefiltered * (F0 * brdf.x + brdf.y);
|
vec3 specIBL = prefiltered * (F0 * brdf.x + brdf.y);
|
||||||
vec3 diffIBL = (1.0 - metallic) * albedo * sh_eval_irradiance(N);
|
vec3 diffIBL = (1.0 - metallic) * albedo * sh_eval_irradiance(N);
|
||||||
|
|
||||||
vec3 color = direct + diffIBL + specIBL;
|
vec3 indirect = diffIBL + specIBL;
|
||||||
|
vec3 color = direct + indirect * ao + emissive;
|
||||||
|
|
||||||
outColor = vec4(color, 1.0);
|
outColor = vec4(color, 1.0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ layout(location = 0) out vec4 outPos;
|
|||||||
layout(location = 1) out vec4 outNorm;
|
layout(location = 1) out vec4 outNorm;
|
||||||
layout(location = 2) out vec4 outAlbedo;
|
layout(location = 2) out vec4 outAlbedo;
|
||||||
layout(location = 3) out uint outObjectID;
|
layout(location = 3) out uint outObjectID;
|
||||||
|
layout(location = 4) out vec4 outExtra;
|
||||||
|
|
||||||
// Keep push constants layout in sync with mesh.vert / GPUDrawPushConstants
|
// Keep push constants layout in sync with mesh.vert / GPUDrawPushConstants
|
||||||
struct Vertex {
|
struct Vertex {
|
||||||
@@ -58,5 +59,13 @@ void main() {
|
|||||||
outPos = vec4(inWorldPos, 1.0);
|
outPos = vec4(inWorldPos, 1.0);
|
||||||
outNorm = vec4(Nw, roughness);
|
outNorm = vec4(Nw, roughness);
|
||||||
outAlbedo = vec4(albedo, metallic);
|
outAlbedo = vec4(albedo, metallic);
|
||||||
|
// Extra G-buffer: x = AO, yzw = emissive
|
||||||
|
float aoStrength = clamp(materialData.extra[0].y, 0.0, 1.0);
|
||||||
|
float aoTex = texture(occlusionTex, inUV).r;
|
||||||
|
float ao = 1.0 - aoStrength + aoStrength * aoTex;
|
||||||
|
vec3 emissiveFactor = materialData.extra[1].rgb;
|
||||||
|
vec3 emissiveTex = texture(emissiveTex, inUV).rgb;
|
||||||
|
vec3 emissive = emissiveTex * emissiveFactor;
|
||||||
|
outExtra = vec4(ao, emissive);
|
||||||
outObjectID = PushConstants.objectID;
|
outObjectID = PushConstants.objectID;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,4 +44,6 @@ layout(set = 1, binding = 0) uniform GLTFMaterialData{
|
|||||||
|
|
||||||
layout(set = 1, binding = 1) uniform sampler2D colorTex;
|
layout(set = 1, binding = 1) uniform sampler2D colorTex;
|
||||||
layout(set = 1, binding = 2) uniform sampler2D metalRoughTex;
|
layout(set = 1, binding = 2) uniform sampler2D metalRoughTex;
|
||||||
layout(set = 1, binding = 3) uniform sampler2D normalMap; // tangent-space normal, UNORM
|
layout(set = 1, binding = 3) uniform sampler2D normalMap; // tangent-space normal, UNORM
|
||||||
|
layout(set = 1, binding = 4) uniform sampler2D occlusionTex; // occlusion (R channel)
|
||||||
|
layout(set = 1, binding = 5) uniform sampler2D emissiveTex; // emissive (RGB, sRGB)
|
||||||
|
|||||||
@@ -61,7 +61,18 @@ void main()
|
|||||||
vec3 specIBL = prefiltered * (F0 * brdf.x + brdf.y);
|
vec3 specIBL = prefiltered * (F0 * brdf.x + brdf.y);
|
||||||
vec3 diffIBL = (1.0 - metallic) * albedo * sh_eval_irradiance(N);
|
vec3 diffIBL = (1.0 - metallic) * albedo * sh_eval_irradiance(N);
|
||||||
|
|
||||||
vec3 color = direct + diffIBL + specIBL;
|
// Ambient occlusion from texture + strength (indirect only)
|
||||||
|
float aoStrength = clamp(materialData.extra[0].y, 0.0, 1.0);
|
||||||
|
float aoTex = texture(occlusionTex, inUV).r;
|
||||||
|
float ao = 1.0 - aoStrength + aoStrength * aoTex;
|
||||||
|
|
||||||
|
// Emissive from texture and factor
|
||||||
|
vec3 emissiveFactor = materialData.extra[1].rgb;
|
||||||
|
vec3 emissiveTex = texture(emissiveTex, inUV).rgb;
|
||||||
|
vec3 emissive = emissiveTex * emissiveFactor;
|
||||||
|
|
||||||
|
vec3 indirect = diffIBL + specIBL;
|
||||||
|
vec3 color = direct + indirect * ao + emissive;
|
||||||
|
|
||||||
// Alpha from baseColor texture and factor (glTF spec)
|
// Alpha from baseColor texture and factor (glTF spec)
|
||||||
float alpha = clamp(baseTex.a * materialData.colorFactors.a, 0.0, 1.0);
|
float alpha = clamp(baseTex.a * materialData.colorFactors.a, 0.0, 1.0);
|
||||||
|
|||||||
@@ -208,12 +208,16 @@ std::shared_ptr<MeshAsset> AssetManager::createMesh(const MeshCreateInfo &info)
|
|||||||
AllocatedBuffer matBuffer = createMaterialBufferWithConstants(opt.constants);
|
AllocatedBuffer matBuffer = createMaterialBufferWithConstants(opt.constants);
|
||||||
|
|
||||||
GLTFMetallic_Roughness::MaterialResources res{};
|
GLTFMetallic_Roughness::MaterialResources res{};
|
||||||
res.colorImage = _engine->_errorCheckerboardImage; // visible fallback for albedo
|
res.colorImage = _engine->_errorCheckerboardImage;
|
||||||
res.colorSampler = _engine->_samplerManager->defaultLinear();
|
res.colorSampler = _engine->_samplerManager->defaultLinear();
|
||||||
res.metalRoughImage = _engine->_whiteImage;
|
res.metalRoughImage = _engine->_whiteImage;
|
||||||
res.metalRoughSampler = _engine->_samplerManager->defaultLinear();
|
res.metalRoughSampler = _engine->_samplerManager->defaultLinear();
|
||||||
res.normalImage = _engine->_flatNormalImage;
|
res.normalImage = _engine->_flatNormalImage;
|
||||||
res.normalSampler = _engine->_samplerManager->defaultLinear();
|
res.normalSampler = _engine->_samplerManager->defaultLinear();
|
||||||
|
res.occlusionImage = _engine->_whiteImage;
|
||||||
|
res.occlusionSampler = _engine->_samplerManager->defaultLinear();
|
||||||
|
res.emissiveImage = _engine->_blackImage;
|
||||||
|
res.emissiveSampler = _engine->_samplerManager->defaultLinear();
|
||||||
res.dataBuffer = matBuffer.buffer;
|
res.dataBuffer = matBuffer.buffer;
|
||||||
res.dataBufferOffset = 0;
|
res.dataBufferOffset = 0;
|
||||||
|
|
||||||
@@ -267,6 +271,27 @@ std::shared_ptr<MeshAsset> AssetManager::createMesh(const MeshCreateInfo &info)
|
|||||||
cache->watchBinding(handle, mat->data.materialSet, 3u, samp, _engine->_flatNormalImage.imageView);
|
cache->watchBinding(handle, mat->data.materialSet, 3u, samp, _engine->_flatNormalImage.imageView);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!opt.occlusionPath.empty())
|
||||||
|
{
|
||||||
|
auto key = buildKey(opt.occlusionPath, opt.occlusionSRGB);
|
||||||
|
key.channels = TextureCache::TextureKey::ChannelsHint::R;
|
||||||
|
if (key.hash != 0)
|
||||||
|
{
|
||||||
|
VkSampler samp = _engine->_samplerManager->defaultLinear();
|
||||||
|
auto handle = cache->request(key, samp);
|
||||||
|
cache->watchBinding(handle, mat->data.materialSet, 4u, samp, _engine->_whiteImage.imageView);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!opt.emissivePath.empty())
|
||||||
|
{
|
||||||
|
auto key = buildKey(opt.emissivePath, opt.emissiveSRGB);
|
||||||
|
if (key.hash != 0)
|
||||||
|
{
|
||||||
|
VkSampler samp = _engine->_samplerManager->defaultLinear();
|
||||||
|
auto handle = cache->request(key, samp);
|
||||||
|
cache->watchBinding(handle, mat->data.materialSet, 5u, samp, _engine->_blackImage.imageView);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mesh = createMesh(info.name, vertsSpan, indsSpan, mat);
|
mesh = createMesh(info.name, vertsSpan, indsSpan, mat);
|
||||||
@@ -466,6 +491,10 @@ AllocatedBuffer AssetManager::createMaterialBufferWithConstants(
|
|||||||
{
|
{
|
||||||
matConstants->extra[0].x = 1.0f; // normal scale default
|
matConstants->extra[0].x = 1.0f; // normal scale default
|
||||||
}
|
}
|
||||||
|
if (matConstants->extra[0].y == 0.0f)
|
||||||
|
{
|
||||||
|
matConstants->extra[0].y = 1.0f;
|
||||||
|
}
|
||||||
// Ensure writes are visible on non-coherent memory
|
// Ensure writes are visible on non-coherent memory
|
||||||
vmaFlushAllocation(_engine->_deviceManager->allocator(), matBuffer.allocation, 0,
|
vmaFlushAllocation(_engine->_deviceManager->allocator(), matBuffer.allocation, 0,
|
||||||
sizeof(GLTFMetallic_Roughness::MaterialConstants));
|
sizeof(GLTFMetallic_Roughness::MaterialConstants));
|
||||||
@@ -525,6 +554,10 @@ std::shared_ptr<MeshAsset> AssetManager::createMesh(const std::string &name,
|
|||||||
matResources.metalRoughSampler = _engine->_samplerManager->defaultLinear();
|
matResources.metalRoughSampler = _engine->_samplerManager->defaultLinear();
|
||||||
matResources.normalImage = _engine->_flatNormalImage;
|
matResources.normalImage = _engine->_flatNormalImage;
|
||||||
matResources.normalSampler = _engine->_samplerManager->defaultLinear();
|
matResources.normalSampler = _engine->_samplerManager->defaultLinear();
|
||||||
|
matResources.occlusionImage = _engine->_whiteImage;
|
||||||
|
matResources.occlusionSampler = _engine->_samplerManager->defaultLinear();
|
||||||
|
matResources.emissiveImage = _engine->_blackImage;
|
||||||
|
matResources.emissiveSampler = _engine->_samplerManager->defaultLinear();
|
||||||
|
|
||||||
AllocatedBuffer matBuffer = createMaterialBufferWithConstants({});
|
AllocatedBuffer matBuffer = createMaterialBufferWithConstants({});
|
||||||
matResources.dataBuffer = matBuffer.buffer;
|
matResources.dataBuffer = matBuffer.buffer;
|
||||||
@@ -569,6 +602,10 @@ std::shared_ptr<GLTFMaterial> AssetManager::createMaterialFromConstants(
|
|||||||
res.metalRoughSampler = _engine->_samplerManager->defaultLinear();
|
res.metalRoughSampler = _engine->_samplerManager->defaultLinear();
|
||||||
res.normalImage = _engine->_flatNormalImage;
|
res.normalImage = _engine->_flatNormalImage;
|
||||||
res.normalSampler = _engine->_samplerManager->defaultLinear();
|
res.normalSampler = _engine->_samplerManager->defaultLinear();
|
||||||
|
res.occlusionImage = _engine->_whiteImage;
|
||||||
|
res.occlusionSampler = _engine->_samplerManager->defaultLinear();
|
||||||
|
res.emissiveImage = _engine->_blackImage;
|
||||||
|
res.emissiveSampler = _engine->_samplerManager->defaultLinear();
|
||||||
|
|
||||||
AllocatedBuffer buf = createMaterialBufferWithConstants(constants);
|
AllocatedBuffer buf = createMaterialBufferWithConstants(constants);
|
||||||
res.dataBuffer = buf.buffer;
|
res.dataBuffer = buf.buffer;
|
||||||
|
|||||||
@@ -28,10 +28,14 @@ public:
|
|||||||
// Optional tangent-space normal map for PBR (placeholder; not wired yet)
|
// Optional tangent-space normal map for PBR (placeholder; not wired yet)
|
||||||
// When enabled later, this will be sampled in shaders and requires tangents.
|
// When enabled later, this will be sampled in shaders and requires tangents.
|
||||||
std::string normalPath;
|
std::string normalPath;
|
||||||
|
std::string occlusionPath;
|
||||||
|
std::string emissivePath;
|
||||||
|
|
||||||
bool albedoSRGB = true;
|
bool albedoSRGB = true;
|
||||||
bool metalRoughSRGB = false;
|
bool metalRoughSRGB = false;
|
||||||
bool normalSRGB = false; // normal maps are typically non-sRGB
|
bool normalSRGB = false; // normal maps are typically non-sRGB
|
||||||
|
bool occlusionSRGB = false;
|
||||||
|
bool emissiveSRGB = true;
|
||||||
|
|
||||||
GLTFMetallic_Roughness::MaterialConstants constants{};
|
GLTFMetallic_Roughness::MaterialConstants constants{};
|
||||||
|
|
||||||
|
|||||||
@@ -70,6 +70,8 @@ void SwapchainManager::init_swapchain()
|
|||||||
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT);
|
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT);
|
||||||
_gBufferAlbedo = _resourceManager->create_image(drawImageExtent, VK_FORMAT_R8G8B8A8_UNORM,
|
_gBufferAlbedo = _resourceManager->create_image(drawImageExtent, VK_FORMAT_R8G8B8A8_UNORM,
|
||||||
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT);
|
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT);
|
||||||
|
_gBufferExtra = _resourceManager->create_image(drawImageExtent, VK_FORMAT_R16G16B16A16_SFLOAT,
|
||||||
|
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT);
|
||||||
_idBuffer = _resourceManager->create_image(drawImageExtent, VK_FORMAT_R32_UINT,
|
_idBuffer = _resourceManager->create_image(drawImageExtent, VK_FORMAT_R32_UINT,
|
||||||
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
|
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
|
||||||
VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
|
VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
|
||||||
@@ -85,6 +87,7 @@ void SwapchainManager::init_swapchain()
|
|||||||
_resourceManager->destroy_image(_gBufferPosition);
|
_resourceManager->destroy_image(_gBufferPosition);
|
||||||
_resourceManager->destroy_image(_gBufferNormal);
|
_resourceManager->destroy_image(_gBufferNormal);
|
||||||
_resourceManager->destroy_image(_gBufferAlbedo);
|
_resourceManager->destroy_image(_gBufferAlbedo);
|
||||||
|
_resourceManager->destroy_image(_gBufferExtra);
|
||||||
_resourceManager->destroy_image(_idBuffer);
|
_resourceManager->destroy_image(_idBuffer);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -196,6 +199,8 @@ void SwapchainManager::resize_swapchain(struct SDL_Window *window)
|
|||||||
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT);
|
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT);
|
||||||
_gBufferAlbedo = _resourceManager->create_image(drawImageExtent, VK_FORMAT_R8G8B8A8_UNORM,
|
_gBufferAlbedo = _resourceManager->create_image(drawImageExtent, VK_FORMAT_R8G8B8A8_UNORM,
|
||||||
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT);
|
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT);
|
||||||
|
_gBufferExtra = _resourceManager->create_image(drawImageExtent, VK_FORMAT_R16G16B16A16_SFLOAT,
|
||||||
|
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT);
|
||||||
_idBuffer = _resourceManager->create_image(drawImageExtent, VK_FORMAT_R32_UINT,
|
_idBuffer = _resourceManager->create_image(drawImageExtent, VK_FORMAT_R32_UINT,
|
||||||
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
|
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
|
||||||
VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
|
VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
|
||||||
@@ -211,6 +216,7 @@ void SwapchainManager::resize_swapchain(struct SDL_Window *window)
|
|||||||
_resourceManager->destroy_image(_gBufferPosition);
|
_resourceManager->destroy_image(_gBufferPosition);
|
||||||
_resourceManager->destroy_image(_gBufferNormal);
|
_resourceManager->destroy_image(_gBufferNormal);
|
||||||
_resourceManager->destroy_image(_gBufferAlbedo);
|
_resourceManager->destroy_image(_gBufferAlbedo);
|
||||||
|
_resourceManager->destroy_image(_gBufferExtra);
|
||||||
_resourceManager->destroy_image(_idBuffer);
|
_resourceManager->destroy_image(_idBuffer);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ public:
|
|||||||
AllocatedImage gBufferPosition() const { return _gBufferPosition; }
|
AllocatedImage gBufferPosition() const { return _gBufferPosition; }
|
||||||
AllocatedImage gBufferNormal() const { return _gBufferNormal; }
|
AllocatedImage gBufferNormal() const { return _gBufferNormal; }
|
||||||
AllocatedImage gBufferAlbedo() const { return _gBufferAlbedo; }
|
AllocatedImage gBufferAlbedo() const { return _gBufferAlbedo; }
|
||||||
|
AllocatedImage gBufferExtra() const { return _gBufferExtra; }
|
||||||
AllocatedImage idBuffer() const { return _idBuffer; }
|
AllocatedImage idBuffer() const { return _idBuffer; }
|
||||||
VkExtent2D windowExtent() const { return _windowExtent; }
|
VkExtent2D windowExtent() const { return _windowExtent; }
|
||||||
|
|
||||||
@@ -51,6 +52,7 @@ private:
|
|||||||
AllocatedImage _gBufferPosition = {};
|
AllocatedImage _gBufferPosition = {};
|
||||||
AllocatedImage _gBufferNormal = {};
|
AllocatedImage _gBufferNormal = {};
|
||||||
AllocatedImage _gBufferAlbedo = {};
|
AllocatedImage _gBufferAlbedo = {};
|
||||||
|
AllocatedImage _gBufferExtra = {};
|
||||||
AllocatedImage _idBuffer = {};
|
AllocatedImage _idBuffer = {};
|
||||||
|
|
||||||
DeletionQueue _deletionQueue;
|
DeletionQueue _deletionQueue;
|
||||||
|
|||||||
@@ -613,6 +613,7 @@ void VulkanEngine::draw()
|
|||||||
RGImageHandle hGBufferPosition = _renderGraph->import_gbuffer_position();
|
RGImageHandle hGBufferPosition = _renderGraph->import_gbuffer_position();
|
||||||
RGImageHandle hGBufferNormal = _renderGraph->import_gbuffer_normal();
|
RGImageHandle hGBufferNormal = _renderGraph->import_gbuffer_normal();
|
||||||
RGImageHandle hGBufferAlbedo = _renderGraph->import_gbuffer_albedo();
|
RGImageHandle hGBufferAlbedo = _renderGraph->import_gbuffer_albedo();
|
||||||
|
RGImageHandle hGBufferExtra = _renderGraph->import_gbuffer_extra();
|
||||||
RGImageHandle hSwapchain = _renderGraph->import_swapchain_image(swapchainImageIndex);
|
RGImageHandle hSwapchain = _renderGraph->import_swapchain_image(swapchainImageIndex);
|
||||||
// For debug overlays (IBL volumes), re-use HDR draw image as a color target.
|
// For debug overlays (IBL volumes), re-use HDR draw image as a color target.
|
||||||
RGImageHandle hDebugColor = hDraw;
|
RGImageHandle hDebugColor = hDraw;
|
||||||
@@ -656,7 +657,7 @@ void VulkanEngine::draw()
|
|||||||
if (auto *geometry = _renderPassManager->getPass<GeometryPass>())
|
if (auto *geometry = _renderPassManager->getPass<GeometryPass>())
|
||||||
{
|
{
|
||||||
RGImageHandle hID = _renderGraph->import_id_buffer();
|
RGImageHandle hID = _renderGraph->import_id_buffer();
|
||||||
geometry->register_graph(_renderGraph.get(), hGBufferPosition, hGBufferNormal, hGBufferAlbedo, hID, hDepth);
|
geometry->register_graph(_renderGraph.get(), hGBufferPosition, hGBufferNormal, hGBufferAlbedo, hGBufferExtra, hID, hDepth);
|
||||||
|
|
||||||
// If ID-buffer picking is enabled and a pick was requested this frame,
|
// If ID-buffer picking is enabled and a pick was requested this frame,
|
||||||
// add a small transfer pass to read back 1 pixel from the ID buffer.
|
// add a small transfer pass to read back 1 pixel from the ID buffer.
|
||||||
@@ -719,7 +720,7 @@ void VulkanEngine::draw()
|
|||||||
}
|
}
|
||||||
if (auto *lighting = _renderPassManager->getPass<LightingPass>())
|
if (auto *lighting = _renderPassManager->getPass<LightingPass>())
|
||||||
{
|
{
|
||||||
lighting->register_graph(_renderGraph.get(), hDraw, hGBufferPosition, hGBufferNormal, hGBufferAlbedo,
|
lighting->register_graph(_renderGraph.get(), hDraw, hGBufferPosition, hGBufferNormal, hGBufferAlbedo, hGBufferExtra,
|
||||||
std::span<RGImageHandle>(hShadowCascades.data(), hShadowCascades.size()));
|
std::span<RGImageHandle>(hShadowCascades.data(), hShadowCascades.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -956,6 +956,18 @@ RGImageHandle RenderGraph::import_gbuffer_albedo()
|
|||||||
return import_image(d);
|
return import_image(d);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RGImageHandle RenderGraph::import_gbuffer_extra()
|
||||||
|
{
|
||||||
|
RGImportedImageDesc d{};
|
||||||
|
d.name = "gBuffer.extra";
|
||||||
|
d.image = _context->getSwapchain()->gBufferExtra().image;
|
||||||
|
d.imageView = _context->getSwapchain()->gBufferExtra().imageView;
|
||||||
|
d.format = _context->getSwapchain()->gBufferExtra().imageFormat;
|
||||||
|
d.extent = _context->getDrawExtent();
|
||||||
|
d.currentLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||||
|
return import_image(d);
|
||||||
|
}
|
||||||
|
|
||||||
RGImageHandle RenderGraph::import_id_buffer()
|
RGImageHandle RenderGraph::import_id_buffer()
|
||||||
{
|
{
|
||||||
RGImportedImageDesc d{};
|
RGImportedImageDesc d{};
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ struct Pass; // fwd
|
|||||||
RGImageHandle import_gbuffer_position();
|
RGImageHandle import_gbuffer_position();
|
||||||
RGImageHandle import_gbuffer_normal();
|
RGImageHandle import_gbuffer_normal();
|
||||||
RGImageHandle import_gbuffer_albedo();
|
RGImageHandle import_gbuffer_albedo();
|
||||||
|
RGImageHandle import_gbuffer_extra();
|
||||||
RGImageHandle import_id_buffer();
|
RGImageHandle import_id_buffer();
|
||||||
RGImageHandle import_swapchain_image(uint32_t index);
|
RGImageHandle import_swapchain_image(uint32_t index);
|
||||||
void add_present_chain(RGImageHandle sourceDraw,
|
void add_present_chain(RGImageHandle sourceDraw,
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ void GLTFMetallic_Roughness::build_pipelines(VulkanEngine *engine)
|
|||||||
layoutBuilder.add_binding(1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
|
layoutBuilder.add_binding(1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
|
||||||
layoutBuilder.add_binding(2, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
|
layoutBuilder.add_binding(2, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
|
||||||
layoutBuilder.add_binding(3, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
|
layoutBuilder.add_binding(3, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
|
||||||
|
layoutBuilder.add_binding(4, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
|
||||||
|
layoutBuilder.add_binding(5, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
|
||||||
|
|
||||||
materialLayout = layoutBuilder.build(engine->_deviceManager->device(),
|
materialLayout = layoutBuilder.build(engine->_deviceManager->device(),
|
||||||
VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT,
|
VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT,
|
||||||
@@ -93,9 +95,10 @@ void GLTFMetallic_Roughness::build_pipelines(VulkanEngine *engine)
|
|||||||
engine->_swapchainManager->gBufferPosition().imageFormat,
|
engine->_swapchainManager->gBufferPosition().imageFormat,
|
||||||
engine->_swapchainManager->gBufferNormal().imageFormat,
|
engine->_swapchainManager->gBufferNormal().imageFormat,
|
||||||
engine->_swapchainManager->gBufferAlbedo().imageFormat,
|
engine->_swapchainManager->gBufferAlbedo().imageFormat,
|
||||||
engine->_swapchainManager->idBuffer().imageFormat
|
engine->_swapchainManager->idBuffer().imageFormat,
|
||||||
|
engine->_swapchainManager->gBufferExtra().imageFormat
|
||||||
};
|
};
|
||||||
b.set_color_attachment_formats(std::span<VkFormat>(gFormats, 4));
|
b.set_color_attachment_formats(std::span<VkFormat>(gFormats, 5));
|
||||||
b.set_depth_format(engine->_swapchainManager->depthImage().imageFormat);
|
b.set_depth_format(engine->_swapchainManager->depthImage().imageFormat);
|
||||||
};
|
};
|
||||||
engine->_pipelineManager->registerGraphics("mesh.gbuffer", gbufferInfo);
|
engine->_pipelineManager->registerGraphics("mesh.gbuffer", gbufferInfo);
|
||||||
@@ -139,6 +142,10 @@ MaterialInstance GLTFMetallic_Roughness::write_material(VkDevice device, Materia
|
|||||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
|
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
|
||||||
writer.write_image(3, resources.normalImage.imageView, resources.normalSampler,
|
writer.write_image(3, resources.normalImage.imageView, resources.normalSampler,
|
||||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
|
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
|
||||||
|
writer.write_image(4, resources.occlusionImage.imageView, resources.occlusionSampler,
|
||||||
|
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
|
||||||
|
writer.write_image(5, resources.emissiveImage.imageView, resources.emissiveSampler,
|
||||||
|
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
|
||||||
|
|
||||||
writer.update_set(device, matData.materialSet);
|
writer.update_set(device, matData.materialSet);
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,10 @@ struct GLTFMetallic_Roughness
|
|||||||
VkSampler metalRoughSampler;
|
VkSampler metalRoughSampler;
|
||||||
AllocatedImage normalImage;
|
AllocatedImage normalImage;
|
||||||
VkSampler normalSampler;
|
VkSampler normalSampler;
|
||||||
|
AllocatedImage occlusionImage;
|
||||||
|
VkSampler occlusionSampler;
|
||||||
|
AllocatedImage emissiveImage;
|
||||||
|
VkSampler emissiveSampler;
|
||||||
VkBuffer dataBuffer;
|
VkBuffer dataBuffer;
|
||||||
uint32_t dataBufferOffset;
|
uint32_t dataBufferOffset;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -69,10 +69,12 @@ void GeometryPass::register_graph(RenderGraph *graph,
|
|||||||
RGImageHandle gbufferPosition,
|
RGImageHandle gbufferPosition,
|
||||||
RGImageHandle gbufferNormal,
|
RGImageHandle gbufferNormal,
|
||||||
RGImageHandle gbufferAlbedo,
|
RGImageHandle gbufferAlbedo,
|
||||||
|
RGImageHandle gbufferExtra,
|
||||||
RGImageHandle idHandle,
|
RGImageHandle idHandle,
|
||||||
RGImageHandle depthHandle)
|
RGImageHandle depthHandle)
|
||||||
{
|
{
|
||||||
if (!graph || !gbufferPosition.valid() || !gbufferNormal.valid() || !gbufferAlbedo.valid() ||
|
if (!graph || !gbufferPosition.valid() || !gbufferNormal.valid() || !gbufferAlbedo.valid() ||
|
||||||
|
!gbufferExtra.valid() ||
|
||||||
!idHandle.valid() || !depthHandle.valid())
|
!idHandle.valid() || !depthHandle.valid())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -81,7 +83,7 @@ void GeometryPass::register_graph(RenderGraph *graph,
|
|||||||
graph->add_pass(
|
graph->add_pass(
|
||||||
"Geometry",
|
"Geometry",
|
||||||
RGPassType::Graphics,
|
RGPassType::Graphics,
|
||||||
[gbufferPosition, gbufferNormal, gbufferAlbedo, idHandle, depthHandle](RGPassBuilder &builder, EngineContext *ctx)
|
[gbufferPosition, gbufferNormal, gbufferAlbedo, gbufferExtra, idHandle, depthHandle](RGPassBuilder &builder, EngineContext *ctx)
|
||||||
{
|
{
|
||||||
VkClearValue clear{};
|
VkClearValue clear{};
|
||||||
clear.color = {{0.f, 0.f, 0.f, 0.f}};
|
clear.color = {{0.f, 0.f, 0.f, 0.f}};
|
||||||
@@ -89,6 +91,9 @@ void GeometryPass::register_graph(RenderGraph *graph,
|
|||||||
builder.write_color(gbufferPosition, true, clear);
|
builder.write_color(gbufferPosition, true, clear);
|
||||||
builder.write_color(gbufferNormal, true, clear);
|
builder.write_color(gbufferNormal, true, clear);
|
||||||
builder.write_color(gbufferAlbedo, true, clear);
|
builder.write_color(gbufferAlbedo, true, clear);
|
||||||
|
VkClearValue clearExtra{};
|
||||||
|
clearExtra.color = {{1.f, 0.f, 0.f, 0.f}}; // AO=1, emissive=0
|
||||||
|
builder.write_color(gbufferExtra, true, clearExtra);
|
||||||
VkClearValue clearID{};
|
VkClearValue clearID{};
|
||||||
clearID.color.uint32[0] = 0u;
|
clearID.color.uint32[0] = 0u;
|
||||||
builder.write_color(idHandle, true, clearID);
|
builder.write_color(idHandle, true, clearID);
|
||||||
@@ -123,11 +128,11 @@ void GeometryPass::register_graph(RenderGraph *graph,
|
|||||||
builder.read_buffer(b, RGBufferUsage::StorageRead, 0, "geom.vertex");
|
builder.read_buffer(b, RGBufferUsage::StorageRead, 0, "geom.vertex");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[this, gbufferPosition, gbufferNormal, gbufferAlbedo, idHandle, depthHandle](VkCommandBuffer cmd,
|
[this, gbufferPosition, gbufferNormal, gbufferAlbedo, gbufferExtra, idHandle, depthHandle](VkCommandBuffer cmd,
|
||||||
const RGPassResources &res,
|
const RGPassResources &res,
|
||||||
EngineContext *ctx)
|
EngineContext *ctx)
|
||||||
{
|
{
|
||||||
draw_geometry(cmd, ctx, res, gbufferPosition, gbufferNormal, gbufferAlbedo, idHandle, depthHandle);
|
draw_geometry(cmd, ctx, res, gbufferPosition, gbufferNormal, gbufferAlbedo, gbufferExtra, idHandle, depthHandle);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,6 +142,7 @@ void GeometryPass::draw_geometry(VkCommandBuffer cmd,
|
|||||||
RGImageHandle gbufferPosition,
|
RGImageHandle gbufferPosition,
|
||||||
RGImageHandle gbufferNormal,
|
RGImageHandle gbufferNormal,
|
||||||
RGImageHandle gbufferAlbedo,
|
RGImageHandle gbufferAlbedo,
|
||||||
|
RGImageHandle /*gbufferExtra*/,
|
||||||
RGImageHandle /*idHandle*/,
|
RGImageHandle /*idHandle*/,
|
||||||
RGImageHandle depthHandle) const
|
RGImageHandle depthHandle) const
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ public:
|
|||||||
RGImageHandle gbufferPosition,
|
RGImageHandle gbufferPosition,
|
||||||
RGImageHandle gbufferNormal,
|
RGImageHandle gbufferNormal,
|
||||||
RGImageHandle gbufferAlbedo,
|
RGImageHandle gbufferAlbedo,
|
||||||
|
RGImageHandle gbufferExtra,
|
||||||
RGImageHandle idHandle,
|
RGImageHandle idHandle,
|
||||||
RGImageHandle depthHandle);
|
RGImageHandle depthHandle);
|
||||||
|
|
||||||
@@ -30,6 +31,7 @@ private:
|
|||||||
RGImageHandle gbufferPosition,
|
RGImageHandle gbufferPosition,
|
||||||
RGImageHandle gbufferNormal,
|
RGImageHandle gbufferNormal,
|
||||||
RGImageHandle gbufferAlbedo,
|
RGImageHandle gbufferAlbedo,
|
||||||
|
RGImageHandle gbufferExtra,
|
||||||
RGImageHandle idHandle,
|
RGImageHandle idHandle,
|
||||||
RGImageHandle depthHandle) const;
|
RGImageHandle depthHandle) const;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ void LightingPass::init(EngineContext *context)
|
|||||||
builder.add_binding(0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
|
builder.add_binding(0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
|
||||||
builder.add_binding(1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
|
builder.add_binding(1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
|
||||||
builder.add_binding(2, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
|
builder.add_binding(2, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
|
||||||
|
builder.add_binding(3, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
|
||||||
_gBufferInputDescriptorLayout = builder.build(
|
_gBufferInputDescriptorLayout = builder.build(
|
||||||
_context->getDevice()->device(), VK_SHADER_STAGE_FRAGMENT_BIT,
|
_context->getDevice()->device(), VK_SHADER_STAGE_FRAGMENT_BIT,
|
||||||
nullptr, VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT);
|
nullptr, VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT);
|
||||||
@@ -55,6 +56,8 @@ void LightingPass::init(EngineContext *context)
|
|||||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
|
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
|
||||||
writer.write_image(2, _context->getSwapchain()->gBufferAlbedo().imageView, _context->getSamplers()->defaultLinear(),
|
writer.write_image(2, _context->getSwapchain()->gBufferAlbedo().imageView, _context->getSamplers()->defaultLinear(),
|
||||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
|
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
|
||||||
|
writer.write_image(3, _context->getSwapchain()->gBufferExtra().imageView, _context->getSamplers()->defaultLinear(),
|
||||||
|
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
|
||||||
writer.update_set(_context->getDevice()->device(), _gBufferInputDescriptorSet);
|
writer.update_set(_context->getDevice()->device(), _gBufferInputDescriptorSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,9 +139,11 @@ void LightingPass::register_graph(RenderGraph *graph,
|
|||||||
RGImageHandle gbufferPosition,
|
RGImageHandle gbufferPosition,
|
||||||
RGImageHandle gbufferNormal,
|
RGImageHandle gbufferNormal,
|
||||||
RGImageHandle gbufferAlbedo,
|
RGImageHandle gbufferAlbedo,
|
||||||
|
RGImageHandle gbufferExtra,
|
||||||
std::span<RGImageHandle> shadowCascades)
|
std::span<RGImageHandle> shadowCascades)
|
||||||
{
|
{
|
||||||
if (!graph || !drawHandle.valid() || !gbufferPosition.valid() || !gbufferNormal.valid() || !gbufferAlbedo.valid())
|
if (!graph || !drawHandle.valid() || !gbufferPosition.valid() || !gbufferNormal.valid() || !gbufferAlbedo.valid() ||
|
||||||
|
!gbufferExtra.valid())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -146,11 +151,12 @@ void LightingPass::register_graph(RenderGraph *graph,
|
|||||||
graph->add_pass(
|
graph->add_pass(
|
||||||
"Lighting",
|
"Lighting",
|
||||||
RGPassType::Graphics,
|
RGPassType::Graphics,
|
||||||
[drawHandle, gbufferPosition, gbufferNormal, gbufferAlbedo, shadowCascades](RGPassBuilder &builder, EngineContext *)
|
[drawHandle, gbufferPosition, gbufferNormal, gbufferAlbedo, gbufferExtra, shadowCascades](RGPassBuilder &builder, EngineContext *)
|
||||||
{
|
{
|
||||||
builder.read(gbufferPosition, RGImageUsage::SampledFragment);
|
builder.read(gbufferPosition, RGImageUsage::SampledFragment);
|
||||||
builder.read(gbufferNormal, RGImageUsage::SampledFragment);
|
builder.read(gbufferNormal, RGImageUsage::SampledFragment);
|
||||||
builder.read(gbufferAlbedo, RGImageUsage::SampledFragment);
|
builder.read(gbufferAlbedo, RGImageUsage::SampledFragment);
|
||||||
|
builder.read(gbufferExtra, RGImageUsage::SampledFragment);
|
||||||
for (size_t i = 0; i < shadowCascades.size(); ++i)
|
for (size_t i = 0; i < shadowCascades.size(); ++i)
|
||||||
{
|
{
|
||||||
if (shadowCascades[i].valid()) builder.read(shadowCascades[i], RGImageUsage::SampledFragment);
|
if (shadowCascades[i].valid()) builder.read(shadowCascades[i], RGImageUsage::SampledFragment);
|
||||||
|
|||||||
@@ -19,7 +19,9 @@ public:
|
|||||||
RGImageHandle drawHandle,
|
RGImageHandle drawHandle,
|
||||||
RGImageHandle gbufferPosition,
|
RGImageHandle gbufferPosition,
|
||||||
RGImageHandle gbufferNormal,
|
RGImageHandle gbufferNormal,
|
||||||
RGImageHandle gbufferAlbedo, std::span<RGImageHandle> shadowCascades);
|
RGImageHandle gbufferAlbedo,
|
||||||
|
RGImageHandle gbufferExtra,
|
||||||
|
std::span<RGImageHandle> shadowCascades);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
EngineContext *_context = nullptr;
|
EngineContext *_context = nullptr;
|
||||||
|
|||||||
@@ -353,9 +353,7 @@ std::optional<std::shared_ptr<LoadedGLTF> > loadGltf(VulkanEngine *engine, std::
|
|||||||
materials.push_back(newMat);
|
materials.push_back(newMat);
|
||||||
file.materials[mat.name.c_str()] = newMat;
|
file.materials[mat.name.c_str()] = newMat;
|
||||||
|
|
||||||
GLTFMetallic_Roughness::MaterialConstants constants;
|
GLTFMetallic_Roughness::MaterialConstants constants{};
|
||||||
// Defaults
|
|
||||||
constants.extra[0].x = 1.0f; // normalScale
|
|
||||||
constants.colorFactors.x = mat.pbrData.baseColorFactor[0];
|
constants.colorFactors.x = mat.pbrData.baseColorFactor[0];
|
||||||
constants.colorFactors.y = mat.pbrData.baseColorFactor[1];
|
constants.colorFactors.y = mat.pbrData.baseColorFactor[1];
|
||||||
constants.colorFactors.z = mat.pbrData.baseColorFactor[2];
|
constants.colorFactors.z = mat.pbrData.baseColorFactor[2];
|
||||||
@@ -363,6 +361,11 @@ std::optional<std::shared_ptr<LoadedGLTF> > loadGltf(VulkanEngine *engine, std::
|
|||||||
|
|
||||||
constants.metal_rough_factors.x = mat.pbrData.metallicFactor;
|
constants.metal_rough_factors.x = mat.pbrData.metallicFactor;
|
||||||
constants.metal_rough_factors.y = mat.pbrData.roughnessFactor;
|
constants.metal_rough_factors.y = mat.pbrData.roughnessFactor;
|
||||||
|
constants.extra[0].x = 1.0f;
|
||||||
|
constants.extra[0].y = mat.occlusionTexture.has_value() ? mat.occlusionTexture->strength : 1.0f;
|
||||||
|
constants.extra[1].x = mat.emissiveFactor[0];
|
||||||
|
constants.extra[1].y = mat.emissiveFactor[1];
|
||||||
|
constants.extra[1].z = mat.emissiveFactor[2];
|
||||||
// write material parameters to buffer
|
// write material parameters to buffer
|
||||||
sceneMaterialConstants[data_index] = constants;
|
sceneMaterialConstants[data_index] = constants;
|
||||||
|
|
||||||
@@ -380,6 +383,10 @@ std::optional<std::shared_ptr<LoadedGLTF> > loadGltf(VulkanEngine *engine, std::
|
|||||||
materialResources.metalRoughSampler = engine->_samplerManager->defaultLinear();
|
materialResources.metalRoughSampler = engine->_samplerManager->defaultLinear();
|
||||||
materialResources.normalImage = engine->_flatNormalImage;
|
materialResources.normalImage = engine->_flatNormalImage;
|
||||||
materialResources.normalSampler = engine->_samplerManager->defaultLinear();
|
materialResources.normalSampler = engine->_samplerManager->defaultLinear();
|
||||||
|
materialResources.occlusionImage = engine->_whiteImage;
|
||||||
|
materialResources.occlusionSampler = engine->_samplerManager->defaultLinear();
|
||||||
|
materialResources.emissiveImage = engine->_blackImage;
|
||||||
|
materialResources.emissiveSampler = engine->_samplerManager->defaultLinear();
|
||||||
|
|
||||||
// set the uniform buffer for the material data
|
// set the uniform buffer for the material data
|
||||||
materialResources.dataBuffer = file.materialDataBuffer.buffer;
|
materialResources.dataBuffer = file.materialDataBuffer.buffer;
|
||||||
@@ -389,6 +396,8 @@ std::optional<std::shared_ptr<LoadedGLTF> > loadGltf(VulkanEngine *engine, std::
|
|||||||
TextureCache::TextureHandle hColor = TextureCache::InvalidHandle;
|
TextureCache::TextureHandle hColor = TextureCache::InvalidHandle;
|
||||||
TextureCache::TextureHandle hMRO = TextureCache::InvalidHandle;
|
TextureCache::TextureHandle hMRO = TextureCache::InvalidHandle;
|
||||||
TextureCache::TextureHandle hNorm = TextureCache::InvalidHandle;
|
TextureCache::TextureHandle hNorm = TextureCache::InvalidHandle;
|
||||||
|
TextureCache::TextureHandle hOcc = TextureCache::InvalidHandle;
|
||||||
|
TextureCache::TextureHandle hEmissive = TextureCache::InvalidHandle;
|
||||||
|
|
||||||
if (cache && mat.pbrData.baseColorTexture.has_value())
|
if (cache && mat.pbrData.baseColorTexture.has_value())
|
||||||
{
|
{
|
||||||
@@ -418,6 +427,35 @@ std::optional<std::shared_ptr<LoadedGLTF> > loadGltf(VulkanEngine *engine, std::
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cache && mat.occlusionTexture.has_value())
|
||||||
|
{
|
||||||
|
const auto &tex = gltf.textures[mat.occlusionTexture->textureIndex];
|
||||||
|
const size_t imgIndex = tex.imageIndex.value();
|
||||||
|
const bool hasSampler = tex.samplerIndex.has_value();
|
||||||
|
const VkSampler sampler = hasSampler ? file.samplers[tex.samplerIndex.value()] : engine->_samplerManager->defaultLinear();
|
||||||
|
auto key = buildTextureKey(imgIndex, false);
|
||||||
|
key.channels = TextureCache::TextureKey::ChannelsHint::R;
|
||||||
|
if (key.hash != 0)
|
||||||
|
{
|
||||||
|
hOcc = cache->request(key, sampler);
|
||||||
|
materialResources.occlusionSampler = sampler;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cache && mat.emissiveTexture.has_value())
|
||||||
|
{
|
||||||
|
const auto &tex = gltf.textures[mat.emissiveTexture->textureIndex];
|
||||||
|
const size_t imgIndex = tex.imageIndex.value();
|
||||||
|
const bool hasSampler = tex.samplerIndex.has_value();
|
||||||
|
const VkSampler sampler = hasSampler ? file.samplers[tex.samplerIndex.value()] : engine->_samplerManager->defaultLinear();
|
||||||
|
auto key = buildTextureKey(imgIndex, true);
|
||||||
|
if (key.hash != 0)
|
||||||
|
{
|
||||||
|
hEmissive = cache->request(key, sampler);
|
||||||
|
materialResources.emissiveSampler = sampler;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (cache && mat.normalTexture.has_value())
|
if (cache && mat.normalTexture.has_value())
|
||||||
{
|
{
|
||||||
const auto &tex = gltf.textures[mat.normalTexture.value().textureIndex];
|
const auto &tex = gltf.textures[mat.normalTexture.value().textureIndex];
|
||||||
@@ -456,6 +494,16 @@ std::optional<std::shared_ptr<LoadedGLTF> > loadGltf(VulkanEngine *engine, std::
|
|||||||
cache->watchBinding(hNorm, newMat->data.materialSet, 3u, materialResources.normalSampler,
|
cache->watchBinding(hNorm, newMat->data.materialSet, 3u, materialResources.normalSampler,
|
||||||
engine->_flatNormalImage.imageView);
|
engine->_flatNormalImage.imageView);
|
||||||
}
|
}
|
||||||
|
if (hOcc != TextureCache::InvalidHandle)
|
||||||
|
{
|
||||||
|
cache->watchBinding(hOcc, newMat->data.materialSet, 4u, materialResources.occlusionSampler,
|
||||||
|
engine->_whiteImage.imageView);
|
||||||
|
}
|
||||||
|
if (hEmissive != TextureCache::InvalidHandle)
|
||||||
|
{
|
||||||
|
cache->watchBinding(hEmissive, newMat->data.materialSet, 5u, materialResources.emissiveSampler,
|
||||||
|
engine->_blackImage.imageView);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data_index++;
|
data_index++;
|
||||||
|
|||||||
@@ -9,9 +9,11 @@ except Exception:
|
|||||||
PIL_OK = False
|
PIL_OK = False
|
||||||
|
|
||||||
DEFAULT_SUFFIX = {
|
DEFAULT_SUFFIX = {
|
||||||
"albedo": ["_albedo", "_basecolor", "_base_colour", "_base_color", "_base", "baseColor", "BaseColor"],
|
"albedo": ["_albedo", "_basecolor", "_base_colour", "_base_color", "_base", "baseColor", "BaseColor"],
|
||||||
"mr": ["_mr", "_orm", "_metalrough", "_metallicroughness", "metallicRoughness", "Metallic"],
|
"mr": ["_mr", "_orm", "_metalrough", "_metallicroughness", "metallicRoughness", "Metallic"],
|
||||||
"normal": ["_normal", "_norm", "_nrm", "_normalgl", "Normal"]
|
"normal": ["_normal", "_norm", "_nrm", "_normalgl", "Normal"],
|
||||||
|
"occlusion": ["_occlusion", "_occ", "_ao"],
|
||||||
|
"emissive": ["_emissive", "_emission", "_emit"],
|
||||||
}
|
}
|
||||||
|
|
||||||
SUPPORTED_IMAGE_EXTS = {".png", ".jpg", ".jpeg", ".tga", ".tif", ".tiff"}
|
SUPPORTED_IMAGE_EXTS = {".png", ".jpg", ".jpeg", ".tga", ".tif", ".tiff"}
|
||||||
@@ -34,8 +36,8 @@ def detect_role_by_suffix(stem, rx):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def parse_gltf_roles(gltf_path: Path):
|
def parse_gltf_roles(gltf_path: Path):
|
||||||
"""glTF(.gltf JSON) to get image role"""
|
"""glTF(.gltf JSON) to get image role (albedo/mr/normal/occlusion/emissive)"""
|
||||||
roles = {} # uri-> role (albedo/mr/normal)
|
roles = {} # uri -> role
|
||||||
if not gltf_path.exists():
|
if not gltf_path.exists():
|
||||||
return roles
|
return roles
|
||||||
if gltf_path.suffix.lower() == ".gltf":
|
if gltf_path.suffix.lower() == ".gltf":
|
||||||
@@ -60,9 +62,16 @@ def parse_gltf_roles(gltf_path: Path):
|
|||||||
if not uri:
|
if not uri:
|
||||||
return
|
return
|
||||||
|
|
||||||
prio = {"normal": 3, "albedo": 2, "mr": 1}
|
prio = {
|
||||||
|
"normal": 4,
|
||||||
|
"albedo": 3,
|
||||||
|
"emissive": 3,
|
||||||
|
"mr": 2,
|
||||||
|
"occlusion": 2,
|
||||||
|
}
|
||||||
|
new_prio = prio.get(role, 0)
|
||||||
old = roles.get(uri)
|
old = roles.get(uri)
|
||||||
if old is None or prio[role] > prio.get(old, 0):
|
if old is None or new_prio > prio.get(old, 0):
|
||||||
roles[uri] = role
|
roles[uri] = role
|
||||||
|
|
||||||
for mat in materials:
|
for mat in materials:
|
||||||
@@ -70,6 +79,8 @@ def parse_gltf_roles(gltf_path: Path):
|
|||||||
base = pbr.get("baseColorTexture", {})
|
base = pbr.get("baseColorTexture", {})
|
||||||
mr = pbr.get("metallicRoughnessTexture", {})
|
mr = pbr.get("metallicRoughnessTexture", {})
|
||||||
nor = mat.get("normalTexture", {})
|
nor = mat.get("normalTexture", {})
|
||||||
|
occ = mat.get("occlusionTexture", {})
|
||||||
|
emis = mat.get("emissiveTexture", {})
|
||||||
|
|
||||||
if "index" in base and base["index"] in tex_to_uri:
|
if "index" in base and base["index"] in tex_to_uri:
|
||||||
mark(tex_to_uri[base["index"]], "albedo")
|
mark(tex_to_uri[base["index"]], "albedo")
|
||||||
@@ -77,6 +88,10 @@ def parse_gltf_roles(gltf_path: Path):
|
|||||||
mark(tex_to_uri[mr["index"]], "mr")
|
mark(tex_to_uri[mr["index"]], "mr")
|
||||||
if "index" in nor and nor["index"] in tex_to_uri:
|
if "index" in nor and nor["index"] in tex_to_uri:
|
||||||
mark(tex_to_uri[nor["index"]], "normal")
|
mark(tex_to_uri[nor["index"]], "normal")
|
||||||
|
if "index" in occ and occ["index"] in tex_to_uri:
|
||||||
|
mark(tex_to_uri[occ["index"]], "occlusion")
|
||||||
|
if "index" in emis and emis["index"] in tex_to_uri:
|
||||||
|
mark(tex_to_uri[emis["index"]], "emissive")
|
||||||
|
|
||||||
return roles
|
return roles
|
||||||
|
|
||||||
@@ -101,6 +116,9 @@ def decide_targets(role, albedo_target, img_path):
|
|||||||
return "bc5", "linear"
|
return "bc5", "linear"
|
||||||
if role == "mr":
|
if role == "mr":
|
||||||
return "bc7", "linear"
|
return "bc7", "linear"
|
||||||
|
if role == "occlusion":
|
||||||
|
# AO is data, not color
|
||||||
|
return "bc7", "linear"
|
||||||
# albedo
|
# albedo
|
||||||
if albedo_target == "auto":
|
if albedo_target == "auto":
|
||||||
if has_meaningful_alpha(img_path):
|
if has_meaningful_alpha(img_path):
|
||||||
@@ -177,6 +195,8 @@ def main():
|
|||||||
help="albedo suffix CSV (Base: %s)" % ",".join(DEFAULT_SUFFIX["albedo"]))
|
help="albedo suffix CSV (Base: %s)" % ",".join(DEFAULT_SUFFIX["albedo"]))
|
||||||
p.add_argument("--suffix-mr", default=",".join(DEFAULT_SUFFIX["mr"]))
|
p.add_argument("--suffix-mr", default=",".join(DEFAULT_SUFFIX["mr"]))
|
||||||
p.add_argument("--suffix-normal", default=",".join(DEFAULT_SUFFIX["normal"]))
|
p.add_argument("--suffix-normal", default=",".join(DEFAULT_SUFFIX["normal"]))
|
||||||
|
p.add_argument("--suffix-occlusion", default=",".join(DEFAULT_SUFFIX["occlusion"]))
|
||||||
|
p.add_argument("--suffix-emissive", default=",".join(DEFAULT_SUFFIX["emissive"]))
|
||||||
p.add_argument("--albedo-target", choices=["auto", "bc1", "bc3", "bc7"], default="bc7",
|
p.add_argument("--albedo-target", choices=["auto", "bc1", "bc3", "bc7"], default="bc7",
|
||||||
help="albedo BC format(auto=non alpha BC1, alpha BC3)")
|
help="albedo BC format(auto=non alpha BC1, alpha BC3)")
|
||||||
p.add_argument("--uastc-quality", type=int, default=2, help="UASTC quality(0~4)")
|
p.add_argument("--uastc-quality", type=int, default=2, help="UASTC quality(0~4)")
|
||||||
@@ -194,6 +214,8 @@ def main():
|
|||||||
"albedo": build_suffix_regex([s.strip() for s in opts.suffix_albedo.split(",") if s.strip()]),
|
"albedo": build_suffix_regex([s.strip() for s in opts.suffix_albedo.split(",") if s.strip()]),
|
||||||
"mr": build_suffix_regex([s.strip() for s in opts.suffix_mr.split(",") if s.strip()]),
|
"mr": build_suffix_regex([s.strip() for s in opts.suffix_mr.split(",") if s.strip()]),
|
||||||
"normal": build_suffix_regex([s.strip() for s in opts.suffix_normal.split(",") if s.strip()]),
|
"normal": build_suffix_regex([s.strip() for s in opts.suffix_normal.split(",") if s.strip()]),
|
||||||
|
"occlusion": build_suffix_regex([s.strip() for s in opts.suffix_occlusion.split(",") if s.strip()]),
|
||||||
|
"emissive": build_suffix_regex([s.strip() for s in opts.suffix_emissive.split(",") if s.strip()]),
|
||||||
}
|
}
|
||||||
|
|
||||||
gltf_roles = {}
|
gltf_roles = {}
|
||||||
|
|||||||
Reference in New Issue
Block a user