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=1) uniform sampler2D normalTex;
|
||||
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];
|
||||
// TLAS for ray query (optional, guarded by sceneData.rtOptions.x)
|
||||
#ifdef GL_EXT_ray_query
|
||||
@@ -279,6 +280,10 @@ void main(){
|
||||
vec3 albedo = albedoSample.rgb;
|
||||
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 V = normalize(camPos - pos);
|
||||
|
||||
@@ -340,7 +345,8 @@ void main(){
|
||||
vec3 specIBL = prefiltered * (F0 * brdf.x + brdf.y);
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ layout(location=0) out vec4 outColor;
|
||||
layout(set=1, binding=0) uniform sampler2D posTex;
|
||||
layout(set=1, binding=1) uniform sampler2D normalTex;
|
||||
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];
|
||||
|
||||
// Tunables for shadow quality and blending
|
||||
@@ -208,6 +209,10 @@ void main(){
|
||||
vec3 albedo = albedoSample.rgb;
|
||||
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 V = normalize(camPos - pos);
|
||||
|
||||
@@ -235,7 +240,8 @@ void main(){
|
||||
vec3 specIBL = prefiltered * (F0 * brdf.x + brdf.y);
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ layout(location = 0) out vec4 outPos;
|
||||
layout(location = 1) out vec4 outNorm;
|
||||
layout(location = 2) out vec4 outAlbedo;
|
||||
layout(location = 3) out uint outObjectID;
|
||||
layout(location = 4) out vec4 outExtra;
|
||||
|
||||
// Keep push constants layout in sync with mesh.vert / GPUDrawPushConstants
|
||||
struct Vertex {
|
||||
@@ -58,5 +59,13 @@ void main() {
|
||||
outPos = vec4(inWorldPos, 1.0);
|
||||
outNorm = vec4(Nw, roughness);
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -45,3 +45,5 @@ layout(set = 1, binding = 0) uniform GLTFMaterialData{
|
||||
layout(set = 1, binding = 1) uniform sampler2D colorTex;
|
||||
layout(set = 1, binding = 2) uniform sampler2D metalRoughTex;
|
||||
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 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)
|
||||
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);
|
||||
|
||||
GLTFMetallic_Roughness::MaterialResources res{};
|
||||
res.colorImage = _engine->_errorCheckerboardImage; // visible fallback for albedo
|
||||
res.colorImage = _engine->_errorCheckerboardImage;
|
||||
res.colorSampler = _engine->_samplerManager->defaultLinear();
|
||||
res.metalRoughImage = _engine->_whiteImage;
|
||||
res.metalRoughSampler = _engine->_samplerManager->defaultLinear();
|
||||
res.normalImage = _engine->_flatNormalImage;
|
||||
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.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);
|
||||
}
|
||||
}
|
||||
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);
|
||||
@@ -466,6 +491,10 @@ AllocatedBuffer AssetManager::createMaterialBufferWithConstants(
|
||||
{
|
||||
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
|
||||
vmaFlushAllocation(_engine->_deviceManager->allocator(), matBuffer.allocation, 0,
|
||||
sizeof(GLTFMetallic_Roughness::MaterialConstants));
|
||||
@@ -525,6 +554,10 @@ std::shared_ptr<MeshAsset> AssetManager::createMesh(const std::string &name,
|
||||
matResources.metalRoughSampler = _engine->_samplerManager->defaultLinear();
|
||||
matResources.normalImage = _engine->_flatNormalImage;
|
||||
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({});
|
||||
matResources.dataBuffer = matBuffer.buffer;
|
||||
@@ -569,6 +602,10 @@ std::shared_ptr<GLTFMaterial> AssetManager::createMaterialFromConstants(
|
||||
res.metalRoughSampler = _engine->_samplerManager->defaultLinear();
|
||||
res.normalImage = _engine->_flatNormalImage;
|
||||
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);
|
||||
res.dataBuffer = buf.buffer;
|
||||
|
||||
@@ -28,10 +28,14 @@ public:
|
||||
// Optional tangent-space normal map for PBR (placeholder; not wired yet)
|
||||
// When enabled later, this will be sampled in shaders and requires tangents.
|
||||
std::string normalPath;
|
||||
std::string occlusionPath;
|
||||
std::string emissivePath;
|
||||
|
||||
bool albedoSRGB = true;
|
||||
bool metalRoughSRGB = false;
|
||||
bool normalSRGB = false; // normal maps are typically non-sRGB
|
||||
bool occlusionSRGB = false;
|
||||
bool emissiveSRGB = true;
|
||||
|
||||
GLTFMetallic_Roughness::MaterialConstants constants{};
|
||||
|
||||
|
||||
@@ -70,6 +70,8 @@ void SwapchainManager::init_swapchain()
|
||||
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT);
|
||||
_gBufferAlbedo = _resourceManager->create_image(drawImageExtent, VK_FORMAT_R8G8B8A8_UNORM,
|
||||
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,
|
||||
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
|
||||
VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
|
||||
@@ -85,6 +87,7 @@ void SwapchainManager::init_swapchain()
|
||||
_resourceManager->destroy_image(_gBufferPosition);
|
||||
_resourceManager->destroy_image(_gBufferNormal);
|
||||
_resourceManager->destroy_image(_gBufferAlbedo);
|
||||
_resourceManager->destroy_image(_gBufferExtra);
|
||||
_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);
|
||||
_gBufferAlbedo = _resourceManager->create_image(drawImageExtent, VK_FORMAT_R8G8B8A8_UNORM,
|
||||
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,
|
||||
VK_IMAGE_USAGE_COLOR_ATTACHMENT_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(_gBufferNormal);
|
||||
_resourceManager->destroy_image(_gBufferAlbedo);
|
||||
_resourceManager->destroy_image(_gBufferExtra);
|
||||
_resourceManager->destroy_image(_idBuffer);
|
||||
});
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ public:
|
||||
AllocatedImage gBufferPosition() const { return _gBufferPosition; }
|
||||
AllocatedImage gBufferNormal() const { return _gBufferNormal; }
|
||||
AllocatedImage gBufferAlbedo() const { return _gBufferAlbedo; }
|
||||
AllocatedImage gBufferExtra() const { return _gBufferExtra; }
|
||||
AllocatedImage idBuffer() const { return _idBuffer; }
|
||||
VkExtent2D windowExtent() const { return _windowExtent; }
|
||||
|
||||
@@ -51,6 +52,7 @@ private:
|
||||
AllocatedImage _gBufferPosition = {};
|
||||
AllocatedImage _gBufferNormal = {};
|
||||
AllocatedImage _gBufferAlbedo = {};
|
||||
AllocatedImage _gBufferExtra = {};
|
||||
AllocatedImage _idBuffer = {};
|
||||
|
||||
DeletionQueue _deletionQueue;
|
||||
|
||||
@@ -613,6 +613,7 @@ void VulkanEngine::draw()
|
||||
RGImageHandle hGBufferPosition = _renderGraph->import_gbuffer_position();
|
||||
RGImageHandle hGBufferNormal = _renderGraph->import_gbuffer_normal();
|
||||
RGImageHandle hGBufferAlbedo = _renderGraph->import_gbuffer_albedo();
|
||||
RGImageHandle hGBufferExtra = _renderGraph->import_gbuffer_extra();
|
||||
RGImageHandle hSwapchain = _renderGraph->import_swapchain_image(swapchainImageIndex);
|
||||
// For debug overlays (IBL volumes), re-use HDR draw image as a color target.
|
||||
RGImageHandle hDebugColor = hDraw;
|
||||
@@ -656,7 +657,7 @@ void VulkanEngine::draw()
|
||||
if (auto *geometry = _renderPassManager->getPass<GeometryPass>())
|
||||
{
|
||||
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,
|
||||
// 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>())
|
||||
{
|
||||
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()));
|
||||
}
|
||||
|
||||
|
||||
@@ -956,6 +956,18 @@ RGImageHandle RenderGraph::import_gbuffer_albedo()
|
||||
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()
|
||||
{
|
||||
RGImportedImageDesc d{};
|
||||
|
||||
@@ -56,6 +56,7 @@ struct Pass; // fwd
|
||||
RGImageHandle import_gbuffer_position();
|
||||
RGImageHandle import_gbuffer_normal();
|
||||
RGImageHandle import_gbuffer_albedo();
|
||||
RGImageHandle import_gbuffer_extra();
|
||||
RGImageHandle import_id_buffer();
|
||||
RGImageHandle import_swapchain_image(uint32_t index);
|
||||
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(2, 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(),
|
||||
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->gBufferNormal().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);
|
||||
};
|
||||
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);
|
||||
writer.write_image(3, resources.normalImage.imageView, resources.normalSampler,
|
||||
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);
|
||||
|
||||
|
||||
@@ -30,6 +30,10 @@ struct GLTFMetallic_Roughness
|
||||
VkSampler metalRoughSampler;
|
||||
AllocatedImage normalImage;
|
||||
VkSampler normalSampler;
|
||||
AllocatedImage occlusionImage;
|
||||
VkSampler occlusionSampler;
|
||||
AllocatedImage emissiveImage;
|
||||
VkSampler emissiveSampler;
|
||||
VkBuffer dataBuffer;
|
||||
uint32_t dataBufferOffset;
|
||||
};
|
||||
|
||||
@@ -69,10 +69,12 @@ void GeometryPass::register_graph(RenderGraph *graph,
|
||||
RGImageHandle gbufferPosition,
|
||||
RGImageHandle gbufferNormal,
|
||||
RGImageHandle gbufferAlbedo,
|
||||
RGImageHandle gbufferExtra,
|
||||
RGImageHandle idHandle,
|
||||
RGImageHandle depthHandle)
|
||||
{
|
||||
if (!graph || !gbufferPosition.valid() || !gbufferNormal.valid() || !gbufferAlbedo.valid() ||
|
||||
!gbufferExtra.valid() ||
|
||||
!idHandle.valid() || !depthHandle.valid())
|
||||
{
|
||||
return;
|
||||
@@ -81,7 +83,7 @@ void GeometryPass::register_graph(RenderGraph *graph,
|
||||
graph->add_pass(
|
||||
"Geometry",
|
||||
RGPassType::Graphics,
|
||||
[gbufferPosition, gbufferNormal, gbufferAlbedo, idHandle, depthHandle](RGPassBuilder &builder, EngineContext *ctx)
|
||||
[gbufferPosition, gbufferNormal, gbufferAlbedo, gbufferExtra, idHandle, depthHandle](RGPassBuilder &builder, EngineContext *ctx)
|
||||
{
|
||||
VkClearValue clear{};
|
||||
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(gbufferNormal, 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{};
|
||||
clearID.color.uint32[0] = 0u;
|
||||
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");
|
||||
}
|
||||
},
|
||||
[this, gbufferPosition, gbufferNormal, gbufferAlbedo, idHandle, depthHandle](VkCommandBuffer cmd,
|
||||
[this, gbufferPosition, gbufferNormal, gbufferAlbedo, gbufferExtra, idHandle, depthHandle](VkCommandBuffer cmd,
|
||||
const RGPassResources &res,
|
||||
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 gbufferNormal,
|
||||
RGImageHandle gbufferAlbedo,
|
||||
RGImageHandle /*gbufferExtra*/,
|
||||
RGImageHandle /*idHandle*/,
|
||||
RGImageHandle depthHandle) const
|
||||
{
|
||||
|
||||
@@ -18,6 +18,7 @@ public:
|
||||
RGImageHandle gbufferPosition,
|
||||
RGImageHandle gbufferNormal,
|
||||
RGImageHandle gbufferAlbedo,
|
||||
RGImageHandle gbufferExtra,
|
||||
RGImageHandle idHandle,
|
||||
RGImageHandle depthHandle);
|
||||
|
||||
@@ -30,6 +31,7 @@ private:
|
||||
RGImageHandle gbufferPosition,
|
||||
RGImageHandle gbufferNormal,
|
||||
RGImageHandle gbufferAlbedo,
|
||||
RGImageHandle gbufferExtra,
|
||||
RGImageHandle idHandle,
|
||||
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(1, 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(
|
||||
_context->getDevice()->device(), VK_SHADER_STAGE_FRAGMENT_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);
|
||||
writer.write_image(2, _context->getSwapchain()->gBufferAlbedo().imageView, _context->getSamplers()->defaultLinear(),
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -136,9 +139,11 @@ void LightingPass::register_graph(RenderGraph *graph,
|
||||
RGImageHandle gbufferPosition,
|
||||
RGImageHandle gbufferNormal,
|
||||
RGImageHandle gbufferAlbedo,
|
||||
RGImageHandle gbufferExtra,
|
||||
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;
|
||||
}
|
||||
@@ -146,11 +151,12 @@ void LightingPass::register_graph(RenderGraph *graph,
|
||||
graph->add_pass(
|
||||
"Lighting",
|
||||
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(gbufferNormal, RGImageUsage::SampledFragment);
|
||||
builder.read(gbufferAlbedo, RGImageUsage::SampledFragment);
|
||||
builder.read(gbufferExtra, RGImageUsage::SampledFragment);
|
||||
for (size_t i = 0; i < shadowCascades.size(); ++i)
|
||||
{
|
||||
if (shadowCascades[i].valid()) builder.read(shadowCascades[i], RGImageUsage::SampledFragment);
|
||||
|
||||
@@ -19,7 +19,9 @@ public:
|
||||
RGImageHandle drawHandle,
|
||||
RGImageHandle gbufferPosition,
|
||||
RGImageHandle gbufferNormal,
|
||||
RGImageHandle gbufferAlbedo, std::span<RGImageHandle> shadowCascades);
|
||||
RGImageHandle gbufferAlbedo,
|
||||
RGImageHandle gbufferExtra,
|
||||
std::span<RGImageHandle> shadowCascades);
|
||||
|
||||
private:
|
||||
EngineContext *_context = nullptr;
|
||||
|
||||
@@ -353,9 +353,7 @@ std::optional<std::shared_ptr<LoadedGLTF> > loadGltf(VulkanEngine *engine, std::
|
||||
materials.push_back(newMat);
|
||||
file.materials[mat.name.c_str()] = newMat;
|
||||
|
||||
GLTFMetallic_Roughness::MaterialConstants constants;
|
||||
// Defaults
|
||||
constants.extra[0].x = 1.0f; // normalScale
|
||||
GLTFMetallic_Roughness::MaterialConstants constants{};
|
||||
constants.colorFactors.x = mat.pbrData.baseColorFactor[0];
|
||||
constants.colorFactors.y = mat.pbrData.baseColorFactor[1];
|
||||
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.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
|
||||
sceneMaterialConstants[data_index] = constants;
|
||||
|
||||
@@ -380,6 +383,10 @@ std::optional<std::shared_ptr<LoadedGLTF> > loadGltf(VulkanEngine *engine, std::
|
||||
materialResources.metalRoughSampler = engine->_samplerManager->defaultLinear();
|
||||
materialResources.normalImage = engine->_flatNormalImage;
|
||||
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
|
||||
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 hMRO = TextureCache::InvalidHandle;
|
||||
TextureCache::TextureHandle hNorm = TextureCache::InvalidHandle;
|
||||
TextureCache::TextureHandle hOcc = TextureCache::InvalidHandle;
|
||||
TextureCache::TextureHandle hEmissive = TextureCache::InvalidHandle;
|
||||
|
||||
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())
|
||||
{
|
||||
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,
|
||||
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++;
|
||||
|
||||
@@ -11,7 +11,9 @@ except Exception:
|
||||
DEFAULT_SUFFIX = {
|
||||
"albedo": ["_albedo", "_basecolor", "_base_colour", "_base_color", "_base", "baseColor", "BaseColor"],
|
||||
"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"}
|
||||
@@ -34,8 +36,8 @@ def detect_role_by_suffix(stem, rx):
|
||||
return None
|
||||
|
||||
def parse_gltf_roles(gltf_path: Path):
|
||||
"""glTF(.gltf JSON) to get image role"""
|
||||
roles = {} # uri-> role (albedo/mr/normal)
|
||||
"""glTF(.gltf JSON) to get image role (albedo/mr/normal/occlusion/emissive)"""
|
||||
roles = {} # uri -> role
|
||||
if not gltf_path.exists():
|
||||
return roles
|
||||
if gltf_path.suffix.lower() == ".gltf":
|
||||
@@ -60,9 +62,16 @@ def parse_gltf_roles(gltf_path: Path):
|
||||
if not uri:
|
||||
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)
|
||||
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
|
||||
|
||||
for mat in materials:
|
||||
@@ -70,6 +79,8 @@ def parse_gltf_roles(gltf_path: Path):
|
||||
base = pbr.get("baseColorTexture", {})
|
||||
mr = pbr.get("metallicRoughnessTexture", {})
|
||||
nor = mat.get("normalTexture", {})
|
||||
occ = mat.get("occlusionTexture", {})
|
||||
emis = mat.get("emissiveTexture", {})
|
||||
|
||||
if "index" in base and base["index"] in tex_to_uri:
|
||||
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")
|
||||
if "index" in nor and nor["index"] in tex_to_uri:
|
||||
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
|
||||
|
||||
@@ -101,6 +116,9 @@ def decide_targets(role, albedo_target, img_path):
|
||||
return "bc5", "linear"
|
||||
if role == "mr":
|
||||
return "bc7", "linear"
|
||||
if role == "occlusion":
|
||||
# AO is data, not color
|
||||
return "bc7", "linear"
|
||||
# albedo
|
||||
if albedo_target == "auto":
|
||||
if has_meaningful_alpha(img_path):
|
||||
@@ -177,6 +195,8 @@ def main():
|
||||
help="albedo suffix CSV (Base: %s)" % ",".join(DEFAULT_SUFFIX["albedo"]))
|
||||
p.add_argument("--suffix-mr", default=",".join(DEFAULT_SUFFIX["mr"]))
|
||||
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",
|
||||
help="albedo BC format(auto=non alpha BC1, alpha BC3)")
|
||||
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()]),
|
||||
"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()]),
|
||||
"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 = {}
|
||||
|
||||
Reference in New Issue
Block a user