diff --git a/modules/camera/src/vkcv/camera/CameraManager.cpp b/modules/camera/src/vkcv/camera/CameraManager.cpp index c8aa4f7e0e493a2aaf5bfd6d93768e169cd255b9..4c713c29e5dad7b72bbdbb1372e4cf0f5bb3d162 100644 --- a/modules/camera/src/vkcv/camera/CameraManager.cpp +++ b/modules/camera/src/vkcv/camera/CameraManager.cpp @@ -130,7 +130,7 @@ namespace vkcv::camera { } Camera& CameraManager::getCamera(uint32_t cameraIndex) { - if (cameraIndex < 0 || cameraIndex > m_cameras.size() - 1) { + if (cameraIndex < 0 || cameraIndex >= m_cameras.size()) { vkcv_log(LogLevel::ERROR, "Invalid camera index: The index must range from 0 to %lu", m_cameras.size()); return getActiveCamera(); } @@ -143,7 +143,7 @@ namespace vkcv::camera { } void CameraManager::setActiveCamera(uint32_t cameraIndex) { - if (cameraIndex < 0 || cameraIndex > m_cameras.size() - 1) { + if (cameraIndex < 0 || cameraIndex >= m_cameras.size()) { vkcv_log(LogLevel::ERROR, "Invalid camera index: The index must range from 0 to %lu", m_cameras.size()); return; } @@ -156,7 +156,7 @@ namespace vkcv::camera { } void CameraManager::setControllerType(uint32_t cameraIndex, ControllerType controllerType) { - if (cameraIndex < 0 || cameraIndex > m_cameras.size() - 1) { + if (cameraIndex < 0 || cameraIndex >= m_cameras.size()) { vkcv_log(LogLevel::ERROR, "Invalid camera index: The index must range from 0 to %lu", m_cameras.size()); return; } @@ -165,7 +165,7 @@ namespace vkcv::camera { } ControllerType CameraManager::getControllerType(uint32_t cameraIndex) { - if (cameraIndex < 0 || cameraIndex > m_cameras.size() - 1) { + if (cameraIndex < 0 || cameraIndex >= m_cameras.size()) { vkcv_log(LogLevel::ERROR, "Invalid camera index: The index must range from 0 to %lu", m_cameras.size()); return ControllerType::NONE; } diff --git a/projects/CMakeLists.txt b/projects/CMakeLists.txt index 437021a6b2ea52ae4a3fccff625f0c7e2ef8ddd0..1fffd2dab6cd34368d7140c857e7b885cea998cb 100644 --- a/projects/CMakeLists.txt +++ b/projects/CMakeLists.txt @@ -2,18 +2,19 @@ include(${vkcv_config_ext}/Project.cmake) # Add new projects/examples here: +add_subdirectory(bindless_textures) +add_subdirectory(fire_works) add_subdirectory(first_triangle) add_subdirectory(first_mesh) add_subdirectory(first_scene) add_subdirectory(head_demo) +add_subdirectory(indirect_dispatch) +add_subdirectory(indirect_draw) +add_subdirectory(mesh_shader) add_subdirectory(particle_simulation) +add_subdirectory(path_tracer) add_subdirectory(rtx_ambient_occlusion) +add_subdirectory(saf_r) add_subdirectory(sph) add_subdirectory(voxelization) -add_subdirectory(mesh_shader) -add_subdirectory(indirect_draw) -add_subdirectory(bindless_textures) -add_subdirectory(saf_r) -add_subdirectory(indirect_dispatch) -add_subdirectory(path_tracer) add_subdirectory(wobble_bobble) \ No newline at end of file diff --git a/projects/fire_works/.gitignore b/projects/fire_works/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..a991f1c077c11db780beb6e3d01c5bd561336690 --- /dev/null +++ b/projects/fire_works/.gitignore @@ -0,0 +1 @@ +fire_works diff --git a/projects/fire_works/CMakeLists.txt b/projects/fire_works/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..7f9fd1fdd30bff0b331138821f10035f4b7dc64d --- /dev/null +++ b/projects/fire_works/CMakeLists.txt @@ -0,0 +1,27 @@ +cmake_minimum_required(VERSION 3.16) +project(fire_works) + +# setting c++ standard for the project +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# adding source files to the project +add_project(fire_works + src/main.cpp) + +# including headers of dependencies and the VkCV framework +target_include_directories(fire_works SYSTEM BEFORE PRIVATE + ${vkcv_include} + ${vkcv_includes} + ${vkcv_camera_include} + ${vkcv_gui_include} + ${vkcv_shader_compiler_include} + ${vkcv_effects_include}) + +# linking with libraries from all dependencies and the VkCV framework +target_link_libraries(fire_works + vkcv + vkcv_camera + vkcv_gui + vkcv_shader_compiler + vkcv_effects) diff --git a/projects/fire_works/shaders/motion.comp b/projects/fire_works/shaders/motion.comp new file mode 100644 index 0000000000000000000000000000000000000000..87cea7fa842a20d5ebd3e55be6bf28d93590508e --- /dev/null +++ b/projects/fire_works/shaders/motion.comp @@ -0,0 +1,41 @@ +#version 450 core +#extension GL_GOOGLE_include_directive : enable +#extension GL_ARB_separate_shader_objects : enable + +layout(local_size_x = 256) in; + +#include "particle.inc" + +layout(set=0, binding=0, std430) coherent buffer particleBuffer { + particle_t particles []; +}; + +layout( push_constant ) uniform constants{ + float dt; +}; + +const float g = 9.81; + +void main() { + uint id = gl_GlobalInvocationID.x; + + if (id >= particles.length()) { + return; + } + + vec3 position = particles[id].position; + float lifetime = particles[id].lifetime; + vec3 velocity = particles[id].velocity; + + if (lifetime > dt) { + lifetime -= dt; + } else { + lifetime = 0.0f; + } + + position = position + velocity * dt; + velocity = velocity + vec3(0.0f, -g, 0.0f) * dt; + + particles[id].position = position; + particles[id].velocity = velocity; +} diff --git a/projects/fire_works/shaders/particle.frag b/projects/fire_works/shaders/particle.frag new file mode 100644 index 0000000000000000000000000000000000000000..a13b295a7543f489b23b97a93e904e60e3dac334 --- /dev/null +++ b/projects/fire_works/shaders/particle.frag @@ -0,0 +1,16 @@ +#version 450 + +layout(location = 0) in vec2 passPos; +layout(location = 1) in vec3 passColor; + +layout(location = 0) out vec3 outColor; + +void main() { + const float value = length(passPos); + + if (value < 0.5f) { + outColor = passColor; + } else { + discard; + } +} \ No newline at end of file diff --git a/projects/fire_works/shaders/particle.inc b/projects/fire_works/shaders/particle.inc new file mode 100644 index 0000000000000000000000000000000000000000..f3da76332452a17256a53dabb0ade6abb0b4caea --- /dev/null +++ b/projects/fire_works/shaders/particle.inc @@ -0,0 +1,13 @@ +#ifndef PARTICLE_INC +#define PARTICLE_INC + +struct particle_t { + vec3 position; + float lifetime; + vec3 velocity; + float size; + vec3 color; + float mass; +}; + +#endif // PARTICLE_INC \ No newline at end of file diff --git a/projects/fire_works/shaders/particle.vert b/projects/fire_works/shaders/particle.vert new file mode 100644 index 0000000000000000000000000000000000000000..ba4dbdb2cfdaf929e74a8668b8adb619b6a548fa --- /dev/null +++ b/projects/fire_works/shaders/particle.vert @@ -0,0 +1,30 @@ +#version 450 +#extension GL_GOOGLE_include_directive : enable + +#include "particle.inc" + +layout(set=0, binding=0, std430) readonly buffer particleBuffer { + particle_t particles []; +}; + +layout(location = 0) in vec2 vertexPos; + +layout(location = 0) out vec2 passPos; +layout(location = 1) out vec3 passColor; + +layout( push_constant ) uniform constants{ + mat4 mvp; +}; + +void main() { + vec3 position = particles[gl_InstanceIndex].position; + float size = particles[gl_InstanceIndex].size; + vec3 color = particles[gl_InstanceIndex].color; + + passPos = vertexPos; + passColor = color; + + // align particle to face camera + gl_Position = mvp * vec4(position, 1); // transform position into projected view space + gl_Position.xy += vertexPos * size * 2.0f; // move position directly in view space +} \ No newline at end of file diff --git a/projects/fire_works/shaders/tonemapping.comp b/projects/fire_works/shaders/tonemapping.comp new file mode 100644 index 0000000000000000000000000000000000000000..5e6cc8412a77939888fc8e961ea5e9ef29534a81 --- /dev/null +++ b/projects/fire_works/shaders/tonemapping.comp @@ -0,0 +1,21 @@ +#version 440 + +layout(set=0, binding=0, rgba16f) readonly uniform image2D inImage; +layout(set=0, binding=1, rgba8) writeonly 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; + + vec3 tonemapped = linearColor / (dot(linearColor, vec3(0.21, 0.71, 0.08)) + 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/fire_works/src/main.cpp b/projects/fire_works/src/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..93d820f6680c54f6ecb88c5b1dda760dc7b8840b --- /dev/null +++ b/projects/fire_works/src/main.cpp @@ -0,0 +1,318 @@ + +#include <vkcv/Core.hpp> +#include <vkcv/DrawcallRecording.hpp> + +#include <vkcv/camera/CameraManager.hpp> +#include <vkcv/shader/GLSLCompiler.hpp> +#include <vkcv/gui/GUI.hpp> +#include <vkcv/effects/BloomAndFlaresEffect.hpp> + +struct particle_t { + glm::vec3 position; + float lifetime; + glm::vec3 velocity; + float size; + glm::vec3 color; + float pad2; +}; + +int main(int argc, const char **argv) { + vkcv::Core core = vkcv::Core::create( + "Firework", + VK_MAKE_VERSION(0, 0, 1), + {vk::QueueFlagBits::eTransfer, vk::QueueFlagBits::eGraphics, vk::QueueFlagBits::eCompute}, + { VK_KHR_SWAPCHAIN_EXTENSION_NAME } + ); + + vkcv::WindowHandle windowHandle = core.createWindow("Firework", 800, 600, true); + vkcv::Window& window = core.getWindow (windowHandle); + vkcv::camera::CameraManager cameraManager (window); + + uint32_t trackballIdx = cameraManager.addCamera(vkcv::camera::ControllerType::TRACKBALL); + cameraManager.getCamera(trackballIdx).setCenter(glm::vec3(0.0f, 0.0f, 0.0f)); // set camera to look at the center of the particle volume + cameraManager.addCamera(vkcv::camera::ControllerType::PILOT); + + cameraManager.getCamera(trackballIdx).setNearFar(0.1f, 50.0f); + cameraManager.getCamera(trackballIdx).setPosition(glm::vec3(0, 0, -25)); + + vkcv::gui::GUI gui (core, windowHandle); + + auto swapchainExtent = core.getSwapchain(windowHandle).getExtent(); + + const vk::Format depthFormat = vk::Format::eD32Sfloat; + + vkcv::ImageHandle depthBuffer = core.createImage( + depthFormat, + swapchainExtent.width, + swapchainExtent.height + ).getHandle(); + + const vk::Format colorFormat = vk::Format::eR16G16B16A16Sfloat; + + vkcv::ImageHandle colorBuffer = core.createImage( + colorFormat, + swapchainExtent.width, + swapchainExtent.height, + 1, false, true, true + ).getHandle(); + + vkcv::shader::GLSLCompiler compiler; + vkcv::ShaderProgram particleShaderProgram; + + compiler.compile(vkcv::ShaderStage::VERTEX, "shaders/particle.vert", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + particleShaderProgram.addShader(shaderStage, path); + }); + compiler.compile(vkcv::ShaderStage::FRAGMENT, "shaders/particle.frag", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + particleShaderProgram.addShader(shaderStage, path); + }); + + vkcv::DescriptorBindings descriptorBindings; + vkcv::DescriptorBinding binding { + 0, + vkcv::DescriptorType::STORAGE_BUFFER, + 1, + vkcv::ShaderStage::VERTEX | vkcv::ShaderStage::COMPUTE, + false, + false + }; + + descriptorBindings.insert(std::make_pair(0, binding)); + + vkcv::DescriptorSetLayoutHandle descriptorSetLayout = core.createDescriptorSetLayout(descriptorBindings); + vkcv::DescriptorSetHandle descriptorSet = core.createDescriptorSet(descriptorSetLayout); + + std::vector<particle_t> particles; + + for (size_t i = 0; i < 1024; i++) { + particle_t particle; + particle.position = glm::vec3( + 2.0f * (std::rand() % RAND_MAX) / RAND_MAX - 1.0f, + 2.0f * (std::rand() % RAND_MAX) / RAND_MAX - 1.0f, + 2.0f * (std::rand() % RAND_MAX) / RAND_MAX - 1.0f + ); + + particle.lifetime = 0.0f; + particle.velocity = glm::vec3(0.0f); + particle.size = 0.01f; + particle.color = glm::vec3(1.0f, 0.0f, 0.0f); + + particles.push_back(particle); + } + + vkcv::Buffer<particle_t> particleBuffer = core.createBuffer<particle_t>( + vkcv::BufferType::STORAGE, + particles.size() + ); + + particleBuffer.fill(particles); + + vkcv::DescriptorWrites writes; + writes.writeStorageBuffer(0, particleBuffer.getHandle()); + + core.writeDescriptorSet(descriptorSet, writes); + + vkcv::Buffer<glm::vec2> trianglePositions = core.createBuffer<glm::vec2>(vkcv::BufferType::VERTEX, 3); + trianglePositions.fill({ + glm::vec2(-1.0f, -1.0f), + glm::vec2(+0.0f, +1.5f), + glm::vec2(+1.0f, -1.0f) + }); + + vkcv::Buffer<uint16_t> triangleIndices = core.createBuffer<uint16_t>(vkcv::BufferType::INDEX, 3); + triangleIndices.fill({ + 0, 1, 2 + }); + + vkcv::Mesh triangleMesh ( + { vkcv::VertexBufferBinding(0, trianglePositions.getVulkanHandle()) }, + triangleIndices.getVulkanHandle(), + triangleIndices.getCount() + ); + + const std::vector<vkcv::VertexAttachment> vertexAttachments = particleShaderProgram.getVertexAttachments(); + + const std::vector<vkcv::VertexBufferBinding> vertexBufferBindings = { + vkcv::VertexBufferBinding(0, trianglePositions.getVulkanHandle()) + }; + + std::vector<vkcv::VertexBinding> bindings; + for (size_t i = 0; i < vertexAttachments.size(); i++) { + bindings.push_back(vkcv::createVertexBinding(i, {vertexAttachments[i]})); + } + + const vkcv::VertexLayout particleLayout { bindings }; + + const vkcv::AttachmentDescription present_color_attachment( + vkcv::AttachmentOperation::STORE, + vkcv::AttachmentOperation::CLEAR, + colorFormat + ); + + const vkcv::AttachmentDescription depth_attachment( + vkcv::AttachmentOperation::STORE, + vkcv::AttachmentOperation::CLEAR, + depthFormat + ); + + vkcv::PassConfig particlePassDefinition({present_color_attachment, depth_attachment}, vkcv::Multisampling::None); + vkcv::PassHandle particlePass = core.createPass(particlePassDefinition); + + vkcv::GraphicsPipelineConfig particlePipelineDefinition{ + particleShaderProgram, + UINT32_MAX, + UINT32_MAX, + particlePass, + {particleLayout}, + {descriptorSetLayout}, + true + }; + + // particlePipelineDefinition.m_blendMode = vkcv::BlendMode::Additive; + + vkcv::GraphicsPipelineHandle particlePipeline = core.createGraphicsPipeline(particlePipelineDefinition); + + std::vector<vkcv::DrawcallInfo> drawcallsParticles; + + drawcallsParticles.push_back(vkcv::DrawcallInfo( + triangleMesh, + { vkcv::DescriptorSetUsage(0, descriptorSet) }, + particleBuffer.getCount() + )); + + vkcv::ShaderProgram motionShader; + compiler.compile(vkcv::ShaderStage::COMPUTE, "shaders/motion.comp", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + motionShader.addShader(shaderStage, path); + }); + + vkcv::ComputePipelineHandle motionPipeline = core.createComputePipeline({ + motionShader, + { descriptorSetLayout } + }); + + vkcv::effects::BloomAndFlaresEffect bloomAndFlares (core); + bloomAndFlares.setUpsamplingLimit(3); + + vkcv::ShaderProgram tonemappingShader; + compiler.compile(vkcv::ShaderStage::COMPUTE, "shaders/tonemapping.comp", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + tonemappingShader.addShader(shaderStage, path); + }); + + vkcv::DescriptorSetLayoutHandle tonemappingDescriptorLayout = core.createDescriptorSetLayout(tonemappingShader.getReflectedDescriptors().at(0)); + vkcv::DescriptorSetHandle tonemappingDescriptor = core.createDescriptorSet(tonemappingDescriptorLayout); + vkcv::ComputePipelineHandle tonemappingPipe = core.createComputePipeline({ + tonemappingShader, + { tonemappingDescriptorLayout } + }); + + vkcv::ImageHandle swapchainImage = vkcv::ImageHandle::createSwapchainImageHandle(); + + auto start = std::chrono::system_clock::now(); + auto current = start; + + while (vkcv::Window::hasOpenWindow()) { + vkcv::Window::pollEvents(); + + uint32_t swapchainWidth, swapchainHeight; + if (!core.beginFrame(swapchainWidth, swapchainHeight, windowHandle)) { + continue; + } + + if ((core.getImageWidth(colorBuffer) != swapchainWidth) || + (core.getImageHeight(colorBuffer) != swapchainHeight)) { + colorBuffer = core.createImage( + colorFormat, + swapchainWidth, + swapchainHeight, + 1, false, true, true + ).getHandle(); + } + + if ((core.getImageWidth(depthBuffer) != swapchainWidth) || + (core.getImageHeight(depthBuffer) != swapchainHeight)) { + depthBuffer = core.createImage( + depthFormat, + swapchainWidth, + swapchainHeight + ).getHandle(); + } + + auto next = std::chrono::system_clock::now(); + + auto time = std::chrono::duration_cast<std::chrono::microseconds>(next - start); + auto deltatime = std::chrono::duration_cast<std::chrono::microseconds>(next - current); + + current = next; + + // auto t = static_cast<float>(0.000001 * static_cast<double>(time.count())); + auto dt = static_cast<float>(0.000001 * static_cast<double>(deltatime.count())); + + auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics); + + uint32_t motionDispatchCount[3]; + motionDispatchCount[0] = std::ceil(particleBuffer.getCount() / 256.f); + motionDispatchCount[1] = 1; + motionDispatchCount[2] = 1; + + vkcv::PushConstants pushConstantsTime (sizeof(float)); + pushConstantsTime.appendDrawcall(dt); + + core.recordComputeDispatchToCmdStream( + cmdStream, + motionPipeline, + motionDispatchCount, + {vkcv::DescriptorSetUsage(0, descriptorSet) }, + pushConstantsTime + ); + + cameraManager.update(dt); + + const auto& camera = cameraManager.getActiveCamera(); + + vkcv::PushConstants pushConstantsDraw (sizeof(glm::mat4)); + pushConstantsDraw.appendDrawcall(camera.getMVP()); + + core.recordDrawcallsToCmdStream( + cmdStream, + particlePass, + particlePipeline, + pushConstantsDraw, + {drawcallsParticles}, + { colorBuffer, depthBuffer }, + windowHandle + ); + + bloomAndFlares.recordEffect(cmdStream, colorBuffer, colorBuffer); + + core.prepareImageForStorage(cmdStream, colorBuffer); + core.prepareImageForStorage(cmdStream, swapchainImage); + + vkcv::DescriptorWrites tonemappingDescriptorWrites; + tonemappingDescriptorWrites.writeStorageImage( + 0, colorBuffer + ).writeStorageImage( + 1, swapchainImage + ); + + core.writeDescriptorSet(tonemappingDescriptor, tonemappingDescriptorWrites); + + uint32_t tonemappingDispatchCount[3]; + tonemappingDispatchCount[0] = std::ceil(swapchainWidth / 8.f); + tonemappingDispatchCount[1] = std::ceil(swapchainHeight / 8.f); + tonemappingDispatchCount[2] = 1; + + core.recordComputeDispatchToCmdStream( + cmdStream, + tonemappingPipe, + tonemappingDispatchCount, + {vkcv::DescriptorSetUsage(0, tonemappingDescriptor) }, + vkcv::PushConstants(0) + ); + + core.prepareSwapchainImageForPresent(cmdStream); + core.submitCommandStream(cmdStream); + + core.endFrame(windowHandle); + } + + return 0; +}