diff --git a/projects/CMakeLists.txt b/projects/CMakeLists.txt index 4ea6cb3cbc1537ebd572e9483cc4e20acc3e46ab..d1acdbcbf011aab7de96f060aeb55ede83c516eb 100644 --- a/projects/CMakeLists.txt +++ b/projects/CMakeLists.txt @@ -4,6 +4,7 @@ add_subdirectory(first_triangle) add_subdirectory(first_mesh) add_subdirectory(first_scene) add_subdirectory(particle_simulation) +add_subdirectory(sph) add_subdirectory(voxelization) add_subdirectory(mesh_shader) add_subdirectory(saf_r) diff --git a/projects/particle_simulation/src/main.cpp b/projects/particle_simulation/src/main.cpp index ddbe6195e66e78504d4bccb32b3b09ff680ab414..ae3c6795a66cdc81297986acb224a63055d02c44 100644 --- a/projects/particle_simulation/src/main.cpp +++ b/projects/particle_simulation/src/main.cpp @@ -22,7 +22,7 @@ int main(int argc, const char **argv) { {vk::QueueFlagBits::eTransfer, vk::QueueFlagBits::eGraphics, vk::QueueFlagBits::eCompute}, { VK_KHR_SWAPCHAIN_EXTENSION_NAME } ); - vkcv::WindowHandle windowHandle = core.createWindow(applicationName, windowWidth, windowHeight, true); + vkcv::WindowHandle windowHandle = core.createWindow(applicationName, windowWidth, windowHeight, false); vkcv::Window& window = core.getWindow(windowHandle); vkcv::camera::CameraManager cameraManager(window); @@ -61,7 +61,7 @@ int main(int argc, const char **argv) { shaderPathFragment = "shaders/shader_space.frag"; } else if (strcmp(argv[i], "--water") == 0) { - shaderPathCompute = "shaders/shader_water.comp"; + shaderPathCompute = "shaders/shader_water1.comp"; shaderPathFragment = "shaders/shader_water.frag"; } else if (strcmp(argv[i], "--gravity") == 0) { diff --git a/projects/sph/.gitignore b/projects/sph/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..4964f89e973f38358aa57f564f56d3d4b0c328a9 --- /dev/null +++ b/projects/sph/.gitignore @@ -0,0 +1 @@ +particle_simulation \ No newline at end of file diff --git a/projects/sph/CMakeLists.txt b/projects/sph/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..592aa4409ae3e01f4054f430bab7c424d25219d0 --- /dev/null +++ b/projects/sph/CMakeLists.txt @@ -0,0 +1,35 @@ +cmake_minimum_required(VERSION 3.16) +project(sph) + +# setting c++ standard for the project +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# this should fix the execution path to load local files from the project +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) + +# adding source files to the project +add_executable(sph + src/main.cpp + src/Particle.hpp + src/Particle.cpp + src/BloomAndFlares.hpp + src/BloomAndFlares.cpp + src/PipelineInit.hpp + src/PipelineInit.cpp) + +# this should fix the execution path to load local files from the project (for MSVC) +if(MSVC) + set_target_properties(sph PROPERTIES RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) + set_target_properties(sph PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) + + # in addition to setting the output directory, the working directory has to be set + # by default visual studio sets the working directory to the build directory, when using the debugger + set_target_properties(sph PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) +endif() + +# including headers of dependencies and the VkCV framework +target_include_directories(sph SYSTEM BEFORE PRIVATE ${vkcv_include} ${vkcv_includes} ${vkcv_testing_include} ${vkcv_camera_include} ${vkcv_shader_compiler_include}) + +# linking with libraries from all dependencies and the VkCV framework +target_link_libraries(sph vkcv vkcv_testing vkcv_camera vkcv_shader_compiler) diff --git a/projects/sph/shaders/bloom/composite.comp b/projects/sph/shaders/bloom/composite.comp new file mode 100644 index 0000000000000000000000000000000000000000..87b5ddb975106232d1cd3b6e5b8dc7e623dd0b59 --- /dev/null +++ b/projects/sph/shaders/bloom/composite.comp @@ -0,0 +1,38 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +layout(set=0, binding=0) uniform texture2D blurImage; +layout(set=0, binding=1) uniform texture2D lensImage; +layout(set=0, binding=2) uniform sampler linearSampler; +layout(set=0, binding=3, r11f_g11f_b10f) uniform image2D colorBuffer; + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + + +void main() +{ + if(any(greaterThanEqual(gl_GlobalInvocationID.xy, imageSize(colorBuffer)))){ + return; + } + + ivec2 pixel_coord = ivec2(gl_GlobalInvocationID.xy); + vec2 pixel_size = vec2(1.0f) / textureSize(sampler2D(blurImage, linearSampler), 0); + vec2 UV = pixel_coord.xy * pixel_size; + + vec4 composite_color = vec4(0.0f); + + vec3 blur_color = texture(sampler2D(blurImage, linearSampler), UV).rgb; + vec3 lens_color = texture(sampler2D(lensImage, linearSampler), UV).rgb; + vec3 main_color = imageLoad(colorBuffer, pixel_coord).rgb; + + // composite blur and lens features + float bloom_weight = 0.01f; + float lens_weight = 0.f; + float main_weight = 1 - (bloom_weight + lens_weight); + + composite_color.rgb = blur_color * bloom_weight + + lens_color * lens_weight + + main_color * main_weight; + + imageStore(colorBuffer, pixel_coord, composite_color); +} \ No newline at end of file diff --git a/projects/sph/shaders/bloom/downsample.comp b/projects/sph/shaders/bloom/downsample.comp new file mode 100644 index 0000000000000000000000000000000000000000..2ab00c7c92798769153634f3479c5b7f3fb61d94 --- /dev/null +++ b/projects/sph/shaders/bloom/downsample.comp @@ -0,0 +1,76 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +layout(set=0, binding=0) uniform texture2D inBlurImage; +layout(set=0, binding=1) uniform sampler inImageSampler; +layout(set=0, binding=2, r11f_g11f_b10f) uniform writeonly image2D outBlurImage; + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + + +void main() +{ + if(any(greaterThanEqual(gl_GlobalInvocationID.xy, imageSize(outBlurImage)))){ + return; + } + + ivec2 pixel_coord = ivec2(gl_GlobalInvocationID.xy); + vec2 pixel_size = vec2(1.0f) / imageSize(outBlurImage); + vec2 UV = pixel_coord.xy * pixel_size; + vec2 UV_offset = UV + 0.5f * pixel_size; + + vec2 color_fetches[13] = { + // center neighbourhood (RED) + vec2(-1, 1), // LT + vec2(-1, -1), // LB + vec2( 1, -1), // RB + vec2( 1, 1), // RT + + vec2(-2, 2), // LT + vec2( 0, 2), // CT + vec2( 2, 2), // RT + + vec2(0 ,-2), // LC + vec2(0 , 0), // CC + vec2(2, 0), // CR + + vec2(-2, -2), // LB + vec2(0 , -2), // CB + vec2(2 , -2) // RB + }; + + float color_weights[13] = { + // 0.5f + 1.f/8.f, + 1.f/8.f, + 1.f/8.f, + 1.f/8.f, + + // 0.125f + 1.f/32.f, + 1.f/16.f, + 1.f/32.f, + + // 0.25f + 1.f/16.f, + 1.f/8.f, + 1.f/16.f, + + // 0.125f + 1.f/32.f, + 1.f/16.f, + 1.f/32.f + }; + + vec3 sampled_color = vec3(0.0f); + + for(uint i = 0; i < 13; i++) + { + vec2 color_fetch = UV_offset + color_fetches[i] * pixel_size; + vec3 color = texture(sampler2D(inBlurImage, inImageSampler), color_fetch).rgb; + color *= color_weights[i]; + sampled_color += color; + } + + imageStore(outBlurImage, pixel_coord, vec4(sampled_color, 1.f)); +} \ No newline at end of file diff --git a/projects/sph/shaders/bloom/lensFlares.comp b/projects/sph/shaders/bloom/lensFlares.comp new file mode 100644 index 0000000000000000000000000000000000000000..ce27d8850b709f61332d467914ddc944dc63109f --- /dev/null +++ b/projects/sph/shaders/bloom/lensFlares.comp @@ -0,0 +1,109 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +layout(set=0, binding=0) uniform texture2D blurBuffer; +layout(set=0, binding=1) uniform sampler linearSampler; +layout(set=0, binding=2, r11f_g11f_b10f) uniform image2D lensBuffer; + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +vec3 sampleColorChromaticAberration(vec2 _uv) +{ + vec2 toCenter = (vec2(0.5) - _uv); + + vec3 colorScales = vec3(-1, 0, 1); + float aberrationScale = 0.1; + vec3 scaleFactors = colorScales * aberrationScale; + + float r = texture(sampler2D(blurBuffer, linearSampler), _uv + toCenter * scaleFactors.r).r; + float g = texture(sampler2D(blurBuffer, linearSampler), _uv + toCenter * scaleFactors.g).g; + float b = texture(sampler2D(blurBuffer, linearSampler), _uv + toCenter * scaleFactors.b).b; + return vec3(r, g, b); +} + +// _uv assumed to be flipped UV coordinates! +vec3 ghost_vectors(vec2 _uv) +{ + vec2 ghost_vec = (vec2(0.5f) - _uv); + + const uint c_ghost_count = 64; + const float c_ghost_spacing = length(ghost_vec) / c_ghost_count; + + ghost_vec *= c_ghost_spacing; + + vec3 ret_color = vec3(0.0f); + + for (uint i = 0; i < c_ghost_count; ++i) + { + // sample scene color + vec2 s_uv = fract(_uv + ghost_vec * vec2(i)); + vec3 s = sampleColorChromaticAberration(s_uv); + + // tint/weight + float d = distance(s_uv, vec2(0.5)); + float weight = 1.0f - smoothstep(0.0f, 0.75f, d); + s *= weight; + + ret_color += s; + } + + ret_color /= c_ghost_count; + return ret_color; +} + +vec3 halo(vec2 _uv) +{ + const float c_aspect_ratio = float(imageSize(lensBuffer).x) / float(imageSize(lensBuffer).y); + const float c_radius = 0.6f; + const float c_halo_thickness = 0.1f; + + vec2 halo_vec = vec2(0.5) - _uv; + //halo_vec.x /= c_aspect_ratio; + halo_vec = normalize(halo_vec); + //halo_vec.x *= c_aspect_ratio; + + + //vec2 w_uv = (_uv - vec2(0.5, 0.0)) * vec2(c_aspect_ratio, 1.0) + vec2(0.5, 0.0); + vec2 w_uv = _uv; + float d = distance(w_uv, vec2(0.5)); // distance to center + + float distance_to_halo = abs(d - c_radius); + + float halo_weight = 0.0f; + if(abs(d - c_radius) <= c_halo_thickness) + { + float distance_to_border = c_halo_thickness - distance_to_halo; + halo_weight = distance_to_border / c_halo_thickness; + + //halo_weight = clamp((halo_weight / 0.4f), 0.0f, 1.0f); + halo_weight = pow(halo_weight, 2.0f); + + + //halo_weight = 1.0f; + } + + return sampleColorChromaticAberration(_uv + halo_vec) * halo_weight; +} + + + +void main() +{ + if(any(greaterThanEqual(gl_GlobalInvocationID.xy, imageSize(lensBuffer)))){ + return; + } + + ivec2 pixel_coord = ivec2(gl_GlobalInvocationID.xy); + vec2 pixel_size = vec2(1.0f) / imageSize(lensBuffer); + vec2 UV = pixel_coord.xy * pixel_size; + + vec2 flipped_UV = vec2(1.0f) - UV; + + vec3 color = vec3(0.0f); + + color += ghost_vectors(flipped_UV); + color += halo(UV); + color *= 0.5f; + + imageStore(lensBuffer, pixel_coord, vec4(color, 0.0f)); +} \ No newline at end of file diff --git a/projects/sph/shaders/bloom/upsample.comp b/projects/sph/shaders/bloom/upsample.comp new file mode 100644 index 0000000000000000000000000000000000000000..0ddeedb5b5af9e476dc19012fed6430544006c0e --- /dev/null +++ b/projects/sph/shaders/bloom/upsample.comp @@ -0,0 +1,45 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +layout(set=0, binding=0) uniform texture2D inUpsampleImage; +layout(set=0, binding=1) uniform sampler inImageSampler; +layout(set=0, binding=2, r11f_g11f_b10f) uniform image2D outUpsampleImage; + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +void main() +{ + if(any(greaterThanEqual(gl_GlobalInvocationID.xy, imageSize(outUpsampleImage)))){ + return; + } + + + ivec2 pixel_coord = ivec2(gl_GlobalInvocationID.xy); + vec2 pixel_size = vec2(1.0f) / imageSize(outUpsampleImage); + vec2 UV = pixel_coord.xy * pixel_size; + + const float gauss_kernel[3] = {1.f, 2.f, 1.f}; + const float gauss_weight = 16.f; + + vec3 sampled_color = vec3(0.f); + + for(int i = -1; i <= 1; i++) + { + for(int j = -1; j <= 1; j++) + { + vec2 sample_location = UV + vec2(j, i) * pixel_size; + vec3 color = texture(sampler2D(inUpsampleImage, inImageSampler), sample_location).rgb; + color *= gauss_kernel[j+1]; + color *= gauss_kernel[i+1]; + color /= gauss_weight; + + sampled_color += color; + } + } + + //vec3 prev_color = imageLoad(outUpsampleImage, pixel_coord).rgb; + //float bloomRimStrength = 0.75f; // adjust this to change strength of bloom + //sampled_color = mix(prev_color, sampled_color, bloomRimStrength); + + imageStore(outUpsampleImage, pixel_coord, vec4(sampled_color, 1.f)); +} \ No newline at end of file diff --git a/projects/sph/shaders/flip.comp b/projects/sph/shaders/flip.comp new file mode 100644 index 0000000000000000000000000000000000000000..f5cd7bf3ea5ed8d04de970c816989230421c0b52 --- /dev/null +++ b/projects/sph/shaders/flip.comp @@ -0,0 +1,53 @@ +#version 450 core +#extension GL_ARB_separate_shader_objects : enable +#extension GL_GOOGLE_include_directive : enable + +layout(local_size_x = 256) in; + +struct Particle +{ + vec3 position; + float padding; + vec3 velocity; + float density; + vec3 force; + float pressure; +}; + +layout(std430, binding = 1) readonly buffer buffer_inParticle +{ + Particle inParticle[]; +}; + +layout(std430, binding = 0) writeonly buffer buffer_outParticle +{ + Particle outParticle[]; +}; + +layout( push_constant ) uniform constants{ + float h; + float mass; + float gasConstant; + float offset; + float gravity; + float viscosity; + float ABSORBTION; + float dt; + vec3 gravityDir; + float particleCount; +}; + +void main() { + uint id = gl_GlobalInvocationID.x; + + if(id >= int(particleCount)) + { + return; + } + + outParticle[id].force = inParticle[id].force; + outParticle[id].density = inParticle[id].density; + outParticle[id].pressure = inParticle[id].pressure; + outParticle[id].position = inParticle[id].position; + outParticle[id].velocity = inParticle[id].velocity; +} diff --git a/projects/sph/shaders/force.comp b/projects/sph/shaders/force.comp new file mode 100644 index 0000000000000000000000000000000000000000..ea9b378b48a23fd0208ab18d884dbccda5ab21f4 --- /dev/null +++ b/projects/sph/shaders/force.comp @@ -0,0 +1,95 @@ +#version 450 core +#extension GL_ARB_separate_shader_objects : enable +#extension GL_GOOGLE_include_directive : enable + +const float PI = 3.1415926535897932384626433832795; + +layout(local_size_x = 256) in; + +struct Particle +{ + vec3 position; + float padding; + vec3 velocity; + float density; + vec3 force; + float pressure; + +}; + +layout(std430, binding = 1) readonly buffer buffer_inParticle +{ + Particle inParticle[]; +}; + +layout(std430, binding = 0) writeonly buffer buffer_outParticle +{ + Particle outParticle[]; +}; + +layout( push_constant ) uniform constants{ + float h; + float mass; + float gasConstant; + float offset; + float gravity; + float viscosity; + float ABSORBTION; + float dt; + vec3 gravityDir; + float particleCount; +}; + +float spiky(float r) +{ + return (15.f / (PI * pow(h, 6)) * pow((h-r), 3)) * int(0<=r && r<=h); +} + +float grad_spiky(float r) +{ + return -45.f / (PI * pow(h, 6)) * pow((h-r), 2) * int(0<=r && r<=h); +} + + + +float laplacian(float r) +{ + return (45.f / (PI * pow(h,6)) * (h - r)) * int(0<=r && r<=h); +} + +vec3 pressureForce = vec3(0, 0, 0); +vec3 viscosityForce = vec3(0, 0, 0); +vec3 externalForce = vec3(0, 0, 0); + +void main() { + + uint id = gl_GlobalInvocationID.x; + + if(id >= int(particleCount)) + { + return; + } + + externalForce = inParticle[id].density * gravity * vec3(-gravityDir.x,gravityDir.y,gravityDir.z); + + for(uint i = 0; i < int(particleCount); i++) + { + if (id != i) + { + vec3 dir = inParticle[id].position - inParticle[i].position; + float dist = length(dir); + if(dist != 0) + { + pressureForce += mass * -(inParticle[id].pressure + inParticle[i].pressure)/(2.f * inParticle[i].density) * grad_spiky(dist) * normalize(dir); + viscosityForce += mass * (inParticle[i].velocity - inParticle[id].velocity)/inParticle[i].density * laplacian(dist); + } + } + } + viscosityForce *= viscosity; + + outParticle[id].force = externalForce + pressureForce + viscosityForce; + outParticle[id].density = inParticle[id].density; + outParticle[id].pressure = inParticle[id].pressure; + outParticle[id].position = inParticle[id].position; + outParticle[id].velocity = inParticle[id].velocity; +} diff --git a/projects/sph/shaders/particleShading.inc b/projects/sph/shaders/particleShading.inc new file mode 100644 index 0000000000000000000000000000000000000000..b2d1832b9ccd6ba05a585b59bdfdedd4729e80f8 --- /dev/null +++ b/projects/sph/shaders/particleShading.inc @@ -0,0 +1,6 @@ +float circleFactor(vec2 triangleCoordinates){ + // percentage of distance from center to circle edge + float p = clamp((0.4 - length(triangleCoordinates)) / 0.4, 0, 1); + // remapping for nice falloff + return sqrt(p); +} \ No newline at end of file diff --git a/projects/sph/shaders/particle_params.inc b/projects/sph/shaders/particle_params.inc new file mode 100644 index 0000000000000000000000000000000000000000..e35cce13be1bc84f045da276fe46666d0b852984 --- /dev/null +++ b/projects/sph/shaders/particle_params.inc @@ -0,0 +1,8 @@ +#define h 0.4 +#define mass 0.15 +#define gasConstant 3000 +#define offset 1800 +#define gravity -1000 +#define viscosity 1500 +#define ABSORBTION 0.9 +#define dt 0.0005 diff --git a/projects/sph/shaders/pressure.comp b/projects/sph/shaders/pressure.comp new file mode 100644 index 0000000000000000000000000000000000000000..05b3af3afb490b427cc1297f21a82a779d4c8ecb --- /dev/null +++ b/projects/sph/shaders/pressure.comp @@ -0,0 +1,72 @@ +#version 450 core +#extension GL_ARB_separate_shader_objects : enable +#extension GL_GOOGLE_include_directive : enable + +const float PI = 3.1415926535897932384626433832795; + +layout(local_size_x = 256) in; + +struct Particle +{ + vec3 position; + float padding; + vec3 velocity; + float density; + vec3 force; + float pressure; + +}; + +layout(std430, binding = 0) readonly buffer buffer_inParticle +{ + Particle inParticle[]; +}; + +layout(std430, binding = 1) writeonly buffer buffer_outParticle +{ + Particle outParticle[]; +}; + +layout( push_constant ) uniform constants{ + float h; + float mass; + float gasConstant; + float offset; + float gravity; + float viscosity; + float ABSORBTION; + float dt; + vec3 gravityDir; + float particleCount; +}; + +float poly6(float r) +{ + return (315.f * pow((pow(h,2)-pow(r,2)), 3)/(64.f*PI*pow(h, 9))) * int(0<=r && r<=h); +} + +float densitySum = 0.f; + +void main() { + + uint id = gl_GlobalInvocationID.x; + + if(id >= int(particleCount)) + { + return; + } + + for(uint i = 0; i < int(particleCount); i++) + { + if (id != i) + { + float dist = distance(inParticle[id].position, inParticle[i].position); + densitySum += mass * poly6(dist); + } + } + outParticle[id].density = max(densitySum,0.0000001f); + outParticle[id].pressure = max((densitySum - offset), 0.0000001f) * gasConstant; + outParticle[id].position = inParticle[id].position; + outParticle[id].velocity = inParticle[id].velocity; + outParticle[id].force = inParticle[id].force; +} diff --git a/projects/sph/shaders/shader.vert b/projects/sph/shaders/shader.vert new file mode 100644 index 0000000000000000000000000000000000000000..f5531ffa4f26d3652e8e35971c16af6dda2e3b45 --- /dev/null +++ b/projects/sph/shaders/shader.vert @@ -0,0 +1,49 @@ +#version 460 core +#extension GL_ARB_separate_shader_objects : enable + +layout(location = 0) in vec3 particle; + +struct Particle +{ + vec3 position; + float padding; + vec3 velocity; + float density; + vec3 force; + float pressure; +}; + +layout(std430, binding = 2) readonly buffer buffer_inParticle1 +{ + Particle inParticle1[]; +}; + +layout(std430, binding = 3) readonly buffer buffer_inParticle2 +{ + Particle inParticle2[]; +}; + +layout( push_constant ) uniform constants{ + mat4 view; + mat4 projection; +}; + +layout(location = 0) out vec2 passTriangleCoordinates; +layout(location = 1) out vec3 passVelocity; + +void main() +{ + int id = gl_InstanceIndex; + passVelocity = inParticle1[id].velocity; + + // particle position in view space + vec4 positionView = view * vec4(inParticle1[id].position, 1); + // by adding the triangle position in view space the mesh is always camera facing + positionView.xyz += particle; + // multiply with projection matrix for final position + gl_Position = projection * positionView; + + // 0.01 corresponds to vertex position size in main + float normalizationDivider = 0.012; + passTriangleCoordinates = particle.xy / normalizationDivider; +} \ No newline at end of file diff --git a/projects/sph/shaders/shader_water.frag b/projects/sph/shaders/shader_water.frag new file mode 100644 index 0000000000000000000000000000000000000000..2aee4ec692a2ada060a77389099b2c279e9c338c --- /dev/null +++ b/projects/sph/shaders/shader_water.frag @@ -0,0 +1,28 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable +#extension GL_GOOGLE_include_directive : enable + +#include "particleShading.inc" + +layout(location = 0) in vec2 passTriangleCoordinates; +layout(location = 1) in vec3 passVelocity; + +layout(location = 0) out vec3 outColor; + +layout(set=0, binding=0) uniform uColor { + vec4 color; +} Color; + +layout(set=0,binding=1) uniform uPosition{ + vec2 position; +} Position; + +void main() +{ + float p = length(passVelocity)/100.f; + outColor = vec3(0.f+p/3.f, 0.05f+p/2.f, 0.4f+p); + + // make the triangle look like a circle + outColor *= circleFactor(passTriangleCoordinates); + +} diff --git a/projects/sph/shaders/tonemapping.comp b/projects/sph/shaders/tonemapping.comp new file mode 100644 index 0000000000000000000000000000000000000000..26f0232d66e3475afdd1266c0cc6288b47ed1c38 --- /dev/null +++ b/projects/sph/shaders/tonemapping.comp @@ -0,0 +1,19 @@ +#version 440 + +layout(set=0, binding=0, rgba16f) uniform image2D inImage; +layout(set=0, binding=1, rgba8) uniform image2D outImage; + + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +void main(){ + + if(any(greaterThanEqual(gl_GlobalInvocationID.xy, imageSize(inImage)))){ + return; + } + ivec2 uv = ivec2(gl_GlobalInvocationID.xy); + vec3 linearColor = imageLoad(inImage, uv).rgb; + 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/sph/shaders/updateData.comp b/projects/sph/shaders/updateData.comp new file mode 100644 index 0000000000000000000000000000000000000000..3c935b232aff11388cc3b371e5524fa30486b36f --- /dev/null +++ b/projects/sph/shaders/updateData.comp @@ -0,0 +1,109 @@ +#version 450 core +#extension GL_ARB_separate_shader_objects : enable +#extension GL_GOOGLE_include_directive : enable + +layout(local_size_x = 256) in; + +struct Particle +{ + vec3 position; + float padding; + vec3 velocity; + float density; + vec3 force; + float pressure; + +}; + +layout(std430, binding = 0) readonly buffer buffer_inParticle +{ + Particle inParticle[]; +}; + +layout(std430, binding = 1) writeonly buffer buffer_outParticle +{ + Particle outParticle[]; +}; + +layout( push_constant ) uniform constants{ + float h; + float mass; + float gasConstant; + float offset; + float gravity; + float viscosity; + float ABSORBTION; + float dt; + vec3 gravityDir; + float particleCount; +}; + +void main() { + + uint id = gl_GlobalInvocationID.x; + + if(id >= int(particleCount)) + { + return; + } + + vec3 accel = inParticle[id].force / inParticle[id].density; + vec3 vel_new = inParticle[id].velocity + (dt * accel); + + vec3 out_force = inParticle[id].force; + float out_density = inParticle[id].density; + float out_pressure = inParticle[id].pressure; + + if (length(vel_new) > 100.f) + { + vel_new = normalize(vel_new)*50; + out_density = 0.01f; + out_pressure = 0.01f; + out_force = gravity * vec3(-gravityDir.x,gravityDir.y,gravityDir.z); + } + + vec3 pos_new = inParticle[id].position + (dt * vel_new); + + // Überprüfe Randbedingungen x + if (inParticle[id].position.x < -1.0) + { + vel_new = reflect(vel_new.xyz, vec3(1.f,0.f,0.f)) * ABSORBTION; + pos_new.x = -1.0 + 0.01f; + } + else if (inParticle[id].position.x > 1.0) + { + vel_new = reflect(vel_new,vec3(1.f,0.f,0.f)) * ABSORBTION; + pos_new.x = 1.0 - 0.01f; + } + + // Überprüfe Randbedingungen y + if (inParticle[id].position.y < -1.0) + { + vel_new = reflect(vel_new,vec3(0.f,1.f,0.f)) * ABSORBTION; + pos_new.y = -1.0 + 0.01f; + + } + else if (inParticle[id].position.y > 1.0) + { + vel_new = reflect(vel_new,vec3(0.f,1.f,0.f)) * ABSORBTION; + pos_new.y = 1.0 - 0.01f; + } + + // Überprüfe Randbedingungen z + if (inParticle[id].position.z < -1.0 ) + { + vel_new = reflect(vel_new,vec3(0.f,0.f,1.f)) * ABSORBTION; + pos_new.z = -1.0 + 0.01f; + } + else if (inParticle[id].position.z > 1.0 ) + { + vel_new = reflect(vel_new,vec3(0.f,0.f,1.f)) * ABSORBTION; + pos_new.z = 1.0 - 0.01f; + } + + outParticle[id].force = out_force; + outParticle[id].density = out_density; + outParticle[id].pressure = out_pressure; + outParticle[id].position = pos_new; + outParticle[id].velocity = vel_new; +} diff --git a/projects/sph/src/BloomAndFlares.cpp b/projects/sph/src/BloomAndFlares.cpp new file mode 100644 index 0000000000000000000000000000000000000000..09534815afcd8ab238b79da5c6bbceb6672b043a --- /dev/null +++ b/projects/sph/src/BloomAndFlares.cpp @@ -0,0 +1,285 @@ +#include "BloomAndFlares.hpp" +#include <vkcv/shader/GLSLCompiler.hpp> + +BloomAndFlares::BloomAndFlares( + vkcv::Core *p_Core, + vk::Format colorBufferFormat, + uint32_t width, + uint32_t height) : + + p_Core(p_Core), + m_ColorBufferFormat(colorBufferFormat), + m_Width(width), + m_Height(height), + m_LinearSampler(p_Core->createSampler(vkcv::SamplerFilterType::LINEAR, + vkcv::SamplerFilterType::LINEAR, + vkcv::SamplerMipmapMode::LINEAR, + vkcv::SamplerAddressMode::CLAMP_TO_EDGE)), + m_Blur(p_Core->createImage(colorBufferFormat, width, height, 1, true, true, false)), + m_LensFeatures(p_Core->createImage(colorBufferFormat, width, height, 1, false, true, false)) +{ + vkcv::shader::GLSLCompiler compiler; + + // DOWNSAMPLE + vkcv::ShaderProgram dsProg; + compiler.compile(vkcv::ShaderStage::COMPUTE, + "shaders/bloom/downsample.comp", + [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) + { + dsProg.addShader(shaderStage, path); + }); + for(uint32_t mipLevel = 0; mipLevel < m_Blur.getMipCount(); mipLevel++) + { + m_DownsampleDescSetLayouts.push_back( + p_Core->createDescriptorSetLayout(dsProg.getReflectedDescriptors().at(0))); + + m_DownsampleDescSets.push_back( + p_Core->createDescriptorSet(m_DownsampleDescSetLayouts.back())); + } + m_DownsamplePipe = p_Core->createComputePipeline({ + dsProg, { p_Core->getDescriptorSetLayout(m_DownsampleDescSetLayouts[0]).vulkanHandle } }); + + // UPSAMPLE + vkcv::ShaderProgram usProg; + compiler.compile(vkcv::ShaderStage::COMPUTE, + "shaders/bloom/upsample.comp", + [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) + { + usProg.addShader(shaderStage, path); + }); + for(uint32_t mipLevel = 0; mipLevel < m_Blur.getMipCount(); mipLevel++) + { + m_UpsampleDescSetLayouts.push_back( + p_Core->createDescriptorSetLayout(usProg.getReflectedDescriptors().at(0))); + m_UpsampleDescSets.push_back( + p_Core->createDescriptorSet(m_UpsampleDescSetLayouts.back())); + } + m_UpsamplePipe = p_Core->createComputePipeline({ + usProg, { p_Core->getDescriptorSetLayout(m_UpsampleDescSetLayouts[0]).vulkanHandle } }); + + // LENS FEATURES + vkcv::ShaderProgram lensProg; + compiler.compile(vkcv::ShaderStage::COMPUTE, + "shaders/bloom/lensFlares.comp", + [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) + { + lensProg.addShader(shaderStage, path); + }); + m_LensFlareDescSetLayout = p_Core->createDescriptorSetLayout(lensProg.getReflectedDescriptors().at(0)); + m_LensFlareDescSet = p_Core->createDescriptorSet(m_LensFlareDescSetLayout); + m_LensFlarePipe = p_Core->createComputePipeline({ + lensProg, { p_Core->getDescriptorSetLayout(m_LensFlareDescSetLayout).vulkanHandle } }); + + // COMPOSITE + vkcv::ShaderProgram compProg; + compiler.compile(vkcv::ShaderStage::COMPUTE, + "shaders/bloom/composite.comp", + [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) + { + compProg.addShader(shaderStage, path); + }); + m_CompositeDescSetLayout = p_Core->createDescriptorSetLayout(compProg.getReflectedDescriptors().at(0)); + m_CompositeDescSet = p_Core->createDescriptorSet(m_CompositeDescSetLayout); + m_CompositePipe = p_Core->createComputePipeline({ + compProg, { p_Core->getDescriptorSetLayout(m_CompositeDescSetLayout).vulkanHandle } }); +} + +void BloomAndFlares::execDownsamplePipe(const vkcv::CommandStreamHandle &cmdStream, + const vkcv::ImageHandle &colorAttachment) +{ + auto dispatchCountX = static_cast<float>(m_Width) / 8.0f; + auto dispatchCountY = static_cast<float>(m_Height) / 8.0f; + // blur dispatch + uint32_t initialDispatchCount[3] = { + static_cast<uint32_t>(glm::ceil(dispatchCountX)), + static_cast<uint32_t>(glm::ceil(dispatchCountY)), + 1 + }; + + // downsample dispatch of original color attachment + p_Core->prepareImageForSampling(cmdStream, colorAttachment); + p_Core->prepareImageForStorage(cmdStream, m_Blur.getHandle()); + + vkcv::DescriptorWrites initialDownsampleWrites; + initialDownsampleWrites.sampledImageWrites = {vkcv::SampledImageDescriptorWrite(0, colorAttachment)}; + initialDownsampleWrites.samplerWrites = {vkcv::SamplerDescriptorWrite(1, m_LinearSampler)}; + initialDownsampleWrites.storageImageWrites = {vkcv::StorageImageDescriptorWrite(2, m_Blur.getHandle(), 0) }; + p_Core->writeDescriptorSet(m_DownsampleDescSets[0], initialDownsampleWrites); + + p_Core->recordComputeDispatchToCmdStream( + cmdStream, + m_DownsamplePipe, + initialDispatchCount, + {vkcv::DescriptorSetUsage(0, p_Core->getDescriptorSet(m_DownsampleDescSets[0]).vulkanHandle)}, + vkcv::PushConstants(0)); + + // downsample dispatches of blur buffer's mip maps + float mipDispatchCountX = dispatchCountX; + float mipDispatchCountY = dispatchCountY; + for(uint32_t mipLevel = 1; mipLevel < std::min((uint32_t)m_DownsampleDescSets.size(), m_Blur.getMipCount()); mipLevel++) + { + // mip descriptor writes + vkcv::DescriptorWrites mipDescriptorWrites; + mipDescriptorWrites.sampledImageWrites = {vkcv::SampledImageDescriptorWrite(0, m_Blur.getHandle(), mipLevel - 1, true)}; + mipDescriptorWrites.samplerWrites = {vkcv::SamplerDescriptorWrite(1, m_LinearSampler)}; + mipDescriptorWrites.storageImageWrites = {vkcv::StorageImageDescriptorWrite(2, m_Blur.getHandle(), mipLevel) }; + p_Core->writeDescriptorSet(m_DownsampleDescSets[mipLevel], mipDescriptorWrites); + + // mip dispatch calculation + mipDispatchCountX /= 2.0f; + mipDispatchCountY /= 2.0f; + + uint32_t mipDispatchCount[3] = { + static_cast<uint32_t>(glm::ceil(mipDispatchCountX)), + static_cast<uint32_t>(glm::ceil(mipDispatchCountY)), + 1 + }; + + if(mipDispatchCount[0] == 0) + mipDispatchCount[0] = 1; + if(mipDispatchCount[1] == 0) + mipDispatchCount[1] = 1; + + // mip blur dispatch + p_Core->recordComputeDispatchToCmdStream( + cmdStream, + m_DownsamplePipe, + mipDispatchCount, + {vkcv::DescriptorSetUsage(0, p_Core->getDescriptorSet(m_DownsampleDescSets[mipLevel]).vulkanHandle)}, + vkcv::PushConstants(0)); + + // image barrier between mips + p_Core->recordImageMemoryBarrier(cmdStream, m_Blur.getHandle()); + } +} + +void BloomAndFlares::execUpsamplePipe(const vkcv::CommandStreamHandle &cmdStream) +{ + // upsample dispatch + p_Core->prepareImageForStorage(cmdStream, m_Blur.getHandle()); + + const uint32_t upsampleMipLevels = std::min( + static_cast<uint32_t>(m_UpsampleDescSets.size() - 1), + static_cast<uint32_t>(3) + ); + + // upsample dispatch for each mip map + for(uint32_t mipLevel = upsampleMipLevels; mipLevel > 0; mipLevel--) + { + // mip descriptor writes + vkcv::DescriptorWrites mipUpsampleWrites; + mipUpsampleWrites.sampledImageWrites = {vkcv::SampledImageDescriptorWrite(0, m_Blur.getHandle(), mipLevel, true)}; + mipUpsampleWrites.samplerWrites = {vkcv::SamplerDescriptorWrite(1, m_LinearSampler)}; + mipUpsampleWrites.storageImageWrites = {vkcv::StorageImageDescriptorWrite(2, m_Blur.getHandle(), mipLevel - 1) }; + p_Core->writeDescriptorSet(m_UpsampleDescSets[mipLevel], mipUpsampleWrites); + + auto mipDivisor = glm::pow(2.0f, static_cast<float>(mipLevel) - 1.0f); + + auto upsampleDispatchX = static_cast<float>(m_Width) / mipDivisor; + auto upsampleDispatchY = static_cast<float>(m_Height) / mipDivisor; + upsampleDispatchX /= 8.0f; + upsampleDispatchY /= 8.0f; + + const uint32_t upsampleDispatchCount[3] = { + static_cast<uint32_t>(glm::ceil(upsampleDispatchX)), + static_cast<uint32_t>(glm::ceil(upsampleDispatchY)), + 1 + }; + + p_Core->recordComputeDispatchToCmdStream( + cmdStream, + m_UpsamplePipe, + upsampleDispatchCount, + {vkcv::DescriptorSetUsage(0, p_Core->getDescriptorSet(m_UpsampleDescSets[mipLevel]).vulkanHandle)}, + vkcv::PushConstants(0) + ); + // image barrier between mips + p_Core->recordImageMemoryBarrier(cmdStream, m_Blur.getHandle()); + } +} + +void BloomAndFlares::execLensFeaturePipe(const vkcv::CommandStreamHandle &cmdStream) +{ + // lens feature generation descriptor writes + p_Core->prepareImageForSampling(cmdStream, m_Blur.getHandle()); + p_Core->prepareImageForStorage(cmdStream, m_LensFeatures.getHandle()); + + vkcv::DescriptorWrites lensFeatureWrites; + lensFeatureWrites.sampledImageWrites = {vkcv::SampledImageDescriptorWrite(0, m_Blur.getHandle(), 0)}; + lensFeatureWrites.samplerWrites = {vkcv::SamplerDescriptorWrite(1, m_LinearSampler)}; + lensFeatureWrites.storageImageWrites = {vkcv::StorageImageDescriptorWrite(2, m_LensFeatures.getHandle(), 0)}; + p_Core->writeDescriptorSet(m_LensFlareDescSet, lensFeatureWrites); + + auto dispatchCountX = static_cast<float>(m_Width) / 8.0f; + auto dispatchCountY = static_cast<float>(m_Height) / 8.0f; + // lens feature generation dispatch + uint32_t lensFeatureDispatchCount[3] = { + static_cast<uint32_t>(glm::ceil(dispatchCountX)), + static_cast<uint32_t>(glm::ceil(dispatchCountY)), + 1 + }; + p_Core->recordComputeDispatchToCmdStream( + cmdStream, + m_LensFlarePipe, + lensFeatureDispatchCount, + {vkcv::DescriptorSetUsage(0, p_Core->getDescriptorSet(m_LensFlareDescSet).vulkanHandle)}, + vkcv::PushConstants(0)); +} + +void BloomAndFlares::execCompositePipe(const vkcv::CommandStreamHandle &cmdStream, + const vkcv::ImageHandle &colorAttachment) +{ + p_Core->prepareImageForSampling(cmdStream, m_Blur.getHandle()); + p_Core->prepareImageForSampling(cmdStream, m_LensFeatures.getHandle()); + p_Core->prepareImageForStorage(cmdStream, colorAttachment); + + // bloom composite descriptor write + vkcv::DescriptorWrites compositeWrites; + compositeWrites.sampledImageWrites = {vkcv::SampledImageDescriptorWrite(0, m_Blur.getHandle()), + vkcv::SampledImageDescriptorWrite(1, m_LensFeatures.getHandle())}; + compositeWrites.samplerWrites = {vkcv::SamplerDescriptorWrite(2, m_LinearSampler)}; + compositeWrites.storageImageWrites = {vkcv::StorageImageDescriptorWrite(3, colorAttachment)}; + p_Core->writeDescriptorSet(m_CompositeDescSet, compositeWrites); + + float dispatchCountX = static_cast<float>(m_Width) / 8.0f; + float dispatchCountY = static_cast<float>(m_Height) / 8.0f; + + uint32_t compositeDispatchCount[3] = { + static_cast<uint32_t>(glm::ceil(dispatchCountX)), + static_cast<uint32_t>(glm::ceil(dispatchCountY)), + 1 + }; + + // bloom composite dispatch + p_Core->recordComputeDispatchToCmdStream( + cmdStream, + m_CompositePipe, + compositeDispatchCount, + {vkcv::DescriptorSetUsage(0, p_Core->getDescriptorSet(m_CompositeDescSet).vulkanHandle)}, + vkcv::PushConstants(0)); +} + +void BloomAndFlares::execWholePipeline(const vkcv::CommandStreamHandle &cmdStream, + const vkcv::ImageHandle &colorAttachment) +{ + execDownsamplePipe(cmdStream, colorAttachment); + execUpsamplePipe(cmdStream); + execLensFeaturePipe(cmdStream); + execCompositePipe(cmdStream, colorAttachment); +} + +void BloomAndFlares::updateImageDimensions(uint32_t width, uint32_t height) +{ + if ((width == m_Width) && (height == m_Height)) { + return; + } + + m_Width = width; + m_Height = height; + + p_Core->getContext().getDevice().waitIdle(); + m_Blur = p_Core->createImage(m_ColorBufferFormat, m_Width, m_Height, 1, true, true, false); + m_LensFeatures = p_Core->createImage(m_ColorBufferFormat, m_Width, m_Height, 1, false, true, false); +} + + diff --git a/projects/sph/src/BloomAndFlares.hpp b/projects/sph/src/BloomAndFlares.hpp new file mode 100644 index 0000000000000000000000000000000000000000..1644d38e9c98c7a0bf74d48b173f0e627214f1e1 --- /dev/null +++ b/projects/sph/src/BloomAndFlares.hpp @@ -0,0 +1,51 @@ +#pragma once +#include <vkcv/Core.hpp> +#include <glm/glm.hpp> + +class BloomAndFlares{ +public: + BloomAndFlares(vkcv::Core *p_Core, + vk::Format colorBufferFormat, + uint32_t width, + uint32_t height); + + void execWholePipeline(const vkcv::CommandStreamHandle &cmdStream, const vkcv::ImageHandle &colorAttachment); + + void updateImageDimensions(uint32_t width, uint32_t height); + +private: + vkcv::Core *p_Core; + + vk::Format m_ColorBufferFormat; + uint32_t m_Width; + uint32_t m_Height; + + vkcv::SamplerHandle m_LinearSampler; + vkcv::Image m_Blur; + vkcv::Image m_LensFeatures; + + + vkcv::ComputePipelineHandle m_DownsamplePipe; + std::vector<vkcv::DescriptorSetLayoutHandle> m_DownsampleDescSetLayouts; + std::vector<vkcv::DescriptorSetHandle> m_DownsampleDescSets; // per mip desc set + + vkcv::ComputePipelineHandle m_UpsamplePipe; + std::vector<vkcv::DescriptorSetLayoutHandle> m_UpsampleDescSetLayouts; + std::vector<vkcv::DescriptorSetHandle> m_UpsampleDescSets; // per mip desc set + + vkcv::ComputePipelineHandle m_LensFlarePipe; + vkcv::DescriptorSetLayoutHandle m_LensFlareDescSetLayout; + vkcv::DescriptorSetHandle m_LensFlareDescSet; + + vkcv::ComputePipelineHandle m_CompositePipe; + vkcv::DescriptorSetLayoutHandle m_CompositeDescSetLayout; + vkcv::DescriptorSetHandle m_CompositeDescSet; + + void execDownsamplePipe(const vkcv::CommandStreamHandle &cmdStream, const vkcv::ImageHandle &colorAttachment); + void execUpsamplePipe(const vkcv::CommandStreamHandle &cmdStream); + void execLensFeaturePipe(const vkcv::CommandStreamHandle &cmdStream); + void execCompositePipe(const vkcv::CommandStreamHandle &cmdStream, const vkcv::ImageHandle &colorAttachment); +}; + + + diff --git a/projects/sph/src/Particle.cpp b/projects/sph/src/Particle.cpp new file mode 100644 index 0000000000000000000000000000000000000000..329236989b7cab502e7a4e1bb5aa27869bed53cb --- /dev/null +++ b/projects/sph/src/Particle.cpp @@ -0,0 +1,39 @@ + +#include "Particle.hpp" + +Particle::Particle(glm::vec3 position, glm::vec3 velocity) +: m_position(position), + m_velocity(velocity) +{ + m_density = 0.f; + m_force = glm::vec3(0.f); + m_pressure = 0.f; +} + +const glm::vec3& Particle::getPosition()const{ + return m_position; +} + +void Particle::setPosition( const glm::vec3 pos ){ + m_position = pos; +} + +const glm::vec3& Particle::getVelocity()const{ + return m_velocity; +} + +void Particle::setVelocity( const glm::vec3 vel ){ + m_velocity = vel; +} + +const float& Particle::getDensity()const { + return m_density; +} + +const glm::vec3& Particle::getForce()const { + return m_force; +} + +const float& Particle::getPressure()const { + return m_pressure; +} \ No newline at end of file diff --git a/projects/sph/src/Particle.hpp b/projects/sph/src/Particle.hpp new file mode 100644 index 0000000000000000000000000000000000000000..6c4ab50b74cc4544976318c23e36f4b91989ee66 --- /dev/null +++ b/projects/sph/src/Particle.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include <glm/glm.hpp> + +class Particle { + +public: + Particle(glm::vec3 position, glm::vec3 velocity); + + const glm::vec3& getPosition()const; + + void setPosition( const glm::vec3 pos ); + + const glm::vec3& getVelocity()const; + + void setVelocity( const glm::vec3 vel ); + + const float& getDensity()const; + + const glm::vec3& getForce()const; + + const float& getPressure()const; + + + +private: + // all properties of the Particle + glm::vec3 m_position; + float m_padding1; + glm::vec3 m_velocity; + float m_density; + glm::vec3 m_force; + float m_pressure; +}; diff --git a/projects/sph/src/PipelineInit.cpp b/projects/sph/src/PipelineInit.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6cf941fa0d8f8716b7d05daf9b6fb618b0fa7d85 --- /dev/null +++ b/projects/sph/src/PipelineInit.cpp @@ -0,0 +1,27 @@ +#include "PipelineInit.hpp" + +vkcv::DescriptorSetHandle PipelineInit::ComputePipelineInit(vkcv::Core *pCore, vkcv::ShaderStage shaderStage, std::filesystem::path includePath, + vkcv::ComputePipelineHandle &pipeline) { + vkcv::ShaderProgram shaderProgram; + vkcv::shader::GLSLCompiler compiler; + compiler.compile(shaderStage, includePath, + [&](vkcv::ShaderStage shaderStage1, const std::filesystem::path& path1) {shaderProgram.addShader(shaderStage1, path1); + }); + vkcv::DescriptorSetLayoutHandle descriptorSetLayout = pCore->createDescriptorSetLayout( + shaderProgram.getReflectedDescriptors().at(0)); + vkcv::DescriptorSetHandle descriptorSet = pCore->createDescriptorSet(descriptorSetLayout); + + const std::vector<vkcv::VertexAttachment> vertexAttachments = shaderProgram.getVertexAttachments(); + + std::vector<vkcv::VertexBinding> bindings; + for (size_t i = 0; i < vertexAttachments.size(); i++) { + bindings.push_back(vkcv::VertexBinding(i, { vertexAttachments[i] })); + } + const vkcv::VertexLayout layout(bindings); + + pipeline = pCore->createComputePipeline({ + shaderProgram, + { pCore->getDescriptorSetLayout(descriptorSetLayout).vulkanHandle } }); + + return descriptorSet; +} \ No newline at end of file diff --git a/projects/sph/src/PipelineInit.hpp b/projects/sph/src/PipelineInit.hpp new file mode 100644 index 0000000000000000000000000000000000000000..e628af0eef9c0558719b405790246946d8720d47 --- /dev/null +++ b/projects/sph/src/PipelineInit.hpp @@ -0,0 +1,12 @@ +#pragma once +#include <vkcv/Core.hpp> +#include <vkcv/shader/GLSLCompiler.hpp> +#include <fstream> + +class PipelineInit{ +public: + static vkcv::DescriptorSetHandle ComputePipelineInit(vkcv::Core *pCore, + vkcv::ShaderStage shaderStage, + std::filesystem::path includePath, + vkcv::ComputePipelineHandle& pipeline); +}; diff --git a/projects/sph/src/main.cpp b/projects/sph/src/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..255fb0e73f068ff9e5e8ce892897a1325631b6e1 --- /dev/null +++ b/projects/sph/src/main.cpp @@ -0,0 +1,409 @@ +#include <iostream> +#include <vkcv/Core.hpp> +#include <GLFW/glfw3.h> +#include <vkcv/camera/CameraManager.hpp> +#include <chrono> +#include <random> +#include <glm/glm.hpp> +#include <glm/gtc/matrix_access.hpp> +#include <glm/gtc/matrix_transform.hpp> +#include <time.h> +#include <vkcv/shader/GLSLCompiler.hpp> +#include "BloomAndFlares.hpp" +#include "PipelineInit.hpp" +#include "Particle.hpp" + +int main(int argc, const char **argv) { + const char *applicationName = "SPH"; + + vkcv::Core core = vkcv::Core::create( + applicationName, + 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(applicationName, 1920, 1080, false); + vkcv::Window& window = core.getWindow(windowHandle); + + vkcv::camera::CameraManager cameraManager(window); + + auto particleIndexBuffer = core.createBuffer<uint16_t>(vkcv::BufferType::INDEX, 3, + vkcv::BufferMemoryType::DEVICE_LOCAL); + uint16_t indices[3] = {0, 1, 2}; + particleIndexBuffer.fill(&indices[0], sizeof(indices)); + + vk::Format colorFormat = vk::Format::eR16G16B16A16Sfloat; + // an example attachment for passes that output to the window + const vkcv::AttachmentDescription present_color_attachment( + vkcv::AttachmentOperation::STORE, + vkcv::AttachmentOperation::CLEAR, + colorFormat); + + + vkcv::PassConfig particlePassDefinition({present_color_attachment}); + vkcv::PassHandle particlePass = core.createPass(particlePassDefinition); + + vkcv::PassConfig computePassDefinition({}); + vkcv::PassHandle computePass = core.createPass(computePassDefinition); + + //rotation + float rotationx = 0; + float rotationy = 0; + + // params + float param_h = 0.20; + float param_mass = 0.03; + float param_gasConstant = 3500; + float param_offset = 200; + float param_gravity = -5000; + float param_viscosity = 10; + float param_ABSORBTION = 0.5; + float param_dt = 0.0005; + + if (!particlePass || !computePass) + { + std::cout << "Error. Could not create renderpass. Exiting." << std::endl; + return EXIT_FAILURE; + } + vkcv::shader::GLSLCompiler compiler; + +// comp shader 1 + vkcv::ComputePipelineHandle computePipeline1; + vkcv::DescriptorSetHandle computeDescriptorSet1 = PipelineInit::ComputePipelineInit(&core, vkcv::ShaderStage::COMPUTE, + "shaders/pressure.comp", computePipeline1); +// comp shader 2 + vkcv::ComputePipelineHandle computePipeline2; + vkcv::DescriptorSetHandle computeDescriptorSet2 = PipelineInit::ComputePipelineInit(&core, vkcv::ShaderStage::COMPUTE, + "shaders/force.comp", computePipeline2); + +//comp shader 3 + vkcv::ComputePipelineHandle computePipeline3; + vkcv::DescriptorSetHandle computeDescriptorSet3 = PipelineInit::ComputePipelineInit(&core, vkcv::ShaderStage::COMPUTE, + "shaders/updateData.comp", computePipeline3); + +//comp shader 4 + vkcv::ComputePipelineHandle computePipeline4; + vkcv::DescriptorSetHandle computeDescriptorSet4 = PipelineInit::ComputePipelineInit(&core, vkcv::ShaderStage::COMPUTE, + "shaders/flip.comp", computePipeline4); + +// shader + vkcv::ShaderProgram particleShaderProgram{}; + compiler.compile(vkcv::ShaderStage::VERTEX, "shaders/shader.vert", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + particleShaderProgram.addShader(shaderStage, path); + }); + compiler.compile(vkcv::ShaderStage::FRAGMENT, "shaders/shader_water.frag", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + particleShaderProgram.addShader(shaderStage, path); + }); + + vkcv::DescriptorSetLayoutHandle descriptorSetLayout = core.createDescriptorSetLayout( + particleShaderProgram.getReflectedDescriptors().at(0)); + vkcv::DescriptorSetHandle descriptorSet = core.createDescriptorSet(descriptorSetLayout); + + vkcv::Buffer<glm::vec3> vertexBuffer = core.createBuffer<glm::vec3>( + vkcv::BufferType::VERTEX, + 3 + ); + const std::vector<vkcv::VertexAttachment> vertexAttachments = particleShaderProgram.getVertexAttachments(); + + const std::vector<vkcv::VertexBufferBinding> vertexBufferBindings = { + vkcv::VertexBufferBinding(0, vertexBuffer.getVulkanHandle())}; + + std::vector<vkcv::VertexBinding> bindings; + for (size_t i = 0; i < vertexAttachments.size(); i++) { + bindings.push_back(vkcv::VertexBinding(i, {vertexAttachments[i]})); + } + + const vkcv::VertexLayout particleLayout(bindings); + + vkcv::GraphicsPipelineConfig particlePipelineDefinition{ + particleShaderProgram, + UINT32_MAX, + UINT32_MAX, + particlePass, + {particleLayout}, + {core.getDescriptorSetLayout(descriptorSetLayout).vulkanHandle}, + true}; + particlePipelineDefinition.m_blendMode = vkcv::BlendMode::Additive; + + const std::vector<glm::vec3> vertices = {glm::vec3(-0.012, 0.012, 0), + glm::vec3(0.012, 0.012, 0), + glm::vec3(0, -0.012, 0)}; + + vertexBuffer.fill(vertices); + + vkcv::GraphicsPipelineHandle particlePipeline = core.createGraphicsPipeline(particlePipelineDefinition); + + vkcv::Buffer<glm::vec4> color = core.createBuffer<glm::vec4>( + vkcv::BufferType::UNIFORM, + 1 + ); + + vkcv::Buffer<glm::vec2> position = core.createBuffer<glm::vec2>( + vkcv::BufferType::UNIFORM, + 1 + ); + + int numberParticles = 20000; + std::vector<Particle> particles; + for (int i = 0; i < numberParticles; i++) { + const float lo = 0.6; + const float hi = 0.9; + const float vlo = 0; + const float vhi = 70; + float x = lo + static_cast <float> (rand()) /( static_cast <float> (RAND_MAX/(hi-lo))); + float y = lo + static_cast <float> (rand()) /( static_cast <float> (RAND_MAX/(hi-lo))); + float z = lo + static_cast <float> (rand()) /( static_cast <float> (RAND_MAX/(hi-lo))); + float vx = vlo + static_cast <float> (rand()) /( static_cast <float> (RAND_MAX/(vhi-vlo))); + float vy = vlo + static_cast <float> (rand()) /( static_cast <float> (RAND_MAX/(vhi-vlo))); + float vz = vlo + static_cast <float> (rand()) /( static_cast <float> (RAND_MAX/(vhi-vlo))); + glm::vec3 pos = glm::vec3(x,y,z); + glm::vec3 vel = glm::vec3(vx,vy,vz); + //glm::vec3 vel = glm::vec3(0.0,0.0,0.0); + particles.push_back(Particle(pos, vel)); + } + + vkcv::Buffer<Particle> particleBuffer1 = core.createBuffer<Particle>( + vkcv::BufferType::STORAGE, + numberParticles * sizeof(glm::vec4) * 3 + + ); + + vkcv::Buffer<Particle> particleBuffer2 = core.createBuffer<Particle>( + vkcv::BufferType::STORAGE, + numberParticles * sizeof(glm::vec4) * 3 + ); + + particleBuffer1.fill(particles); + particleBuffer2.fill(particles); + + vkcv::DescriptorWrites setWrites; + setWrites.uniformBufferWrites = {vkcv::BufferDescriptorWrite(0,color.getHandle()), + vkcv::BufferDescriptorWrite(1,position.getHandle())}; + setWrites.storageBufferWrites = { vkcv::BufferDescriptorWrite(2,particleBuffer1.getHandle()), + vkcv::BufferDescriptorWrite(3,particleBuffer2.getHandle())}; + core.writeDescriptorSet(descriptorSet, setWrites); + + vkcv::DescriptorWrites computeWrites; + computeWrites.storageBufferWrites = { vkcv::BufferDescriptorWrite(0,particleBuffer1.getHandle()), + vkcv::BufferDescriptorWrite(1,particleBuffer2.getHandle())}; + + core.writeDescriptorSet(computeDescriptorSet1, computeWrites); + core.writeDescriptorSet(computeDescriptorSet2, computeWrites); + core.writeDescriptorSet(computeDescriptorSet3, computeWrites); + core.writeDescriptorSet(computeDescriptorSet4, computeWrites); + + if (!particlePipeline || !computePipeline1 || !computePipeline2 || !computePipeline3 || !computePipeline4) + { + std::cout << "Error. Could not create graphics pipeline. Exiting." << std::endl; + return EXIT_FAILURE; + } + + const vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle(); + + const vkcv::Mesh renderMesh({vertexBufferBindings}, particleIndexBuffer.getVulkanHandle(), + particleIndexBuffer.getCount()); + vkcv::DescriptorSetUsage descriptorUsage(0, core.getDescriptorSet(descriptorSet).vulkanHandle); + + auto pos = glm::vec2(0.f); + auto spawnPosition = glm::vec3(0.f); + + std::vector<vkcv::DrawcallInfo> drawcalls; + drawcalls.push_back(vkcv::DrawcallInfo(renderMesh, {descriptorUsage}, numberParticles)); + + auto start = std::chrono::system_clock::now(); + + glm::vec4 colorData = glm::vec4(1.0f, 1.0f, 0.0f, 1.0f); + uint32_t camIndex0 = cameraManager.addCamera(vkcv::camera::ControllerType::PILOT); + uint32_t camIndex1 = cameraManager.addCamera(vkcv::camera::ControllerType::TRACKBALL); + + cameraManager.getCamera(camIndex0).setNearFar(0.1, 30); + cameraManager.getCamera(camIndex1).setNearFar(0.1, 30); + + cameraManager.setActiveCamera(1); + + cameraManager.getCamera(camIndex0).setPosition(glm::vec3(0, 0, -2.5)); + cameraManager.getCamera(camIndex1).setPosition(glm::vec3(0.0f, 0.0f, -2.5f)); + cameraManager.getCamera(camIndex1).setCenter(glm::vec3(0.0f, 0.0f, 0.0f)); + + auto swapchainExtent = core.getSwapchain(window.getSwapchainHandle()).getExtent(); + + vkcv::ImageHandle colorBuffer = core.createImage( + colorFormat, + swapchainExtent.width, + swapchainExtent.height, + 1, false, true, true + ).getHandle(); + BloomAndFlares bloomAndFlares(&core, colorFormat, swapchainExtent.width, swapchainExtent.height); + + //tone mapping shader & pipeline + vkcv::ComputePipelineHandle tonemappingPipe; + vkcv::DescriptorSetHandle tonemappingDescriptor = PipelineInit::ComputePipelineInit(&core, vkcv::ShaderStage::COMPUTE, + "shaders/tonemapping.comp", tonemappingPipe); + + + while (vkcv::Window::hasOpenWindow()) { + vkcv::Window::pollEvents(); + + uint32_t swapchainWidth, swapchainHeight; + if (!core.beginFrame(swapchainWidth, swapchainHeight, windowHandle)) { + continue; + } + + color.fill(&colorData); + position.fill(&pos); + + auto end = std::chrono::system_clock::now(); + float deltatime = 0.000001 * static_cast<float>( std::chrono::duration_cast<std::chrono::microseconds>(end - start).count() ); + start = end; + + cameraManager.update(deltatime); + + // split view and projection to allow for easy billboarding in shader + struct { + glm::mat4 view; + glm::mat4 projection; + } renderingMatrices; + + glm::vec3 gravityDir = glm::rotate(glm::mat4(1.0), glm::radians(rotationx), glm::vec3(0.f,0.f,1.f)) * glm::vec4(0.f,1.f,0.f,0.f); + gravityDir = glm::rotate(glm::mat4(1.0), glm::radians(rotationy), glm::vec3(0.f,1.f,0.f)) * glm::vec4(gravityDir,0.f); + + renderingMatrices.view = cameraManager.getActiveCamera().getView(); + renderingMatrices.view = glm::rotate(renderingMatrices.view, glm::radians(rotationx), glm::vec3(0.f, 0.f, 1.f)); + renderingMatrices.view = glm::rotate(renderingMatrices.view, glm::radians(rotationy), glm::vec3(0.f, 1.f, 0.f)); + renderingMatrices.projection = cameraManager.getActiveCamera().getProjection(); + + // keybindings rotation + if (glfwGetKey(window.getWindow(), GLFW_KEY_LEFT) == GLFW_PRESS) + rotationx += deltatime * 50; + if (glfwGetKey(window.getWindow(), GLFW_KEY_RIGHT) == GLFW_PRESS) + rotationx -= deltatime * 50; + + if (glfwGetKey(window.getWindow(), GLFW_KEY_UP) == GLFW_PRESS) + rotationy += deltatime * 50; + if (glfwGetKey(window.getWindow(), GLFW_KEY_DOWN) == GLFW_PRESS) + rotationy -= deltatime * 50; + + // keybindings params + if (glfwGetKey(window.getWindow(), GLFW_KEY_T) == GLFW_PRESS) + param_h += deltatime * 0.2; + if (glfwGetKey(window.getWindow(), GLFW_KEY_G) == GLFW_PRESS) + param_h -= deltatime * 0.2; + + if (glfwGetKey(window.getWindow(), GLFW_KEY_Y) == GLFW_PRESS) + param_mass += deltatime * 0.2; + if (glfwGetKey(window.getWindow(), GLFW_KEY_H) == GLFW_PRESS) + param_mass -= deltatime * 0.2; + + if (glfwGetKey(window.getWindow(), GLFW_KEY_U) == GLFW_PRESS) + param_gasConstant += deltatime * 1500.0; + if (glfwGetKey(window.getWindow(), GLFW_KEY_J) == GLFW_PRESS) + param_gasConstant -= deltatime * 1500.0; + + if (glfwGetKey(window.getWindow(), GLFW_KEY_I) == GLFW_PRESS) + param_offset += deltatime * 400.0; + if (glfwGetKey(window.getWindow(), GLFW_KEY_K) == GLFW_PRESS) + param_offset -= deltatime * 400.0; + + if (glfwGetKey(window.getWindow(), GLFW_KEY_O) == GLFW_PRESS) + param_viscosity = 50; + if (glfwGetKey(window.getWindow(), GLFW_KEY_L) == GLFW_PRESS) + param_viscosity = 1200; + + + auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics); + + glm::vec4 pushData[3] = { + glm::vec4(param_h,param_mass,param_gasConstant,param_offset), + glm::vec4(param_gravity,param_viscosity,param_ABSORBTION,param_dt), + glm::vec4(gravityDir.x,gravityDir.y,gravityDir.z,(float)numberParticles) + }; + + std::cout << "h: " << param_h << " | mass: " << param_mass << " | gasConstant: " << param_gasConstant << " | offset: " << param_offset << " | viscosity: " << param_viscosity << std::endl; + + vkcv::PushConstants pushConstantsCompute (sizeof(pushData)); + pushConstantsCompute.appendDrawcall(pushData); + + uint32_t computeDispatchCount[3] = {static_cast<uint32_t> (std::ceil(numberParticles/256.f)),1,1}; + + core.recordComputeDispatchToCmdStream(cmdStream, + computePipeline1, + computeDispatchCount, + {vkcv::DescriptorSetUsage(0,core.getDescriptorSet(computeDescriptorSet1).vulkanHandle)}, + pushConstantsCompute); + + core.recordBufferMemoryBarrier(cmdStream, particleBuffer1.getHandle()); + core.recordBufferMemoryBarrier(cmdStream, particleBuffer2.getHandle()); + + core.recordComputeDispatchToCmdStream(cmdStream, + computePipeline2, + computeDispatchCount, + {vkcv::DescriptorSetUsage(0,core.getDescriptorSet(computeDescriptorSet2).vulkanHandle)}, + pushConstantsCompute); + + core.recordBufferMemoryBarrier(cmdStream, particleBuffer1.getHandle()); + core.recordBufferMemoryBarrier(cmdStream, particleBuffer2.getHandle()); + + core.recordComputeDispatchToCmdStream(cmdStream, + computePipeline3, + computeDispatchCount, + { vkcv::DescriptorSetUsage(0,core.getDescriptorSet(computeDescriptorSet3).vulkanHandle) }, + pushConstantsCompute); + + core.recordBufferMemoryBarrier(cmdStream, particleBuffer1.getHandle()); + core.recordBufferMemoryBarrier(cmdStream, particleBuffer2.getHandle()); + + core.recordComputeDispatchToCmdStream(cmdStream, + computePipeline4, + computeDispatchCount, + { vkcv::DescriptorSetUsage(0,core.getDescriptorSet(computeDescriptorSet4).vulkanHandle) }, + pushConstantsCompute); + + core.recordBufferMemoryBarrier(cmdStream, particleBuffer1.getHandle()); + core.recordBufferMemoryBarrier(cmdStream, particleBuffer2.getHandle()); + + + // bloomAndFlares & tonemapping + vkcv::PushConstants pushConstantsDraw (sizeof(renderingMatrices)); + pushConstantsDraw.appendDrawcall(renderingMatrices); + + core.recordDrawcallsToCmdStream( + cmdStream, + particlePass, + particlePipeline, + pushConstantsDraw, + {drawcalls}, + { colorBuffer }, + windowHandle); + + bloomAndFlares.execWholePipeline(cmdStream, colorBuffer); + + core.prepareImageForStorage(cmdStream, colorBuffer); + core.prepareImageForStorage(cmdStream, swapchainInput); + + vkcv::DescriptorWrites tonemappingDescriptorWrites; + tonemappingDescriptorWrites.storageImageWrites = { + vkcv::StorageImageDescriptorWrite(0, colorBuffer), + vkcv::StorageImageDescriptorWrite(1, swapchainInput) + }; + core.writeDescriptorSet(tonemappingDescriptor, tonemappingDescriptorWrites); + + uint32_t tonemappingDispatchCount[3]; + tonemappingDispatchCount[0] = std::ceil(swapchainExtent.width / 8.f); + tonemappingDispatchCount[1] = std::ceil(swapchainExtent.height / 8.f); + tonemappingDispatchCount[2] = 1; + + core.recordComputeDispatchToCmdStream( + cmdStream, + tonemappingPipe, + tonemappingDispatchCount, + {vkcv::DescriptorSetUsage(0, core.getDescriptorSet(tonemappingDescriptor).vulkanHandle) }, + vkcv::PushConstants(0)); + + core.prepareSwapchainImageForPresent(cmdStream); + core.submitCommandStream(cmdStream); + core.endFrame(windowHandle); + } + + return 0; +} diff --git a/src/vkcv/DescriptorManager.cpp b/src/vkcv/DescriptorManager.cpp index 99e6df6f2cf605e7fe4081b6aeb02950c9155318..8c8238f25fb159b7cc665013e9fc3de9b2fda15a 100644 --- a/src/vkcv/DescriptorManager.cpp +++ b/src/vkcv/DescriptorManager.cpp @@ -79,7 +79,7 @@ namespace vkcv } //create the descriptor set's layout from the binding data gathered above - vk::DescriptorSetLayout vulkanHandle = nullptr; + vk::DescriptorSetLayout vulkanHandle; vk::DescriptorSetLayoutCreateInfo layoutInfo({}, bindingsVector); auto result = m_Device.createDescriptorSetLayout(&layoutInfo, nullptr, &vulkanHandle); if (result != vk::Result::eSuccess) { @@ -96,7 +96,7 @@ namespace vkcv { //create and allocate the set based on the layout provided DescriptorSetLayout setLayout = m_DescriptorSetLayouts[setLayoutHandle.getId()]; - vk::DescriptorSet vulkanHandle = nullptr; + vk::DescriptorSet vulkanHandle; vk::DescriptorSetAllocateInfo allocInfo(m_Pools.back(), 1, &setLayout.vulkanHandle); auto result = m_Device.allocateDescriptorSets(&allocInfo, &vulkanHandle); if(result != vk::Result::eSuccess)