diff --git a/projects/fire_works/shaders/add.comp b/projects/fire_works/shaders/add.comp index a13dc0eef12ee0bf58828e71c20bf5a00c05b642..d706c1fcd9517c52eedaa7c15fde86fffbce6940 100644 --- a/projects/fire_works/shaders/add.comp +++ b/projects/fire_works/shaders/add.comp @@ -9,8 +9,16 @@ 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" + +#define NUM_VOXEL_SAMPLES 32 + void main() { const ivec2 res = imageSize(outImage); @@ -25,20 +33,33 @@ void main() { vec4 outTrails = imageLoad(inTrails, uv); vec2 pos = (vec2(uv) + vec2(0.5f)) / vec2(res); + vec2 size = vec2(textureSize(sampler2D(voxelTexture, voxelSampler), 0)); - vec4 outSamples = texture(sampler2D(voxelTexture, voxelSampler), pos); + vec4 outSamples = vec4(0.0f); - // TODO: add noise to the smoke here! + const uint globalID = uv.y + uv.x * res.y; - vec4 result = vec4( - outParticles.rgb * outParticles.a + - outSmoke.rgb * outSmoke.a + - outTrails.rgb * outTrails.a, + for (uint i = 0; i < NUM_VOXEL_SAMPLES; i++) { + vec2 noise = vec2( + randomData[(globalID * NUM_VOXEL_SAMPLES * 2 + i * 2 + 0) % randomData.length()], + randomData[(globalID * NUM_VOXEL_SAMPLES * 2 + i * 2 + 1) % randomData.length()] + ); - outParticles.a + outSmoke.a + outTrails.a - ); + outSamples += texture( + sampler2D(voxelTexture, voxelSampler), + pos + noise * (NUM_VOXEL_SAMPLES - 1.0f) / size + ) * max(0.0f, 1.0f - length(noise)); + } + + outSamples /= NUM_VOXEL_SAMPLES; + + // TODO: add noise to the smoke here! - result = outSamples; + vec4 result = vec4(0.0f); + result += vec4(outParticles.rgb * outParticles.a, outParticles.a); + result += vec4(outSmoke.rgb * outSmoke.a, outSmoke.a); + result += vec4(outTrails.rgb * outTrails.a, outTrails.a); + result += vec4(outSamples.rgb * outSamples.a, outSamples.a); result.r = clamp(result.r, 0, 1); result.g = clamp(result.g, 0, 1); diff --git a/projects/fire_works/shaders/fluid.comp b/projects/fire_works/shaders/fluid.comp new file mode 100644 index 0000000000000000000000000000000000000000..fc382f33866ae101a1ce94fa29a0176b7ce55f0c --- /dev/null +++ b/projects/fire_works/shaders/fluid.comp @@ -0,0 +1,79 @@ +#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; + + 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/physics.inc b/projects/fire_works/shaders/physics.inc index 332d695c764f7e839e7595281b48cd550361eb49..1f92f1cf06e69978f28307cbc9539022993f6a9b 100644 --- a/projects/fire_works/shaders/physics.inc +++ b/projects/fire_works/shaders/physics.inc @@ -6,7 +6,7 @@ const float pi = 3.14159f; const float g = 9.81f; const float friction = 0.001f; -const float flowSpeed = 1.0f; +const float flowRate = 0.75f; const float mediumDensity = 0.0001f; diff --git a/projects/fire_works/shaders/sample.comp b/projects/fire_works/shaders/sample.comp index df3369925bf1e11b128e837176882a270ef3996e..12e342d132b072f2791248126fe6cc078bba032d 100644 --- a/projects/fire_works/shaders/sample.comp +++ b/projects/fire_works/shaders/sample.comp @@ -3,11 +3,7 @@ #include "voxel.inc" -layout(set=0, binding=0, r32ui) readonly uniform uimage3D voxelRed; -layout(set=0, binding=1, r32ui) readonly uniform uimage3D voxelGreen; -layout(set=0, binding=2, r32ui) readonly uniform uimage3D voxelBlue; -layout(set=0, binding=3, r32ui) readonly uniform uimage3D voxelDensity; - +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; @@ -21,21 +17,18 @@ void main() { ivec2 uv = ivec2(gl_GlobalInvocationID.xy); - const ivec3 voxelRes = imageSize(voxelDensity); + const ivec3 voxelRes = imageSize(voxelImage); vec4 voxel = vec4(0.0f); for (int i = 0; i < voxelRes.z; i++) { const ivec3 voxelPos = ivec3(uv, i); - const float red = voxel_read(voxelRed, voxelPos); - const float green = voxel_read(voxelGreen, voxelPos); - const float blue = voxel_read(voxelBlue, voxelPos); - const float density = voxel_read(voxelDensity, voxelPos); + vec4 data = imageLoad(voxelImage, voxelPos); voxel = vec4( - (voxel.rgb + vec3(red, green, blue) * density) * (1.0f - voxel.a), - voxel.a + (density) * (1.0f - voxel.a) + (voxel.rgb + data.rgb * data.a) * (1.0f - voxel.a), + voxel.a + (data.a) * (1.0f - voxel.a) ); } 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/src/main.cpp b/projects/fire_works/src/main.cpp index de2a98b69b3119945bd6df887b7a9cd02a08e5ec..d430216f864ac3e7c5e4d4fa1f6339710b64cb93 100644 --- a/projects/fire_works/src/main.cpp +++ b/projects/fire_works/src/main.cpp @@ -76,7 +76,7 @@ struct draw_smoke_t { #define PARTICLE_COUNT (1024) #define SMOKE_COUNT (512) #define TRAIL_COUNT (2048) -#define RANDOM_DATA_LENGTH (1024) +#define RANDOM_DATA_LENGTH (4096) #define POINT_COUNT (2048 * 256) void InitializeParticles(std::vector<particle_t> &particles) { @@ -734,6 +734,23 @@ int main(int argc, const char **argv) { 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, @@ -791,6 +808,32 @@ int main(int argc, const char **argv) { { 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); @@ -801,7 +844,7 @@ int main(int argc, const char **argv) { vkcv::ComputePipelineHandle voxelSamplePipeline = core.createComputePipeline({ voxelSampleShader, - { voxelDescriptorSetLayout, samplesDescriptorSetLayout } + { voxelOutDescriptorSetLayout, samplesDescriptorSetLayout } }); auto voxelDescriptorSet = core.createDescriptorSet(voxelDescriptorSetLayout); @@ -815,6 +858,35 @@ int main(int argc, const char **argv) { 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); { @@ -836,7 +908,7 @@ int main(int argc, const char **argv) { vkcv::ComputePipelineHandle addPipe = core.createComputePipeline({ addShader, - { addDescriptorLayout } + { addDescriptorLayout, generationDescriptorLayout } }); vkcv::ShaderProgram tonemappingShader; @@ -1113,13 +1185,48 @@ int main(int argc, const char **argv) { ); core.recordEndDebugLabel(cmdStream); - core.recordBeginDebugLabel(cmdStream, "Sample voxels", { 0.5f, 0.5f, 1.0f, 1.0f }); + 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]; @@ -1132,7 +1239,7 @@ int main(int argc, const char **argv) { voxelSamplePipeline, sampleDispatchCount, { - vkcv::DescriptorSetUsage(0, voxelDescriptorSet), + vkcv::DescriptorSetUsage(0, voxelOutDescriptorSet), vkcv::DescriptorSetUsage(1, samplesDescriptorSet) }, vkcv::PushConstants(0) @@ -1163,7 +1270,10 @@ int main(int argc, const char **argv) { cmdStream, addPipe, colorDispatchCount, - { vkcv::DescriptorSetUsage(0, addDescriptor) }, + { + vkcv::DescriptorSetUsage(0, addDescriptor), + vkcv::DescriptorSetUsage(1, generationDescriptorSet) + }, vkcv::PushConstants(0) );