diff --git a/Makefile b/Makefile index 3b0e75c..73487f8 100644 --- a/Makefile +++ b/Makefile @@ -11,6 +11,8 @@ endif shaders: glslangValidator -V gradient.comp -o gradient.comp.spv + glslangValidator -V sky.comp -o sky.comp.spv + glslangValidator -V gradient_color.comp -o gradient_color.comp.spv %.cpp : %.rl ragel -I $(ROOT_DIR) -G1 -o $@ $< diff --git a/gradient_color.comp b/gradient_color.comp new file mode 100644 index 0000000..4627cb4 --- /dev/null +++ b/gradient_color.comp @@ -0,0 +1,31 @@ +#version 460 + +layout (local_size_x = 16, local_size_y = 16) in; + +layout(rgba16f,set = 0, binding = 0) uniform image2D image; + +//push constants block +layout( push_constant ) uniform constants +{ + vec4 data1; + vec4 data2; + vec4 data3; + vec4 data4; +} PushConstants; + +void main() +{ + ivec2 texelCoord = ivec2(gl_GlobalInvocationID.xy); + + ivec2 size = imageSize(image); + + vec4 topColor = PushConstants.data1; + vec4 bottomColor = PushConstants.data2; + + if(texelCoord.x < size.x && texelCoord.y < size.y) + { + float blend = float(texelCoord.y)/(size.y); + + imageStore(image, texelCoord, mix(topColor,bottomColor, blend)); + } +} diff --git a/gradient_color.comp.spv b/gradient_color.comp.spv new file mode 100644 index 0000000..56c176a Binary files /dev/null and b/gradient_color.comp.spv differ diff --git a/sky.comp b/sky.comp new file mode 100644 index 0000000..0503f9a --- /dev/null +++ b/sky.comp @@ -0,0 +1,92 @@ +#version 450 +layout (local_size_x = 16, local_size_y = 16) in; +layout(rgba8,set = 0, binding = 0) uniform image2D image; + +// License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. + +//push constants block +layout( push_constant ) uniform constants +{ + vec4 data1; + vec4 data2; + vec4 data3; + vec4 data4; +} PushConstants; + +// Return random noise in the range [0.0, 1.0], as a function of x. +float Noise2d( in vec2 x ) +{ + float xhash = cos( x.x * 37.0 ); + float yhash = cos( x.y * 57.0 ); + return fract( 415.92653 * ( xhash + yhash ) ); +} + +// Convert Noise2d() into a "star field" by stomping everthing below fThreshhold to zero. +float NoisyStarField( in vec2 vSamplePos, float fThreshhold ) +{ + float StarVal = Noise2d( vSamplePos ); + if ( StarVal >= fThreshhold ) + StarVal = pow( (StarVal - fThreshhold)/(1.0 - fThreshhold), 6.0 ); + else + StarVal = 0.0; + return StarVal; +} + +// Stabilize NoisyStarField() by only sampling at integer values. +float StableStarField( in vec2 vSamplePos, float fThreshhold ) +{ + // Linear interpolation between four samples. + // Note: This approach has some visual artifacts. + // There must be a better way to "anti alias" the star field. + float fractX = fract( vSamplePos.x ); + float fractY = fract( vSamplePos.y ); + vec2 floorSample = floor( vSamplePos ); + float v1 = NoisyStarField( floorSample, fThreshhold ); + float v2 = NoisyStarField( floorSample + vec2( 0.0, 1.0 ), fThreshhold ); + float v3 = NoisyStarField( floorSample + vec2( 1.0, 0.0 ), fThreshhold ); + float v4 = NoisyStarField( floorSample + vec2( 1.0, 1.0 ), fThreshhold ); + + float StarVal = v1 * ( 1.0 - fractX ) * ( 1.0 - fractY ) + + v2 * ( 1.0 - fractX ) * fractY + + v3 * fractX * ( 1.0 - fractY ) + + v4 * fractX * fractY; + return StarVal; +} + +void mainImage( out vec4 fragColor, in vec2 fragCoord ) +{ + vec2 iResolution = imageSize(image); + // Sky Background Color + //vec3 vColor = vec3( 0.1, 0.2, 0.4 ) * fragCoord.y / iResolution.y; + vec3 vColor = PushConstants.data1.xyz * fragCoord.y / iResolution.y; + + // Note: Choose fThreshhold in the range [0.99, 0.9999]. + // Higher values (i.e., closer to one) yield a sparser starfield. + float StarFieldThreshhold = PushConstants.data1.w;//0.97; + + // Stars with a slow crawl. + float xRate = 0.2; + float yRate = -0.06; + vec2 vSamplePos = fragCoord.xy + vec2( xRate * float( 1 ), yRate * float( 1 ) ); + float StarVal = StableStarField( vSamplePos, StarFieldThreshhold ); + vColor += vec3( StarVal ); + + fragColor = vec4(vColor, 1.0); +} + + + +void main() +{ + vec4 value = vec4(0.0, 0.0, 0.0, 1.0); + ivec2 texelCoord = ivec2(gl_GlobalInvocationID.xy); + ivec2 size = imageSize(image); + if(texelCoord.x < size.x && texelCoord.y < size.y) + { + vec4 color; + mainImage(color,texelCoord); + + imageStore(image, texelCoord, color); + } +} + diff --git a/sky.comp.spv b/sky.comp.spv new file mode 100644 index 0000000..b4003ab Binary files /dev/null and b/sky.comp.spv differ diff --git a/vk_engine.cpp b/vk_engine.cpp index bf61633..da914ab 100644 --- a/vk_engine.cpp +++ b/vk_engine.cpp @@ -19,7 +19,7 @@ #define VMA_IMPLEMENTATION #include "vk_mem_alloc.h" -constexpr bool bUseValidationLayers = false; +constexpr bool bUseValidationLayers = true; VulkanEngine* loadedEngine = nullptr; @@ -202,17 +202,35 @@ void VulkanEngine::run() continue; } - ImGui_ImplVulkan_NewFrame(); - ImGui_ImplSDL2_NewFrame(); - ImGui::NewFrame(); - - ImGui::ShowDemoWindow(); - ImGui::Render(); - + render_imgui(); draw(); } } +void VulkanEngine::render_imgui() { + ImGui_ImplVulkan_NewFrame(); + ImGui_ImplSDL2_NewFrame(); + + ImGui::NewFrame(); + + if (ImGui::Begin("background")) { + + ComputeEffect& selected = backgroundEffects[currentBackgroundEffect]; + + ImGui::Text("Selected effect: %s", selected.name); + + ImGui::SliderInt("Effect Index", ¤tBackgroundEffect,0, backgroundEffects.size() - 1); + + ImGui::InputFloat4("data1",(float*)& selected.data.data1); + ImGui::InputFloat4("data2",(float*)& selected.data.data2); + ImGui::InputFloat4("data3",(float*)& selected.data.data3); + ImGui::InputFloat4("data4",(float*)& selected.data.data4); + } + ImGui::End(); + + ImGui::Render(); +} + void VulkanEngine::init_vulkan() { vkb::InstanceBuilder builder; @@ -394,13 +412,14 @@ void VulkanEngine::init_sync_structures() { void VulkanEngine::draw_background(VkCommandBuffer cmd) { -// bind the gradient drawing compute pipeline - vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, _gradientPipeline); + ComputeEffect &effect = backgroundEffects[currentBackgroundEffect]; + + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, effect.pipeline); - // bind the descriptor set containing the draw image for the compute pipeline vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, _gradientPipelineLayout, 0, 1, &_drawImageDescriptors, 0, nullptr); - // execute the compute pipeline dispatch. We are using 16x16 workgroup size so we need to divide by it + vkCmdPushConstants(cmd, _gradientPipelineLayout, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(ComputePushConstants), &effect.data); + vkCmdDispatch(cmd, std::ceil(_drawExtent.width / 16.0), std::ceil(_drawExtent.height / 16.0), 1); } @@ -461,19 +480,28 @@ void VulkanEngine::init_background_pipelines() computeLayout.pSetLayouts = &_drawImageDescriptorLayout; computeLayout.setLayoutCount = 1; + VkPushConstantRange pushConstant{}; + pushConstant.offset = 0; + pushConstant.size = sizeof(ComputePushConstants) ; + pushConstant.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + + computeLayout.pPushConstantRanges = &pushConstant; + computeLayout.pushConstantRangeCount = 1; + VK_CHECK(vkCreatePipelineLayout(_device, &computeLayout, nullptr, &_gradientPipelineLayout)); - VkShaderModule computeDrawShader; + VkShaderModule gradientShader; + bool good = vkutil::load_shader_module("gradient_color.comp.spv", _device, &gradientShader); + assert(good && "failed to load gradient_color.comp.spv"); - if(!vkutil::load_shader_module("gradient.comp.spv", _device, &computeDrawShader)) { - std::println("Error when building compute shader"); - } + VkShaderModule skyShader; + good = vkutil::load_shader_module("sky.comp.spv", _device, &skyShader); VkPipelineShaderStageCreateInfo stageinfo{}; stageinfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; stageinfo.pNext = nullptr; stageinfo.stage = VK_SHADER_STAGE_COMPUTE_BIT; - stageinfo.module = computeDrawShader; + stageinfo.module = gradientShader; stageinfo.pName = "main"; VkComputePipelineCreateInfo computePipelineCreateInfo{}; @@ -482,13 +510,37 @@ void VulkanEngine::init_background_pipelines() computePipelineCreateInfo.layout = _gradientPipelineLayout; computePipelineCreateInfo.stage = stageinfo; - VK_CHECK(vkCreateComputePipelines(_device,VK_NULL_HANDLE,1, &computePipelineCreateInfo, nullptr, &_gradientPipeline)); + ComputeEffect gradient; + gradient.layout = _gradientPipelineLayout; + gradient.name = "gradient"; + gradient.data = {}; + gradient.data.data1 = glm::vec4(1, 0, 0, 1); + gradient.data.data2 = glm::vec4(0, 0, 1, 1); - vkDestroyShaderModule(_device, computeDrawShader, nullptr); + VK_CHECK(vkCreateComputePipelines(_device, VK_NULL_HANDLE, 1, &computePipelineCreateInfo, nullptr, &gradient.pipeline)); - _mainDeletionQueue.push_function([&]() { + // change the shader module only to create the sky + computePipelineCreateInfo.stage.module = skyShader; + ComputeEffect sky; + sky.layout = _gradientPipelineLayout; + sky.name = "sky"; + sky.data = {}; + // default sky + sky.data.data1 = glm::vec4(0.1, 0.2, 0.4, 0.97); + + VK_CHECK(vkCreateComputePipelines(_device, VK_NULL_HANDLE, 1, &computePipelineCreateInfo, nullptr, &sky.pipeline)); + + + backgroundEffects.push_back(gradient); + backgroundEffects.push_back(sky); + + vkDestroyShaderModule(_device, gradientShader, nullptr); + vkDestroyShaderModule(_device, skyShader, nullptr); + + _mainDeletionQueue.push_function([=,this]() { vkDestroyPipelineLayout(_device, _gradientPipelineLayout, nullptr); - vkDestroyPipeline(_device, _gradientPipeline, nullptr); + vkDestroyPipeline(_device, sky.pipeline, nullptr); + vkDestroyPipeline(_device, sky.pipeline, nullptr); }); } diff --git a/vk_engine.h b/vk_engine.h index 712beac..5a7903b 100644 --- a/vk_engine.h +++ b/vk_engine.h @@ -6,6 +6,22 @@ #include #include +struct ComputePushConstants { + glm::vec4 data1; + glm::vec4 data2; + glm::vec4 data3; + glm::vec4 data4; +}; + +struct ComputeEffect { + const char *name; + VkPipeline pipeline; + VkPipelineLayout layout; + + ComputePushConstants data; +}; + + struct DeletionQueue { std::deque> deletors; @@ -85,6 +101,10 @@ public: struct SDL_Window* _window{ nullptr }; DeletionQueue _mainDeletionQueue; + // imgui shader stuff + std::vector backgroundEffects; + int currentBackgroundEffect{0}; + static VulkanEngine& Get(); //initializes everything in the engine @@ -115,4 +135,5 @@ private: void destroy_swapchain(); void draw_background(VkCommandBuffer cmd); void draw_imgui(VkCommandBuffer cmd, VkImageView targetImageView); + void render_imgui(); };