diff --git a/include/vkcv/DescriptorWrites.hpp b/include/vkcv/DescriptorWrites.hpp index 016bdab7c337d3ebada5ed8e9a035606eb937211..f28a6c91e189b13413ffefec0f05e5a0a358ee26 100644 --- a/include/vkcv/DescriptorWrites.hpp +++ b/include/vkcv/DescriptorWrites.hpp @@ -4,9 +4,12 @@ namespace vkcv { struct SampledImageDescriptorWrite { - inline SampledImageDescriptorWrite(uint32_t binding, ImageHandle image) : binding(binding), image(image) {}; + inline SampledImageDescriptorWrite(uint32_t binding, ImageHandle image, uint32_t mipLevel = 0, bool useGeneralLayout = false) + : binding(binding), image(image), mipLevel(mipLevel), useGeneralLayout(useGeneralLayout) {}; uint32_t binding; ImageHandle image; + uint32_t mipLevel; + bool useGeneralLayout; }; struct StorageImageDescriptorWrite { @@ -38,8 +41,8 @@ namespace vkcv { struct DescriptorWrites { std::vector<SampledImageDescriptorWrite> sampledImageWrites; std::vector<StorageImageDescriptorWrite> storageImageWrites; - std::vector<UniformBufferDescriptorWrite> uniformBufferWrites; - std::vector<StorageBufferDescriptorWrite> storageBufferWrites; - std::vector<SamplerDescriptorWrite> samplerWrites; + std::vector<UniformBufferDescriptorWrite> uniformBufferWrites; + std::vector<StorageBufferDescriptorWrite> storageBufferWrites; + std::vector<SamplerDescriptorWrite> samplerWrites; }; } \ No newline at end of file diff --git a/include/vkcv/Image.hpp b/include/vkcv/Image.hpp index 9e1f9708c6318aa6deb750097c414358ffde2c65..eac96975886a92e0043bbb97cbe64f76943efa7e 100644 --- a/include/vkcv/Image.hpp +++ b/include/vkcv/Image.hpp @@ -42,8 +42,10 @@ namespace vkcv { void generateMipChainImmediate(); void recordMipChainGeneration(const vkcv::CommandStreamHandle& cmdStream); private: - ImageManager* const m_manager; - const ImageHandle m_handle; + // TODO: const qualifier removed, very hacky!!! + // Else you cannot recreate an image. Pls fix. + ImageManager* m_manager; + ImageHandle m_handle; Image(ImageManager* manager, const ImageHandle& handle); diff --git a/projects/CMakeLists.txt b/projects/CMakeLists.txt index 99a3cb382ec02bab64e9fc714ceeb7e5cef39ed0..34fbcb0cf8dd3f1d34efd2cc8424994c7da76e32 100644 --- a/projects/CMakeLists.txt +++ b/projects/CMakeLists.txt @@ -1,5 +1,6 @@ # Add new projects/examples here: +add_subdirectory(bloom) add_subdirectory(first_triangle) add_subdirectory(first_mesh) add_subdirectory(first_scene) diff --git a/projects/bloom/.gitignore b/projects/bloom/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..3643183e0628e666abab193e1dd1d92c1774ac61 --- /dev/null +++ b/projects/bloom/.gitignore @@ -0,0 +1 @@ +bloom \ No newline at end of file diff --git a/projects/bloom/CMakeLists.txt b/projects/bloom/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..8171938e7cb430aacce5562af44f628c11c97c54 --- /dev/null +++ b/projects/bloom/CMakeLists.txt @@ -0,0 +1,32 @@ +cmake_minimum_required(VERSION 3.16) +project(bloom) + +# setting c++ standard for the project +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# this should fix the execution path to load local files from the project +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) + +# adding source files to the project +add_executable(bloom src/main.cpp) + +target_sources(bloom PRIVATE + src/BloomAndFlares.cpp + src/BloomAndFlares.hpp) + +# this should fix the execution path to load local files from the project (for MSVC) +if(MSVC) + set_target_properties(bloom PROPERTIES RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) + set_target_properties(bloom PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) + + # in addition to setting the output directory, the working directory has to be set + # by default visual studio sets the working directory to the build directory, when using the debugger + set_target_properties(bloom PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) +endif() + +# including headers of dependencies and the VkCV framework +target_include_directories(bloom SYSTEM BEFORE PRIVATE ${vkcv_include} ${vkcv_includes} ${vkcv_asset_loader_include} ${vkcv_camera_include} ${vkcv_shader_compiler_include}) + +# linking with libraries from all dependencies and the VkCV framework +target_link_libraries(bloom vkcv ${vkcv_libraries} vkcv_asset_loader ${vkcv_asset_loader_libraries} vkcv_camera vkcv_shader_compiler) diff --git a/projects/bloom/resources/Sponza/Sponza.bin b/projects/bloom/resources/Sponza/Sponza.bin new file mode 100644 index 0000000000000000000000000000000000000000..cfedd26ca5a67b6d0a47d44d13a75e14a141717a --- /dev/null +++ b/projects/bloom/resources/Sponza/Sponza.bin @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4b809f7a17687dc99e6f41ca1ea32c06eded8779bf34d16f1f565d750b0ffd68 +size 6347696 diff --git a/projects/bloom/resources/Sponza/Sponza.gltf b/projects/bloom/resources/Sponza/Sponza.gltf new file mode 100644 index 0000000000000000000000000000000000000000..172ea07e21c94465211c860cd805355704cef230 --- /dev/null +++ b/projects/bloom/resources/Sponza/Sponza.gltf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5cc0ecad5c4694088ff820e663619c370421afc1323ac487406e8e9b4735d787 +size 713962 diff --git a/projects/bloom/resources/Sponza/background.png b/projects/bloom/resources/Sponza/background.png new file mode 100644 index 0000000000000000000000000000000000000000..b64def129da38f4e23d89e21b4af1039008a4327 --- /dev/null +++ b/projects/bloom/resources/Sponza/background.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f5b5f900ff8ed83a31750ec8e428b5b91273794ddcbfc4e4b8a6a7e781f8c686 +size 1417666 diff --git a/projects/bloom/resources/Sponza/chain_texture.png b/projects/bloom/resources/Sponza/chain_texture.png new file mode 100644 index 0000000000000000000000000000000000000000..c1e1768cff78e0614ad707eca8602a4c4edab5e5 --- /dev/null +++ b/projects/bloom/resources/Sponza/chain_texture.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d8362cfd472880daeaea37439326a4651d1338680ae69bb2513fc6b17c8de7d4 +size 490895 diff --git a/projects/bloom/resources/Sponza/lion.png b/projects/bloom/resources/Sponza/lion.png new file mode 100644 index 0000000000000000000000000000000000000000..c49c7f0ed31e762e19284d0d3624fbc47664e56b --- /dev/null +++ b/projects/bloom/resources/Sponza/lion.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9f882f746c3a9cd51a9c6eedc1189b97668721d91a3fe49232036e789912c652 +size 2088728 diff --git a/projects/bloom/resources/Sponza/spnza_bricks_a_diff.png b/projects/bloom/resources/Sponza/spnza_bricks_a_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..cde4c7a6511e9a5f03c63ad996437fcdba3ce2df --- /dev/null +++ b/projects/bloom/resources/Sponza/spnza_bricks_a_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b94219c2f5f943f3f4715c74e7d1038bf0ab3b3b3216a758eaee67f875df0851 +size 1928829 diff --git a/projects/bloom/resources/Sponza/sponza_arch_diff.png b/projects/bloom/resources/Sponza/sponza_arch_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..bcd9bda2918d226039f9e2d03902d377b706fab6 --- /dev/null +++ b/projects/bloom/resources/Sponza/sponza_arch_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c0df2c8a01b2843b1c792b494f7173cdbc4f834840fc2177af3e5d690fceda57 +size 1596151 diff --git a/projects/bloom/resources/Sponza/sponza_ceiling_a_diff.png b/projects/bloom/resources/Sponza/sponza_ceiling_a_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..59de631ffac4414cabf69b2dc794c46fc187d6cb --- /dev/null +++ b/projects/bloom/resources/Sponza/sponza_ceiling_a_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ab6c187a81aa68f4eba30119e17fce2e4882a9ec320f70c90482dbe9da82b1c6 +size 1872074 diff --git a/projects/bloom/resources/Sponza/sponza_column_a_diff.png b/projects/bloom/resources/Sponza/sponza_column_a_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..01a82432d3f9939bbefe850bdb900f1ff9a3f6db --- /dev/null +++ b/projects/bloom/resources/Sponza/sponza_column_a_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2c291507e2808bb83e160ab4b020689817df273baad3713a9ad19ac15fac6826 +size 1840992 diff --git a/projects/bloom/resources/Sponza/sponza_column_b_diff.png b/projects/bloom/resources/Sponza/sponza_column_b_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..10a660cce2a5a9b8997772c746058ce23e7d45d7 --- /dev/null +++ b/projects/bloom/resources/Sponza/sponza_column_b_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2820b0267c4289c6cedbb42721792a57ef244ec2d0935941011c2a7d3fe88a9b +size 2170433 diff --git a/projects/bloom/resources/Sponza/sponza_column_c_diff.png b/projects/bloom/resources/Sponza/sponza_column_c_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..bc46fd979044a938d3adca7601689e71504e48bf --- /dev/null +++ b/projects/bloom/resources/Sponza/sponza_column_c_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a0bc993ff59865468ef4530798930c7dfefb07482d71db45bc2a520986b27735 +size 2066950 diff --git a/projects/bloom/resources/Sponza/sponza_curtain_blue_diff.png b/projects/bloom/resources/Sponza/sponza_curtain_blue_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..384c8c2c051160d530eb3ac8b05c9c60752a2d2b --- /dev/null +++ b/projects/bloom/resources/Sponza/sponza_curtain_blue_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b85c6bb3cd5105f48d3812ec8e7a1068521ce69e917300d79e136e19d45422fb +size 9510905 diff --git a/projects/bloom/resources/Sponza/sponza_curtain_diff.png b/projects/bloom/resources/Sponza/sponza_curtain_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..af842e9f5fe18c1f609875e00899a6770fa4488b --- /dev/null +++ b/projects/bloom/resources/Sponza/sponza_curtain_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:563c56bdbbee395a6ef7f0c51c8ac9223c162e517b4cdba0d4654e8de27c98d8 +size 9189263 diff --git a/projects/bloom/resources/Sponza/sponza_curtain_green_diff.png b/projects/bloom/resources/Sponza/sponza_curtain_green_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..6c9b6391a199407637fa71033d79fb58b8b4f0d7 --- /dev/null +++ b/projects/bloom/resources/Sponza/sponza_curtain_green_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:238fe1c7f481388d1c1d578c2da8d411b99e8f0030ab62060a306db333124476 +size 8785458 diff --git a/projects/bloom/resources/Sponza/sponza_details_diff.png b/projects/bloom/resources/Sponza/sponza_details_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..12656686362c3e0a297e060491f33bd7351551f9 --- /dev/null +++ b/projects/bloom/resources/Sponza/sponza_details_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cb1223b3bb82f8757e7df25a6891f1239cdd7ec59990340e952fb2d6b7ea570c +size 1522643 diff --git a/projects/bloom/resources/Sponza/sponza_fabric_blue_diff.png b/projects/bloom/resources/Sponza/sponza_fabric_blue_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..879d16ef84722a4fc13e83a771778de326e4bc54 --- /dev/null +++ b/projects/bloom/resources/Sponza/sponza_fabric_blue_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:467d290bf5d4b2a017da140ba9e244ed8a8a9be5418a9ac9bcb4ad572ae2d7ab +size 2229440 diff --git a/projects/bloom/resources/Sponza/sponza_fabric_diff.png b/projects/bloom/resources/Sponza/sponza_fabric_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..3311287a219d2148620b87fe428fea071688d051 --- /dev/null +++ b/projects/bloom/resources/Sponza/sponza_fabric_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1594f59cc2848db26add47361f4e665e3d8afa147760ed915d839fea42b20287 +size 2267382 diff --git a/projects/bloom/resources/Sponza/sponza_fabric_green_diff.png b/projects/bloom/resources/Sponza/sponza_fabric_green_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..de110f369004388dae4cd5067c63428db3a07834 --- /dev/null +++ b/projects/bloom/resources/Sponza/sponza_fabric_green_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:902b87faab221173bf370cea7c74cb9060b4d870ac6316b190dafded1cb12993 +size 2258220 diff --git a/projects/bloom/resources/Sponza/sponza_flagpole_diff.png b/projects/bloom/resources/Sponza/sponza_flagpole_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..5f6e0812a0df80346318baa3cb50a6888afc58f8 --- /dev/null +++ b/projects/bloom/resources/Sponza/sponza_flagpole_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bfffb62e770959c725d0f3db6dc7dbdd46a380ec55ef884dab94d44ca017b438 +size 1425673 diff --git a/projects/bloom/resources/Sponza/sponza_floor_a_diff.png b/projects/bloom/resources/Sponza/sponza_floor_a_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..788ed764f79ba724f04a2d603076a5b85013e188 --- /dev/null +++ b/projects/bloom/resources/Sponza/sponza_floor_a_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a16f9230fa91f9f31dfca6216ce205f1ef132d44f3b012fbf6efc0fba69770ab +size 1996838 diff --git a/projects/bloom/resources/Sponza/sponza_roof_diff.png b/projects/bloom/resources/Sponza/sponza_roof_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..c5b84261fdd1cc776a94b3ce398c7806b895f9a3 --- /dev/null +++ b/projects/bloom/resources/Sponza/sponza_roof_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7fc412138c20da19f8173e53545e771f4652558dff624d4dc67143e40efe562b +size 2320533 diff --git a/projects/bloom/resources/Sponza/sponza_thorn_diff.png b/projects/bloom/resources/Sponza/sponza_thorn_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..7a9142674a7d4a6f94a48c5152cf0300743b597a --- /dev/null +++ b/projects/bloom/resources/Sponza/sponza_thorn_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a73a17c883cd0d0d67cfda2dc4118400a916366c05b9a5ac465f0c8b30fd9c8e +size 635001 diff --git a/projects/bloom/resources/Sponza/vase_dif.png b/projects/bloom/resources/Sponza/vase_dif.png new file mode 100644 index 0000000000000000000000000000000000000000..61236a81cb324af8797b05099cd264cefe189e56 --- /dev/null +++ b/projects/bloom/resources/Sponza/vase_dif.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:53d06f52bf9e59df4cf00237707cca76c4f692bda61a62b06a30d321311d6dd9 +size 1842101 diff --git a/projects/bloom/resources/Sponza/vase_hanging.png b/projects/bloom/resources/Sponza/vase_hanging.png new file mode 100644 index 0000000000000000000000000000000000000000..36a3cee71d8213225090c74f8c0dce33b9d44378 --- /dev/null +++ b/projects/bloom/resources/Sponza/vase_hanging.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a9d10b4f27a3c9a78d5bac882fdd4b6a6987c262f48fa490670fe5e235951e31 +size 1432804 diff --git a/projects/bloom/resources/Sponza/vase_plant.png b/projects/bloom/resources/Sponza/vase_plant.png new file mode 100644 index 0000000000000000000000000000000000000000..7ad95e702e229f1ebd803e5203a266d15f2c07b9 --- /dev/null +++ b/projects/bloom/resources/Sponza/vase_plant.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d2087371ff02212fb7014b6daefa191cf5676d2227193fff261a5d02f554cb8e +size 998089 diff --git a/projects/bloom/resources/Sponza/vase_round.png b/projects/bloom/resources/Sponza/vase_round.png new file mode 100644 index 0000000000000000000000000000000000000000..c17953abc000c44b8991e23c136c2b67348f3d1b --- /dev/null +++ b/projects/bloom/resources/Sponza/vase_round.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:aa23d48d492d5d4ada2ddb27d1ef22952b214e6eb3b301c65f9d88442723d20a +size 1871399 diff --git a/projects/bloom/resources/shaders/comp.spv b/projects/bloom/resources/shaders/comp.spv new file mode 100644 index 0000000000000000000000000000000000000000..85c7e74cfc0a89917bf6dd1a7ec449368274c1d3 Binary files /dev/null and b/projects/bloom/resources/shaders/comp.spv differ diff --git a/projects/bloom/resources/shaders/composite.comp b/projects/bloom/resources/shaders/composite.comp new file mode 100644 index 0000000000000000000000000000000000000000..190bed0657d70e0217bf654820d0b2b2c58f12c2 --- /dev/null +++ b/projects/bloom/resources/shaders/composite.comp @@ -0,0 +1,38 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +layout(set=0, binding=0) uniform texture2D blurImage; +layout(set=0, binding=1) uniform texture2D lensImage; +layout(set=0, binding=2) uniform sampler linearSampler; +layout(set=0, binding=3, r11f_g11f_b10f) uniform image2D colorBuffer; + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + + +void main() +{ + if(any(greaterThanEqual(gl_GlobalInvocationID.xy, imageSize(colorBuffer)))){ + return; + } + + ivec2 pixel_coord = ivec2(gl_GlobalInvocationID.xy); + vec2 pixel_size = vec2(1.0f) / textureSize(sampler2D(blurImage, linearSampler), 0); + vec2 UV = pixel_coord.xy * pixel_size; + + vec4 composite_color = vec4(0.0f); + + vec3 blur_color = texture(sampler2D(blurImage, linearSampler), UV).rgb; + vec3 lens_color = texture(sampler2D(lensImage, linearSampler), UV).rgb; + vec3 main_color = imageLoad(colorBuffer, pixel_coord).rgb; + + // composite blur and lens features + float bloom_weight = 0.25f; + float lens_weight = 0.25f; + float main_weight = 1 - (bloom_weight + lens_weight); + + composite_color.rgb = blur_color * bloom_weight + + lens_color * lens_weight + + main_color * main_weight; + + imageStore(colorBuffer, pixel_coord, composite_color); +} \ No newline at end of file diff --git a/projects/bloom/resources/shaders/downsample.comp b/projects/bloom/resources/shaders/downsample.comp new file mode 100644 index 0000000000000000000000000000000000000000..2ab00c7c92798769153634f3479c5b7f3fb61d94 --- /dev/null +++ b/projects/bloom/resources/shaders/downsample.comp @@ -0,0 +1,76 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +layout(set=0, binding=0) uniform texture2D inBlurImage; +layout(set=0, binding=1) uniform sampler inImageSampler; +layout(set=0, binding=2, r11f_g11f_b10f) uniform writeonly image2D outBlurImage; + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + + +void main() +{ + if(any(greaterThanEqual(gl_GlobalInvocationID.xy, imageSize(outBlurImage)))){ + return; + } + + ivec2 pixel_coord = ivec2(gl_GlobalInvocationID.xy); + vec2 pixel_size = vec2(1.0f) / imageSize(outBlurImage); + vec2 UV = pixel_coord.xy * pixel_size; + vec2 UV_offset = UV + 0.5f * pixel_size; + + vec2 color_fetches[13] = { + // center neighbourhood (RED) + vec2(-1, 1), // LT + vec2(-1, -1), // LB + vec2( 1, -1), // RB + vec2( 1, 1), // RT + + vec2(-2, 2), // LT + vec2( 0, 2), // CT + vec2( 2, 2), // RT + + vec2(0 ,-2), // LC + vec2(0 , 0), // CC + vec2(2, 0), // CR + + vec2(-2, -2), // LB + vec2(0 , -2), // CB + vec2(2 , -2) // RB + }; + + float color_weights[13] = { + // 0.5f + 1.f/8.f, + 1.f/8.f, + 1.f/8.f, + 1.f/8.f, + + // 0.125f + 1.f/32.f, + 1.f/16.f, + 1.f/32.f, + + // 0.25f + 1.f/16.f, + 1.f/8.f, + 1.f/16.f, + + // 0.125f + 1.f/32.f, + 1.f/16.f, + 1.f/32.f + }; + + vec3 sampled_color = vec3(0.0f); + + for(uint i = 0; i < 13; i++) + { + vec2 color_fetch = UV_offset + color_fetches[i] * pixel_size; + vec3 color = texture(sampler2D(inBlurImage, inImageSampler), color_fetch).rgb; + color *= color_weights[i]; + sampled_color += color; + } + + imageStore(outBlurImage, pixel_coord, vec4(sampled_color, 1.f)); +} \ No newline at end of file diff --git a/projects/bloom/resources/shaders/gammaCorrection.comp b/projects/bloom/resources/shaders/gammaCorrection.comp new file mode 100644 index 0000000000000000000000000000000000000000..f89ad167c846cca8e80f69d33eda83bd6ed00d46 --- /dev/null +++ b/projects/bloom/resources/shaders/gammaCorrection.comp @@ -0,0 +1,20 @@ +#version 440 + +layout(set=0, binding=0, r11f_g11f_b10f) uniform image2D inImage; +layout(set=0, binding=1, rgba8) uniform image2D outImage; + + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +void main(){ + + if(any(greaterThanEqual(gl_GlobalInvocationID.xy, imageSize(inImage)))){ + return; + } + ivec2 uv = ivec2(gl_GlobalInvocationID.xy); + vec3 linearColor = imageLoad(inImage, uv).rgb; + // cheap Reinhard tone mapping + linearColor = linearColor/(linearColor + 1.0f); + vec3 gammaCorrected = pow(linearColor, vec3(1.f / 2.2f)); + imageStore(outImage, uv, vec4(gammaCorrected, 0.f)); +} \ No newline at end of file diff --git a/projects/bloom/resources/shaders/lensFlares.comp b/projects/bloom/resources/shaders/lensFlares.comp new file mode 100644 index 0000000000000000000000000000000000000000..ce27d8850b709f61332d467914ddc944dc63109f --- /dev/null +++ b/projects/bloom/resources/shaders/lensFlares.comp @@ -0,0 +1,109 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +layout(set=0, binding=0) uniform texture2D blurBuffer; +layout(set=0, binding=1) uniform sampler linearSampler; +layout(set=0, binding=2, r11f_g11f_b10f) uniform image2D lensBuffer; + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +vec3 sampleColorChromaticAberration(vec2 _uv) +{ + vec2 toCenter = (vec2(0.5) - _uv); + + vec3 colorScales = vec3(-1, 0, 1); + float aberrationScale = 0.1; + vec3 scaleFactors = colorScales * aberrationScale; + + float r = texture(sampler2D(blurBuffer, linearSampler), _uv + toCenter * scaleFactors.r).r; + float g = texture(sampler2D(blurBuffer, linearSampler), _uv + toCenter * scaleFactors.g).g; + float b = texture(sampler2D(blurBuffer, linearSampler), _uv + toCenter * scaleFactors.b).b; + return vec3(r, g, b); +} + +// _uv assumed to be flipped UV coordinates! +vec3 ghost_vectors(vec2 _uv) +{ + vec2 ghost_vec = (vec2(0.5f) - _uv); + + const uint c_ghost_count = 64; + const float c_ghost_spacing = length(ghost_vec) / c_ghost_count; + + ghost_vec *= c_ghost_spacing; + + vec3 ret_color = vec3(0.0f); + + for (uint i = 0; i < c_ghost_count; ++i) + { + // sample scene color + vec2 s_uv = fract(_uv + ghost_vec * vec2(i)); + vec3 s = sampleColorChromaticAberration(s_uv); + + // tint/weight + float d = distance(s_uv, vec2(0.5)); + float weight = 1.0f - smoothstep(0.0f, 0.75f, d); + s *= weight; + + ret_color += s; + } + + ret_color /= c_ghost_count; + return ret_color; +} + +vec3 halo(vec2 _uv) +{ + const float c_aspect_ratio = float(imageSize(lensBuffer).x) / float(imageSize(lensBuffer).y); + const float c_radius = 0.6f; + const float c_halo_thickness = 0.1f; + + vec2 halo_vec = vec2(0.5) - _uv; + //halo_vec.x /= c_aspect_ratio; + halo_vec = normalize(halo_vec); + //halo_vec.x *= c_aspect_ratio; + + + //vec2 w_uv = (_uv - vec2(0.5, 0.0)) * vec2(c_aspect_ratio, 1.0) + vec2(0.5, 0.0); + vec2 w_uv = _uv; + float d = distance(w_uv, vec2(0.5)); // distance to center + + float distance_to_halo = abs(d - c_radius); + + float halo_weight = 0.0f; + if(abs(d - c_radius) <= c_halo_thickness) + { + float distance_to_border = c_halo_thickness - distance_to_halo; + halo_weight = distance_to_border / c_halo_thickness; + + //halo_weight = clamp((halo_weight / 0.4f), 0.0f, 1.0f); + halo_weight = pow(halo_weight, 2.0f); + + + //halo_weight = 1.0f; + } + + return sampleColorChromaticAberration(_uv + halo_vec) * halo_weight; +} + + + +void main() +{ + if(any(greaterThanEqual(gl_GlobalInvocationID.xy, imageSize(lensBuffer)))){ + return; + } + + ivec2 pixel_coord = ivec2(gl_GlobalInvocationID.xy); + vec2 pixel_size = vec2(1.0f) / imageSize(lensBuffer); + vec2 UV = pixel_coord.xy * pixel_size; + + vec2 flipped_UV = vec2(1.0f) - UV; + + vec3 color = vec3(0.0f); + + color += ghost_vectors(flipped_UV); + color += halo(UV); + color *= 0.5f; + + imageStore(lensBuffer, pixel_coord, vec4(color, 0.0f)); +} \ No newline at end of file diff --git a/projects/bloom/resources/shaders/perMeshResources.inc b/projects/bloom/resources/shaders/perMeshResources.inc new file mode 100644 index 0000000000000000000000000000000000000000..95e4fb7c27009965659d14a9c72acfec950c37e3 --- /dev/null +++ b/projects/bloom/resources/shaders/perMeshResources.inc @@ -0,0 +1,2 @@ +layout(set=1, binding=0) uniform texture2D albedoTexture; +layout(set=1, binding=1) uniform sampler textureSampler; \ No newline at end of file diff --git a/projects/bloom/resources/shaders/shader.frag b/projects/bloom/resources/shaders/shader.frag new file mode 100644 index 0000000000000000000000000000000000000000..3e95b4508f112c1ed9aa4a7050a98fa789dccd09 --- /dev/null +++ b/projects/bloom/resources/shaders/shader.frag @@ -0,0 +1,45 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable +#extension GL_GOOGLE_include_directive : enable + +#include "perMeshResources.inc" + +layout(location = 0) in vec3 passNormal; +layout(location = 1) in vec2 passUV; +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; +}; +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(10); + vec3 sun = sunColor * clamp(dot(N, L), 0, 1); + sun *= shadowTest(passPos); + 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/bloom/resources/shaders/shader.vert b/projects/bloom/resources/shaders/shader.vert new file mode 100644 index 0000000000000000000000000000000000000000..926f86af2860cb57c44d2d5ee78712b6ae155e5c --- /dev/null +++ b/projects/bloom/resources/shaders/shader.vert @@ -0,0 +1,22 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +layout(location = 0) in vec3 inPosition; +layout(location = 1) in vec3 inNormal; +layout(location = 2) in vec2 inUV; + +layout(location = 0) out vec3 passNormal; +layout(location = 1) out vec2 passUV; +layout(location = 2) out vec3 passPos; + +layout( push_constant ) uniform constants{ + mat4 mvp; + mat4 model; +}; + +void main() { + gl_Position = mvp * vec4(inPosition, 1.0); + passNormal = mat3(model) * inNormal; // assuming no weird stuff like shearing or non-uniform scaling + passUV = inUV; + passPos = (model * vec4(inPosition, 1)).xyz; +} \ No newline at end of file diff --git a/projects/bloom/resources/shaders/shadow.frag b/projects/bloom/resources/shaders/shadow.frag new file mode 100644 index 0000000000000000000000000000000000000000..848f853f556660b4900b5db7fb6fc98d57c1cd5b --- /dev/null +++ b/projects/bloom/resources/shaders/shadow.frag @@ -0,0 +1,6 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +void main() { + +} \ No newline at end of file diff --git a/projects/bloom/resources/shaders/shadow.vert b/projects/bloom/resources/shaders/shadow.vert new file mode 100644 index 0000000000000000000000000000000000000000..e0f41d42d575fa64fedbfa04adf89ac0f4aeebe8 --- /dev/null +++ b/projects/bloom/resources/shaders/shadow.vert @@ -0,0 +1,12 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +layout(location = 0) in vec3 inPosition; + +layout( push_constant ) uniform constants{ + mat4 mvp; +}; + +void main() { + gl_Position = mvp * vec4(inPosition, 1.0); +} \ No newline at end of file diff --git a/projects/bloom/resources/shaders/upsample.comp b/projects/bloom/resources/shaders/upsample.comp new file mode 100644 index 0000000000000000000000000000000000000000..0ddeedb5b5af9e476dc19012fed6430544006c0e --- /dev/null +++ b/projects/bloom/resources/shaders/upsample.comp @@ -0,0 +1,45 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +layout(set=0, binding=0) uniform texture2D inUpsampleImage; +layout(set=0, binding=1) uniform sampler inImageSampler; +layout(set=0, binding=2, r11f_g11f_b10f) uniform image2D outUpsampleImage; + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +void main() +{ + if(any(greaterThanEqual(gl_GlobalInvocationID.xy, imageSize(outUpsampleImage)))){ + return; + } + + + ivec2 pixel_coord = ivec2(gl_GlobalInvocationID.xy); + vec2 pixel_size = vec2(1.0f) / imageSize(outUpsampleImage); + vec2 UV = pixel_coord.xy * pixel_size; + + const float gauss_kernel[3] = {1.f, 2.f, 1.f}; + const float gauss_weight = 16.f; + + vec3 sampled_color = vec3(0.f); + + for(int i = -1; i <= 1; i++) + { + for(int j = -1; j <= 1; j++) + { + vec2 sample_location = UV + vec2(j, i) * pixel_size; + vec3 color = texture(sampler2D(inUpsampleImage, inImageSampler), sample_location).rgb; + color *= gauss_kernel[j+1]; + color *= gauss_kernel[i+1]; + color /= gauss_weight; + + sampled_color += color; + } + } + + //vec3 prev_color = imageLoad(outUpsampleImage, pixel_coord).rgb; + //float bloomRimStrength = 0.75f; // adjust this to change strength of bloom + //sampled_color = mix(prev_color, sampled_color, bloomRimStrength); + + imageStore(outUpsampleImage, pixel_coord, vec4(sampled_color, 1.f)); +} \ No newline at end of file diff --git a/projects/bloom/src/BloomAndFlares.cpp b/projects/bloom/src/BloomAndFlares.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6f26db9de0f2c8334b6dd7e5dd6cf4b6f48baedc --- /dev/null +++ b/projects/bloom/src/BloomAndFlares.cpp @@ -0,0 +1,274 @@ +#include "BloomAndFlares.hpp" +#include <vkcv/shader/GLSLCompiler.hpp> + +BloomAndFlares::BloomAndFlares( + vkcv::Core *p_Core, + vk::Format colorBufferFormat, + uint32_t width, + uint32_t height) : + + p_Core(p_Core), + m_ColorBufferFormat(colorBufferFormat), + m_Width(width), + m_Height(height), + m_LinearSampler(p_Core->createSampler(vkcv::SamplerFilterType::LINEAR, + vkcv::SamplerFilterType::LINEAR, + vkcv::SamplerMipmapMode::LINEAR, + vkcv::SamplerAddressMode::CLAMP_TO_EDGE)), + m_Blur(p_Core->createImage(colorBufferFormat, width, height, 1, true, true, false)), + m_LensFeatures(p_Core->createImage(colorBufferFormat, width, height, 1, false, true, false)) +{ + vkcv::shader::GLSLCompiler compiler; + + // DOWNSAMPLE + vkcv::ShaderProgram dsProg; + compiler.compile(vkcv::ShaderStage::COMPUTE, + "resources/shaders/downsample.comp", + [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) + { + dsProg.addShader(shaderStage, path); + }); + for(uint32_t mipLevel = 0; mipLevel < m_Blur.getMipCount(); mipLevel++) + { + m_DownsampleDescSets.push_back( + p_Core->createDescriptorSet(dsProg.getReflectedDescriptors()[0])); + } + m_DownsamplePipe = p_Core->createComputePipeline( + dsProg, { p_Core->getDescriptorSet(m_DownsampleDescSets[0]).layout }); + + // UPSAMPLE + vkcv::ShaderProgram usProg; + compiler.compile(vkcv::ShaderStage::COMPUTE, + "resources/shaders/upsample.comp", + [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) + { + usProg.addShader(shaderStage, path); + }); + for(uint32_t mipLevel = 0; mipLevel < m_Blur.getMipCount(); mipLevel++) + { + m_UpsampleDescSets.push_back( + p_Core->createDescriptorSet(usProg.getReflectedDescriptors()[0])); + } + m_UpsamplePipe = p_Core->createComputePipeline( + usProg, { p_Core->getDescriptorSet(m_UpsampleDescSets[0]).layout }); + + // LENS FEATURES + vkcv::ShaderProgram lensProg; + compiler.compile(vkcv::ShaderStage::COMPUTE, + "resources/shaders/lensFlares.comp", + [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) + { + lensProg.addShader(shaderStage, path); + }); + m_LensFlareDescSet = p_Core->createDescriptorSet(lensProg.getReflectedDescriptors()[0]); + m_LensFlarePipe = p_Core->createComputePipeline( + lensProg, { p_Core->getDescriptorSet(m_LensFlareDescSet).layout }); + + // COMPOSITE + vkcv::ShaderProgram compProg; + compiler.compile(vkcv::ShaderStage::COMPUTE, + "resources/shaders/composite.comp", + [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) + { + compProg.addShader(shaderStage, path); + }); + m_CompositeDescSet = p_Core->createDescriptorSet(compProg.getReflectedDescriptors()[0]); + m_CompositePipe = p_Core->createComputePipeline( + compProg, { p_Core->getDescriptorSet(m_CompositeDescSet).layout }); +} + +void BloomAndFlares::execDownsamplePipe(const vkcv::CommandStreamHandle &cmdStream, + const vkcv::ImageHandle &colorAttachment) +{ + auto dispatchCountX = static_cast<float>(m_Width) / 8.0f; + auto dispatchCountY = static_cast<float>(m_Height) / 8.0f; + // blur dispatch + uint32_t initialDispatchCount[3] = { + static_cast<uint32_t>(glm::ceil(dispatchCountX)), + static_cast<uint32_t>(glm::ceil(dispatchCountY)), + 1 + }; + + // downsample dispatch of original color attachment + p_Core->prepareImageForSampling(cmdStream, colorAttachment); + p_Core->prepareImageForStorage(cmdStream, m_Blur.getHandle()); + + vkcv::DescriptorWrites initialDownsampleWrites; + initialDownsampleWrites.sampledImageWrites = {vkcv::SampledImageDescriptorWrite(0, colorAttachment)}; + initialDownsampleWrites.samplerWrites = {vkcv::SamplerDescriptorWrite(1, m_LinearSampler)}; + initialDownsampleWrites.storageImageWrites = {vkcv::StorageImageDescriptorWrite(2, m_Blur.getHandle(), 0) }; + p_Core->writeDescriptorSet(m_DownsampleDescSets[0], initialDownsampleWrites); + + p_Core->recordComputeDispatchToCmdStream( + cmdStream, + m_DownsamplePipe, + initialDispatchCount, + {vkcv::DescriptorSetUsage(0, p_Core->getDescriptorSet(m_DownsampleDescSets[0]).vulkanHandle)}, + vkcv::PushConstantData(nullptr, 0)); + + // downsample dispatches of blur buffer's mip maps + float mipDispatchCountX = dispatchCountX; + float mipDispatchCountY = dispatchCountY; + for(uint32_t mipLevel = 1; mipLevel < m_DownsampleDescSets.size(); mipLevel++) + { + // mip descriptor writes + vkcv::DescriptorWrites mipDescriptorWrites; + mipDescriptorWrites.sampledImageWrites = {vkcv::SampledImageDescriptorWrite(0, m_Blur.getHandle(), mipLevel - 1, true)}; + mipDescriptorWrites.samplerWrites = {vkcv::SamplerDescriptorWrite(1, m_LinearSampler)}; + mipDescriptorWrites.storageImageWrites = {vkcv::StorageImageDescriptorWrite(2, m_Blur.getHandle(), mipLevel) }; + p_Core->writeDescriptorSet(m_DownsampleDescSets[mipLevel], mipDescriptorWrites); + + // mip dispatch calculation + mipDispatchCountX /= 2.0f; + mipDispatchCountY /= 2.0f; + + uint32_t mipDispatchCount[3] = { + static_cast<uint32_t>(glm::ceil(mipDispatchCountX)), + static_cast<uint32_t>(glm::ceil(mipDispatchCountY)), + 1 + }; + + if(mipDispatchCount[0] == 0) + mipDispatchCount[0] = 1; + if(mipDispatchCount[1] == 0) + mipDispatchCount[1] = 1; + + // mip blur dispatch + p_Core->recordComputeDispatchToCmdStream( + cmdStream, + m_DownsamplePipe, + mipDispatchCount, + {vkcv::DescriptorSetUsage(0, p_Core->getDescriptorSet(m_DownsampleDescSets[mipLevel]).vulkanHandle)}, + vkcv::PushConstantData(nullptr, 0)); + + // image barrier between mips + p_Core->recordImageMemoryBarrier(cmdStream, m_Blur.getHandle()); + } +} + +void BloomAndFlares::execUpsamplePipe(const vkcv::CommandStreamHandle &cmdStream) +{ + // upsample dispatch + p_Core->prepareImageForStorage(cmdStream, m_Blur.getHandle()); + + const uint32_t upsampleMipLevels = std::min( + static_cast<uint32_t>(m_UpsampleDescSets.size() - 1), + static_cast<uint32_t>(5) + ); + + // upsample dispatch for each mip map + for(uint32_t mipLevel = upsampleMipLevels; mipLevel > 0; mipLevel--) + { + // mip descriptor writes + vkcv::DescriptorWrites mipUpsampleWrites; + mipUpsampleWrites.sampledImageWrites = {vkcv::SampledImageDescriptorWrite(0, m_Blur.getHandle(), mipLevel, true)}; + mipUpsampleWrites.samplerWrites = {vkcv::SamplerDescriptorWrite(1, m_LinearSampler)}; + mipUpsampleWrites.storageImageWrites = {vkcv::StorageImageDescriptorWrite(2, m_Blur.getHandle(), mipLevel - 1) }; + p_Core->writeDescriptorSet(m_UpsampleDescSets[mipLevel], mipUpsampleWrites); + + auto mipDivisor = glm::pow(2.0f, static_cast<float>(mipLevel) - 1.0f); + + auto upsampleDispatchX = static_cast<float>(m_Width) / mipDivisor; + auto upsampleDispatchY = static_cast<float>(m_Height) / mipDivisor; + upsampleDispatchX /= 8.0f; + upsampleDispatchY /= 8.0f; + + const uint32_t upsampleDispatchCount[3] = { + static_cast<uint32_t>(glm::ceil(upsampleDispatchX)), + static_cast<uint32_t>(glm::ceil(upsampleDispatchY)), + 1 + }; + + p_Core->recordComputeDispatchToCmdStream( + cmdStream, + m_UpsamplePipe, + upsampleDispatchCount, + {vkcv::DescriptorSetUsage(0, p_Core->getDescriptorSet(m_UpsampleDescSets[mipLevel]).vulkanHandle)}, + vkcv::PushConstantData(nullptr, 0) + ); + // image barrier between mips + p_Core->recordImageMemoryBarrier(cmdStream, m_Blur.getHandle()); + } +} + +void BloomAndFlares::execLensFeaturePipe(const vkcv::CommandStreamHandle &cmdStream) +{ + // lens feature generation descriptor writes + p_Core->prepareImageForSampling(cmdStream, m_Blur.getHandle()); + p_Core->prepareImageForStorage(cmdStream, m_LensFeatures.getHandle()); + + vkcv::DescriptorWrites lensFeatureWrites; + lensFeatureWrites.sampledImageWrites = {vkcv::SampledImageDescriptorWrite(0, m_Blur.getHandle(), 0)}; + lensFeatureWrites.samplerWrites = {vkcv::SamplerDescriptorWrite(1, m_LinearSampler)}; + lensFeatureWrites.storageImageWrites = {vkcv::StorageImageDescriptorWrite(2, m_LensFeatures.getHandle(), 0)}; + p_Core->writeDescriptorSet(m_LensFlareDescSet, lensFeatureWrites); + + auto dispatchCountX = static_cast<float>(m_Width) / 8.0f; + auto dispatchCountY = static_cast<float>(m_Height) / 8.0f; + // lens feature generation dispatch + uint32_t lensFeatureDispatchCount[3] = { + static_cast<uint32_t>(glm::ceil(dispatchCountX)), + static_cast<uint32_t>(glm::ceil(dispatchCountY)), + 1 + }; + p_Core->recordComputeDispatchToCmdStream( + cmdStream, + m_LensFlarePipe, + lensFeatureDispatchCount, + {vkcv::DescriptorSetUsage(0, p_Core->getDescriptorSet(m_LensFlareDescSet).vulkanHandle)}, + vkcv::PushConstantData(nullptr, 0)); +} + +void BloomAndFlares::execCompositePipe(const vkcv::CommandStreamHandle &cmdStream, + const vkcv::ImageHandle &colorAttachment) +{ + p_Core->prepareImageForSampling(cmdStream, m_Blur.getHandle()); + p_Core->prepareImageForSampling(cmdStream, m_LensFeatures.getHandle()); + p_Core->prepareImageForStorage(cmdStream, colorAttachment); + + // bloom composite descriptor write + vkcv::DescriptorWrites compositeWrites; + compositeWrites.sampledImageWrites = {vkcv::SampledImageDescriptorWrite(0, m_Blur.getHandle()), + vkcv::SampledImageDescriptorWrite(1, m_LensFeatures.getHandle())}; + compositeWrites.samplerWrites = {vkcv::SamplerDescriptorWrite(2, m_LinearSampler)}; + compositeWrites.storageImageWrites = {vkcv::StorageImageDescriptorWrite(3, colorAttachment)}; + p_Core->writeDescriptorSet(m_CompositeDescSet, compositeWrites); + + float dispatchCountX = static_cast<float>(m_Width) / 8.0f; + float dispatchCountY = static_cast<float>(m_Height) / 8.0f; + + uint32_t compositeDispatchCount[3] = { + static_cast<uint32_t>(glm::ceil(dispatchCountX)), + static_cast<uint32_t>(glm::ceil(dispatchCountY)), + 1 + }; + + // bloom composite dispatch + p_Core->recordComputeDispatchToCmdStream( + cmdStream, + m_CompositePipe, + compositeDispatchCount, + {vkcv::DescriptorSetUsage(0, p_Core->getDescriptorSet(m_CompositeDescSet).vulkanHandle)}, + vkcv::PushConstantData(nullptr, 0)); +} + +void BloomAndFlares::execWholePipeline(const vkcv::CommandStreamHandle &cmdStream, + const vkcv::ImageHandle &colorAttachment) +{ + execDownsamplePipe(cmdStream, colorAttachment); + execUpsamplePipe(cmdStream); + execLensFeaturePipe(cmdStream); + execCompositePipe(cmdStream, colorAttachment); +} + +void BloomAndFlares::updateImageDimensions(uint32_t width, uint32_t height) +{ + m_Width = width; + m_Height = height; + + p_Core->getContext().getDevice().waitIdle(); + m_Blur = p_Core->createImage(m_ColorBufferFormat, m_Width, m_Height, 1, true, true, false); + m_LensFeatures = p_Core->createImage(m_ColorBufferFormat, m_Width, m_Height, 1, false, true, false); +} + + diff --git a/projects/bloom/src/BloomAndFlares.hpp b/projects/bloom/src/BloomAndFlares.hpp new file mode 100644 index 0000000000000000000000000000000000000000..756b1ca154ea5232df04eb09a88bb743c5bd28aa --- /dev/null +++ b/projects/bloom/src/BloomAndFlares.hpp @@ -0,0 +1,47 @@ +#pragma once +#include <vkcv/Core.hpp> +#include <glm/glm.hpp> + +class BloomAndFlares{ +public: + BloomAndFlares(vkcv::Core *p_Core, + vk::Format colorBufferFormat, + uint32_t width, + uint32_t height); + + void execWholePipeline(const vkcv::CommandStreamHandle &cmdStream, const vkcv::ImageHandle &colorAttachment); + + void updateImageDimensions(uint32_t width, uint32_t height); + +private: + vkcv::Core *p_Core; + + vk::Format m_ColorBufferFormat; + uint32_t m_Width; + uint32_t m_Height; + + vkcv::SamplerHandle m_LinearSampler; + vkcv::Image m_Blur; + vkcv::Image m_LensFeatures; + + + vkcv::PipelineHandle m_DownsamplePipe; + std::vector<vkcv::DescriptorSetHandle> m_DownsampleDescSets; // per mip desc set + + vkcv::PipelineHandle m_UpsamplePipe; + std::vector<vkcv::DescriptorSetHandle> m_UpsampleDescSets; // per mip desc set + + vkcv::PipelineHandle m_LensFlarePipe; + vkcv::DescriptorSetHandle m_LensFlareDescSet; + + vkcv::PipelineHandle m_CompositePipe; + vkcv::DescriptorSetHandle m_CompositeDescSet; + + void execDownsamplePipe(const vkcv::CommandStreamHandle &cmdStream, const vkcv::ImageHandle &colorAttachment); + void execUpsamplePipe(const vkcv::CommandStreamHandle &cmdStream); + void execLensFeaturePipe(const vkcv::CommandStreamHandle &cmdStream); + void execCompositePipe(const vkcv::CommandStreamHandle &cmdStream, const vkcv::ImageHandle &colorAttachment); +}; + + + diff --git a/projects/bloom/src/main.cpp b/projects/bloom/src/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..14a3533bd812dda4bb1fd30ccab89318440b1a1f --- /dev/null +++ b/projects/bloom/src/main.cpp @@ -0,0 +1,421 @@ +#include <iostream> +#include <vkcv/Core.hpp> +#include <GLFW/glfw3.h> +#include <vkcv/camera/CameraManager.hpp> +#include <chrono> +#include <vkcv/asset/asset_loader.hpp> +#include <vkcv/shader/GLSLCompiler.hpp> +#include <vkcv/Logger.hpp> +#include "BloomAndFlares.hpp" +#include <glm/glm.hpp> + +int main(int argc, const char** argv) { + const char* applicationName = "Bloom"; + + uint32_t windowWidth = 1920; + uint32_t windowHeight = 1080; + + vkcv::Window window = vkcv::Window::create( + applicationName, + windowWidth, + windowHeight, + true + ); + + vkcv::camera::CameraManager cameraManager(window); + uint32_t camIndex = cameraManager.addCamera(vkcv::camera::ControllerType::PILOT); + uint32_t camIndex2 = cameraManager.addCamera(vkcv::camera::ControllerType::TRACKBALL); + + cameraManager.getCamera(camIndex).setPosition(glm::vec3(0.f, 0.f, 3.f)); + cameraManager.getCamera(camIndex).setNearFar(0.1f, 30.0f); + cameraManager.getCamera(camIndex).setYaw(180.0f); + + cameraManager.getCamera(camIndex2).setNearFar(0.1f, 30.0f); + + window.initEvents(); + + vkcv::Core core = vkcv::Core::create( + window, + applicationName, + VK_MAKE_VERSION(0, 0, 1), + { vk::QueueFlagBits::eTransfer,vk::QueueFlagBits::eGraphics, vk::QueueFlagBits::eCompute }, + {}, + { "VK_KHR_swapchain" } + ); + + const char* path = argc > 1 ? argv[1] : "resources/Sponza/Sponza.gltf"; + vkcv::asset::Scene scene; + int result = vkcv::asset::loadScene(path, scene); + + if (result == 1) { + std::cout << "Scene loading successful!" << std::endl; + } + else { + std::cout << "Scene loading failed: " << result << std::endl; + return 1; + } + + // build index and vertex buffers + assert(!scene.vertexGroups.empty()); + std::vector<std::vector<uint8_t>> vBuffers; + std::vector<std::vector<uint8_t>> iBuffers; + + std::vector<vkcv::VertexBufferBinding> vBufferBindings; + std::vector<std::vector<vkcv::VertexBufferBinding>> vertexBufferBindings; + std::vector<vkcv::asset::VertexAttribute> vAttributes; + + for (int i = 0; i < scene.vertexGroups.size(); i++) { + + vBuffers.push_back(scene.vertexGroups[i].vertexBuffer.data); + iBuffers.push_back(scene.vertexGroups[i].indexBuffer.data); + + auto& attributes = scene.vertexGroups[i].vertexBuffer.attributes; + + std::sort(attributes.begin(), attributes.end(), [](const vkcv::asset::VertexAttribute& x, const vkcv::asset::VertexAttribute& y) { + return static_cast<uint32_t>(x.type) < static_cast<uint32_t>(y.type); + }); + } + + std::vector<vkcv::Buffer<uint8_t>> vertexBuffers; + for (const vkcv::asset::VertexGroup& group : scene.vertexGroups) { + vertexBuffers.push_back(core.createBuffer<uint8_t>( + vkcv::BufferType::VERTEX, + group.vertexBuffer.data.size())); + vertexBuffers.back().fill(group.vertexBuffer.data); + } + + std::vector<vkcv::Buffer<uint8_t>> indexBuffers; + for (const auto& dataBuffer : iBuffers) { + indexBuffers.push_back(core.createBuffer<uint8_t>( + vkcv::BufferType::INDEX, + dataBuffer.size())); + indexBuffers.back().fill(dataBuffer); + } + + int vertexBufferIndex = 0; + for (const auto& vertexGroup : scene.vertexGroups) { + for (const auto& attribute : vertexGroup.vertexBuffer.attributes) { + vAttributes.push_back(attribute); + vBufferBindings.push_back(vkcv::VertexBufferBinding(attribute.offset, vertexBuffers[vertexBufferIndex].getVulkanHandle())); + } + vertexBufferBindings.push_back(vBufferBindings); + vBufferBindings.clear(); + vertexBufferIndex++; + } + + const vk::Format colorBufferFormat = vk::Format::eB10G11R11UfloatPack32; + const vkcv::AttachmentDescription color_attachment( + vkcv::AttachmentOperation::STORE, + vkcv::AttachmentOperation::CLEAR, + colorBufferFormat + ); + + const vk::Format depthBufferFormat = vk::Format::eD32Sfloat; + const vkcv::AttachmentDescription depth_attachment( + vkcv::AttachmentOperation::STORE, + vkcv::AttachmentOperation::CLEAR, + depthBufferFormat + ); + + vkcv::PassConfig forwardPassDefinition({ color_attachment, depth_attachment }); + vkcv::PassHandle forwardPass = core.createPass(forwardPassDefinition); + + vkcv::shader::GLSLCompiler compiler; + + vkcv::ShaderProgram forwardProgram; + compiler.compile(vkcv::ShaderStage::VERTEX, std::filesystem::path("resources/shaders/shader.vert"), + [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + forwardProgram.addShader(shaderStage, path); + }); + compiler.compile(vkcv::ShaderStage::FRAGMENT, std::filesystem::path("resources/shaders/shader.frag"), + [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + forwardProgram.addShader(shaderStage, path); + }); + + const std::vector<vkcv::VertexAttachment> vertexAttachments = forwardProgram.getVertexAttachments(); + + std::vector<vkcv::VertexBinding> vertexBindings; + for (size_t i = 0; i < vertexAttachments.size(); i++) { + vertexBindings.push_back(vkcv::VertexBinding(i, { vertexAttachments[i] })); + } + const vkcv::VertexLayout vertexLayout (vertexBindings); + + // shadow map + vkcv::SamplerHandle shadowSampler = core.createSampler( + vkcv::SamplerFilterType::NEAREST, + vkcv::SamplerFilterType::NEAREST, + vkcv::SamplerMipmapMode::NEAREST, + vkcv::SamplerAddressMode::CLAMP_TO_EDGE + ); + const vk::Format shadowMapFormat = vk::Format::eD16Unorm; + const uint32_t shadowMapResolution = 1024; + const vkcv::Image shadowMap = core.createImage(shadowMapFormat, shadowMapResolution, shadowMapResolution, 1); + + // light info buffer + struct LightInfo { + glm::vec3 direction; + float padding; + glm::mat4 lightMatrix; + }; + LightInfo lightInfo; + vkcv::Buffer lightBuffer = core.createBuffer<LightInfo>(vkcv::BufferType::UNIFORM, sizeof(glm::vec3)); + + vkcv::DescriptorSetHandle forwardShadingDescriptorSet = + core.createDescriptorSet({ forwardProgram.getReflectedDescriptors()[0] }); + + vkcv::DescriptorWrites forwardDescriptorWrites; + forwardDescriptorWrites.uniformBufferWrites = { vkcv::UniformBufferDescriptorWrite(0, lightBuffer.getHandle()) }; + forwardDescriptorWrites.sampledImageWrites = { vkcv::SampledImageDescriptorWrite(1, shadowMap.getHandle()) }; + forwardDescriptorWrites.samplerWrites = { vkcv::SamplerDescriptorWrite(2, shadowSampler) }; + core.writeDescriptorSet(forwardShadingDescriptorSet, forwardDescriptorWrites); + + vkcv::SamplerHandle colorSampler = core.createSampler( + vkcv::SamplerFilterType::LINEAR, + vkcv::SamplerFilterType::LINEAR, + vkcv::SamplerMipmapMode::LINEAR, + vkcv::SamplerAddressMode::REPEAT + ); + + // prepare per mesh descriptor sets + std::vector<vkcv::DescriptorSetHandle> perMeshDescriptorSets; + 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]; + + int baseColorIndex = material.baseColor; + if (baseColorIndex < 0) { + vkcv_log(vkcv::LogLevel::WARNING, "Material lacks base color"); + baseColorIndex = 0; + } + + vkcv::asset::Texture& sceneTexture = scene.textures[baseColorIndex]; + + sceneImages.push_back(core.createImage(vk::Format::eR8G8B8A8Srgb, sceneTexture.w, sceneTexture.h)); + sceneImages.back().fill(sceneTexture.data.data()); + + vkcv::DescriptorWrites setWrites; + setWrites.sampledImageWrites = { + vkcv::SampledImageDescriptorWrite(0, sceneImages.back().getHandle()) + }; + setWrites.samplerWrites = { + vkcv::SamplerDescriptorWrite(1, colorSampler), + }; + core.writeDescriptorSet(perMeshDescriptorSets.back(), setWrites); + } + + const vkcv::PipelineConfig forwardPipelineConfig { + forwardProgram, + windowWidth, + windowHeight, + forwardPass, + vertexLayout, + { core.getDescriptorSet(forwardShadingDescriptorSet).layout, + core.getDescriptorSet(perMeshDescriptorSets[0]).layout }, + true + }; + + vkcv::PipelineHandle forwardPipeline = core.createGraphicsPipeline(forwardPipelineConfig); + + if (!forwardPipeline) { + std::cout << "Error. Could not create graphics pipeline. Exiting." << std::endl; + return EXIT_FAILURE; + } + + vkcv::ImageHandle depthBuffer = core.createImage(depthBufferFormat, windowWidth, windowHeight).getHandle(); + vkcv::ImageHandle colorBuffer = core.createImage(colorBufferFormat, windowWidth, windowHeight, 1, false, true, true).getHandle(); + + const vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle(); + + vkcv::ShaderProgram shadowShader; + compiler.compile(vkcv::ShaderStage::VERTEX, "resources/shaders/shadow.vert", + [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + shadowShader.addShader(shaderStage, path); + }); + compiler.compile(vkcv::ShaderStage::FRAGMENT, "resources/shaders/shadow.frag", + [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + shadowShader.addShader(shaderStage, path); + }); + + const std::vector<vkcv::AttachmentDescription> shadowAttachments = { + vkcv::AttachmentDescription(vkcv::AttachmentOperation::STORE, vkcv::AttachmentOperation::CLEAR, shadowMapFormat) + }; + const vkcv::PassConfig shadowPassConfig(shadowAttachments); + const vkcv::PassHandle shadowPass = core.createPass(shadowPassConfig); + const vkcv::PipelineConfig shadowPipeConfig{ + shadowShader, + shadowMapResolution, + shadowMapResolution, + shadowPass, + vertexLayout, + {}, + false + }; + const vkcv::PipelineHandle shadowPipe = core.createGraphicsPipeline(shadowPipeConfig); + + std::vector<std::array<glm::mat4, 2>> mainPassMatrices; + std::vector<glm::mat4> mvpLight; + + // gamma correction compute shader + vkcv::ShaderProgram gammaCorrectionProgram; + compiler.compile(vkcv::ShaderStage::COMPUTE, "resources/shaders/gammaCorrection.comp", + [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + gammaCorrectionProgram.addShader(shaderStage, path); + }); + vkcv::DescriptorSetHandle gammaCorrectionDescriptorSet = core.createDescriptorSet(gammaCorrectionProgram.getReflectedDescriptors()[0]); + vkcv::PipelineHandle gammaCorrectionPipeline = core.createComputePipeline(gammaCorrectionProgram, + { core.getDescriptorSet(gammaCorrectionDescriptorSet).layout }); + + BloomAndFlares baf(&core, colorBufferFormat, windowWidth, windowHeight); + + + // model matrices per mesh + std::vector<glm::mat4> modelMatrices; + modelMatrices.resize(scene.vertexGroups.size(), glm::mat4(1.f)); + for (const auto& mesh : scene.meshes) { + const glm::mat4 m = *reinterpret_cast<const glm::mat4*>(&mesh.modelMatrix[0]); + for (const auto& vertexGroupIndex : mesh.vertexGroups) { + modelMatrices[vertexGroupIndex] = m; + } + } + + // prepare drawcalls + std::vector<vkcv::Mesh> meshes; + for (int i = 0; i < scene.vertexGroups.size(); i++) { + vkcv::Mesh mesh( + vertexBufferBindings[i], + indexBuffers[i].getVulkanHandle(), + scene.vertexGroups[i].numIndices); + meshes.push_back(mesh); + } + + std::vector<vkcv::DrawcallInfo> drawcalls; + std::vector<vkcv::DrawcallInfo> shadowDrawcalls; + for (int i = 0; i < meshes.size(); i++) { + drawcalls.push_back(vkcv::DrawcallInfo(meshes[i], { + vkcv::DescriptorSetUsage(0, core.getDescriptorSet(forwardShadingDescriptorSet).vulkanHandle), + vkcv::DescriptorSetUsage(1, core.getDescriptorSet(perMeshDescriptorSets[i]).vulkanHandle) })); + shadowDrawcalls.push_back(vkcv::DrawcallInfo(meshes[i], {})); + } + + auto start = std::chrono::system_clock::now(); + const auto appStartTime = start; + while (window.isWindowOpen()) { + vkcv::Window::pollEvents(); + + uint32_t swapchainWidth, swapchainHeight; + if (!core.beginFrame(swapchainWidth, swapchainHeight)) { + continue; + } + + if ((swapchainWidth != windowWidth) || ((swapchainHeight != windowHeight))) { + depthBuffer = core.createImage(depthBufferFormat, swapchainWidth, swapchainHeight).getHandle(); + colorBuffer = core.createImage(colorBufferFormat, swapchainWidth, swapchainHeight, 1, false, true, true).getHandle(); + + baf.updateImageDimensions(swapchainWidth, swapchainHeight); + + windowWidth = swapchainWidth; + windowHeight = swapchainHeight; + } + + auto end = std::chrono::system_clock::now(); + auto deltatime = std::chrono::duration_cast<std::chrono::microseconds>(end - start); + + 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.0001f * static_cast<float>(duration.count()); + lightInfo.direction = glm::normalize(glm::vec3(std::cos(sunTheta), 1, std::sin(sunTheta))); + + const float shadowProjectionSize = 20.f; + glm::mat4 projectionLight = glm::ortho( + -shadowProjectionSize, + shadowProjectionSize, + -shadowProjectionSize, + shadowProjectionSize, + -shadowProjectionSize, + shadowProjectionSize); + + glm::mat4 vulkanCorrectionMatrix(1.f); + vulkanCorrectionMatrix[2][2] = 0.5; + vulkanCorrectionMatrix[3][2] = 0.5; + projectionLight = vulkanCorrectionMatrix * projectionLight; + + const glm::mat4 viewLight = glm::lookAt(glm::vec3(0), -lightInfo.direction, glm::vec3(0, -1, 0)); + + lightInfo.lightMatrix = projectionLight * viewLight; + lightBuffer.fill({ lightInfo }); + + const glm::mat4 viewProjectionCamera = cameraManager.getActiveCamera().getMVP(); + + mainPassMatrices.clear(); + mvpLight.clear(); + for (const auto& m : modelMatrices) { + mainPassMatrices.push_back({ viewProjectionCamera * m, m }); + mvpLight.push_back(lightInfo.lightMatrix * m); + } + + vkcv::PushConstantData pushConstantData((void*)mainPassMatrices.data(), 2 * sizeof(glm::mat4)); + const std::vector<vkcv::ImageHandle> renderTargets = { colorBuffer, depthBuffer }; + + const vkcv::PushConstantData shadowPushConstantData((void*)mvpLight.data(), sizeof(glm::mat4)); + + auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics); + + // shadow map + core.recordDrawcallsToCmdStream( + cmdStream, + shadowPass, + shadowPipe, + shadowPushConstantData, + shadowDrawcalls, + { shadowMap.getHandle() }); + core.prepareImageForSampling(cmdStream, shadowMap.getHandle()); + + // main pass + core.recordDrawcallsToCmdStream( + cmdStream, + forwardPass, + forwardPipeline, + pushConstantData, + drawcalls, + renderTargets); + + const uint32_t gammaCorrectionLocalGroupSize = 8; + const uint32_t gammaCorrectionDispatchCount[3] = { + static_cast<uint32_t>(glm::ceil(static_cast<float>(windowWidth) / static_cast<float>(gammaCorrectionLocalGroupSize))), + static_cast<uint32_t>(glm::ceil(static_cast<float>(windowHeight) / static_cast<float>(gammaCorrectionLocalGroupSize))), + 1 + }; + + baf.execWholePipeline(cmdStream, colorBuffer); + + core.prepareImageForStorage(cmdStream, swapchainInput); + + // gamma correction descriptor write + vkcv::DescriptorWrites gammaCorrectionDescriptorWrites; + gammaCorrectionDescriptorWrites.storageImageWrites = { + vkcv::StorageImageDescriptorWrite(0, colorBuffer), + vkcv::StorageImageDescriptorWrite(1, swapchainInput) }; + core.writeDescriptorSet(gammaCorrectionDescriptorSet, gammaCorrectionDescriptorWrites); + + // gamma correction dispatch + core.recordComputeDispatchToCmdStream( + cmdStream, + gammaCorrectionPipeline, + gammaCorrectionDispatchCount, + { vkcv::DescriptorSetUsage(0, core.getDescriptorSet(gammaCorrectionDescriptorSet).vulkanHandle) }, + vkcv::PushConstantData(nullptr, 0)); + + // present and end + core.prepareSwapchainImageForPresent(cmdStream); + core.submitCommandStream(cmdStream); + + core.endFrame(); + } + + return 0; +} diff --git a/src/vkcv/DescriptorManager.cpp b/src/vkcv/DescriptorManager.cpp index 265532232304106f7271fdd445d52074b7c011a1..8e565a766cd407dc33c0291d3d07b01d6d3066e7 100644 --- a/src/vkcv/DescriptorManager.cpp +++ b/src/vkcv/DescriptorManager.cpp @@ -107,10 +107,11 @@ namespace vkcv std::vector<WriteDescriptorSetInfo> writeInfos; for (const auto& write : writes.sampledImageWrites) { + vk::ImageLayout layout = write.useGeneralLayout ? vk::ImageLayout::eGeneral : vk::ImageLayout::eShaderReadOnlyOptimal; const vk::DescriptorImageInfo imageInfo( nullptr, - imageManager.getVulkanImageView(write.image), - vk::ImageLayout::eShaderReadOnlyOptimal + imageManager.getVulkanImageView(write.image, write.mipLevel), + layout ); imageInfos.push_back(imageInfo);