diff --git a/projects/rt_ambient_occlusion/CMakeLists.txt b/projects/rt_ambient_occlusion/CMakeLists.txt index ce4a2fce46c55bdfc778a48a6d48fb8f396fac6d..ac335427ec1bb7bae14c45d1db00e7ab3a2ac97c 100644 --- a/projects/rt_ambient_occlusion/CMakeLists.txt +++ b/projects/rt_ambient_occlusion/CMakeLists.txt @@ -14,15 +14,19 @@ target_include_directories(rt_ambient_occlusion SYSTEM BEFORE PRIVATE ${vkcv_includes} ${vkcv_asset_loader_include} ${vkcv_camera_include} + ${vkcv_gui_include} ${vkcv_scene_include} ${vkcv_shader_compiler_include} - ${vkcv_scene_include}) + ${vkcv_scene_include} + ${vkcv_upscaling_include}) # linking with libraries from all dependencies and the VkCV framework target_link_libraries(rt_ambient_occlusion vkcv ${vkcv_libraries} vkcv_asset_loader ${vkcv_asset_loader_libraries} vkcv_camera + vkcv_gui vkcv_scene vkcv_shader_compiler - vkcv_scene) + vkcv_scene + vkcv_upscaling) diff --git a/projects/rt_ambient_occlusion/resources/shaders/ambientOcclusion.rchit b/projects/rt_ambient_occlusion/resources/shaders/ambientOcclusion.rchit index 9c8a24032ff6136fad922a54a4dc050823648355..7a69ae2186e9f2fb38df9f202976a6a39bda97a5 100644 --- a/projects/rt_ambient_occlusion/resources/shaders/ambientOcclusion.rchit +++ b/projects/rt_ambient_occlusion/resources/shaders/ambientOcclusion.rchit @@ -38,12 +38,13 @@ layout(binding = 2, set = 0, scalar) buffer rtObjects { ObjDesc objects[]; }; -layout(binding = 3, set = 0, scalar) buffer rtInstanceCount { - int instanceCount; +layout(binding = 3, set = 0, scalar) buffer rtContext { + uint instanceCount; + uint sampleCount; }; void main() { - int instanceIndex = gl_InstanceID + gl_GeometryIndexEXT * instanceCount; + int instanceIndex = gl_InstanceID + gl_GeometryIndexEXT * int(instanceCount); if (instanceIndex >= objects.length()) { payload.hitSky = 1.0f; diff --git a/projects/rt_ambient_occlusion/resources/shaders/ambientOcclusion.rgen b/projects/rt_ambient_occlusion/resources/shaders/ambientOcclusion.rgen index f0646e779a9ad225eadcabbdf314d6bacfeac48a..cecbfcc9aaff3dea184112c9e8988a290dbb9518 100644 --- a/projects/rt_ambient_occlusion/resources/shaders/ambientOcclusion.rgen +++ b/projects/rt_ambient_occlusion/resources/shaders/ambientOcclusion.rgen @@ -1,5 +1,6 @@ #version 460 #extension GL_EXT_ray_tracing : require +#extension GL_EXT_scalar_block_layout : require #define M_PI 3.1415926535897932384626433832795 @@ -13,6 +14,11 @@ layout(location = 0) rayPayloadEXT Payload { layout(binding = 0, set = 0, rgba16) uniform image2D outImg; // the output image -> maybe use 16 bit values? layout(binding = 1, set = 0) uniform accelerationStructureEXT tlas; // top level acceleration structure +layout(binding = 3, set = 0, scalar) buffer rtContext { + uint instanceCount; + uint sampleCount; +}; + layout( push_constant ) uniform constants { vec4 camera_position; // as origin for ray generation vec4 camera_right; // for computing ray direction @@ -102,7 +108,7 @@ float CastShadowRay(vec3 orig, vec3 dir) { return payload.hitSky; } -vec3 sampleCosineDistribution(vec2 xi){ +vec3 sampleCosineDistribution(vec2 xi) { float phi = 2 * M_PI * xi.y; return vec3( sqrt(xi.x) * cos(phi), @@ -117,7 +123,7 @@ struct Basis{ vec3 forward; }; -Basis buildBasisAroundNormal(vec3 N){ +Basis buildBasisAroundNormal(vec3 N) { Basis basis; basis.up = N; basis.right = abs(basis.up.x) < 0.99 ? vec3(1, 0, 0) : vec3(0, 0, 1); @@ -126,18 +132,7 @@ Basis buildBasisAroundNormal(vec3 N){ return basis; } -vec3 sampleTangentToWorldSpace(vec3 tangentSpaceSample, vec3 N){ - Basis tangentBasis = buildBasisAroundNormal(N); - return ( - tangentBasis.right * tangentSpaceSample.x + - tangentBasis.up * tangentSpaceSample.y + - tangentBasis.forward * tangentSpaceSample.z - ); -} - -void main(){ - uint rayCount = 16; // the amount of rays to be casted - +void main() { initRandom(gl_LaunchIDEXT.xy); uvec2 pixel = gl_LaunchIDEXT.xy; @@ -145,22 +140,30 @@ void main(){ vec3 pos, norm; // AO rays from where? TraceCameraRay(pixelIsSky, pos, norm); - if (pixelIsSky){ + if (pixelIsSky) { // Don't compute ambient occlusion for the sky imageStore(outImg, ivec2(pixel), vec4(0.8,0.8,0.8,1.0)); return; } + Basis tangentBasis = buildBasisAroundNormal(norm); + // Compute ambient occlusion float aoValue = 0.0f; - for(uint i = 0; i < rayCount; i++){ + + for (uint i = 0; i < sampleCount; i++) { vec3 sampleTangentSpace = sampleCosineDistribution(random()); - vec3 sampleWorldSpace = sampleTangentToWorldSpace(sampleTangentSpace, norm); - aoValue += CastShadowRay(pos, sampleWorldSpace); + vec3 sampleWorldSpace = ( + tangentBasis.right * sampleTangentSpace.x + + tangentBasis.up * sampleTangentSpace.y + + tangentBasis.forward * sampleTangentSpace.z + ); + + aoValue += CastShadowRay(pos, sampleWorldSpace) * max(-dot(norm, -sampleWorldSpace), 0); } - aoValue /= rayCount; + aoValue /= max(sampleCount, 1); imageStore(outImg, ivec2(pixel), vec4(vec3(aoValue), 1)); } diff --git a/projects/rt_ambient_occlusion/src/main.cpp b/projects/rt_ambient_occlusion/src/main.cpp index 4e587f2ecf35668623bfba615cf65de0673644ea..49d6156405af2149c0fe0ec38c72635554af690e 100644 --- a/projects/rt_ambient_occlusion/src/main.cpp +++ b/projects/rt_ambient_occlusion/src/main.cpp @@ -1,7 +1,9 @@ #include <vkcv/Core.hpp> #include <vkcv/camera/CameraManager.hpp> +#include <vkcv/gui/GUI.hpp> #include <vkcv/shader/GLSLCompiler.hpp> #include <vkcv/scene/Scene.hpp> +#include <vkcv/upscaling/FSRUpscaling.hpp> /** * Note: This project is based on the following tutorial https://github.com/Apress/Ray-Tracing-Gems-II/tree/main/Chapter_16. @@ -147,16 +149,38 @@ int main(int argc, const char** argv) { objDescBuffer.fill(objDescList); - auto instanceCountBuffer = vkcv::buffer<uint32_t>(core, vkcv::BufferType::STORAGE, 1); - instanceCountBuffer.fill(&instanceCount); + auto contextBuffer = vkcv::buffer<uint32_t>( + core, vkcv::BufferType::STORAGE, 2 + ); + + uint32_t* context = contextBuffer.map(); + + context[0] = instanceCount; + context[1] = 16; { vkcv::DescriptorWrites writes; writes.writeAcceleration(1, { scene_tlas }); writes.writeStorageBuffer(2, objDescBuffer.getHandle()); - writes.writeStorageBuffer(3, instanceCountBuffer.getHandle()); + writes.writeStorageBuffer(3, contextBuffer.getHandle()); core.writeDescriptorSet(descriptorSetHandles[0], writes); } + + vkcv::upscaling::FSRUpscaling upscaling (core); + + uint32_t fsrWidth = core.getWindow(windowHandle).getWidth(); + uint32_t fsrHeight = core.getWindow(windowHandle).getHeight(); + + vkcv::upscaling::FSRQualityMode fsrMode = vkcv::upscaling::FSRQualityMode::NONE; + int fsrModeIndex = static_cast<int>(fsrMode); + + const std::vector<const char*> fsrModeNames = { + "None", + "Ultra Quality", + "Quality", + "Balanced", + "Performance" + }; struct RaytracingPushConstantData { glm::vec4 camera_position; // as origin for ray generation @@ -169,25 +193,69 @@ int main(int argc, const char** argv) { shaderProgram, descriptorSetLayoutHandles )); - - vkcv::ImageHandle depthBuffer; + + const vk::Format depthBufferFormat = vk::Format::eD32Sfloat; + const vk::Format colorBufferFormat = vk::Format::eR16G16B16A16Sfloat; + + vkcv::ImageConfig depthBufferConfig ( + fsrWidth, + fsrHeight + ); + + vkcv::ImageHandle depthBuffer = core.createImage( + depthBufferFormat, + depthBufferConfig + ); + + vkcv::ImageConfig colorBufferConfig ( + fsrWidth, + fsrHeight + ); + + colorBufferConfig.setSupportingStorage(true); + colorBufferConfig.setSupportingColorAttachment(true); + + vkcv::ImageHandle colorBuffer; const vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle(); + vkcv::gui::GUI gui (core, windowHandle); + core.run([&](const vkcv::WindowHandle &windowHandle, double t, double dt, uint32_t swapchainWidth, uint32_t swapchainHeight) { - if ((!depthBuffer) || - (swapchainWidth != core.getImageWidth(depthBuffer)) || - ((swapchainHeight != core.getImageHeight(depthBuffer)))) { + uint32_t width, height; + vkcv::upscaling::getFSRResolution( + fsrMode, + swapchainWidth, swapchainHeight, + width, height + ); + + if ((!colorBuffer) || (!depthBuffer) || + (width != fsrWidth) || (height != fsrHeight)) { + fsrWidth = width; + fsrHeight = height; + + depthBufferConfig.setWidth(fsrWidth); + depthBufferConfig.setHeight(fsrHeight); + depthBuffer = core.createImage( - vk::Format::eD32Sfloat, - vkcv::ImageConfig( - swapchainWidth, - swapchainHeight - ) + depthBufferFormat, + depthBufferConfig + ); + + colorBufferConfig.setWidth(fsrWidth); + colorBufferConfig.setHeight(fsrHeight); + + colorBuffer = core.createImage( + colorBufferFormat, + colorBufferConfig ); } + if ((!colorBuffer) || (!depthBuffer)) { + return; + } + cameraManager.update(dt); const std::vector<vkcv::ImageHandle> renderTargets = { swapchainInput, depthBuffer }; @@ -205,26 +273,59 @@ int main(int argc, const char** argv) { { vkcv::DescriptorWrites writes; - writes.writeStorageImage(0, swapchainInput); + writes.writeStorageImage(0, colorBuffer); core.writeDescriptorSet(shaderDescriptorSet, writes); } auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics); - core.prepareImageForStorage(cmdStream, swapchainInput); + core.prepareImageForStorage(cmdStream, colorBuffer); core.recordRayGenerationToCmdStream( cmdStream, pipeline, - vkcv::DispatchSize(swapchainWidth, swapchainHeight), + vkcv::DispatchSize(width, height), { vkcv::useDescriptorSet(0, shaderDescriptorSet) }, pushConstants, windowHandle ); + + core.prepareImageForSampling(cmdStream, colorBuffer); + core.prepareImageForStorage(cmdStream, swapchainInput); + + upscaling.recordUpscaling(cmdStream, colorBuffer, swapchainInput); core.prepareSwapchainImageForPresent(cmdStream); core.submitCommandStream(cmdStream); + + gui.beginGUI(); + + ImGui::Begin("Settings"); + + int sampleCount = static_cast<int>(context[1]); + + ImGui::SliderInt("Samples", &sampleCount, 1, 128); + + if (static_cast<uint32_t>(sampleCount) != context[1]) { + context[1] = static_cast<uint32_t>(sampleCount); + } + + float sharpness = upscaling.getSharpness(); + + ImGui::Combo("FSR Quality Mode", &fsrModeIndex, fsrModeNames.data(), fsrModeNames.size()); + ImGui::DragFloat("FSR Sharpness", &sharpness, 0.001, 0.0f, 1.0f); + + if ((fsrModeIndex >= 0) && (fsrModeIndex <= 4)) { + fsrMode = static_cast<vkcv::upscaling::FSRQualityMode>(fsrModeIndex); + } + + upscaling.setSharpness(sharpness); + + ImGui::End(); + + gui.endGUI(); }); - + + contextBuffer.unmap(); return 0; } diff --git a/src/vkcv/ImageManager.cpp b/src/vkcv/ImageManager.cpp index 4e65cc45f83915861ef4b67c388e5ae35ef9d35c..5ecbd70f93e47c7e4a0a1a85dc0eef8e4cc00279 100644 --- a/src/vkcv/ImageManager.cpp +++ b/src/vkcv/ImageManager.cpp @@ -414,6 +414,10 @@ namespace vkcv { mipLevelCount = mipLevelsMax - mipLevelOffset; } + if (mipLevelCount <= 0) { + return {}; + } + vk::ImageSubresourceRange imageSubresourceRange( aspectFlags, mipLevelOffset, @@ -480,14 +484,16 @@ namespace vkcv { newLayout ); - cmdBuffer.pipelineBarrier( - vk::PipelineStageFlagBits::eAllCommands, - vk::PipelineStageFlagBits::eAllCommands, - {}, - nullptr, - nullptr, - transitionBarrier - ); + if (transitionBarrier.subresourceRange.levelCount > 0) { + cmdBuffer.pipelineBarrier( + vk::PipelineStageFlagBits::eAllCommands, + vk::PipelineStageFlagBits::eAllCommands, + {}, + nullptr, + nullptr, + transitionBarrier + ); + } for (auto& layer : image.m_layers) { layer = newLayout;