diff --git a/CMakeLists.txt b/CMakeLists.txt index 2b74cee67be765d9d60850ad2c28c5510f3eadcc..1b69b6d5947b04efa72e1699e74c7022791693da 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,6 +9,10 @@ option(BUILD_DOXYGEN_DOCS "Enables building the VkCV doxygen documentation" OFF) option(BUILD_SHARED "Enables building VkCV as shared libraries" OFF) option(BUILD_VMA_VULKAN_VERSION "Enforce a specific Vulkan version for VMA" OFF) +if ((WIN32) AND (NOT BUILD_VMA_VULKAN_VERSION)) + set(BUILD_VMA_VULKAN_VERSION "1.3.0") +endif() + if (BUILD_PROJECTS) set(BUILD_MODULES ${BUILD_PROJECTS}) endif() diff --git a/modules/algorithm/src/vkcv/algorithm/SinglePassDownsampler.cpp b/modules/algorithm/src/vkcv/algorithm/SinglePassDownsampler.cpp index 3a8408eb4434dbb0c065ef7a16842e8fc73275a1..eee9f361493c35c5214ac661448f3cc80384e4ee 100644 --- a/modules/algorithm/src/vkcv/algorithm/SinglePassDownsampler.cpp +++ b/modules/algorithm/src/vkcv/algorithm/SinglePassDownsampler.cpp @@ -3,6 +3,7 @@ #include <cstdint> #include <cmath> +#include <vector> #define A_CPU 1 #include <ffx_a.h> @@ -237,8 +238,9 @@ namespace vkcv::algorithm { { m_descriptorSetLayout } )); - uint32_t zeroes [m_globalCounter.getCount()]; - memset(zeroes, 0, m_globalCounter.getSize()); + std::vector<uint32_t> zeroes; + zeroes.resize(m_globalCounter.getCount()); + memset(zeroes.data(), 0, m_globalCounter.getSize()); m_globalCounter.fill(zeroes); } 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/add.comp b/projects/fire_works/shaders/add.comp new file mode 100644 index 0000000000000000000000000000000000000000..737a1f3fd69e237f7692be8a6b36b1e14f1fd50a --- /dev/null +++ b/projects/fire_works/shaders/add.comp @@ -0,0 +1,64 @@ +#version 440 +#extension GL_GOOGLE_include_directive : enable + +layout(set=0, binding=0) uniform texture2D voxelTexture; +layout(set=0, binding=1) uniform sampler voxelSampler; + +layout(set=0, binding=2, rgba16f) restrict readonly uniform image2D inParticles; +layout(set=0, binding=3, rgba16f) restrict readonly uniform image2D inSmoke; +layout(set=0, binding=4, rgba16f) restrict readonly uniform image2D inTrails; +layout(set=0, binding=5, rgba16f) restrict writeonly uniform image2D outImage; + +layout(set=1, binding=0, std430) readonly buffer randomBuffer { + float randomData []; +}; + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +#include "physics.inc" +#include "smoke.inc" + +#define NUM_VOXEL_SAMPLES 32 + +shared vec2 sc_data [NUM_VOXEL_SAMPLES]; + +void main() { + const float localRadian = 0.25f * pi * randomData[gl_LocalInvocationIndex % randomData.length()]; + + sc_data[gl_LocalInvocationIndex % NUM_VOXEL_SAMPLES] = vec2( + sin(localRadian), cos(localRadian) + ); + + memoryBarrierShared(); + barrier(); + + const ivec2 res = imageSize(outImage); + + if(any(greaterThanEqual(gl_GlobalInvocationID.xy, res))){ + return; + } + + ivec2 uv = ivec2(gl_GlobalInvocationID.xy); + + vec4 outParticles = imageLoad(inParticles, uv); + vec4 outSmoke = imageLoad(inSmoke, uv); + vec4 outTrails = imageLoad(inTrails, uv); + + vec2 pos = (vec2(uv) + vec2(0.5f)) / vec2(res); + + vec4 outSamples = texture(sampler2D(voxelTexture, voxelSampler), pos); + + vec4 result = vec4(0.0f); + + result = smokeBlend(result, outParticles); + result = smokeBlend(result, outTrails); + result = smokeBlend(result, outSmoke); + result = smokeBlend(result, outSamples * 0.1f); + + result.r = clamp(result.r, 0, 1); + result.g = clamp(result.g, 0, 1); + result.b = clamp(result.b, 0, 1); + result.a = clamp(result.a, 0, 1); + + imageStore(outImage, uv, result); +} \ No newline at end of file diff --git a/projects/fire_works/shaders/clear.comp b/projects/fire_works/shaders/clear.comp new file mode 100644 index 0000000000000000000000000000000000000000..4668538c4a38aefec868f1817a126b5278f8fd78 --- /dev/null +++ b/projects/fire_works/shaders/clear.comp @@ -0,0 +1,25 @@ +#version 440 +#extension GL_GOOGLE_include_directive : enable + +#include "physics.inc" +#include "voxel.inc" + +layout(set=0, binding=0, r32ui) restrict writeonly uniform uimage3D voxelRed; +layout(set=0, binding=1, r32ui) restrict writeonly uniform uimage3D voxelGreen; +layout(set=0, binding=2, r32ui) restrict writeonly uniform uimage3D voxelBlue; +layout(set=0, binding=3, r32ui) restrict writeonly uniform uimage3D voxelDensity; + +layout(local_size_x = 4, local_size_y = 4, local_size_z = 4) in; + +void main() { + if(any(greaterThanEqual(gl_GlobalInvocationID.xyz, imageSize(voxelDensity)))){ + return; + } + + ivec3 pos = ivec3(gl_GlobalInvocationID.xyz); + + voxel_write(voxelRed, pos, 0.0f); + voxel_write(voxelGreen, pos, 0.0f); + voxel_write(voxelBlue, pos, 0.0f); + voxel_write(voxelDensity, pos, mediumDensity); +} \ No newline at end of file diff --git a/projects/fire_works/shaders/event.inc b/projects/fire_works/shaders/event.inc new file mode 100644 index 0000000000000000000000000000000000000000..a2ab170c0894da68638b0e61e4af90a7a5fcdd64 --- /dev/null +++ b/projects/fire_works/shaders/event.inc @@ -0,0 +1,21 @@ +#ifndef EVENT_INC +#define EVENT_INC + +struct event_t { + vec3 direction; + float startTime; + vec3 color; + float velocity; + + uint count; + uint index; + uint parent; + uint continuous; + + float lifetime; + float mass; + float size; + uint contCount; +}; + +#endif // EVENT_INC \ No newline at end of file diff --git a/projects/fire_works/shaders/fluid.comp b/projects/fire_works/shaders/fluid.comp new file mode 100644 index 0000000000000000000000000000000000000000..076a280739e542c0579a7b80eaeaf4993df13edf --- /dev/null +++ b/projects/fire_works/shaders/fluid.comp @@ -0,0 +1,80 @@ +#version 450 core +#extension GL_GOOGLE_include_directive : enable +#extension GL_ARB_separate_shader_objects : enable + +layout(local_size_x = 4, local_size_y = 4, local_size_z = 4) in; + +#include "physics.inc" +#include "voxel.inc" + +layout(set=0, binding=0) uniform texture3D voxelTexture; +layout(set=0, binding=1) uniform sampler voxelSampler; +layout(set=0, binding=2, rgba16) restrict writeonly uniform image3D fluidImage; + +vec4 getDataFrom(vec3 position, vec3 offset) { + return texture( + sampler3D( + voxelTexture, + voxelSampler + ), + position + offset + ); +} + +shared vec4 cachedData [4][4][4]; + +void storeCachedData(vec3 position) { + uvec3 localId = gl_LocalInvocationID; + cachedData[localId.x][localId.y][localId.z] = getDataFrom(position, vec3(0)); +} + +vec4 getCachedData() { + uvec3 localId = gl_LocalInvocationID; + return cachedData[localId.x][localId.y][localId.z]; +} + +vec4 loadCachedData(vec3 position, ivec3 offset, ivec3 size) { + uvec3 localId = gl_LocalInvocationID; + ivec3 index = ivec3(localId) + offset; + + // TOO SPECIAL FOR GPU TO WORK..! + //if ((any(lessThan(index, ivec3(0)))) || (any(greaterThan(index, ivec3(gl_WorkGroupSize))))) { + return getDataFrom(position, vec3(offset) / vec3(size)); + //} else { + // return cachedData[index.x][index.y][index.z]; + //} +} + +void main() { + uvec3 id = gl_GlobalInvocationID; + ivec3 size = imageSize(fluidImage); + + if (any(greaterThanEqual(id, size))) { + return; + } + + vec3 position = (vec3(id) + vec3(0.5f)) / vec3(size); + + storeCachedData(position); + memoryBarrierShared(); + barrier(); + + vec4 extData [6]; + + extData[0] = loadCachedData(position, ivec3(+1, 0, 0), size); + extData[1] = loadCachedData(position, ivec3(-1, 0, 0), size); + extData[2] = loadCachedData(position, ivec3(0, +1, 0), size); + extData[3] = loadCachedData(position, ivec3(0, -1, 0), size); + extData[4] = loadCachedData(position, ivec3(0, 0, +1), size); + extData[5] = loadCachedData(position, ivec3(0, 0, -1), size); + + vec4 data = vec4(0); + + for (uint i = 0; i < 6; i++) { + data += extData[i]; + } + + data = mix(getCachedData(), (data / 6), flowRate); + + imageStore(fluidImage, ivec3(id), data); +} diff --git a/projects/fire_works/shaders/generation.comp b/projects/fire_works/shaders/generation.comp new file mode 100644 index 0000000000000000000000000000000000000000..eb585236d993099350d0c0e6b97adcbfd28fdf57 --- /dev/null +++ b/projects/fire_works/shaders/generation.comp @@ -0,0 +1,179 @@ +#version 450 core +#extension GL_GOOGLE_include_directive : enable +#extension GL_ARB_separate_shader_objects : enable + +layout(local_size_x = 256) in; + +#include "physics.inc" +#include "particle.inc" + +layout(set=0, binding=0, std430) buffer particleBuffer { + particle_t particles []; +}; + +layout(set=0, binding=1, std430) readonly buffer particleBufferCopy { + particle_t particlesCopy []; +}; + +layout(set=1, binding=0, std430) readonly buffer randomBuffer { + float randomData []; +}; + +#include "event.inc" + +layout(set=1, binding=1, std430) buffer eventBuffer { + event_t events []; +}; + +layout(set=1, binding=2, std430) buffer startIndexBuffer { + uint startIndex []; +}; + +#include "smoke.inc" + +layout(set=2, binding=0, std430) writeonly buffer smokeBuffer { + smoke_t smokes []; +}; + +layout(set=2, binding=1, std430) buffer smokeIndexBuffer { + uint smokeIndex; + uint trailIndex; + uint pointIndex; +}; + +#include "trail.inc" + +layout(set=3, binding=0, std430) writeonly buffer trailBuffer { + trail_t trails []; +}; + +#include "point.inc" + +layout(set=3, binding=1, std430) readonly buffer pointBuffer { + point_t points []; +}; + +layout( push_constant ) uniform constants{ + float t; + float dt; +}; + +void main() { + uint id = gl_GlobalInvocationID.x; + + if (id >= particles.length()) { + return; + } + + float lifetime = particles[id].lifetime; + + if (lifetime > 0.0f) { + return; + } + + uint event_id = events.length(); + uint index = 0; + + for (uint i = 0; i < events.length(); i++) { + const float start = events[i].startTime; + + if ((events[i].continuous < 1) && (t < start)) { + continue; + } + + index = atomicAdd(events[i].index, 1); + + if (events[i].continuous < 1) { + if (events[i].count > index) { + event_id = i; + break; + } else { + atomicAdd(events[i].index, -1); + } + } else { + if (events[i].continuous > index){ + event_id = i; + break; + } else { + if (events[i].contCount > 0) { + atomicAdd(events[i].contCount, -1); + events[i].index = 0; + } + + atomicAdd(events[i].index, -1); + } + } + } + + if (event_id >= events.length()) { + return; + } + + lifetime = events[event_id].lifetime * (1.0f + 0.1f * randomData[(id + 1) % randomData.length()]); + + vec3 direction; + if (dot(events[event_id].direction, events[event_id].direction) <= 0.0f) { + direction = vec3( + randomData[(id * 3 + 0) % randomData.length()], + randomData[(id * 3 + 1) % randomData.length()], + randomData[(id * 3 + 2) % randomData.length()] + ); + } else { + direction = events[event_id].direction; + } + + vec3 color = normalize(events[event_id].color); + const float v = events[event_id].velocity; + + vec3 velocity = vec3(0.0f); + float size = events[event_id].size; + + const uint pid = events[event_id].parent; + + if (pid < events.length()) { + const uint spawnCount = events[pid].count; + const uint spawnId = startIndex[pid] + (id % spawnCount); + + if (spawnId < particlesCopy.length()) { + particles[id].position = particlesCopy[spawnId].position; + velocity += particlesCopy[spawnId].velocity; + size = particlesCopy[spawnId].size; + } + } + + if ((0 == index) && (events[event_id].continuous < 1)) { + const uint sid = atomicAdd(smokeIndex, 1) % smokes.length(); + + smokes[sid].position = particles[id].position; + smokes[sid].size = size * (1.0f + friction); + smokes[sid].velocity = velocity; + smokes[sid].scaling = v; + smokes[sid].color = mix(color, vec3(1.0f), 0.5f); + smokes[sid].eventID = event_id; + } + + velocity += normalize(direction) * v * (1.0f + 0.1f * randomData[(id + 2) % randomData.length()]);; + + const float split = pow(1.0f / events[event_id].count, 1.0f / 3.0f); + + particles[id].lifetime = lifetime; + particles[id].velocity = velocity; + particles[id].size = size * split; + particles[id].color = color; + particles[id].mass = events[event_id].mass / events[event_id].count; + particles[id].eventId = event_id; + + { + const uint tid = atomicAdd(trailIndex, 1) % trails.length(); + const uint trailLen = 96 + int(randomData[(tid + id) % randomData.length()] * 32); + + const uint startIndex = atomicAdd(pointIndex, trailLen) % points.length(); + + trails[tid].particleIndex = id; + trails[tid].startIndex = startIndex; + trails[tid].endIndex = (startIndex + trailLen - 1) % points.length(); + trails[tid].useCount = 0; + trails[tid].color = mix(color, vec3(1.0f), 0.75f); + trails[tid].lifetime = lifetime + (dt * trailLen) * 0.5f; + } +} diff --git a/projects/fire_works/shaders/motion.comp b/projects/fire_works/shaders/motion.comp new file mode 100644 index 0000000000000000000000000000000000000000..51d6fc5f60f2eff380376fead6cc189af68b52e6 --- /dev/null +++ b/projects/fire_works/shaders/motion.comp @@ -0,0 +1,49 @@ +#version 450 core +#extension GL_GOOGLE_include_directive : enable +#extension GL_ARB_separate_shader_objects : enable + +layout(local_size_x = 256) in; + +#include "physics.inc" +#include "particle.inc" + +layout(set=0, binding=0, std430) coherent buffer particleBuffer { + particle_t particles []; +}; + +layout( push_constant ) uniform constants{ + float t; + float dt; +}; + +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; + } + + const float fading = 1.0f / (1.0f + friction); + + position = position + velocity * dt; + + if (particles[id].mass > 0){ + velocity = velocity * fading + vec3(0.0f, -g, 0.0f) * dt; + } else { + velocity = velocity * fading; + } + + particles[id].position = position; + particles[id].lifetime = lifetime; + 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..173c83ef5d7a79bfd8446c5586b7a2f487fe7546 --- /dev/null +++ b/projects/fire_works/shaders/particle.frag @@ -0,0 +1,21 @@ +#version 450 + +layout(location = 0) in vec2 passPos; +layout(location = 1) in flat vec3 passColor; +layout(location = 2) in flat float passLifetime; + +layout(location = 0) out vec4 outColor; + +void main() { + if (passLifetime <= 0.0f) { + discard; + } + + const float value = length(passPos); + + if (value < 0.5f) { + outColor = vec4(passColor, 1.0f - max(value * 2.0f, 0.0f)); + } 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..488c9349d72ac04d48e523d5c9a97057bdfb1f78 --- /dev/null +++ b/projects/fire_works/shaders/particle.inc @@ -0,0 +1,15 @@ +#ifndef PARTICLE_INC +#define PARTICLE_INC + +struct particle_t { + vec3 position; + float lifetime; + vec3 velocity; + float size; + vec3 color; + float mass; + vec3 pad0; + uint eventId; +}; + +#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..ff0e862ebcf0f66c3412e9ce04b610cea760151d --- /dev/null +++ b/projects/fire_works/shaders/particle.vert @@ -0,0 +1,40 @@ +#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 flat vec3 passColor; +layout(location = 2) out flat float passLifetime; + +layout( push_constant ) uniform constants{ + mat4 mvp; + uint width; + uint height; +}; + +void main() { + vec3 position = particles[gl_InstanceIndex].position; + float lifetime = particles[gl_InstanceIndex].lifetime; + float size = particles[gl_InstanceIndex].size; + vec3 color = particles[gl_InstanceIndex].color; + + if (width > height) { + passPos = vertexPos * vec2(1.0f * width / height, 1.0f); + } else { + passPos = vertexPos * vec2(1.0f, 1.0f * height / width); + } + + passColor = color; + passLifetime = lifetime; + + // 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/physics.inc b/projects/fire_works/shaders/physics.inc new file mode 100644 index 0000000000000000000000000000000000000000..e14c62e33a6fd42ebfa8d31591394989de68a8d2 --- /dev/null +++ b/projects/fire_works/shaders/physics.inc @@ -0,0 +1,13 @@ +#ifndef PHYSICS_INC +#define PHYSICS_INC + +const float pi = 3.14159f; + +const float g = 9.81f; +const float friction = 0.004f; + +const float flowRate = 0.75f; +const float mediumDensity = 0.0002f; +const float trailWidth = 0.25f; + +#endif // PHYSICS_INC \ No newline at end of file diff --git a/projects/fire_works/shaders/point.inc b/projects/fire_works/shaders/point.inc new file mode 100644 index 0000000000000000000000000000000000000000..54663c1e2eae4c641fbe5485d4c0ac41cb323df6 --- /dev/null +++ b/projects/fire_works/shaders/point.inc @@ -0,0 +1,11 @@ +#ifndef POINT_INC +#define POINT_INC + +struct point_t { + vec3 position; + float size; + vec3 velocity; + float scaling; +}; + +#endif // POINT_INC \ No newline at end of file diff --git a/projects/fire_works/shaders/sample.comp b/projects/fire_works/shaders/sample.comp new file mode 100644 index 0000000000000000000000000000000000000000..1eef49df639cfbc9f35c53ace21b1ce50ab62459 --- /dev/null +++ b/projects/fire_works/shaders/sample.comp @@ -0,0 +1,39 @@ +#version 440 +#extension GL_GOOGLE_include_directive : enable + +#include "voxel.inc" +#include "smoke.inc" + +layout(set=0, binding=0, rgba16) restrict readonly uniform image3D voxelImage; +layout(set=1, binding=0, rgba16f) restrict writeonly uniform image2D outImage; + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +void main() { + const ivec2 res = imageSize(outImage); + + if(any(greaterThanEqual(gl_GlobalInvocationID.xy, res))){ + return; + } + + ivec2 uv = ivec2(gl_GlobalInvocationID.xy); + + const ivec3 voxelRes = imageSize(voxelImage); + + vec4 voxel = vec4(0.0f); + + for (int i = 0; i < voxelRes.z; i++) { + const ivec3 voxelPos = ivec3(uv, i); + + vec4 data = imageLoad(voxelImage, voxelPos); + + voxel = smokeBlend(voxel, data); + } + + voxel.r = clamp(voxel.r, 0, 1); + voxel.g = clamp(voxel.g, 0, 1); + voxel.b = clamp(voxel.b, 0, 1); + voxel.a = clamp(voxel.a, 0, 1); + + imageStore(outImage, uv, voxel); +} \ No newline at end of file diff --git a/projects/fire_works/shaders/scale.comp b/projects/fire_works/shaders/scale.comp new file mode 100644 index 0000000000000000000000000000000000000000..44cfb8e48ad078f5ee8f63785b513a20f27ef542 --- /dev/null +++ b/projects/fire_works/shaders/scale.comp @@ -0,0 +1,40 @@ +#version 450 core +#extension GL_GOOGLE_include_directive : enable +#extension GL_ARB_separate_shader_objects : enable + +layout(local_size_x = 256) in; + +#include "physics.inc" +#include "smoke.inc" + +layout(set=0, binding=0, std430) buffer smokeBuffer { + smoke_t smokes []; +}; + +layout( push_constant ) uniform constants{ + float t; + float dt; +}; + +void main() { + uint id = gl_GlobalInvocationID.x; + + if (id >= smokes.length()) { + return; + } + + vec3 position = smokes[id].position; + float size = smokes[id].size; + vec3 velocity = smokes[id].velocity; + + const float scaling = smokes[id].scaling; + const float fading = 1.0f / (1.0f + friction); + + position = position + velocity * dt; + velocity = velocity * fading + vec3(0.0f, 0.2f, 0.0f) * dt; //smoke is lighter than air right? + vec3(0.0f, -g, 0.0f) * dt; + size = size + scaling * dt; + + smokes[id].position = position; + smokes[id].size = size; + smokes[id].velocity = velocity; +} diff --git a/projects/fire_works/shaders/smoke.frag b/projects/fire_works/shaders/smoke.frag new file mode 100644 index 0000000000000000000000000000000000000000..8ded98ac5fbae554959a83df5bbf7451d286e832 --- /dev/null +++ b/projects/fire_works/shaders/smoke.frag @@ -0,0 +1,57 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable +#extension GL_GOOGLE_include_directive : enable + +#include "physics.inc" +#include "smoke.inc" + +layout(location = 0) in vec3 passPos; +layout(location = 1) in vec3 passDir; +layout(location = 2) in vec3 passColor; +layout(location = 3) in float passDensity; +layout(location = 4) in flat int passSmokeIndex; + +layout(location = 0) out vec4 outColor; + +layout(set=1, binding=0, std430) readonly buffer randomBuffer { + float randomData []; +}; + +#define NUM_SMOKE_SAMPLES 16 + +void main() { + if (passDensity <= mediumDensity) { + discard; + } + + vec3 start = passPos; + vec3 end = start + normalize(passDir) * 3.5f; + + vec4 result = vec4(0); + + for (uint i = 0; i < NUM_SMOKE_SAMPLES; i++) { + vec3 position = ( + end + (start - end) * i / (NUM_SMOKE_SAMPLES - 1) + ); + + vec4 data = vec4(passColor, passDensity); + + float fallOff = max(1.0f - length(position), 0.0f); + + const uint randomIndex = (passSmokeIndex * NUM_SMOKE_SAMPLES + i) % randomData.length(); + const float alpha = (1.0f + randomData[randomIndex] * 0.1f) * data.a * fallOff; + + result = smokeBlend(result, vec4(data.rgb, alpha)); + } + + result.r = clamp(result.r, 0, 1); + result.g = clamp(result.g, 0, 1); + result.b = clamp(result.b, 0, 1); + result.a = clamp(result.a, 0, 1); + + if (result.a < 1.0f) { + outColor = result; + } else { + discard; + } +} \ No newline at end of file diff --git a/projects/fire_works/shaders/smoke.inc b/projects/fire_works/shaders/smoke.inc new file mode 100644 index 0000000000000000000000000000000000000000..8886a788273cd5fa5c871749e0c9012f2722ed41 --- /dev/null +++ b/projects/fire_works/shaders/smoke.inc @@ -0,0 +1,31 @@ +#ifndef SMOKE_INC +#define SMOKE_INC + +struct smoke_t { + vec3 position; + float size; + vec3 velocity; + float scaling; + vec3 color; + uint eventID; +}; + +float smokeDensity(float size) { + if (size > 0.0f) { + return 0.025f / size; + } else { + return 0.0f; + } +} + +vec4 smokeBlend(vec4 dst, vec4 src) { + const float f = max(1.0f - dst.a, 0.0f); + const float a = clamp(0.0f, 1.0f, src.a); + + return vec4( + dst.rgb + src.rgb * a * f, + dst.a + a * f + ); +} + +#endif // SMOKE_INC \ No newline at end of file diff --git a/projects/fire_works/shaders/smoke.vert b/projects/fire_works/shaders/smoke.vert new file mode 100644 index 0000000000000000000000000000000000000000..634117f79789797cd68e00cac3dbd59fb6515f79 --- /dev/null +++ b/projects/fire_works/shaders/smoke.vert @@ -0,0 +1,38 @@ +#version 450 +#extension GL_GOOGLE_include_directive : enable + +#include "smoke.inc" + +layout(set=0, binding=0, std430) readonly buffer smokeBuffer { + smoke_t smokes []; +}; + +layout(location = 0) in vec3 vertexPos; + +layout(location = 0) out vec3 passPos; +layout(location = 1) out vec3 passDir; +layout(location = 2) out vec3 passColor; +layout(location = 3) out float passDensity; +layout(location = 4) out flat int passSmokeIndex; + +layout( push_constant ) uniform constants{ + mat4 mvp; + vec3 camera; +}; + +void main() { + vec3 position = smokes[gl_InstanceIndex].position; + float size = smokes[gl_InstanceIndex].size; + vec3 color = smokes[gl_InstanceIndex].color; + + vec3 pos = position + vertexPos * size; + + passPos = vertexPos; + passDir = pos - camera; + passColor = color; + passDensity = smokeDensity(size); + passSmokeIndex = gl_InstanceIndex; + + // transform position into projected view space + gl_Position = mvp * vec4(pos, 1); +} \ 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/shaders/trail.comp b/projects/fire_works/shaders/trail.comp new file mode 100644 index 0000000000000000000000000000000000000000..8e811a5251948e979fcacc0d2c1e95e27411fa87 --- /dev/null +++ b/projects/fire_works/shaders/trail.comp @@ -0,0 +1,98 @@ +#version 450 core +#extension GL_GOOGLE_include_directive : enable +#extension GL_ARB_separate_shader_objects : enable + +layout(local_size_x = 256) in; + +#include "physics.inc" +#include "particle.inc" + +layout(set=0, binding=0, std430) readonly buffer particleBuffer { + particle_t particles []; +}; + +#include "trail.inc" + +layout(set=1, binding=0, std430) coherent buffer trailBuffer { + trail_t trails []; +}; + +#include "point.inc" + +layout(set=1, binding=1, std430) buffer pointBuffer { + point_t points []; +}; + +layout( push_constant ) uniform constants{ + float t; + float dt; +}; + +void main() { + uint id = gl_GlobalInvocationID.x; + + if (id >= trails.length()) { + return; + } + + const uint particleIndex = trails[id].particleIndex; + const uint startIndex = trails[id].startIndex; + const uint endIndex = trails[id].endIndex; + + uint useCount = trails[id].useCount; + float lifetime = trails[id].lifetime; + + if (lifetime > dt) { + lifetime -= dt; + } else { + lifetime = 0.0f; + } + + const uint available = (endIndex - startIndex) % points.length(); + + float trailLife = dt * available; + float fading = 1.0f / (1.0f + friction); + + if (lifetime <= trailLife) { + fading *= (lifetime / trailLife); + + if (useCount > 0) { + useCount--; + } + } else + if (available > useCount) { + useCount++; + } + + for (uint i = useCount; i > 1; i--) { + const uint x = (startIndex + (i - 1)) % points.length(); + const uint y = (startIndex + (i - 2)) % points.length(); + + vec3 position = points[y].position; + float size = points[y].size; + vec3 velocity = points[y].velocity; + + const float scaling = points[y].scaling; + + size = size * fading + scaling * dt; + + points[x].position = position; + points[x].size = size; + points[x].velocity = velocity; + points[x].scaling = scaling; + } + + vec3 position = particles[particleIndex].position; + float size = particles[particleIndex].size; + vec3 velocity = particles[particleIndex].velocity; + + const float trailFactor = mediumDensity / friction; + + points[startIndex].position = position * fading; + points[startIndex].size = trailWidth * size * fading; + points[startIndex].velocity = velocity * fading; + points[startIndex].scaling = trailFactor * length(velocity) * fading; + + trails[id].useCount = useCount; + trails[id].lifetime = lifetime; +} diff --git a/projects/fire_works/shaders/trail.geom b/projects/fire_works/shaders/trail.geom new file mode 100644 index 0000000000000000000000000000000000000000..027943473d05cddc9db6019c7ee36771fcb91d2e --- /dev/null +++ b/projects/fire_works/shaders/trail.geom @@ -0,0 +1,96 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable +#extension GL_GOOGLE_include_directive : enable + +#define INSTANCE_LEN (16) + +layout(points) in; +layout (triangle_strip, max_vertices = (INSTANCE_LEN * 2)) out; +layout(invocations = 8) in; + +#include "physics.inc" +#include "point.inc" + +layout(set=0, binding=1, std430) readonly buffer pointBuffer { + point_t points []; +}; + +layout(location = 0) in vec3 geomColor [1]; +layout(location = 1) in uint geomTrailIndex [1]; +layout(location = 2) in vec3 geomTrailColor [1]; +layout(location = 3) in uint geomStartIndex [1]; +layout(location = 4) in uint geomUseCount [1]; + +layout(location = 0) out vec3 passPos; +layout(location = 1) out vec3 passDir; +layout(location = 2) out vec3 passColor; +layout(location = 3) out float passDensity; +layout(location = 4) out flat int passSmokeIndex; + +layout( push_constant ) uniform constants{ + mat4 mvp; + vec3 camera; +}; + +void main() { + const vec3 color = geomColor[0]; + const uint id = geomTrailIndex[0]; + + const vec3 trailColor = geomTrailColor[0]; + + const uint startIndex = geomStartIndex[0]; + const uint useCount = geomUseCount[0]; + + const uint indexOffset = (gl_InvocationID * (INSTANCE_LEN - 1)); + const uint instanceIndex = startIndex + indexOffset; + + uint count = min(INSTANCE_LEN, useCount); + + if ((indexOffset >= useCount) && (indexOffset + INSTANCE_LEN > useCount)) { + count = indexOffset - useCount; + } + + if (count <= 1) { + return; + } + + const float trailFactor = mediumDensity / friction; + + for (uint i = 0; i < count; i++) { + const float u = float(indexOffset + i + 1) / float(useCount); + + const uint index = (instanceIndex + i) % points.length(); + + const vec3 position = points[index].position; + const float size = points[index].size; + const vec3 velocity = points[index].velocity; + + const vec3 dir = normalize(cross(abs(velocity), position - camera)); + + vec3 offset = dir * size; + float density = trailFactor * (1.0f - u * u) / size; + + const vec3 p0 = position - offset; + const vec3 p1 = position + offset; + + passPos = vec3(u, -1.0f, -1.0f); + passDir = vec3(-0.1f * u, +0.2f, 2.0f); + passColor = mix(color, trailColor, u); + passDensity = density; + passSmokeIndex = int(id); + + gl_Position = mvp * vec4(p0, 1); + EmitVertex(); + + passPos = vec3(u, +1.0f, -1.0f); + passDir = vec3(-0.1f * u, -0.2f, 2.0f); + passColor = mix(color, trailColor, u); + passDensity = density; + passSmokeIndex = int(id); + + gl_Position = mvp * vec4(p1, 1); + EmitVertex(); + } + + EndPrimitive(); +} \ No newline at end of file diff --git a/projects/fire_works/shaders/trail.inc b/projects/fire_works/shaders/trail.inc new file mode 100644 index 0000000000000000000000000000000000000000..75cc49ad4038f28f27890e088fe4dd4e511d1f6b --- /dev/null +++ b/projects/fire_works/shaders/trail.inc @@ -0,0 +1,13 @@ +#ifndef TRAIL_INC +#define TRAIL_INC + +struct trail_t { + uint particleIndex; + uint startIndex; + uint endIndex; + uint useCount; + vec3 color; + float lifetime; +}; + +#endif // TRAIL_INC \ No newline at end of file diff --git a/projects/fire_works/shaders/trail.vert b/projects/fire_works/shaders/trail.vert new file mode 100644 index 0000000000000000000000000000000000000000..871beb9544c33ec790d079a050b4780efbec363f --- /dev/null +++ b/projects/fire_works/shaders/trail.vert @@ -0,0 +1,40 @@ +#version 450 +#extension GL_GOOGLE_include_directive : enable + +#include "trail.inc" + +layout(set=0, binding=0, std430) readonly buffer trailBuffer { + trail_t trails []; +}; + +#include "particle.inc" + +layout(set=2, binding=0, std430) readonly buffer particleBuffer { + particle_t particles []; +}; + +layout(location = 0) out vec3 geomColor; +layout(location = 1) out uint geomTrailIndex; +layout(location = 2) out vec3 geomTrailColor; +layout(location = 3) out uint geomStartIndex; +layout(location = 4) out uint geomUseCount; + +void main() { + const uint particleIndex = trails[gl_InstanceIndex].particleIndex; + const float lifetime = trails[gl_InstanceIndex].lifetime; + + geomColor = particles[particleIndex].color; + geomTrailIndex = gl_InstanceIndex; + geomTrailColor = trails[gl_InstanceIndex].color; + geomStartIndex = trails[gl_InstanceIndex].startIndex; + + const uint useCount = trails[gl_InstanceIndex].useCount; + + if (lifetime > 0.0f) { + geomUseCount = useCount; + } else { + geomUseCount = 0; + } + + gl_Position = vec4(gl_InstanceIndex, lifetime, useCount, 0.0f); +} \ No newline at end of file diff --git a/projects/fire_works/shaders/voxel.comp b/projects/fire_works/shaders/voxel.comp new file mode 100644 index 0000000000000000000000000000000000000000..459c06f381c5c7c9cc26074cd22ecf2869e30350 --- /dev/null +++ b/projects/fire_works/shaders/voxel.comp @@ -0,0 +1,36 @@ +#version 450 core +#extension GL_GOOGLE_include_directive : enable +#extension GL_ARB_separate_shader_objects : enable + +layout(local_size_x = 4, local_size_y = 4, local_size_z = 4) in; + +#include "physics.inc" +#include "voxel.inc" + +layout(set=0, binding=0, r32ui) restrict readonly uniform uimage3D voxelRed; +layout(set=0, binding=1, r32ui) restrict readonly uniform uimage3D voxelGreen; +layout(set=0, binding=2, r32ui) restrict readonly uniform uimage3D voxelBlue; +layout(set=0, binding=3, r32ui) restrict readonly uniform uimage3D voxelDensity; + +layout(set=1, binding=0, rgba16) restrict writeonly uniform image3D voxelImage; + +void main() { + ivec3 pos = ivec3(gl_GlobalInvocationID); + ivec3 size = imageSize(voxelImage); + + if (any(greaterThanEqual(pos, size))) { + return; + } + + const float red = voxel_read(voxelRed, pos); + const float green = voxel_read(voxelGreen, pos); + const float blue = voxel_read(voxelBlue, pos); + const float density = voxel_read(voxelDensity, pos); + + imageStore(voxelImage, pos, vec4( + red, + green, + blue, + density + )); +} diff --git a/projects/fire_works/shaders/voxel.inc b/projects/fire_works/shaders/voxel.inc new file mode 100644 index 0000000000000000000000000000000000000000..9e2c895e4a0c688262de7404039aefe6650191bd --- /dev/null +++ b/projects/fire_works/shaders/voxel.inc @@ -0,0 +1,18 @@ +#ifndef VOXEL_INC +#define VOXEL_INC + +#define VOXEL_NORM_VALUE 0xFF + +#define voxel_add(img, pos, value) imageAtomicAdd(img, ivec3((imageSize(img) - ivec3(1)) * pos), uint(VOXEL_NORM_VALUE * value)) + +#define voxel_write(img, pos, value) imageStore(img, pos, uvec4(VOXEL_NORM_VALUE * value)); +#define voxel_read(img, pos) imageLoad(img, pos).r / float(VOXEL_NORM_VALUE); + +// https://stackoverflow.com/questions/51108596/linearize-depth +float linearize_depth(float d,float zNear,float zFar) { + return zNear * zFar / (zFar + d * (zNear - zFar)); +} + +#define voxel_pos(pos) vec3((pos.xy + vec2(1.0f)) * 0.5f, linearize_depth(pos.y, 0.1f, 50.0f)) + +#endif // VOXEL_INC \ No newline at end of file diff --git a/projects/fire_works/shaders/voxel_particle.comp b/projects/fire_works/shaders/voxel_particle.comp new file mode 100644 index 0000000000000000000000000000000000000000..12b68eed09746f38286a5b1f1b4e9e56d4bfe105 --- /dev/null +++ b/projects/fire_works/shaders/voxel_particle.comp @@ -0,0 +1,59 @@ +#version 450 +#extension GL_GOOGLE_include_directive : enable +#extension GL_ARB_separate_shader_objects : enable + +layout(local_size_x = 256) in; + +#include "physics.inc" +#include "particle.inc" + +layout(set=0, binding=0, std430) readonly buffer particleBuffer { + particle_t particles []; +}; + +#include "voxel.inc" + +layout(set=1, binding=0, r32ui) uniform uimage3D voxelRed; +layout(set=1, binding=1, r32ui) uniform uimage3D voxelGreen; +layout(set=1, binding=2, r32ui) uniform uimage3D voxelBlue; +layout(set=1, binding=3, r32ui) uniform uimage3D voxelDensity; + +layout( push_constant ) uniform constants{ + mat4 mvp; +}; + +void main() { + uint id = gl_GlobalInvocationID.x; + + if (id >= particles.length()) { + return; + } + + vec3 position = particles[id].position; + float lifetime = particles[id].lifetime; + + if (lifetime <= 0.0f) { + return; + } + + vec4 cs_pos = mvp * vec4(position, 1); + + if (abs(cs_pos.w) <= 0.0f) { + return; + } + + vec3 ndc_pos = cs_pos.xyz / cs_pos.w; + vec3 pos = voxel_pos(ndc_pos); + + if ((any(greaterThanEqual(pos, vec3(1.5f)))) || (any(lessThanEqual(pos, vec3(-0.5f))))) { + return; + } + + float size = particles[id].size; + vec3 color = particles[id].color; + + voxel_add(voxelRed, pos, color.r); + voxel_add(voxelGreen, pos, color.g); + voxel_add(voxelBlue, pos, color.b); + voxel_add(voxelDensity, pos, 1.0f); +} diff --git a/projects/fire_works/shaders/voxel_smoke.comp b/projects/fire_works/shaders/voxel_smoke.comp new file mode 100644 index 0000000000000000000000000000000000000000..de216ab29a6671e1547600d072edaf510b775154 --- /dev/null +++ b/projects/fire_works/shaders/voxel_smoke.comp @@ -0,0 +1,72 @@ +#version 450 core +#extension GL_GOOGLE_include_directive : enable +#extension GL_ARB_separate_shader_objects : enable + +layout(local_size_x = 256) in; + +#include "physics.inc" +#include "smoke.inc" + +layout(set=0, binding=0, std430) readonly buffer smokeBuffer { + smoke_t smokes []; +}; + +#include "voxel.inc" + +layout(set=1, binding=0, r32ui) uniform uimage3D voxelRed; +layout(set=1, binding=1, r32ui) uniform uimage3D voxelGreen; +layout(set=1, binding=2, r32ui) uniform uimage3D voxelBlue; +layout(set=1, binding=3, r32ui) uniform uimage3D voxelDensity; + +layout( push_constant ) uniform constants{ + mat4 mvp; +}; + +#define NUM_SMOKE_SAMPLES 4 + +void main() { + uint id = gl_GlobalInvocationID.x; + + if (id >= smokes.length()) { + return; + } + + vec3 position = smokes[id].position; + float size = smokes[id].size; + + const float density = smokeDensity(size); + + if (density <= mediumDensity) { + return; + } + + vec3 offset = vec3(-size); + + for (;offset.x <= size; offset.x += size / NUM_SMOKE_SAMPLES) { + for (;offset.y <= size; offset.y += size / NUM_SMOKE_SAMPLES) { + for (;offset.z <= size; offset.z += size / NUM_SMOKE_SAMPLES) { + vec4 cs_pos = mvp * vec4(position + offset, 1); + + if (abs(cs_pos.w) <= 0.0f) { + return; + } + + vec3 ndc_pos = cs_pos.xyz / cs_pos.w; + vec3 pos = voxel_pos(ndc_pos); + + if ((any(greaterThanEqual(pos, vec3(1.5f)))) || (any(lessThanEqual(pos, vec3(-0.5f))))) { + return; + } + + vec3 color = smokes[id].color; + + float local_density = density * max(1.0f - length(offset / size), 0.0f); + + voxel_add(voxelRed, pos, color.r); + voxel_add(voxelGreen, pos, color.g); + voxel_add(voxelBlue, pos, color.b); + voxel_add(voxelDensity, pos, local_density); + } + } + } +} diff --git a/projects/fire_works/shaders/voxel_trail.comp b/projects/fire_works/shaders/voxel_trail.comp new file mode 100644 index 0000000000000000000000000000000000000000..56971a1719075e0dd9a6a5bb6b52e89c11f8489c --- /dev/null +++ b/projects/fire_works/shaders/voxel_trail.comp @@ -0,0 +1,87 @@ +#version 450 core +#extension GL_GOOGLE_include_directive : enable +#extension GL_ARB_separate_shader_objects : enable + +layout(local_size_x = 256) in; + +#include "physics.inc" + +#include "trail.inc" + +layout(set=0, binding=0, std430) coherent buffer trailBuffer { + trail_t trails []; +}; + +#include "point.inc" + +layout(set=0, binding=1, std430) buffer pointBuffer { + point_t points []; +}; + +#include "voxel.inc" + +layout(set=1, binding=0, r32ui) uniform uimage3D voxelRed; +layout(set=1, binding=1, r32ui) uniform uimage3D voxelGreen; +layout(set=1, binding=2, r32ui) uniform uimage3D voxelBlue; +layout(set=1, binding=3, r32ui) uniform uimage3D voxelDensity; + +#include "smoke.inc" + +layout( push_constant ) uniform constants{ + mat4 mvp; +}; + +void main() { + uint id = gl_GlobalInvocationID.x; + + if (id >= trails.length()) { + return; + } + + const uint particleIndex = trails[id].particleIndex; + const uint startIndex = trails[id].startIndex; + + uint useCount = trails[id].useCount; + + if (useCount <= 0) { + return; + } + + vec3 color = trails[id].color; + float lifetime = trails[id].lifetime; + + if (lifetime <= 0.0f) { + return; + } + + for (uint i = 0; i < useCount; i++) { + const uint x = (startIndex + i) % points.length(); + + vec3 position = points[x].position; + float size = points[x].size; + + const float density = smokeDensity(size); + + if (density <= mediumDensity) { + break; + } + + vec4 cs_pos = mvp * vec4(position, 1); + + if (abs(cs_pos.w) <= 0.0f) { + return; + } + + vec3 ndc_pos = cs_pos.xyz / cs_pos.w; + vec3 pos = voxel_pos(ndc_pos); + + if ((any(greaterThanEqual(pos, vec3(1.5f)))) || (any(lessThanEqual(pos, vec3(-0.5f))))) { + continue; + } + + voxel_add(voxelRed, pos, color.r); + voxel_add(voxelGreen, pos, color.g); + voxel_add(voxelBlue, pos, color.b); + voxel_add(voxelDensity, pos, density); + } +} diff --git a/projects/fire_works/src/main.cpp b/projects/fire_works/src/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d430216f864ac3e7c5e4d4fa1f6339710b64cb93 --- /dev/null +++ b/projects/fire_works/src/main.cpp @@ -0,0 +1,1389 @@ + +#include <array> + +#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 mass; + glm::vec3 pad0; + uint32_t eventId; +}; + +struct event_t { + glm::vec3 direction; + float startTime; + glm::vec3 color; + float velocity; + + uint32_t count; + uint32_t index; + uint32_t parent; + uint32_t continuous; + + float lifetime; + float mass; + float size; + uint32_t contCount; +}; + +struct smoke_t { + glm::vec3 position; + float size; + glm::vec3 velocity; + float scaling; + glm::vec3 color; + float eventID; +}; + +struct trail_t { + uint32_t particleIndex; + uint32_t startIndex; + uint32_t endIndex; + uint32_t useCount; + glm::vec3 color; + float lifetime; +}; + +struct point_t { + glm::vec3 position; + float size; + glm::vec3 velocity; + float scaling; +}; + +struct draw_particles_t { + glm::mat4 mvp; + uint32_t width; + uint32_t height; +}; + +struct draw_smoke_t { + glm::mat4 mvp; + glm::vec3 camera; +}; + +#define PARTICLE_COUNT (1024) +#define SMOKE_COUNT (512) +#define TRAIL_COUNT (2048) +#define RANDOM_DATA_LENGTH (4096) +#define POINT_COUNT (2048 * 256) + +void InitializeParticles(std::vector<particle_t> &particles) { + for (size_t i = 0; i < particles.size(); 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 [i] = particle; + } +} + +void InitializeFireworkEvents(std::vector<event_t>& events) { + events.emplace_back(glm::vec3(0, 1, 0), 0.5f, glm::vec3(0.0f, 1.0f, 0.0f), 12.5f, + + 1, 0, UINT_MAX, 0, + + 1.0f, 1.0f, 0.5f, 0); + + events.emplace_back(glm::vec3(0.0f), 1.5f, glm::vec3(0.0f, 1.0f, 1.0f), 10.0f, + + 100, 0, events.size() - 1, 0, + + 10.0f, 1.0f, 0.0f, 0); + + events.emplace_back(glm::vec3(0.5, 1, 0), 0.25f, glm::vec3(0.0f, 1.5f, 0.0f), 15.0f, + + 1, 0, UINT_MAX, 0, + + 0.5f, 1.0f, 0.5f, 0); + + events.emplace_back(glm::vec3(0.0f), 0.75f, glm::vec3(0.0f, 1.5f, 1.0f), 8.0f, + + 150, 0, events.size() - 1, 0, + + 10.0f, 1.0f, 0.0f, 0); + + events.emplace_back(glm::vec3(-2.5, 3, 0.5), 1.0f, glm::vec3(246.0f, 189.0f, 255.0f), 12.5f, + + 1, 0, UINT_MAX, 0, + + 1.0f, 1.0f, 0.5f, 0); + + events.emplace_back(glm::vec3(0.0f), 2.0f, glm::vec3(235.0f, 137.0f, 250.0f), 8.0f, + + 75, 0, events.size() - 1, 0, + + 10.0f, 1.0f, 0.0f, 0); +} + +void InitializeSparklerEvents(std::vector<event_t> &events) { + events.emplace_back(glm::vec3(0, 1, 0), 0.0f, glm::vec3(251.0f, 255.0f, 145.0f), 1.0f, + + 1, 0, UINT_MAX, 0, + + 8.0f, 0.0f, 0.5f, 0); + + events.emplace_back(glm::vec3(0.0f), 0.0f, glm::vec3(251.0f, 255.0f, 145.0f), 10.0f, + + 1000, 1, events.size() - 1, 10, + + 0.5f, -1.0f, 0.0f, 100); +} + +void InitializeNestedFireworkEvents(std::vector<event_t>& events) { + events.emplace_back(glm::vec3(0, 2, 0), 0.0f, glm::vec3(0.0f, 1.0f, 0.0f), 12.5f, + + 1, 0, UINT_MAX, 0, + + 1.0f, 1.0f, 0.5f, 0); + + events.emplace_back(glm::vec3(0.0f), 0.9f, glm::vec3(0.0f, 1.0f, 1.0f), 7.0f, + + 100, 0, events.size() - 1, 0, + + 10.1f, 1.0f, 0.0f, 0); + + events.emplace_back(glm::vec3(0.0f), 2.0f, glm::vec3(0.0f, 0.0f, 0.0f), 10.0f, + + 100, 0, events.size() - 1, 0, + + 10.0f, 1.0f, 0.0f, 0); + + events.emplace_back(glm::vec3(0.0f), 1.0f, glm::vec3(42.0f,0.0f, 1.0f), 12.5f, + + 100, 0, events.size() - 2, 0, + + 1.0f, 1.0f, 0.5f, 0); + + events.emplace_back(glm::vec3(0.0f), 1.5f, glm::vec3(42.0f, 0.0f, 1.0f), 10.0f, + + 100, 0, events.size() - 1, 0, + + 10.0f, 1.0f, 0.0f, 0); + + events.emplace_back(glm::vec3(0.0f), 2.0f, glm::vec3(42.0f, 0.0f, 1.0f), 10.0f, + + 100, 0, events.size() - 1, 0, + + 10.0f, 1.0f, 0.0f, 0); +} + +void ChangeColor(std::vector<event_t>& events, glm::vec3 color) { + for (int i = 0; i < events.size(); i++) { + events [i].color = color; + } +} + +int main(int argc, const char **argv) { + vkcv::Features features; + + features.requireExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME); + + vkcv::Core core = vkcv::Core::create( + "Firework", + VK_MAKE_VERSION(0, 0, 1), + {vk::QueueFlagBits::eTransfer, vk::QueueFlagBits::eGraphics, vk::QueueFlagBits::eCompute}, + features + ); + + 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 + uint32_t pilotIdx = cameraManager.addCamera(vkcv::camera::ControllerType::PILOT); + + cameraManager.getCamera(trackballIdx).setNearFar(0.1f, 50.0f); + cameraManager.getCamera(trackballIdx).setPosition(glm::vec3(0, 0, -25)); + + cameraManager.getCamera(pilotIdx).setNearFar(0.1f, 50.0f); + cameraManager.getCamera(pilotIdx).setPosition(glm::vec3(0, 0, 25)); + + cameraManager.setActiveCamera(pilotIdx); + + vkcv::gui::GUI gui (core, windowHandle); + vkcv::shader::GLSLCompiler compiler; + + vkcv::DescriptorBindings descriptorBindings0; + vkcv::DescriptorBinding binding0 { + 0, + vkcv::DescriptorType::STORAGE_BUFFER, + 1, + vkcv::ShaderStage::VERTEX | vkcv::ShaderStage::COMPUTE, + false, + false + }; + vkcv::DescriptorBinding binding1 { + 1, + vkcv::DescriptorType::STORAGE_BUFFER, + + 1, + vkcv::ShaderStage::COMPUTE, + false, + false + }; + + descriptorBindings0.insert(std::make_pair(0, binding0)); + descriptorBindings0.insert(std::make_pair(1, binding1)); + + vkcv::DescriptorSetLayoutHandle descriptorSetLayout = core.createDescriptorSetLayout(descriptorBindings0); + vkcv::DescriptorSetHandle descriptorSet = core.createDescriptorSet(descriptorSetLayout); + + vkcv::ShaderProgram generationShader; + compiler.compile(vkcv::ShaderStage::COMPUTE, "shaders/generation.comp", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + generationShader.addShader(shaderStage, path); + }); + + auto generationBindings = generationShader.getReflectedDescriptors().at(1); + generationBindings[0].shaderStages |= vkcv::ShaderStage::FRAGMENT; + + vkcv::DescriptorSetLayoutHandle generationDescriptorLayout = core.createDescriptorSetLayout( + generationBindings + ); + + vkcv::DescriptorSetHandle generationDescriptorSet = core.createDescriptorSet(generationDescriptorLayout); + + vkcv::DescriptorBindings descriptorBindings1; + + descriptorBindings1.insert(std::make_pair(0, binding0)); + descriptorBindings1.insert(std::make_pair(1, binding1)); + + vkcv::DescriptorSetLayoutHandle smokeDescriptorLayout = core.createDescriptorSetLayout(descriptorBindings1); + vkcv::DescriptorSetHandle smokeDescriptorSet = core.createDescriptorSet(smokeDescriptorLayout); + + vkcv::DescriptorBindings descriptorBindings2; + vkcv::DescriptorBinding binding2 { + 1, + vkcv::DescriptorType::STORAGE_BUFFER, + 1, + vkcv::ShaderStage::GEOMETRY | vkcv::ShaderStage::COMPUTE, + false, + false + }; + + descriptorBindings2.insert(std::make_pair(0, binding0)); + descriptorBindings2.insert(std::make_pair(1, binding2)); + + vkcv::DescriptorSetLayoutHandle trailDescriptorLayout = core.createDescriptorSetLayout( + descriptorBindings2 + ); + + vkcv::DescriptorSetHandle trailDescriptorSet = core.createDescriptorSet(trailDescriptorLayout); + + vkcv::ComputePipelineHandle generationPipeline = core.createComputePipeline({ + generationShader, + { + descriptorSetLayout, + generationDescriptorLayout, + smokeDescriptorLayout, + trailDescriptorLayout + } + }); + + vkcv::ShaderProgram trailShader; + compiler.compile(vkcv::ShaderStage::COMPUTE, "shaders/trail.comp", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + trailShader.addShader(shaderStage, path); + }); + + vkcv::ComputePipelineHandle trailComputePipeline = core.createComputePipeline({ + trailShader, + { descriptorSetLayout, trailDescriptorLayout } + }); + + vkcv::ShaderProgram scaleShader; + compiler.compile(vkcv::ShaderStage::COMPUTE, "shaders/scale.comp", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + scaleShader.addShader(shaderStage, path); + }); + + vkcv::ComputePipelineHandle scalePipeline = core.createComputePipeline({ + scaleShader, + { smokeDescriptorLayout } + }); + + auto swapchainExtent = core.getSwapchain(windowHandle).getExtent(); + + const vk::Format colorFormat = vk::Format::eR16G16B16A16Sfloat; + + std::array<vkcv::ImageHandle, 4> colorBuffers; + for (size_t i = 0; i < colorBuffers.size(); i++) { + colorBuffers[i] = core.createImage( + colorFormat, + swapchainExtent.width, + swapchainExtent.height, + 1, false, true, true + ).getHandle(); + } + + 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::ShaderProgram trailShaderProgram; + compiler.compile(vkcv::ShaderStage::VERTEX, "shaders/trail.vert", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + trailShaderProgram.addShader(shaderStage, path); + }); + compiler.compile(vkcv::ShaderStage::GEOMETRY, "shaders/trail.geom", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + trailShaderProgram.addShader(shaderStage, path); + }); + + vkcv::ShaderProgram smokeShaderProgram; + compiler.compile(vkcv::ShaderStage::VERTEX, "shaders/smoke.vert", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + smokeShaderProgram.addShader(shaderStage, path); + }); + compiler.compile(vkcv::ShaderStage::FRAGMENT, "shaders/smoke.frag", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + smokeShaderProgram.addShader(shaderStage, path); + trailShaderProgram.addShader(shaderStage, path); + }); + + std::vector<particle_t> particles; + particles.resize(PARTICLE_COUNT); + InitializeParticles(particles); + + vkcv::Buffer<particle_t> particleBuffer = core.createBuffer<particle_t>( + vkcv::BufferType::STORAGE, + particles.size(), + vkcv::BufferMemoryType::DEVICE_LOCAL, + false, + true + ); + + particleBuffer.fill(particles); + + vkcv::Buffer<particle_t> particleBufferCopy = + core.createBuffer<particle_t>(vkcv::BufferType::STORAGE, particles.size()); + + particleBufferCopy.fill(particles); + + { + vkcv::DescriptorWrites writes; + writes.writeStorageBuffer(0, particleBuffer.getHandle()); + writes.writeStorageBuffer(1, particleBufferCopy.getHandle()); + core.writeDescriptorSet(descriptorSet, writes); + } + + std::vector<float> randomData; + randomData.reserve(RANDOM_DATA_LENGTH); + + for (size_t i = 0; i < RANDOM_DATA_LENGTH; i++) { + randomData.push_back( + 2.0f * static_cast<float>(std::rand() % RAND_MAX) / static_cast<float>(RAND_MAX) - 1.0f + ); + } + + vkcv::Buffer<float> randomBuffer = core.createBuffer<float>( + vkcv::BufferType::STORAGE, + randomData.size() + ); + + randomBuffer.fill(randomData); + + std::vector<event_t> events; + InitializeFireworkEvents(events); + + vkcv::Buffer<event_t> eventBuffer = core.createBuffer<event_t>( + vkcv::BufferType::STORAGE, + events.size() + ); + + eventBuffer.fill(events); + + { + vkcv::DescriptorWrites writes; + writes.writeStorageBuffer(0, randomBuffer.getHandle()); + writes.writeStorageBuffer(1, eventBuffer.getHandle()); + core.writeDescriptorSet(generationDescriptorSet, writes); + } + + vkcv::Buffer<uint32_t> startIndexBuffer = + core.createBuffer<uint32_t>(vkcv::BufferType::STORAGE, eventBuffer.getCount()); + + { + vkcv::DescriptorWrites writes; + writes.writeStorageBuffer(2, startIndexBuffer.getHandle()); + core.writeDescriptorSet(generationDescriptorSet, writes); + } + + std::vector<smoke_t> smokes; + smokes.reserve(SMOKE_COUNT); + + for (size_t i = 0; i < SMOKE_COUNT; i++) { + smoke_t smoke; + smoke.position = glm::vec3(0.0f); + smoke.size = 0.0f; + + smoke.velocity = glm::vec3(0.0f); + smoke.scaling = 0.0f; + + smoke.color = glm::vec3(0.0f); + + smokes.push_back(smoke); + } + + vkcv::Buffer<smoke_t> smokeBuffer = core.createBuffer<smoke_t>( + vkcv::BufferType::STORAGE, + smokes.size() + ); + + smokeBuffer.fill(smokes); + + vkcv::Buffer<uint32_t> smokeIndexBuffer = core.createBuffer<uint32_t>( + vkcv::BufferType::STORAGE, 3, vkcv::BufferMemoryType::HOST_VISIBLE + ); + + uint32_t* smokeIndices = smokeIndexBuffer.map(); + memset(smokeIndices, 0, smokeIndexBuffer.getSize()); + + { + vkcv::DescriptorWrites writes; + writes.writeStorageBuffer(0, smokeBuffer.getHandle()); + writes.writeStorageBuffer(1, smokeIndexBuffer.getHandle()); + core.writeDescriptorSet(smokeDescriptorSet, writes); + } + + std::vector<trail_t> trails; + trails.reserve(TRAIL_COUNT); + + for (size_t i = 0; i < TRAIL_COUNT; i++) { + trail_t trail; + + trail.particleIndex = 0; + trail.startIndex = 0; + trail.endIndex = 0; + trail.useCount = 0; + trail.color = glm::vec3(0.0f); + trail.lifetime = 0.0f; + + trails.push_back(trail); + } + + vkcv::Buffer<trail_t> trailBuffer = core.createBuffer<trail_t>( + vkcv::BufferType::STORAGE, + trails.size() + ); + + trailBuffer.fill(trails); + + std::vector<point_t> points; + points.reserve(POINT_COUNT); + + for (size_t i = 0; i < POINT_COUNT; i++) { + point_t point; + + point.position = glm::vec3(0.0f); + point.size = 0.0f; + point.velocity = glm::vec3(0.0f); + point.scaling = 0.0f; + + points.push_back(point); + } + + vkcv::Buffer<point_t> pointBuffer = core.createBuffer<point_t>( + vkcv::BufferType::STORAGE, + points.size() + ); + + pointBuffer.fill(points); + + { + vkcv::DescriptorWrites writes; + writes.writeStorageBuffer(0, trailBuffer.getHandle()); + writes.writeStorageBuffer(1, pointBuffer.getHandle()); + core.writeDescriptorSet(trailDescriptorSet, writes); + } + + vkcv::Buffer<glm::vec3> cubePositions = core.createBuffer<glm::vec3>(vkcv::BufferType::VERTEX, 8); + cubePositions.fill({ + glm::vec3(-1.0f, -1.0f, -1.0f), + glm::vec3(+1.0f, -1.0f, -1.0f), + glm::vec3(-1.0f, +1.0f, -1.0f), + glm::vec3(+1.0f, +1.0f, -1.0f), + glm::vec3(-1.0f, -1.0f, +1.0f), + glm::vec3(+1.0f, -1.0f, +1.0f), + glm::vec3(-1.0f, +1.0f, +1.0f), + glm::vec3(+1.0f, +1.0f, +1.0f) + }); + + vkcv::Buffer<uint16_t> cubeIndices = core.createBuffer<uint16_t>(vkcv::BufferType::INDEX, 36); + cubeIndices.fill({ + 0, 2, 3, + 0, 3, 1, + 1, 3, 7, + 1, 7, 5, + + 5, 7, 6, + 5, 6, 4, + 4, 6, 2, + 4, 2, 0, + + 2, 6, 7, + 2, 7, 3, + 1, 5, 4, + 1, 4, 0 + }); + + vkcv::Mesh cubeMesh ( + { vkcv::VertexBufferBinding(0, cubePositions.getVulkanHandle()) }, + cubeIndices.getVulkanHandle(), + cubeIndices.getCount() + ); + + const std::vector<vkcv::VertexAttachment> vaSmoke = smokeShaderProgram.getVertexAttachments(); + + std::vector<vkcv::VertexBinding> vbSmoke; + for (size_t i = 0; i < vaSmoke.size(); i++) { + vbSmoke.push_back(vkcv::createVertexBinding(i, { vaSmoke[i] })); + } + + const vkcv::VertexLayout smokeLayout { vbSmoke }; + + vkcv::PassHandle renderPass = core.createPass(vkcv::PassConfig( + { + vkcv::AttachmentDescription( + vkcv::AttachmentOperation::STORE, + vkcv::AttachmentOperation::CLEAR, + colorFormat + ) + }, + vkcv::Multisampling::None + )); + + vkcv::GraphicsPipelineConfig smokePipelineDefinition{ + smokeShaderProgram, + UINT32_MAX, + UINT32_MAX, + renderPass, + {smokeLayout}, + {smokeDescriptorLayout, generationDescriptorLayout}, + true + }; + + smokePipelineDefinition.m_blendMode = vkcv::BlendMode::Additive; + + vkcv::GraphicsPipelineHandle smokePipeline = core.createGraphicsPipeline(smokePipelineDefinition); + + const std::vector<vkcv::VertexAttachment> vaTrail = trailShaderProgram.getVertexAttachments(); + + std::vector<vkcv::VertexBinding> vbTrail; + for (size_t i = 0; i < vaTrail.size(); i++) { + vbTrail.push_back(vkcv::createVertexBinding(i, { vaTrail[i] })); + } + + const vkcv::VertexLayout trailLayout { vbTrail }; + + vkcv::GraphicsPipelineConfig trailPipelineDefinition{ + trailShaderProgram, + UINT32_MAX, + UINT32_MAX, + renderPass, + {trailLayout}, + {trailDescriptorLayout, generationDescriptorLayout, descriptorSetLayout}, + true + }; + + trailPipelineDefinition.m_PrimitiveTopology = vkcv::PrimitiveTopology::PointList; + trailPipelineDefinition.m_blendMode = vkcv::BlendMode::Additive; + + vkcv::GraphicsPipelineHandle trailPipeline = core.createGraphicsPipeline(trailPipelineDefinition); + + std::vector<vkcv::DrawcallInfo> drawcallsSmokes; + + drawcallsSmokes.push_back(vkcv::DrawcallInfo( + cubeMesh, + { + vkcv::DescriptorSetUsage(0, smokeDescriptorSet), + vkcv::DescriptorSetUsage(1, generationDescriptorSet), + }, + smokeBuffer.getCount() + )); + + 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() + ); + + vkcv::Mesh trailMesh ( + {}, + triangleIndices.getVulkanHandle(), + 1 + ); + + std::vector<vkcv::DrawcallInfo> drawcallsTrails; + + drawcallsTrails.push_back(vkcv::DrawcallInfo( + trailMesh, + { + vkcv::DescriptorSetUsage(0, trailDescriptorSet), + vkcv::DescriptorSetUsage(1, generationDescriptorSet), + vkcv::DescriptorSetUsage(2, descriptorSet) + }, + trailBuffer.getCount() + )); + + const std::vector<vkcv::VertexAttachment> vaParticles = particleShaderProgram.getVertexAttachments(); + + std::vector<vkcv::VertexBinding> vbParticles; + for (size_t i = 0; i < vaParticles.size(); i++) { + vbParticles.push_back(vkcv::createVertexBinding(i, { vaParticles[i] })); + } + + const vkcv::VertexLayout particleLayout { vbParticles }; + + vkcv::GraphicsPipelineConfig particlePipelineDefinition{ + particleShaderProgram, + UINT32_MAX, + UINT32_MAX, + renderPass, + {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 } + }); + + const uint32_t voxelWidth = 160; + const uint32_t voxelHeight = 90; + const uint32_t voxelDepth = 64; + + std::vector<uint32_t> zeroVoxel; + zeroVoxel.resize(voxelWidth * voxelHeight * voxelDepth, 0); + + vkcv::Image voxelRed = core.createImage( + vk::Format::eR32Uint, + voxelWidth, + voxelHeight, + voxelDepth, + false, true + ); + + vkcv::Image voxelGreen = core.createImage( + vk::Format::eR32Uint, + voxelWidth, + voxelHeight, + voxelDepth, + false, true + ); + + vkcv::Image voxelBlue = core.createImage( + vk::Format::eR32Uint, + voxelWidth, + voxelHeight, + voxelDepth, + false, true + ); + + vkcv::Image voxelDensity = core.createImage( + vk::Format::eR32Uint, + voxelWidth, + voxelHeight, + voxelDepth, + false, true + ); + + std::array<vkcv::ImageHandle, 2> voxelData { + core.createImage( + vk::Format::eR16G16B16A16Sfloat, + voxelWidth, + voxelHeight, + voxelDepth, + false, true + ).getHandle(), + core.createImage( + vk::Format::eR16G16B16A16Sfloat, + voxelWidth, + voxelHeight, + voxelDepth, + false, true + ).getHandle() + }; + + vkcv::Image voxelSamples = core.createImage( + colorFormat, + voxelWidth, + voxelHeight, + 1, false, true + ); + + vkcv::SamplerHandle voxelSampler = core.createSampler( + vkcv::SamplerFilterType::LINEAR, + vkcv::SamplerFilterType::LINEAR, + vkcv::SamplerMipmapMode::LINEAR, + vkcv::SamplerAddressMode::CLAMP_TO_EDGE + ); + + vkcv::ShaderProgram voxelClearShader; + compiler.compile(vkcv::ShaderStage::COMPUTE, "shaders/clear.comp", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + voxelClearShader.addShader(shaderStage, path); + }); + + const auto& voxelBindings = voxelClearShader.getReflectedDescriptors().at(0); + auto voxelDescriptorSetLayout = core.createDescriptorSetLayout(voxelBindings); + + vkcv::ComputePipelineHandle voxelClearPipeline = core.createComputePipeline({ + voxelClearShader, + { voxelDescriptorSetLayout } + }); + + vkcv::ShaderProgram voxelParticleShader; + compiler.compile(vkcv::ShaderStage::COMPUTE, "shaders/voxel_particle.comp", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + voxelParticleShader.addShader(shaderStage, path); + }); + + vkcv::ComputePipelineHandle voxelParticlePipeline = core.createComputePipeline({ + voxelParticleShader, + { descriptorSetLayout, voxelDescriptorSetLayout } + }); + + vkcv::ShaderProgram voxelSmokeShader; + compiler.compile(vkcv::ShaderStage::COMPUTE, "shaders/voxel_smoke.comp", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + voxelSmokeShader.addShader(shaderStage, path); + }); + + vkcv::ComputePipelineHandle voxelSmokePipeline = core.createComputePipeline({ + voxelSmokeShader, + { smokeDescriptorLayout, voxelDescriptorSetLayout } + }); + + vkcv::ShaderProgram voxelTrailShader; + compiler.compile(vkcv::ShaderStage::COMPUTE, "shaders/voxel_trail.comp", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + voxelTrailShader.addShader(shaderStage, path); + }); + + vkcv::ComputePipelineHandle voxelTrailPipeline = core.createComputePipeline({ + voxelTrailShader, + { trailDescriptorLayout, voxelDescriptorSetLayout } + }); + + vkcv::ShaderProgram voxelShader; + compiler.compile(vkcv::ShaderStage::COMPUTE, "shaders/voxel.comp", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + voxelShader.addShader(shaderStage, path); + }); + + const auto& voxelOutBindings = voxelShader.getReflectedDescriptors().at(1); + auto voxelOutDescriptorSetLayout = core.createDescriptorSetLayout(voxelOutBindings); + + vkcv::ComputePipelineHandle voxelPipeline = core.createComputePipeline({ + voxelShader, + { voxelDescriptorSetLayout, voxelOutDescriptorSetLayout } + }); + + vkcv::ShaderProgram fluidShader; + compiler.compile(vkcv::ShaderStage::COMPUTE, "shaders/fluid.comp", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + fluidShader.addShader(shaderStage, path); + }); + + const auto& fluidBindings = fluidShader.getReflectedDescriptors().at(0); + auto fluidDescriptorSetLayout = core.createDescriptorSetLayout(fluidBindings); + + vkcv::ComputePipelineHandle fluidPipeline = core.createComputePipeline({ + fluidShader, + { fluidDescriptorSetLayout } + }); + + vkcv::ShaderProgram voxelSampleShader; + compiler.compile(vkcv::ShaderStage::COMPUTE, "shaders/sample.comp", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + voxelSampleShader.addShader(shaderStage, path); + }); + + const auto& sampleBindings = voxelSampleShader.getReflectedDescriptors().at(1); + auto samplesDescriptorSetLayout = core.createDescriptorSetLayout(sampleBindings); + + vkcv::ComputePipelineHandle voxelSamplePipeline = core.createComputePipeline({ + voxelSampleShader, + { voxelOutDescriptorSetLayout, samplesDescriptorSetLayout } + }); + + auto voxelDescriptorSet = core.createDescriptorSet(voxelDescriptorSetLayout); + + { + vkcv::DescriptorWrites writes; + writes.writeStorageImage(0, voxelRed.getHandle()); + writes.writeStorageImage(1, voxelGreen.getHandle()); + writes.writeStorageImage(2, voxelBlue.getHandle()); + writes.writeStorageImage(3, voxelDensity.getHandle()); + core.writeDescriptorSet(voxelDescriptorSet, writes); + } + + auto voxelOutDescriptorSet = core.createDescriptorSet(voxelOutDescriptorSetLayout); + + { + vkcv::DescriptorWrites writes; + writes.writeStorageImage(0, voxelData[0]); + core.writeDescriptorSet(voxelOutDescriptorSet, writes); + } + + std::array<vkcv::DescriptorSetHandle, 2> fluidDescriptorSet { + core.createDescriptorSet(fluidDescriptorSetLayout), + core.createDescriptorSet(fluidDescriptorSetLayout) + }; + + { + vkcv::DescriptorWrites writes; + writes.writeSampledImage(0, voxelData[0]); + writes.writeSampler(1, voxelSampler); + writes.writeStorageImage(2, voxelData[1]); + core.writeDescriptorSet(fluidDescriptorSet[0], writes); + } + + { + vkcv::DescriptorWrites writes; + writes.writeSampledImage(0, voxelData[1]); + writes.writeSampler(1, voxelSampler); + writes.writeStorageImage(2, voxelData[0]); + core.writeDescriptorSet(fluidDescriptorSet[1], writes); + } + + auto samplesDescriptorSet = core.createDescriptorSet(samplesDescriptorSetLayout); + + { + vkcv::DescriptorWrites writes; + writes.writeStorageImage(0, voxelSamples.getHandle()); + core.writeDescriptorSet(samplesDescriptorSet, writes); + } + + vkcv::effects::BloomAndFlaresEffect bloomAndFlares (core); + bloomAndFlares.setUpsamplingLimit(3); + + vkcv::ShaderProgram addShader; + compiler.compile(vkcv::ShaderStage::COMPUTE, "shaders/add.comp", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + addShader.addShader(shaderStage, path); + }); + + vkcv::DescriptorSetLayoutHandle addDescriptorLayout = core.createDescriptorSetLayout(addShader.getReflectedDescriptors().at(0)); + vkcv::DescriptorSetHandle addDescriptor = core.createDescriptorSet(addDescriptorLayout); + + vkcv::ComputePipelineHandle addPipe = core.createComputePipeline({ + addShader, + { addDescriptorLayout, generationDescriptorLayout } + }); + + 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; + } + + for (size_t i = 0; i < colorBuffers.size(); i++) { + if ((core.getImageWidth(colorBuffers[i]) != swapchainWidth) || + (core.getImageHeight(colorBuffers[i]) != swapchainHeight)) { + colorBuffers[i] = core.createImage( + colorFormat, + swapchainWidth, + swapchainHeight, + 1, false, true, true + ).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; + + float time_values [2]; + time_values[0] = 0.000001f * static_cast<float>(time.count()); + time_values[1] = 0.000001f * static_cast<float>(deltatime.count()); + + std::cout << time_values[0] << " " << time_values[1] << std::endl; + + auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics); + + uint32_t voxelDispatchCount[3]; + voxelDispatchCount[0] = std::ceil(voxelWidth / 4.f); + voxelDispatchCount[1] = std::ceil(voxelHeight / 4.f); + voxelDispatchCount[2] = std::ceil(voxelDepth / 4.f); + + core.recordBeginDebugLabel(cmdStream, "Voxel clear", { 0.5f, 0.25f, 0.8f, 1.0f }); + core.prepareImageForStorage(cmdStream, voxelRed.getHandle()); + core.prepareImageForStorage(cmdStream, voxelGreen.getHandle()); + core.prepareImageForStorage(cmdStream, voxelBlue.getHandle()); + core.prepareImageForStorage(cmdStream, voxelDensity.getHandle()); + + core.recordComputeDispatchToCmdStream( + cmdStream, + voxelClearPipeline, + voxelDispatchCount, + { vkcv::DescriptorSetUsage(0, voxelDescriptorSet) }, + vkcv::PushConstants(0) + ); + core.recordEndDebugLabel(cmdStream); + + core.recordBufferMemoryBarrier(cmdStream, eventBuffer.getHandle()); + core.recordBufferMemoryBarrier(cmdStream, smokeBuffer.getHandle()); + core.recordBufferMemoryBarrier(cmdStream, smokeIndexBuffer.getHandle()); + core.recordBufferMemoryBarrier(cmdStream, particleBuffer.getHandle()); + core.recordBufferMemoryBarrier(cmdStream, trailBuffer.getHandle()); + core.recordBufferMemoryBarrier(cmdStream, pointBuffer.getHandle()); + + uint32_t particleDispatchCount[3]; + particleDispatchCount[0] = std::ceil(particleBuffer.getCount() / 256.f); + particleDispatchCount[1] = 1; + particleDispatchCount[2] = 1; + + vkcv::PushConstants pushConstantsTime (2 * sizeof(float)); + pushConstantsTime.appendDrawcall(time_values); + + core.recordBeginDebugLabel(cmdStream, "Generation", { 0.0f, 0.0f, 1.0f, 1.0f }); + core.recordComputeDispatchToCmdStream( + cmdStream, + generationPipeline, + particleDispatchCount, + { + vkcv::DescriptorSetUsage(0, descriptorSet), + vkcv::DescriptorSetUsage(1, generationDescriptorSet), + vkcv::DescriptorSetUsage(2, smokeDescriptorSet), + vkcv::DescriptorSetUsage(3, trailDescriptorSet) + }, + pushConstantsTime + ); + core.recordEndDebugLabel(cmdStream); + + core.recordBufferMemoryBarrier(cmdStream, smokeBuffer.getHandle()); + + uint32_t smokeDispatchCount[3]; + smokeDispatchCount[0] = std::ceil(smokeBuffer.getCount() / 256.f); + smokeDispatchCount[1] = 1; + smokeDispatchCount[2] = 1; + + core.recordBeginDebugLabel(cmdStream, "Smoke scaling", { 0.0f, 0.0f, 1.0f, 1.0f }); + core.recordComputeDispatchToCmdStream( + cmdStream, + scalePipeline, + smokeDispatchCount, + { vkcv::DescriptorSetUsage(0, smokeDescriptorSet) }, + pushConstantsTime + ); + core.recordEndDebugLabel(cmdStream); + + core.recordBufferMemoryBarrier(cmdStream, particleBuffer.getHandle()); + + core.recordBeginDebugLabel(cmdStream, "Particle motion", { 0.0f, 0.0f, 1.0f, 1.0f }); + core.recordComputeDispatchToCmdStream( + cmdStream, + motionPipeline, + particleDispatchCount, + { vkcv::DescriptorSetUsage(0, descriptorSet) }, + pushConstantsTime + ); + core.recordEndDebugLabel(cmdStream); + + core.recordBufferMemoryBarrier(cmdStream, particleBuffer.getHandle()); + core.recordBufferMemoryBarrier(cmdStream, trailBuffer.getHandle()); + core.recordBufferMemoryBarrier(cmdStream, pointBuffer.getHandle()); + + uint32_t trailDispatchCount[3]; + trailDispatchCount[0] = std::ceil(trailBuffer.getCount() / 256.f); + trailDispatchCount[1] = 1; + trailDispatchCount[2] = 1; + + core.recordBeginDebugLabel(cmdStream, "Trail update", { 0.0f, 0.0f, 1.0f, 1.0f }); + core.recordComputeDispatchToCmdStream( + cmdStream, + trailComputePipeline, + trailDispatchCount, + { + vkcv::DescriptorSetUsage(0, descriptorSet), + vkcv::DescriptorSetUsage(1, trailDescriptorSet) + }, + pushConstantsTime + ); + core.recordEndDebugLabel(cmdStream); + + cameraManager.update(time_values[1]); + + const auto& camera = cameraManager.getActiveCamera(); + + core.recordBufferMemoryBarrier(cmdStream, particleBuffer.getHandle()); + + draw_particles_t draw_particles { + camera.getMVP(), + swapchainWidth, + swapchainHeight + }; + + vkcv::PushConstants pushConstantsDraw0 (sizeof(draw_particles_t)); + pushConstantsDraw0.appendDrawcall(draw_particles); + + core.recordBeginDebugLabel(cmdStream, "Draw particles", { 1.0f, 0.0f, 1.0f, 1.0f }); + core.recordDrawcallsToCmdStream( + cmdStream, + renderPass, + particlePipeline, + pushConstantsDraw0, + { drawcallsParticles }, + { colorBuffers[0] }, + windowHandle + ); + core.recordEndDebugLabel(cmdStream); + + vkcv::PushConstants pushConstantsVoxel (sizeof(glm::mat4)); + pushConstantsVoxel.appendDrawcall(camera.getMVP()); + + core.recordBeginDebugLabel(cmdStream, "Particle voxel update", { 1.0f, 0.5f, 0.8f, 1.0f }); + core.prepareImageForStorage(cmdStream, voxelRed.getHandle()); + core.prepareImageForStorage(cmdStream, voxelGreen.getHandle()); + core.prepareImageForStorage(cmdStream, voxelBlue.getHandle()); + core.prepareImageForStorage(cmdStream, voxelDensity.getHandle()); + + core.recordComputeDispatchToCmdStream( + cmdStream, + voxelParticlePipeline, + particleDispatchCount, + { + vkcv::DescriptorSetUsage(0, descriptorSet), + vkcv::DescriptorSetUsage(1, voxelDescriptorSet) + }, + pushConstantsVoxel + ); + core.recordEndDebugLabel(cmdStream); + + core.recordBufferMemoryBarrier(cmdStream, smokeBuffer.getHandle()); + + draw_smoke_t draw_smoke { + camera.getMVP(), + camera.getPosition() + }; + + core.recordBeginDebugLabel(cmdStream, "Draw smoke", { 1.0f, 0.5f, 1.0f, 1.0f }); + vkcv::PushConstants pushConstantsDraw1 (sizeof(draw_smoke_t)); + pushConstantsDraw1.appendDrawcall(draw_smoke); + + core.recordDrawcallsToCmdStream( + cmdStream, + renderPass, + smokePipeline, + pushConstantsDraw1, + { drawcallsSmokes }, + { colorBuffers[1] }, + windowHandle + ); + core.recordEndDebugLabel(cmdStream); + + core.recordBeginDebugLabel(cmdStream, "Smoke voxel update", { 1.0f, 0.7f, 0.8f, 1.0f }); + core.prepareImageForStorage(cmdStream, voxelRed.getHandle()); + core.prepareImageForStorage(cmdStream, voxelGreen.getHandle()); + core.prepareImageForStorage(cmdStream, voxelBlue.getHandle()); + core.prepareImageForStorage(cmdStream, voxelDensity.getHandle()); + + core.recordComputeDispatchToCmdStream( + cmdStream, + voxelSmokePipeline, + smokeDispatchCount, + { + vkcv::DescriptorSetUsage(0, smokeDescriptorSet), + vkcv::DescriptorSetUsage(1, voxelDescriptorSet) + }, + pushConstantsVoxel + ); + core.recordEndDebugLabel(cmdStream); + + core.recordBufferMemoryBarrier(cmdStream, trailBuffer.getHandle()); + core.recordBufferMemoryBarrier(cmdStream, pointBuffer.getHandle()); + + core.recordBeginDebugLabel(cmdStream, "Draw trails", { 0.75f, 0.5f, 1.0f, 1.0f }); + core.recordDrawcallsToCmdStream( + cmdStream, + renderPass, + trailPipeline, + pushConstantsDraw1, + { drawcallsTrails }, + { colorBuffers[2] }, + windowHandle + ); + core.recordEndDebugLabel(cmdStream); + + core.recordBeginDebugLabel(cmdStream, "Trail voxel update", { 1.0f, 0.9f, 0.8f, 1.0f }); + core.prepareImageForStorage(cmdStream, voxelRed.getHandle()); + core.prepareImageForStorage(cmdStream, voxelGreen.getHandle()); + core.prepareImageForStorage(cmdStream, voxelBlue.getHandle()); + core.prepareImageForStorage(cmdStream, voxelDensity.getHandle()); + + core.recordComputeDispatchToCmdStream( + cmdStream, + voxelTrailPipeline, + trailDispatchCount, + { + vkcv::DescriptorSetUsage(0, trailDescriptorSet), + vkcv::DescriptorSetUsage(1, voxelDescriptorSet) + }, + pushConstantsVoxel + ); + core.recordEndDebugLabel(cmdStream); + + core.recordBeginDebugLabel(cmdStream, "Combine voxel data", { 0.5f, 0.5f, 0.5f, 1.0f }); + + core.prepareImageForStorage(cmdStream, voxelRed.getHandle()); + core.prepareImageForStorage(cmdStream, voxelGreen.getHandle()); + core.prepareImageForStorage(cmdStream, voxelBlue.getHandle()); + core.prepareImageForStorage(cmdStream, voxelDensity.getHandle()); + + core.prepareImageForStorage(cmdStream, voxelData[0]); + + core.recordComputeDispatchToCmdStream( + cmdStream, + voxelPipeline, + voxelDispatchCount, + { + vkcv::DescriptorSetUsage(0, voxelDescriptorSet), + vkcv::DescriptorSetUsage(1, voxelOutDescriptorSet) + }, + vkcv::PushConstants(0) + ); + + core.recordEndDebugLabel(cmdStream); + + core.recordBeginDebugLabel(cmdStream, "Fluid voxel data", { 0.2f, 0.2f, 0.9f, 1.0f }); + + for (size_t i = 0; i < 8; i++) { + core.prepareImageForSampling(cmdStream, voxelData[i % 2]); + core.prepareImageForStorage(cmdStream, voxelData[(i + 1) % 2]); + + core.recordComputeDispatchToCmdStream( + cmdStream, + fluidPipeline, + voxelDispatchCount, + { vkcv::DescriptorSetUsage(0, fluidDescriptorSet[i % 2]) }, + vkcv::PushConstants(0) + ); + } + + core.recordEndDebugLabel(cmdStream); + + core.recordBeginDebugLabel(cmdStream, "Sample voxels", { 0.5f, 0.5f, 1.0f, 1.0f }); + + core.prepareImageForStorage(cmdStream, voxelData[0]); + core.prepareImageForStorage(cmdStream, voxelSamples.getHandle()); + + uint32_t sampleDispatchCount[3]; + sampleDispatchCount[0] = std::ceil(voxelWidth / 8.f); + sampleDispatchCount[1] = std::ceil(voxelHeight / 8.f); + sampleDispatchCount[2] = 1; + + core.recordComputeDispatchToCmdStream( + cmdStream, + voxelSamplePipeline, + sampleDispatchCount, + { + vkcv::DescriptorSetUsage(0, voxelOutDescriptorSet), + vkcv::DescriptorSetUsage(1, samplesDescriptorSet) + }, + vkcv::PushConstants(0) + ); + + core.recordEndDebugLabel(cmdStream); + + core.recordBeginDebugLabel(cmdStream, "Add rendered images", { 0.5f, 0.5f, 1.0f, 1.0f }); + + vkcv::DescriptorWrites addDescriptorWrites; + addDescriptorWrites.writeSampledImage(0, voxelSamples.getHandle()); + addDescriptorWrites.writeSampler(1, voxelSampler); + + for (size_t i = 0; i < colorBuffers.size(); i++) { + addDescriptorWrites.writeStorageImage(2 + i, colorBuffers[i]); + core.prepareImageForStorage(cmdStream, colorBuffers[i]); + } + + core.writeDescriptorSet(addDescriptor, addDescriptorWrites); + core.prepareImageForSampling(cmdStream, voxelSamples.getHandle()); + + uint32_t colorDispatchCount[3]; + colorDispatchCount[0] = std::ceil(swapchainWidth / 8.f); + colorDispatchCount[1] = std::ceil(swapchainHeight / 8.f); + colorDispatchCount[2] = 1; + + core.recordComputeDispatchToCmdStream( + cmdStream, + addPipe, + colorDispatchCount, + { + vkcv::DescriptorSetUsage(0, addDescriptor), + vkcv::DescriptorSetUsage(1, generationDescriptorSet) + }, + vkcv::PushConstants(0) + ); + + core.recordEndDebugLabel(cmdStream); + + bloomAndFlares.recordEffect(cmdStream, colorBuffers.back(), colorBuffers.back()); + + core.recordBeginDebugLabel(cmdStream, "Tonemapping", { 0.0f, 1.0f, 0.0f, 1.0f }); + core.prepareImageForStorage(cmdStream, colorBuffers.back()); + core.prepareImageForStorage(cmdStream, swapchainImage); + + vkcv::DescriptorWrites tonemappingDescriptorWrites; + tonemappingDescriptorWrites.writeStorageImage( + 0, colorBuffers.back() + ).writeStorageImage( + 1, swapchainImage + ); + + core.writeDescriptorSet(tonemappingDescriptor, tonemappingDescriptorWrites); + + core.recordComputeDispatchToCmdStream( + cmdStream, + tonemappingPipe, + colorDispatchCount, + {vkcv::DescriptorSetUsage(0, tonemappingDescriptor) }, + vkcv::PushConstants(0) + ); + + core.recordEndDebugLabel(cmdStream); + + core.prepareSwapchainImageForPresent(cmdStream); + core.submitCommandStream(cmdStream); + + gui.beginGUI(); + ImGui::Begin("Settings"); + + bool listbox = ImGui::BeginListBox(" "); + bool firework = ImGui::Selectable("Firework"); + bool sparkler = ImGui::Selectable("Sparkler"); + bool nested = ImGui::Selectable("Nested Firework"); + ImGui::EndListBox(); + bool resetTime = ImGui::Button("Reset"); + auto color = glm::vec3(0.0f); + + if (!events.empty()) { + color = events[0].color; + } + + bool colorChanged = ImGui::ColorPicker3("Color", (float*) & color); + + ImGui::End(); + gui.endGUI(); + + core.endFrame(windowHandle); + + particleBuffer.read(particles); + sort(particles.begin(), particles.end(), + [](const particle_t p1, const particle_t p2) { + return p1.eventId < p2.eventId; + }); + + std::vector<uint32_t> startingIndex; + startingIndex.resize(events.size()); + uint32_t eventIdCheck = std::numeric_limits<uint32_t>::max(); + + for (size_t i = 0; i < particles.size(); i++) { + if (particles[i].eventId != eventIdCheck) { + eventIdCheck = particles [i].eventId; + if (eventIdCheck < startingIndex.size()) { + startingIndex [eventIdCheck] = i; + } + } + } + + startIndexBuffer.fill(startingIndex); + + if (firework) { + events.clear(); + InitializeFireworkEvents(events); + resetTime = true; + } else if (sparkler) { + events.clear(); + InitializeSparklerEvents(events); + resetTime = true; + } else if (nested) { + events.clear(); + InitializeNestedFireworkEvents(events); + resetTime = true; + } + + if (colorChanged) { + ChangeColor(events, color); + resetTime = true; + } + + if (resetTime) { + start = std::chrono::system_clock::now(); + InitializeParticles(particles); + particleBuffer.fill(particles); + eventBuffer.fill(events); + smokeBuffer.fill(smokes); + trailBuffer.fill(trails); + pointBuffer.fill(points); + + memset(smokeIndices, 0, smokeIndexBuffer.getSize()); + } + + particleBufferCopy.fill(particles); + } + + smokeIndexBuffer.unmap(); + return 0; +} diff --git a/src/vkcv/Core.cpp b/src/vkcv/Core.cpp index c853f38395eda1445da3205eebadafe0cc9fa74f..176e07ae073135b0311226f825cc639cc34e9610 100644 --- a/src/vkcv/Core.cpp +++ b/src/vkcv/Core.cpp @@ -364,9 +364,9 @@ namespace vkcv clearValues.emplace_back(std::array<float, 4>{ clear, - clear, - clear, - 1.f + clear, + clear, + 0.f }); } } diff --git a/src/vkcv/ImageManager.cpp b/src/vkcv/ImageManager.cpp index 83246db6ecb28c5a513d36dc37d4ad5faa2e4b5f..17ed1b8338e219ec12e094b836a3ec2208bf1bb1 100644 --- a/src/vkcv/ImageManager.cpp +++ b/src/vkcv/ImageManager.cpp @@ -506,8 +506,10 @@ namespace vkcv { switch (format) { case vk::Format::eR8Unorm: return 1; + case vk::Format::eR16Unorm: + return 2; + case vk::Format::eR32Uint: case vk::Format::eR8G8B8A8Srgb: - return 4; case vk::Format::eR8G8B8A8Unorm: return 4; case vk::Format::eR16G16B16A16Sfloat: