diff --git a/include/vkcv/DescriptorWrites.hpp b/include/vkcv/DescriptorWrites.hpp index d67e8e3233e184b207d109e652adeca43407d7e0..016bdab7c337d3ebada5ed8e9a035606eb937211 100644 --- a/include/vkcv/DescriptorWrites.hpp +++ b/include/vkcv/DescriptorWrites.hpp @@ -10,9 +10,11 @@ namespace vkcv { }; struct StorageImageDescriptorWrite { - inline StorageImageDescriptorWrite(uint32_t binding, ImageHandle image) : binding(binding), image(image) {}; + inline StorageImageDescriptorWrite(uint32_t binding, ImageHandle image, uint32_t mipLevel = 0) + : binding(binding), image(image), mipLevel(mipLevel) {}; uint32_t binding; ImageHandle image; + uint32_t mipLevel; }; struct UniformBufferDescriptorWrite { diff --git a/include/vkcv/Image.hpp b/include/vkcv/Image.hpp index 98a0943bcb253142c836cdfd13623a13f18c2871..9e1f9708c6318aa6deb750097c414358ffde2c65 100644 --- a/include/vkcv/Image.hpp +++ b/include/vkcv/Image.hpp @@ -32,11 +32,15 @@ namespace vkcv { [[nodiscard]] vkcv::ImageHandle getHandle() const; - + + [[nodiscard]] + uint32_t getMipCount() const; + void switchLayout(vk::ImageLayout newLayout); void fill(void* data, size_t size = SIZE_MAX); void generateMipChainImmediate(); + void recordMipChainGeneration(const vkcv::CommandStreamHandle& cmdStream); private: ImageManager* const m_manager; const ImageHandle m_handle; diff --git a/projects/voxelization/CMakeLists.txt b/projects/voxelization/CMakeLists.txt index 33cfaef6197079b72ab2f295a4503e80be724db4..bc87996096226af4e3f3d05c3e10bb287c61cc8d 100644 --- a/projects/voxelization/CMakeLists.txt +++ b/projects/voxelization/CMakeLists.txt @@ -26,7 +26,7 @@ if(MSVC) endif() # including headers of dependencies and the VkCV framework -target_include_directories(voxelization SYSTEM BEFORE PRIVATE ${vkcv_include} ${vkcv_includes} ${vkcv_asset_loader_include} ${vkcv_camera_include} ${vkcv_shader_compiler_include}) +target_include_directories(voxelization SYSTEM BEFORE PRIVATE ${vkcv_include} ${vkcv_includes} ${vkcv_asset_loader_include} ${vkcv_camera_include} ${vkcv_shader_compiler_include} ${vkcv_gui_include}) # linking with libraries from all dependencies and the VkCV framework -target_link_libraries(voxelization vkcv ${vkcv_libraries} vkcv_asset_loader ${vkcv_asset_loader_libraries} vkcv_camera vkcv_shader_compiler) +target_link_libraries(voxelization vkcv ${vkcv_libraries} vkcv_asset_loader ${vkcv_asset_loader_libraries} vkcv_camera vkcv_shader_compiler vkcv_gui) diff --git a/projects/voxelization/resources/shaders/lightInfo.inc b/projects/voxelization/resources/shaders/lightInfo.inc new file mode 100644 index 0000000000000000000000000000000000000000..4345d4f1504d27df7392b34bcaf17efdcfecef33 --- /dev/null +++ b/projects/voxelization/resources/shaders/lightInfo.inc @@ -0,0 +1,6 @@ +struct LightInfo{ + vec3 L; float padding; + vec3 sunColor; + float sunStrength; + mat4 lightMatrix; +}; \ No newline at end of file diff --git a/projects/voxelization/resources/shaders/shader.frag b/projects/voxelization/resources/shaders/shader.frag index edafc8c0077416cb57aedc4e7358126846507e18..8653ae5958ce3b42eac6b1eaa6813f85b6ed589c 100644 --- a/projects/voxelization/resources/shaders/shader.frag +++ b/projects/voxelization/resources/shaders/shader.frag @@ -3,6 +3,8 @@ #extension GL_GOOGLE_include_directive : enable #include "perMeshResources.inc" +#include "lightInfo.inc" +#include "shadowMapping.inc" layout(location = 0) in vec3 passNormal; layout(location = 1) in vec2 passUV; @@ -11,35 +13,16 @@ layout(location = 2) in vec3 passPos; layout(location = 0) out vec3 outColor; layout(set=0, binding=0) uniform sunBuffer { - vec3 L; float padding; - mat4 lightMatrix; + LightInfo lightInfo; }; layout(set=0, binding=1) uniform texture2D shadowMap; layout(set=0, binding=2) uniform sampler shadowMapSampler; -float shadowTest(vec3 worldPos){ - vec4 lightPos = lightMatrix * vec4(worldPos, 1); - lightPos /= lightPos.w; - lightPos.xy = lightPos.xy * 0.5 + 0.5; - - if(any(lessThan(lightPos.xy, vec2(0))) || any(greaterThan(lightPos.xy, vec2(1)))){ - return 1; - } - - lightPos.z = clamp(lightPos.z, 0, 1); - - float shadowMapSample = texture(sampler2D(shadowMap, shadowMapSampler), lightPos.xy).r; - float bias = 0.01f; - shadowMapSample += bias; - return shadowMapSample < lightPos.z ? 0 : 1; -} - void main() { - vec3 N = normalize(passNormal); - vec3 sunColor = vec3(1); - vec3 sun = sunColor * clamp(dot(N, L), 0, 1); - sun *= shadowTest(passPos); - vec3 ambient = vec3(0.1); - vec3 albedo = texture(sampler2D(albedoTexture, textureSampler), passUV).rgb; - outColor = albedo * (sun + ambient); + vec3 N = normalize(passNormal); + vec3 sun = lightInfo.sunStrength * lightInfo.sunColor * clamp(dot(N, lightInfo.L), 0, 1); + sun *= shadowTest(passPos, lightInfo, shadowMap, shadowMapSampler); + vec3 ambient = vec3(0.05); + vec3 albedo = texture(sampler2D(albedoTexture, textureSampler), passUV).rgb; + outColor = albedo * (sun + ambient); } \ No newline at end of file diff --git a/projects/voxelization/resources/shaders/shadowMapping.inc b/projects/voxelization/resources/shaders/shadowMapping.inc new file mode 100644 index 0000000000000000000000000000000000000000..1fa34a388c35b96a3316e972ca562d35e2c3cf90 --- /dev/null +++ b/projects/voxelization/resources/shaders/shadowMapping.inc @@ -0,0 +1,16 @@ +float shadowTest(vec3 worldPos, LightInfo lightInfo, texture2D shadowMap, sampler shadowMapSampler){ + vec4 lightPos = lightInfo.lightMatrix * vec4(worldPos, 1); + lightPos /= lightPos.w; + lightPos.xy = lightPos.xy * 0.5 + 0.5; + + if(any(lessThan(lightPos.xy, vec2(0))) || any(greaterThan(lightPos.xy, vec2(1)))){ + return 1; + } + + lightPos.z = clamp(lightPos.z, 0, 1); + + float shadowMapSample = texture(sampler2D(shadowMap, shadowMapSampler), lightPos.xy).r; + float bias = 0.01f; + shadowMapSample += bias; + return shadowMapSample < lightPos.z ? 0 : 1; +} \ No newline at end of file diff --git a/projects/voxelization/resources/shaders/gammaCorrection.comp b/projects/voxelization/resources/shaders/tonemapping.comp similarity index 60% rename from projects/voxelization/resources/shaders/gammaCorrection.comp rename to projects/voxelization/resources/shaders/tonemapping.comp index 411a59c3e38b3414adbda260803c3f3322b16ff2..2383302fa946e7d92871039daff28232df2eafdd 100644 --- a/projects/voxelization/resources/shaders/gammaCorrection.comp +++ b/projects/voxelization/resources/shaders/tonemapping.comp @@ -11,8 +11,9 @@ void main(){ if(any(greaterThanEqual(gl_GlobalInvocationID.xy, imageSize(inImage)))){ return; } - ivec2 uv = ivec2(gl_GlobalInvocationID.xy); - vec3 linearColor = imageLoad(inImage, uv).rgb; - vec3 gammaCorrected = pow(linearColor, vec3(1.f / 2.2f)); + ivec2 uv = ivec2(gl_GlobalInvocationID.xy); + vec3 linearColor = imageLoad(inImage, uv).rgb; + vec3 tonemapped = linearColor / (linearColor + 1); // reinhard tonemapping + vec3 gammaCorrected = pow(tonemapped, vec3(1.f / 2.2f)); imageStore(outImage, uv, vec4(gammaCorrected, 0.f)); } \ No newline at end of file diff --git a/projects/voxelization/resources/shaders/voxel.inc b/projects/voxelization/resources/shaders/voxel.inc index d2b4400235817e3be1739dc46857ab42f260ebf7..25c0a82bbc887913a4d69ccdeee2b0d8934828c8 100644 --- a/projects/voxelization/resources/shaders/voxel.inc +++ b/projects/voxelization/resources/shaders/voxel.inc @@ -7,12 +7,26 @@ uint flattenVoxelUVToIndex(ivec3 UV, ivec3 voxelImageSize){ return UV.x + UV.y * voxelImageSize.x + UV.z * voxelImageSize.x* voxelImageSize.y; } +// packed voxel data: +// 1 bit opacity +// 7 bit exposure +// 8 bit blue +// 8 bit green +// 8 bit red +float maxExposure = 16.f; + uint packVoxelInfo(vec3 color){ - uint opaqueBit = 1 << 31; - uint redBits = uint(color.r * 255); - uint greenBits = uint(color.g * 255) << 8; - uint blueBits = uint(color.b * 255) << 16; - return opaqueBit | redBits | greenBits | blueBits; + + color = clamp(color, vec3(0), vec3(maxExposure)); + float maxComponent = max(max(max(color.r, color.g), color.b), 1.f); + color /= maxComponent; + + uint opaqueBit = 1 << 31; + uint exposureBits = (0x0000007F & uint(maxComponent / maxExposure * 127)) << 24; + uint redBits = (0x000000FF & uint(color.r * 255)) << 0; + uint greenBits = (0x000000FF & uint(color.g * 255)) << 8; + uint blueBits = (0x000000FF & uint(color.b * 255)) << 16; + return opaqueBit | exposureBits | blueBits | greenBits | redBits; } vec4 unpackVoxelInfo(uint packed){ @@ -20,6 +34,9 @@ vec4 unpackVoxelInfo(uint packed){ rgba.r = (packed >> 0 & 0x000000FF) / 255.f; rgba.g = (packed >> 8 & 0x000000FF) / 255.f; rgba.b = (packed >> 16 & 0x000000FF) / 255.f; - rgba.a = packed >> 31; + rgba.a = packed >> 31; + + rgba.rgb *= (packed >> 24 & 0x0000007F) / 127.f * maxExposure; + return rgba; } \ No newline at end of file diff --git a/projects/voxelization/resources/shaders/voxelization.frag b/projects/voxelization/resources/shaders/voxelization.frag index 7ea161ce4f5a4d59bb3d50c78553df0dfb5ab4ec..a49b13185ec26b069661141cfdbbfbbe45d14fd3 100644 --- a/projects/voxelization/resources/shaders/voxelization.frag +++ b/projects/voxelization/resources/shaders/voxelization.frag @@ -4,9 +4,12 @@ #include "voxel.inc" #include "perMeshResources.inc" +#include "lightInfo.inc" +#include "shadowMapping.inc" -layout(location = 0) in vec3 passPos; -layout(location = 1) out vec2 passUV; +layout(location = 0) in vec3 passPos; +layout(location = 1) in vec2 passUV; +layout(location = 2) in vec3 passN; layout(set=0, binding=0, std430) buffer voxelizationBuffer{ uint packedVoxelData[]; @@ -18,6 +21,13 @@ layout(set=0, binding=1) uniform voxelizationInfo{ layout(set=0, binding=2, r8) uniform image3D voxelImage; +layout(set=0, binding=3) uniform sunBuffer { + LightInfo lightInfo; +}; + +layout(set=0, binding=4) uniform texture2D shadowMap; +layout(set=0, binding=5) uniform sampler shadowMapSampler; + vec3 worldToVoxelCoordinates(vec3 world, VoxelInfo info){ return (world - info.offset) / info.extent + 0.5f; } @@ -34,7 +44,14 @@ void main() { return; } uint flatIndex = flattenVoxelUVToIndex(UV, voxelImageSize); + + vec3 albedo = texture(sampler2D(albedoTexture, textureSampler), passUV).rgb; + + vec3 N = normalize(passN); + float NoL = clamp(dot(N, lightInfo.L), 0, 1); + vec3 sun = lightInfo.sunStrength * lightInfo.sunColor * NoL * shadowTest(passPos, lightInfo, shadowMap, shadowMapSampler); + vec3 color = albedo * sun; + color = albedo * sun; - vec3 color = texture(sampler2D(albedoTexture, textureSampler), passUV).rgb; atomicMax(packedVoxelData[flatIndex], packVoxelInfo(color)); } \ No newline at end of file diff --git a/projects/voxelization/resources/shaders/voxelization.geom b/projects/voxelization/resources/shaders/voxelization.geom index 19e31e2d2d032b5a9e5c273f6420c6449be9203e..56542d960d65db6ca12c5f84837cb0c0a9ff0ded 100644 --- a/projects/voxelization/resources/shaders/voxelization.geom +++ b/projects/voxelization/resources/shaders/voxelization.geom @@ -6,9 +6,11 @@ layout (triangle_strip, max_vertices = 3) out; layout(location = 0) in vec3 passPosIn[3]; layout(location = 1) in vec2 passUVIn[3]; +layout(location = 2) in vec3 passNIn[3]; layout(location = 0) out vec3 passPos; layout(location = 1) out vec2 passUV; +layout(location = 2) out vec3 passN; void main() { // compute geometric normal, no normalization necessary @@ -29,6 +31,7 @@ void main() { gl_Position.z = gl_Position.z * 0.5 + 0.5; // xyz are kept in NDC range [-1, 1] so swizzling works, but vulkan needs final z in range [0, 1] passPos = passPosIn[i]; passUV = passUVIn[i]; + passN = passNIn[i]; EmitVertex(); } EndPrimitive(); diff --git a/projects/voxelization/resources/shaders/voxelization.vert b/projects/voxelization/resources/shaders/voxelization.vert index 7a43c08b64d3df384d3a7e627d789db9be99f680..1302a42441b5b9c8ea7d24f97d29b684e4d64993 100644 --- a/projects/voxelization/resources/shaders/voxelization.vert +++ b/projects/voxelization/resources/shaders/voxelization.vert @@ -7,6 +7,7 @@ layout(location = 2) in vec2 inUV; layout(location = 0) out vec3 passPos; layout(location = 1) out vec2 passUV; +layout(location = 2) out vec3 passN; layout( push_constant ) uniform constants{ mat4 mvp; @@ -17,4 +18,5 @@ void main() { gl_Position = mvp * vec4(inPosition, 1.0); passPos = (model * vec4(inPosition, 1)).xyz; passUV = inUV; + passN = inNormal; } \ No newline at end of file diff --git a/projects/voxelization/src/Voxelization.cpp b/projects/voxelization/src/Voxelization.cpp index b04fab9a2b61430d24bd7171d9fd3637dd428699..a04131cedfdc508e14a3dbd97da642e1af48f8da 100644 --- a/projects/voxelization/src/Voxelization.cpp +++ b/projects/voxelization/src/Voxelization.cpp @@ -1,6 +1,7 @@ #include "Voxelization.hpp" #include <vkcv/shader/GLSLCompiler.hpp> #include <glm/gtc/matrix_transform.hpp> +#include <algorithm> vkcv::ShaderProgram loadVoxelizationShader() { vkcv::shader::GLSLCompiler compiler; @@ -62,10 +63,15 @@ const uint32_t voxelResolution = 128; uint32_t voxelCount = voxelResolution * voxelResolution * voxelResolution; const vk::Format voxelizationDummyFormat = vk::Format::eR8Unorm; -Voxelization::Voxelization(vkcv::Core* corePtr, const Dependencies& dependencies) +Voxelization::Voxelization( + vkcv::Core* corePtr, + const Dependencies& dependencies, + vkcv::BufferHandle lightInfoBuffer, + vkcv::ImageHandle shadowMap, + vkcv::SamplerHandle shadowSampler) : m_corePtr(corePtr), - m_voxelImage(m_corePtr->createImage(vk::Format::eR16G16B16A16Sfloat, voxelResolution, voxelResolution, voxelResolution, false, true)), + m_voxelImage(m_corePtr->createImage(vk::Format::eR16G16B16A16Sfloat, voxelResolution, voxelResolution, voxelResolution, true, true)), m_dummyRenderTarget(m_corePtr->createImage(voxelizationDummyFormat, voxelResolution, voxelResolution, 1, false, false, true)), m_voxelInfoBuffer(m_corePtr->createBuffer<VoxelizationInfo>(vkcv::BufferType::UNIFORM, 1)), m_voxelBuffer(m_corePtr->createBuffer<VoxelBufferContent>(vkcv::BufferType::STORAGE, voxelCount)){ @@ -100,7 +106,12 @@ Voxelization::Voxelization(vkcv::Core* corePtr, const Dependencies& dependencies vkcv::DescriptorWrites voxelizationDescriptorWrites; voxelizationDescriptorWrites.storageBufferWrites = { vkcv::StorageBufferDescriptorWrite(0, m_voxelBuffer.getHandle()) }; - voxelizationDescriptorWrites.uniformBufferWrites = { vkcv::UniformBufferDescriptorWrite(1, m_voxelInfoBuffer.getHandle()) }; + voxelizationDescriptorWrites.uniformBufferWrites = { + vkcv::UniformBufferDescriptorWrite(1, m_voxelInfoBuffer.getHandle()), + vkcv::UniformBufferDescriptorWrite(3, lightInfoBuffer) + }; + voxelizationDescriptorWrites.sampledImageWrites = { vkcv::SampledImageDescriptorWrite(4, shadowMap) }; + voxelizationDescriptorWrites.samplerWrites = { vkcv::SamplerDescriptorWrite(5, shadowSampler) }; voxelizationDescriptorWrites.storageImageWrites = { vkcv::StorageImageDescriptorWrite(2, m_voxelImage.getHandle()) }; m_corePtr->writeDescriptorSet(m_voxelizationDescriptorSet, voxelizationDescriptorWrites); @@ -143,13 +154,6 @@ Voxelization::Voxelization(vkcv::Core* corePtr, const Dependencies& dependencies voxelIndexData.push_back(i); } - vkcv::DescriptorWrites voxelVisualisationDescriptorWrite; - voxelVisualisationDescriptorWrite.storageImageWrites = - { vkcv::StorageImageDescriptorWrite(0, m_voxelImage.getHandle()) }; - voxelVisualisationDescriptorWrite.uniformBufferWrites = - { vkcv::UniformBufferDescriptorWrite(1, m_voxelInfoBuffer.getHandle()) }; - m_corePtr->writeDescriptorSet(m_visualisationDescriptorSet, voxelVisualisationDescriptorWrite); - const vkcv::DescriptorSetUsage voxelizationDescriptorUsage(0, m_corePtr->getDescriptorSet(m_visualisationDescriptorSet).vulkanHandle); vkcv::ShaderProgram resetVoxelShader = loadVoxelResetShader(); @@ -262,17 +266,32 @@ void Voxelization::voxelizeMeshes( vkcv::PushConstantData(nullptr, 0)); m_corePtr->recordImageMemoryBarrier(cmdStream, m_voxelImage.getHandle()); + + m_voxelImage.recordMipChainGeneration(cmdStream); } void Voxelization::renderVoxelVisualisation( - vkcv::CommandStreamHandle cmdStream, - const glm::mat4& viewProjectin, - const std::vector<vkcv::ImageHandle>& renderTargets) { + vkcv::CommandStreamHandle cmdStream, + const glm::mat4& viewProjectin, + const std::vector<vkcv::ImageHandle>& renderTargets, + uint32_t mipLevel) { const vkcv::PushConstantData voxelVisualisationPushConstantData((void*)&viewProjectin, sizeof(glm::mat4)); + mipLevel = std::clamp(mipLevel, (uint32_t)0, m_voxelImage.getMipCount()-1); + + // write descriptor set + vkcv::DescriptorWrites voxelVisualisationDescriptorWrite; + voxelVisualisationDescriptorWrite.storageImageWrites = + { vkcv::StorageImageDescriptorWrite(0, m_voxelImage.getHandle(), mipLevel) }; + voxelVisualisationDescriptorWrite.uniformBufferWrites = + { vkcv::UniformBufferDescriptorWrite(1, m_voxelInfoBuffer.getHandle()) }; + m_corePtr->writeDescriptorSet(m_visualisationDescriptorSet, voxelVisualisationDescriptorWrite); + + uint32_t drawVoxelCount = voxelCount / exp2(mipLevel); + const auto drawcall = vkcv::DrawcallInfo( - vkcv::Mesh({}, nullptr, voxelCount), + vkcv::Mesh({}, nullptr, drawVoxelCount), { vkcv::DescriptorSetUsage(0, m_corePtr->getDescriptorSet(m_visualisationDescriptorSet).vulkanHandle) }); m_corePtr->recordDrawcallsToCmdStream( @@ -282,4 +301,8 @@ void Voxelization::renderVoxelVisualisation( voxelVisualisationPushConstantData, { drawcall }, renderTargets); +} + +void Voxelization::setVoxelExtent(float extent) { + m_voxelExtent = extent; } \ No newline at end of file diff --git a/projects/voxelization/src/Voxelization.hpp b/projects/voxelization/src/Voxelization.hpp index f9b96998b39e24a3481d130efa68ebaa813b8256..25830b171edb9154e37b2d597c2bbbf2daea6b2e 100644 --- a/projects/voxelization/src/Voxelization.hpp +++ b/projects/voxelization/src/Voxelization.hpp @@ -9,7 +9,12 @@ public: vk::Format colorBufferFormat; vk::Format depthBufferFormat; }; - Voxelization(vkcv::Core* corePtr, const Dependencies& dependencies); + Voxelization( + vkcv::Core* corePtr, + const Dependencies& dependencies, + vkcv::BufferHandle lightInfoBuffer, + vkcv::ImageHandle shadowMap, + vkcv::SamplerHandle shadowSampler); void voxelizeMeshes( vkcv::CommandStreamHandle cmdStream, @@ -21,7 +26,10 @@ public: void renderVoxelVisualisation( vkcv::CommandStreamHandle cmdStream, const glm::mat4& viewProjectin, - const std::vector<vkcv::ImageHandle>& renderTargets); + const std::vector<vkcv::ImageHandle>& renderTargets, + uint32_t mipLevel); + + void setVoxelExtent(float extent); private: vkcv::Core* m_corePtr; @@ -55,5 +63,5 @@ private: }; vkcv::Buffer<VoxelizationInfo> m_voxelInfoBuffer; - const float m_voxelExtent = 20.f; + float m_voxelExtent = 20.f; }; \ No newline at end of file diff --git a/projects/voxelization/src/main.cpp b/projects/voxelization/src/main.cpp index 1899a3b1e439e67a5ccee7bc21bf7366c1db8b2e..309c75d5cad862ed8bbd43cfd001c05a660caeec 100644 --- a/projects/voxelization/src/main.cpp +++ b/projects/voxelization/src/main.cpp @@ -8,12 +8,13 @@ #include <vkcv/Logger.hpp> #include "Voxelization.hpp" #include <glm/glm.hpp> +#include "vkcv/gui/GUI.hpp" int main(int argc, const char** argv) { const char* applicationName = "Voxelization"; - uint32_t windowWidth = 800; - uint32_t windowHeight = 600; + uint32_t windowWidth = 1280; + uint32_t windowHeight = 720; vkcv::Window window = vkcv::Window::create( applicationName, @@ -155,9 +156,11 @@ int main(int argc, const char** argv) { // light info buffer struct LightInfo { - glm::vec3 direction; - float padding; - glm::mat4 lightMatrix; + glm::vec3 direction; + float padding; + glm::vec3 sunColor = glm::vec3(1.f); + float sunStrength = 8.f; + glm::mat4 lightMatrix; }; LightInfo lightInfo; vkcv::Buffer lightBuffer = core.createBuffer<LightInfo>(vkcv::BufferType::UNIFORM, sizeof(glm::vec3)); @@ -178,26 +181,25 @@ int main(int argc, const char** argv) { vkcv::SamplerAddressMode::REPEAT ); - // prepare per mesh descriptor sets - std::vector<vkcv::DescriptorSetHandle> perMeshDescriptorSets; + // create descriptor sets + std::vector<vkcv::DescriptorSetHandle> materialDescriptorSets; std::vector<vkcv::Image> sceneImages; - for (const auto& vertexGroup : scene.vertexGroups) { - perMeshDescriptorSets.push_back(core.createDescriptorSet(forwardProgram.getReflectedDescriptors()[1])); - - const auto& material = scene.materials[vertexGroup.materialIndex]; + for (const auto& material : scene.materials) { int baseColorIndex = material.baseColor; if (baseColorIndex < 0) { vkcv_log(vkcv::LogLevel::WARNING, "Material lacks base color"); baseColorIndex = 0; } + materialDescriptorSets.push_back(core.createDescriptorSet(forwardProgram.getReflectedDescriptors()[1])); + vkcv::asset::Texture& sceneTexture = scene.textures[baseColorIndex]; sceneImages.push_back(core.createImage(vk::Format::eR8G8B8A8Srgb, sceneTexture.w, sceneTexture.h, 1, true)); sceneImages.back().fill(sceneTexture.data.data()); sceneImages.back().generateMipChainImmediate(); - sceneImages.back().switchLayout(vk::ImageLayout::eShaderReadOnlyOptimal); + sceneImages.back().switchLayout(vk::ImageLayout::eShaderReadOnlyOptimal); vkcv::DescriptorWrites setWrites; setWrites.sampledImageWrites = { @@ -206,7 +208,12 @@ int main(int argc, const char** argv) { setWrites.samplerWrites = { vkcv::SamplerDescriptorWrite(1, colorSampler), }; - core.writeDescriptorSet(perMeshDescriptorSets.back(), setWrites); + core.writeDescriptorSet(materialDescriptorSets.back(), setWrites); + } + + std::vector<vkcv::DescriptorSetHandle> perMeshDescriptorSets; + for (const auto& vertexGroup : scene.vertexGroups) { + perMeshDescriptorSets.push_back(materialDescriptorSets[vertexGroup.materialIndex]); } const vkcv::PipelineConfig forwardPipelineConfig { @@ -268,15 +275,15 @@ int main(int argc, const char** argv) { } }); - // gamma correction compute shader - vkcv::ShaderProgram gammaCorrectionProgram; - compiler.compile(vkcv::ShaderStage::COMPUTE, "resources/shaders/gammaCorrection.comp", + // tonemapping compute shader + vkcv::ShaderProgram tonemappingProgram; + compiler.compile(vkcv::ShaderStage::COMPUTE, "resources/shaders/tonemapping.comp", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { - gammaCorrectionProgram.addShader(shaderStage, path); + tonemappingProgram.addShader(shaderStage, path); }); - vkcv::DescriptorSetHandle gammaCorrectionDescriptorSet = core.createDescriptorSet(gammaCorrectionProgram.getReflectedDescriptors()[0]); - vkcv::PipelineHandle gammaCorrectionPipeline = core.createComputePipeline(gammaCorrectionProgram, - { core.getDescriptorSet(gammaCorrectionDescriptorSet).layout }); + vkcv::DescriptorSetHandle tonemappingDescriptorSet = core.createDescriptorSet(tonemappingProgram.getReflectedDescriptors()[0]); + vkcv::PipelineHandle tonemappingPipeline = core.createComputePipeline(tonemappingProgram, + { core.getDescriptorSet(tonemappingDescriptorSet).layout }); // model matrices per mesh std::vector<glm::mat4> modelMatrices; @@ -312,7 +319,18 @@ int main(int argc, const char** argv) { voxelDependencies.colorBufferFormat = colorBufferFormat; voxelDependencies.depthBufferFormat = depthBufferFormat; voxelDependencies.vertexLayout = vertexLayout; - Voxelization voxelization(&core, voxelDependencies); + Voxelization voxelization( + &core, + voxelDependencies, + lightBuffer.getHandle(), + shadowMap.getHandle(), + shadowSampler); + + vkcv::gui::GUI gui(core, window); + + glm::vec2 lightAngles(90.f, 0.f); + int voxelVisualisationMip = 0; + float voxelizationExtent = 20.f; auto start = std::chrono::system_clock::now(); const auto appStartTime = start; @@ -336,19 +354,20 @@ int main(int argc, const char** argv) { auto deltatime = std::chrono::duration_cast<std::chrono::microseconds>(end - start); // update descriptor sets which use swapchain image - vkcv::DescriptorWrites gammaCorrectionDescriptorWrites; - gammaCorrectionDescriptorWrites.storageImageWrites = { + vkcv::DescriptorWrites tonemappingDescriptorWrites; + tonemappingDescriptorWrites.storageImageWrites = { vkcv::StorageImageDescriptorWrite(0, colorBuffer), vkcv::StorageImageDescriptorWrite(1, swapchainInput) }; - core.writeDescriptorSet(gammaCorrectionDescriptorSet, gammaCorrectionDescriptorWrites); + core.writeDescriptorSet(tonemappingDescriptorSet, tonemappingDescriptorWrites); start = end; cameraManager.update(0.000001 * static_cast<double>(deltatime.count())); - auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - appStartTime); - - const float sunTheta = 0.001f * static_cast<float>(duration.count()); - lightInfo.direction = glm::normalize(glm::vec3(std::cos(sunTheta), 1, std::sin(sunTheta))); + glm::vec2 lightAngleRadian = glm::radians(lightAngles); + lightInfo.direction = glm::normalize(glm::vec3( + std::cos(lightAngleRadian.x) * std::cos(lightAngleRadian.y), + std::sin(lightAngleRadian.x), + std::cos(lightAngleRadian.x) * std::sin(lightAngleRadian.y))); const float shadowProjectionSize = 20.f; glm::mat4 projectionLight = glm::ortho( @@ -395,6 +414,7 @@ int main(int argc, const char** argv) { { shadowMap.getHandle() }); core.prepareImageForSampling(cmdStream, shadowMap.getHandle()); + voxelization.setVoxelExtent(voxelizationExtent); voxelization.voxelizeMeshes( cmdStream, cameraManager.getActiveCamera().getPosition(), @@ -412,13 +432,13 @@ int main(int argc, const char** argv) { renderTargets); if (renderVoxelVis) { - voxelization.renderVoxelVisualisation(cmdStream, viewProjectionCamera, renderTargets); + voxelization.renderVoxelVisualisation(cmdStream, viewProjectionCamera, renderTargets, voxelVisualisationMip); } - const uint32_t gammaCorrectionLocalGroupSize = 8; - const uint32_t gammaCorrectionDispatchCount[3] = { - static_cast<uint32_t>(glm::ceil(windowWidth / static_cast<float>(gammaCorrectionLocalGroupSize))), - static_cast<uint32_t>(glm::ceil(windowHeight / static_cast<float>(gammaCorrectionLocalGroupSize))), + const uint32_t tonemappingLocalGroupSize = 8; + const uint32_t tonemappingDispatchCount[3] = { + static_cast<uint32_t>(glm::ceil(windowWidth / static_cast<float>(tonemappingLocalGroupSize))), + static_cast<uint32_t>(glm::ceil(windowHeight / static_cast<float>(tonemappingLocalGroupSize))), 1 }; @@ -427,15 +447,29 @@ int main(int argc, const char** argv) { core.recordComputeDispatchToCmdStream( cmdStream, - gammaCorrectionPipeline, - gammaCorrectionDispatchCount, - { vkcv::DescriptorSetUsage(0, core.getDescriptorSet(gammaCorrectionDescriptorSet).vulkanHandle) }, + tonemappingPipeline, + tonemappingDispatchCount, + { vkcv::DescriptorSetUsage(0, core.getDescriptorSet(tonemappingDescriptorSet).vulkanHandle) }, vkcv::PushConstantData(nullptr, 0)); // present and end core.prepareSwapchainImageForPresent(cmdStream); core.submitCommandStream(cmdStream); + gui.beginGUI(); + + ImGui::Begin("Settings"); + ImGui::DragFloat2("Light angles", &lightAngles.x); + ImGui::ColorEdit3("Sun color", &lightInfo.sunColor.x); + ImGui::DragFloat("Sun strength", &lightInfo.sunStrength); + ImGui::Checkbox("Draw voxel visualisation", &renderVoxelVis); + ImGui::SliderInt("Visualisation mip", &voxelVisualisationMip, 0, 7); + ImGui::DragFloat("Voxelization extent", &voxelizationExtent, 1.f, 0.f); + voxelVisualisationMip = std::max(voxelVisualisationMip, 0); + ImGui::End(); + + gui.endGUI(); + core.endFrame(); } diff --git a/src/vkcv/DescriptorManager.cpp b/src/vkcv/DescriptorManager.cpp index f591daf90b47b57a758b2b24c7fa87b5c33e3c46..265532232304106f7271fdd445d52074b7c011a1 100644 --- a/src/vkcv/DescriptorManager.cpp +++ b/src/vkcv/DescriptorManager.cpp @@ -128,7 +128,7 @@ namespace vkcv for (const auto& write : writes.storageImageWrites) { const vk::DescriptorImageInfo imageInfo( nullptr, - imageManager.getVulkanImageView(write.image), + imageManager.getVulkanImageView(write.image, write.mipLevel), vk::ImageLayout::eGeneral ); diff --git a/src/vkcv/Image.cpp b/src/vkcv/Image.cpp index dca6358b84aa7bcd0f38e78d3fa7aa49fd11395d..c48b015335e00f23a892bb96d3e89a2c0877ae61 100644 --- a/src/vkcv/Image.cpp +++ b/src/vkcv/Image.cpp @@ -56,7 +56,11 @@ namespace vkcv{ vkcv::ImageHandle Image::getHandle() const { return m_handle; } - + + uint32_t Image::getMipCount() const { + return m_manager->getImageMipCount(m_handle); + } + void Image::fill(void *data, size_t size) { m_manager->fillImage(m_handle, data, size); } @@ -64,6 +68,10 @@ namespace vkcv{ void Image::generateMipChainImmediate() { m_manager->generateImageMipChainImmediate(m_handle); } + + void Image::recordMipChainGeneration(const vkcv::CommandStreamHandle& cmdStream) { + m_manager->recordImageMipChainGenerationToCmdStream(cmdStream, m_handle); + } Image::Image(ImageManager* manager, const ImageHandle& handle) : m_manager(manager), diff --git a/src/vkcv/ImageManager.cpp b/src/vkcv/ImageManager.cpp index dd54da39cfe1d832e006e4f673e6aa8e1eb7ee18..a3364ce0dfd6f59dc78c85b43570eea25cfc052d 100644 --- a/src/vkcv/ImageManager.cpp +++ b/src/vkcv/ImageManager.cpp @@ -438,6 +438,60 @@ namespace vkcv { ); } + void ImageManager::recordImageMipGenerationToCmdBuffer(vk::CommandBuffer cmdBuffer, const ImageHandle& handle) { + + const auto id = handle.getId(); + if (id >= m_images.size()) { + vkcv_log(vkcv::LogLevel::ERROR, "Invalid image handle"); + return; + } + + auto& image = m_images[id]; + recordImageLayoutTransition(handle, vk::ImageLayout::eGeneral, cmdBuffer); + + vk::ImageAspectFlags aspectMask = isDepthImageFormat(image.m_format) ? + vk::ImageAspectFlagBits::eDepth : vk::ImageAspectFlagBits::eColor; + + uint32_t srcWidth = image.m_width; + uint32_t srcHeight = image.m_height; + uint32_t srcDepth = image.m_depth; + + auto half = [](uint32_t in) { + return std::max<uint32_t>(in / 2, 1); + }; + + uint32_t dstWidth = half(srcWidth); + uint32_t dstHeight = half(srcHeight); + uint32_t dstDepth = half(srcDepth); + + for (uint32_t srcMip = 0; srcMip < image.m_viewPerMip.size() - 1; srcMip++) { + uint32_t dstMip = srcMip + 1; + vk::ImageBlit region( + vk::ImageSubresourceLayers(aspectMask, srcMip, 0, 1), + { vk::Offset3D(0, 0, 0), vk::Offset3D(srcWidth, srcHeight, srcDepth) }, + vk::ImageSubresourceLayers(aspectMask, dstMip, 0, 1), + { vk::Offset3D(0, 0, 0), vk::Offset3D(dstWidth, dstHeight, dstDepth) }); + + cmdBuffer.blitImage( + image.m_handle, + vk::ImageLayout::eGeneral, + image.m_handle, + vk::ImageLayout::eGeneral, + region, + vk::Filter::eLinear); + + srcWidth = dstWidth; + srcHeight = dstHeight; + srcDepth = dstDepth; + + dstWidth = half(dstWidth); + dstHeight = half(dstHeight); + dstDepth = half(dstDepth); + + recordImageMemoryBarrier(handle, cmdBuffer); + } + } + void ImageManager::generateImageMipChainImmediate(const ImageHandle& handle) { const auto& device = m_core->getContext().getDevice(); @@ -450,62 +504,23 @@ namespace vkcv { return; } - const auto id = handle.getId(); - if (id >= m_images.size()) { - vkcv_log(vkcv::LogLevel::ERROR, "Invalid image handle"); - return; - } - auto& image = m_images[id]; - switchImageLayoutImmediate(handle, vk::ImageLayout::eGeneral); - - const auto record = [&image, this, handle](const vk::CommandBuffer cmdBuffer) { - - vk::ImageAspectFlags aspectMask = isDepthImageFormat(image.m_format) ? - vk::ImageAspectFlagBits::eDepth : vk::ImageAspectFlagBits::eColor; - - uint32_t srcWidth = image.m_width; - uint32_t srcHeight = image.m_height; - uint32_t srcDepth = image.m_depth; - - auto half = [](uint32_t in) { - return std::max<uint32_t>(in / 2, 1); - }; - - uint32_t dstWidth = half(image.m_width); - uint32_t dstHeight = half(image.m_height); - uint32_t dstDepth = half(image.m_depth); - - for (uint32_t srcMip = 0; srcMip < image.m_viewPerMip.size() - 1; srcMip++) { - uint32_t dstMip = srcMip + 1; - vk::ImageBlit region( - vk::ImageSubresourceLayers(aspectMask, srcMip, 0, 1), - { vk::Offset3D(0, 0, 0), vk::Offset3D(srcWidth, srcHeight, srcDepth) }, - vk::ImageSubresourceLayers(aspectMask, dstMip, 0, 1), - { vk::Offset3D(0, 0, 0), vk::Offset3D(dstWidth, dstHeight, dstDepth) }); - - cmdBuffer.blitImage( - image.m_handle, - vk::ImageLayout::eGeneral, - image.m_handle, - vk::ImageLayout::eGeneral, - region, - vk::Filter::eLinear); - - srcWidth = dstWidth; - srcHeight = dstHeight; - srcDepth = dstDepth; - - dstWidth = half(dstWidth); - dstHeight = half(dstHeight); - dstDepth = half(dstDepth); - - recordImageMemoryBarrier(handle, cmdBuffer); - } + const auto record = [this, handle](const vk::CommandBuffer cmdBuffer) { + recordImageMipGenerationToCmdBuffer(cmdBuffer, handle); }; m_core->recordAndSubmitCommandsImmediate(submitInfo, record, nullptr); } + void ImageManager::recordImageMipChainGenerationToCmdStream( + const vkcv::CommandStreamHandle& cmdStream, + const ImageHandle& handle) { + + const auto record = [this, handle](const vk::CommandBuffer cmdBuffer) { + recordImageMipGenerationToCmdBuffer(cmdBuffer, handle); + }; + m_core->recordCommandsToStream(cmdStream, record, nullptr); + } + uint32_t ImageManager::getImageWidth(const ImageHandle &handle) const { const uint64_t id = handle.getId(); const bool isSwapchainImage = handle.isSwapchainImage(); @@ -590,6 +605,22 @@ namespace vkcv { return isSwapchainFormat ? m_swapchainImages[m_currentSwapchainInputImage].m_format : m_images[id].m_format; } + uint32_t ImageManager::getImageMipCount(const ImageHandle& handle) const { + const uint64_t id = handle.getId(); + const bool isSwapchainFormat = handle.isSwapchainImage(); + + if (handle.isSwapchainImage()) { + return 1; + } + + if (id >= m_images.size()) { + vkcv_log(LogLevel::ERROR, "Invalid handle"); + return 0; + } + + return m_images[id].m_viewPerMip.size(); + } + void ImageManager::setCurrentSwapchainImageIndex(int index) { m_currentSwapchainInputImage = index; } diff --git a/src/vkcv/ImageManager.hpp b/src/vkcv/ImageManager.hpp index 69ab7943f34bcf3579a4384cbd59e78e5b7aacdf..ecba7eb5959c1d78a0be41e0b3ac555bffd92d95 100644 --- a/src/vkcv/ImageManager.hpp +++ b/src/vkcv/ImageManager.hpp @@ -60,7 +60,9 @@ namespace vkcv { * @param id Image handle id */ void destroyImageById(uint64_t id); - + + void recordImageMipGenerationToCmdBuffer(vk::CommandBuffer cmdBuffer, const ImageHandle& handle); + public: ~ImageManager() noexcept; ImageManager(ImageManager&& other) = delete; @@ -101,6 +103,7 @@ namespace vkcv { void fillImage(const ImageHandle& handle, void* data, size_t size); void generateImageMipChainImmediate(const ImageHandle& handle); + void recordImageMipChainGenerationToCmdStream(const vkcv::CommandStreamHandle& cmdStream, const ImageHandle& handle); [[nodiscard]] uint32_t getImageWidth(const ImageHandle& handle) const; @@ -114,6 +117,9 @@ namespace vkcv { [[nodiscard]] vk::Format getImageFormat(const ImageHandle& handle) const; + [[nodiscard]] + uint32_t getImageMipCount(const ImageHandle& handle) const; + void setCurrentSwapchainImageIndex(int index); void setSwapchainImages(const std::vector<vk::Image>& images, std::vector<vk::ImageView> views, uint32_t width, uint32_t height, vk::Format format);