From afcec095a75950bb6dfbfbf1bea2bd7f7fb1d58c Mon Sep 17 00:00:00 2001 From: Michael Mason Date: Mon, 21 Oct 2024 22:23:18 -0400 Subject: [PATCH 01/10] fixed grass shader --- .gitignore | 6 ++++++ CMakeLists.txt | 1 - src/CMakeLists.txt | 2 -- src/shaders/grass.tesc | 4 ++++ 4 files changed, 10 insertions(+), 3 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3ba7471 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +################################################################################ +# This .gitignore file was automatically created by Microsoft(R) Visual Studio. +################################################################################ + +/build +/bin diff --git a/CMakeLists.txt b/CMakeLists.txt index 860a279..9d5a7e0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,7 +18,6 @@ ELSE(USE_D2D_WSI) find_package(XCB REQUIRED) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DVK_USE_PLATFORM_XCB_KHR") ENDIF(USE_D2D_WSI) - # Todo : android? ENDIF(WIN32) # Set preprocessor defines diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index aea02fe..54fa1ee 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -43,8 +43,6 @@ foreach(SHADER_SOURCE ${SHADER_SOURCES}) ExternalTarget("Shaders" ${fname}.spv) add_dependencies(vulkan_grass_rendering ${fname}.spv) endif(WIN32) - - # TODO: Build shaders on not windows endforeach() target_link_libraries(vulkan_grass_rendering ${ASSIMP_LIBRARIES} Vulkan::Vulkan glfw) diff --git a/src/shaders/grass.tesc b/src/shaders/grass.tesc index f9ffd07..b2131c3 100644 --- a/src/shaders/grass.tesc +++ b/src/shaders/grass.tesc @@ -9,6 +9,10 @@ layout(set = 0, binding = 0) uniform CameraBufferObject { } camera; // TODO: Declare tessellation control shader inputs and outputs +in gl_PerVertex +{ + vec4 gl_Position; +} gl_in[gl_MaxPatchVertices]; void main() { // Don't move the origin location of the patch From 516a30d58e129a4c57e9c8281d0db3d7d7e74a02 Mon Sep 17 00:00:00 2001 From: Michael Mason Date: Tue, 22 Oct 2024 20:07:09 -0400 Subject: [PATCH 02/10] setup descriptor sets for compute --- src/Blades.cpp | 4 +- src/Blades.h | 3 ++ src/Renderer.cpp | 102 ++++++++++++++++++++++++++++++++++++++- src/Renderer.h | 4 ++ src/main.cpp | 4 ++ src/shaders/compute.comp | 25 +++++++++- 6 files changed, 136 insertions(+), 6 deletions(-) diff --git a/src/Blades.cpp b/src/Blades.cpp index 80e3d76..6a342be 100644 --- a/src/Blades.cpp +++ b/src/Blades.cpp @@ -44,8 +44,8 @@ Blades::Blades(Device* device, VkCommandPool commandPool, float planeDim) : Mode indirectDraw.firstVertex = 0; indirectDraw.firstInstance = 0; - BufferUtils::CreateBufferFromData(device, commandPool, blades.data(), NUM_BLADES * sizeof(Blade), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, bladesBuffer, bladesBufferMemory); - BufferUtils::CreateBuffer(device, NUM_BLADES * sizeof(Blade), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, culledBladesBuffer, culledBladesBufferMemory); + BufferUtils::CreateBufferFromData(device, commandPool, blades.data(), getBladesBufferSize(), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, bladesBuffer, bladesBufferMemory); + BufferUtils::CreateBuffer(device, getBladesBufferSize(), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, culledBladesBuffer, culledBladesBufferMemory); BufferUtils::CreateBufferFromData(device, commandPool, &indirectDraw, sizeof(BladeDrawIndirect), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT, numBladesBuffer, numBladesBufferMemory); } diff --git a/src/Blades.h b/src/Blades.h index 9bd1eed..f41f686 100644 --- a/src/Blades.h +++ b/src/Blades.h @@ -84,5 +84,8 @@ class Blades : public Model { VkBuffer GetBladesBuffer() const; VkBuffer GetCulledBladesBuffer() const; VkBuffer GetNumBladesBuffer() const; + static constexpr size_t getBladesBufferSize() { + return NUM_BLADES * sizeof(Blade); + } ~Blades(); }; diff --git a/src/Renderer.cpp b/src/Renderer.cpp index b445d04..1c1ae8f 100644 --- a/src/Renderer.cpp +++ b/src/Renderer.cpp @@ -198,6 +198,35 @@ void Renderer::CreateComputeDescriptorSetLayout() { // TODO: Create the descriptor set layout for the compute pipeline // Remember this is like a class definition stating why types of information // will be stored at each binding + + VkDescriptorSetLayoutBinding inputGrass = {}; + inputGrass.binding = 0; + inputGrass.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + inputGrass.descriptorCount = 1; + inputGrass.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + + VkDescriptorSetLayoutBinding outputGrass = {}; + outputGrass.binding = 1; + outputGrass.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + outputGrass.descriptorCount = 1; + outputGrass.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + + VkDescriptorSetLayoutBinding numBlades = {}; + numBlades.binding = 2; + numBlades.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + numBlades.descriptorCount = 1; + numBlades.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + + std::vector bindings = { inputGrass, outputGrass, numBlades }; + + VkDescriptorSetLayoutCreateInfo layoutInfo = {}; + layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + layoutInfo.bindingCount = bindings.size(); + layoutInfo.pBindings = bindings.data(); + + if (vkCreateDescriptorSetLayout(logicalDevice, &layoutInfo, nullptr, &computeDescriptorSetLayout) != VK_SUCCESS) { + throw std::runtime_error("Failed to create descriptor set layout"); + } } void Renderer::CreateDescriptorPool() { @@ -216,6 +245,7 @@ void Renderer::CreateDescriptorPool() { { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER , 1 }, // TODO: Add any additional types and counts of descriptors you will need to allocate + { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 3 * static_cast(scene->GetBlades().size())} }; VkDescriptorPoolCreateInfo poolInfo = {}; @@ -360,6 +390,69 @@ void Renderer::CreateTimeDescriptorSet() { void Renderer::CreateComputeDescriptorSets() { // TODO: Create Descriptor sets for the compute pipeline // The descriptors should point to Storage buffers which will hold the grass blades, the culled grass blades, and the output number of grass blades + computeDescriptorSets.resize(scene->GetBlades().size()); + + // Describe the desciptor set + VkDescriptorSetLayout layouts[] = { computeDescriptorSetLayout }; + VkDescriptorSetAllocateInfo allocInfo = {}; + allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + allocInfo.descriptorPool = descriptorPool; + allocInfo.descriptorSetCount = static_cast(computeDescriptorSets.size()); + allocInfo.pSetLayouts = layouts; + + // Allocate descriptor sets + if (vkAllocateDescriptorSets(logicalDevice, &allocInfo, computeDescriptorSets.data()) != VK_SUCCESS) { + throw std::runtime_error("Failed to allocate descriptor set"); + } + + std::vector descriptorWrites(3 * computeDescriptorSets.size()); + + for (uint32_t i = 0; i < scene->GetBlades().size(); ++i) { + // input grass + VkDescriptorBufferInfo inputGrassBufferInfo = {}; + inputGrassBufferInfo.buffer = scene->GetBlades()[i]->GetBladesBuffer(); + inputGrassBufferInfo.offset = 0; + inputGrassBufferInfo.range = scene->GetBlades()[i]->getBladesBufferSize(); + + // culled grass + VkDescriptorBufferInfo culledGrassBufferInfo = {}; + culledGrassBufferInfo.buffer = scene->GetBlades()[i]->GetCulledBladesBuffer(); + culledGrassBufferInfo.offset = 0; + culledGrassBufferInfo.range = scene->GetBlades()[i]->getBladesBufferSize(); + + // num blades (indirect draw call buffer) + VkDescriptorBufferInfo numBladesBufferInfo = {}; + numBladesBufferInfo.buffer = scene->GetBlades()[i]->GetNumBladesBuffer(); + numBladesBufferInfo.offset = 0; + numBladesBufferInfo.range = sizeof(BladeDrawIndirect); + + descriptorWrites[3 * i + 0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrites[3 * i + 0].dstSet = computeDescriptorSets[i]; + descriptorWrites[3 * i + 0].dstBinding = 0; + descriptorWrites[3 * i + 0].dstArrayElement = 0; + descriptorWrites[3 * i + 0].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + descriptorWrites[3 * i + 0].descriptorCount = 1; + descriptorWrites[3 * i + 0].pBufferInfo = &inputGrassBufferInfo; + + descriptorWrites[3 * i + 1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrites[3 * i + 1].dstSet = computeDescriptorSets[i]; + descriptorWrites[3 * i + 1].dstBinding = 1; + descriptorWrites[3 * i + 1].dstArrayElement = 0; + descriptorWrites[3 * i + 1].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + descriptorWrites[3 * i + 1].descriptorCount = 1; + descriptorWrites[3 * i + 1].pBufferInfo = &culledGrassBufferInfo; + + descriptorWrites[3 * i + 2].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrites[3 * i + 2].dstSet = computeDescriptorSets[i]; + descriptorWrites[3 * i + 2].dstBinding = 2; + descriptorWrites[3 * i + 2].dstArrayElement = 0; + descriptorWrites[3 * i + 2].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + descriptorWrites[3 * i + 2].descriptorCount = 1; + descriptorWrites[3 * i + 2].pBufferInfo = &numBladesBufferInfo; + } + + // Update descriptor sets + vkUpdateDescriptorSets(logicalDevice, static_cast(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr); } void Renderer::CreateGraphicsPipeline() { @@ -717,7 +810,7 @@ void Renderer::CreateComputePipeline() { computeShaderStageInfo.pName = "main"; // TODO: Add the compute dsecriptor set layout you create to this list - std::vector descriptorSetLayouts = { cameraDescriptorSetLayout, timeDescriptorSetLayout }; + std::vector descriptorSetLayouts = { cameraDescriptorSetLayout, timeDescriptorSetLayout, computeDescriptorSetLayout }; // Create pipeline layout VkPipelineLayoutCreateInfo pipelineLayoutInfo = {}; @@ -884,6 +977,10 @@ void Renderer::RecordComputeCommandBuffer() { vkCmdBindDescriptorSets(computeCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, computePipelineLayout, 1, 1, &timeDescriptorSet, 0, nullptr); // TODO: For each group of blades bind its descriptor set and dispatch + for (size_t i = 0; i < scene->GetBlades().size(); ++i) { + vkCmdBindDescriptorSets(computeCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, computePipelineLayout, 2, 1, &computeDescriptorSets[i], 0, nullptr); + vkCmdDispatch(computeCommandBuffer, NUM_BLADES / WORKGROUP_SIZE, 1, 1); + } // ~ End recording ~ if (vkEndCommandBuffer(computeCommandBuffer) != VK_SUCCESS) { @@ -1050,13 +1147,14 @@ Renderer::~Renderer() { vkDestroyPipeline(logicalDevice, grassPipeline, nullptr); vkDestroyPipeline(logicalDevice, computePipeline, nullptr); + vkDestroyPipelineLayout(logicalDevice, computePipelineLayout, nullptr); vkDestroyPipelineLayout(logicalDevice, graphicsPipelineLayout, nullptr); vkDestroyPipelineLayout(logicalDevice, grassPipelineLayout, nullptr); - vkDestroyPipelineLayout(logicalDevice, computePipelineLayout, nullptr); vkDestroyDescriptorSetLayout(logicalDevice, cameraDescriptorSetLayout, nullptr); vkDestroyDescriptorSetLayout(logicalDevice, modelDescriptorSetLayout, nullptr); vkDestroyDescriptorSetLayout(logicalDevice, timeDescriptorSetLayout, nullptr); + vkDestroyDescriptorSetLayout(logicalDevice, computeDescriptorSetLayout, nullptr); vkDestroyDescriptorPool(logicalDevice, descriptorPool, nullptr); diff --git a/src/Renderer.h b/src/Renderer.h index 95e025f..b698e72 100644 --- a/src/Renderer.h +++ b/src/Renderer.h @@ -56,12 +56,16 @@ class Renderer { VkDescriptorSetLayout cameraDescriptorSetLayout; VkDescriptorSetLayout modelDescriptorSetLayout; VkDescriptorSetLayout timeDescriptorSetLayout; + VkDescriptorSetLayout grassDescriptorSetLayout; + VkDescriptorSetLayout computeDescriptorSetLayout; VkDescriptorPool descriptorPool; VkDescriptorSet cameraDescriptorSet; std::vector modelDescriptorSets; VkDescriptorSet timeDescriptorSet; + std::vector grassDescriptorSets; + std::vector computeDescriptorSets; VkPipelineLayout graphicsPipelineLayout; VkPipelineLayout grassPipelineLayout; diff --git a/src/main.cpp b/src/main.cpp index 8bf822b..6fa280e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -159,7 +159,11 @@ int main() { delete blades; delete camera; delete renderer; + + // this base code SUCKS delete swapChain; + vkDestroySurfaceKHR(instance->GetVkInstance(), surface, nullptr); + delete device; delete instance; DestroyWindow(); diff --git a/src/shaders/compute.comp b/src/shaders/compute.comp index 0fd0224..e63edff 100644 --- a/src/shaders/compute.comp +++ b/src/shaders/compute.comp @@ -22,10 +22,25 @@ struct Blade { }; // TODO: Add bindings to: -// 1. Store the input blades +// 1. Store the input blades ??? store? or read? // 2. Write out the culled blades // 3. Write the total number of blades remaining +layout(set = 2, binding = 0) readonly buffer InputGrass { + Blade inputBlades[]; +} inputBlades; + +layout(set = 2, binding = 1) writeonly buffer CulledGrass { + Blade culledBlades[]; +} culledBlades; + +layout(set = 2, binding = 2) buffer NumBlades { + uint vertexCount; + uint instanceCount; + uint firstVertex; + uint firstInstance; +} numBlades; + // The project is using vkCmdDrawIndirect to use a buffer as the arguments for a draw call // This is sort of an advanced feature so we've showed you what this buffer should look like // @@ -43,7 +58,10 @@ bool inBounds(float value, float bounds) { void main() { // Reset the number of blades to 0 if (gl_GlobalInvocationID.x == 0) { - // numBlades.vertexCount = 0; + numBlades.vertexCount = 0; + numBlades.instanceCount = 1; + numBlades.firstVertex = 0; + numBlades.firstInstance = 0; } barrier(); // Wait till all threads reach this point @@ -53,4 +71,7 @@ void main() { // to the culled blades buffer // Note: to do this, you will need to use an atomic operation to read and update numBlades.vertexCount // You want to write the visible blades to the buffer without write conflicts between threads + + uint index = atomicAdd(numBlades.vertexCount, 1); + culledBlades.culledBlades[index] = inputBlades.inputBlades[gl_GlobalInvocationID.x]; } From f5e0146cf9d9bc5a9bab4ab88c5b64e4032d70ba Mon Sep 17 00:00:00 2001 From: Michael Mason Date: Wed, 23 Oct 2024 09:36:33 -0400 Subject: [PATCH 03/10] looks like grass!! --- src/Blades.cpp | 2 +- src/Blades.h | 2 +- src/Renderer.cpp | 87 +++++++++++++++++++++++++++++++--------- src/Renderer.h | 1 + src/shaders/compute.comp | 5 --- src/shaders/grass.frag | 14 ++++++- src/shaders/grass.tesc | 21 ++++++++++ src/shaders/grass.tese | 16 ++++++++ src/shaders/grass.vert | 15 ++++++- 9 files changed, 135 insertions(+), 28 deletions(-) diff --git a/src/Blades.cpp b/src/Blades.cpp index 6a342be..38518b0 100644 --- a/src/Blades.cpp +++ b/src/Blades.cpp @@ -45,7 +45,7 @@ Blades::Blades(Device* device, VkCommandPool commandPool, float planeDim) : Mode indirectDraw.firstInstance = 0; BufferUtils::CreateBufferFromData(device, commandPool, blades.data(), getBladesBufferSize(), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, bladesBuffer, bladesBufferMemory); - BufferUtils::CreateBuffer(device, getBladesBufferSize(), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, culledBladesBuffer, culledBladesBufferMemory); + BufferUtils::CreateBuffer(device, getBladesBufferSize(), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, culledBladesBuffer, culledBladesBufferMemory); BufferUtils::CreateBufferFromData(device, commandPool, &indirectDraw, sizeof(BladeDrawIndirect), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT, numBladesBuffer, numBladesBufferMemory); } diff --git a/src/Blades.h b/src/Blades.h index f41f686..3c8df2a 100644 --- a/src/Blades.h +++ b/src/Blades.h @@ -4,7 +4,7 @@ #include #include "Model.h" -constexpr static unsigned int NUM_BLADES = 1 << 13; +constexpr static unsigned int NUM_BLADES = 1 << 13; // 1 < 13 constexpr static float MIN_HEIGHT = 1.3f; constexpr static float MAX_HEIGHT = 2.5f; constexpr static float MIN_WIDTH = 0.1f; diff --git a/src/Renderer.cpp b/src/Renderer.cpp index 1c1ae8f..3cce34e 100644 --- a/src/Renderer.cpp +++ b/src/Renderer.cpp @@ -21,6 +21,7 @@ Renderer::Renderer(Device* device, SwapChain* swapChain, Scene* scene, Camera* c CreateModelDescriptorSetLayout(); CreateTimeDescriptorSetLayout(); CreateComputeDescriptorSetLayout(); + CreateGrassDescriptorSetLayout(); CreateDescriptorPool(); CreateCameraDescriptorSet(); CreateModelDescriptorSets(); @@ -195,10 +196,6 @@ void Renderer::CreateTimeDescriptorSetLayout() { } void Renderer::CreateComputeDescriptorSetLayout() { - // TODO: Create the descriptor set layout for the compute pipeline - // Remember this is like a class definition stating why types of information - // will be stored at each binding - VkDescriptorSetLayoutBinding inputGrass = {}; inputGrass.binding = 0; inputGrass.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; @@ -221,7 +218,7 @@ void Renderer::CreateComputeDescriptorSetLayout() { VkDescriptorSetLayoutCreateInfo layoutInfo = {}; layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; - layoutInfo.bindingCount = bindings.size(); + layoutInfo.bindingCount = static_cast(bindings.size()); layoutInfo.pBindings = bindings.data(); if (vkCreateDescriptorSetLayout(logicalDevice, &layoutInfo, nullptr, &computeDescriptorSetLayout) != VK_SUCCESS) { @@ -229,6 +226,28 @@ void Renderer::CreateComputeDescriptorSetLayout() { } } +void Renderer::CreateGrassDescriptorSetLayout() +{ + VkDescriptorSetLayoutBinding uboLayoutBinding = {}; + uboLayoutBinding.binding = 0; + uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + uboLayoutBinding.descriptorCount = 1; + uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; + uboLayoutBinding.pImmutableSamplers = nullptr; + + std::vector bindings = { uboLayoutBinding }; + + // Create the descriptor set layout + VkDescriptorSetLayoutCreateInfo layoutInfo = {}; + layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + layoutInfo.bindingCount = static_cast(bindings.size()); + layoutInfo.pBindings = bindings.data(); + + if (vkCreateDescriptorSetLayout(logicalDevice, &layoutInfo, nullptr, &grassDescriptorSetLayout) != VK_SUCCESS) { + throw std::runtime_error("Failed to create descriptor set layout"); + } +} + void Renderer::CreateDescriptorPool() { // Describe which descriptor types that the descriptor sets will contain std::vector poolSizes = { @@ -244,7 +263,7 @@ void Renderer::CreateDescriptorPool() { // Time (compute) { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER , 1 }, - // TODO: Add any additional types and counts of descriptors you will need to allocate + // Blades (compute) { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 3 * static_cast(scene->GetBlades().size())} }; @@ -348,8 +367,43 @@ void Renderer::CreateModelDescriptorSets() { } void Renderer::CreateGrassDescriptorSets() { - // TODO: Create Descriptor sets for the grass. // This should involve creating descriptor sets which point to the model matrix of each group of grass blades + grassDescriptorSets.resize(scene->GetModels().size()); + + // Describe the desciptor set + VkDescriptorSetLayout layouts[] = { grassDescriptorSetLayout }; + VkDescriptorSetAllocateInfo allocInfo = {}; + allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + allocInfo.descriptorPool = descriptorPool; + allocInfo.descriptorSetCount = static_cast(grassDescriptorSets.size()); + allocInfo.pSetLayouts = layouts; + + // Allocate descriptor sets + if (vkAllocateDescriptorSets(logicalDevice, &allocInfo, grassDescriptorSets.data()) != VK_SUCCESS) { + throw std::runtime_error("Failed to allocate descriptor set"); + } + + std::vector descriptorWrites(grassDescriptorSets.size()); + + for (uint32_t i = 0; i < scene->GetModels().size(); ++i) { + VkDescriptorBufferInfo modelBufferInfo = {}; + modelBufferInfo.buffer = scene->GetBlades()[i]->GetModelBuffer(); + modelBufferInfo.offset = 0; + modelBufferInfo.range = sizeof(ModelBufferObject); + + descriptorWrites[i].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrites[i].dstSet = grassDescriptorSets[i]; + descriptorWrites[i].dstBinding = 0; + descriptorWrites[i].dstArrayElement = 0; + descriptorWrites[i].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + descriptorWrites[i].descriptorCount = 1; + descriptorWrites[i].pBufferInfo = &modelBufferInfo; + descriptorWrites[i].pImageInfo = nullptr; + descriptorWrites[i].pTexelBufferView = nullptr; + } + + // Update descriptor sets + vkUpdateDescriptorSets(logicalDevice, static_cast(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr); } void Renderer::CreateTimeDescriptorSet() { @@ -388,7 +442,6 @@ void Renderer::CreateTimeDescriptorSet() { } void Renderer::CreateComputeDescriptorSets() { - // TODO: Create Descriptor sets for the compute pipeline // The descriptors should point to Storage buffers which will hold the grass blades, the culled grass blades, and the output number of grass blades computeDescriptorSets.resize(scene->GetBlades().size()); @@ -693,7 +746,7 @@ void Renderer::CreateGrassPipeline() { rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; rasterizer.depthClampEnable = VK_FALSE; rasterizer.rasterizerDiscardEnable = VK_FALSE; - rasterizer.polygonMode = VK_POLYGON_MODE_FILL; + rasterizer.polygonMode = VK_POLYGON_MODE_FILL; // TODO: use VK_POLYGON_MODE_FILL rasterizer.lineWidth = 1.0f; rasterizer.cullMode = VK_CULL_MODE_NONE; rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; @@ -747,7 +800,7 @@ void Renderer::CreateGrassPipeline() { colorBlending.blendConstants[2] = 0.0f; colorBlending.blendConstants[3] = 0.0f; - std::vector descriptorSetLayouts = { cameraDescriptorSetLayout, modelDescriptorSetLayout }; + std::vector descriptorSetLayouts = { cameraDescriptorSetLayout, grassDescriptorSetLayout }; // Pipeline layout: used to specify uniform values VkPipelineLayoutCreateInfo pipelineLayoutInfo = {}; @@ -809,7 +862,6 @@ void Renderer::CreateComputePipeline() { computeShaderStageInfo.module = computeShaderModule; computeShaderStageInfo.pName = "main"; - // TODO: Add the compute dsecriptor set layout you create to this list std::vector descriptorSetLayouts = { cameraDescriptorSetLayout, timeDescriptorSetLayout, computeDescriptorSetLayout }; // Create pipeline layout @@ -976,7 +1028,6 @@ void Renderer::RecordComputeCommandBuffer() { // Bind descriptor set for time uniforms vkCmdBindDescriptorSets(computeCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, computePipelineLayout, 1, 1, &timeDescriptorSet, 0, nullptr); - // TODO: For each group of blades bind its descriptor set and dispatch for (size_t i = 0; i < scene->GetBlades().size(); ++i) { vkCmdBindDescriptorSets(computeCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, computePipelineLayout, 2, 1, &computeDescriptorSets[i], 0, nullptr); vkCmdDispatch(computeCommandBuffer, NUM_BLADES / WORKGROUP_SIZE, 1, 1); @@ -1072,14 +1123,13 @@ void Renderer::RecordCommandBuffers() { for (uint32_t j = 0; j < scene->GetBlades().size(); ++j) { VkBuffer vertexBuffers[] = { scene->GetBlades()[j]->GetCulledBladesBuffer() }; VkDeviceSize offsets[] = { 0 }; - // TODO: Uncomment this when the buffers are populated - // vkCmdBindVertexBuffers(commandBuffers[i], 0, 1, vertexBuffers, offsets); - // TODO: Bind the descriptor set for each grass blades model + vkCmdBindVertexBuffers(commandBuffers[i], 0, 1, vertexBuffers, offsets); + + vkCmdBindDescriptorSets(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, grassPipelineLayout, 1, 1, &grassDescriptorSets[j], 0, nullptr); // Draw - // TODO: Uncomment this when the buffers are populated - // vkCmdDrawIndirect(commandBuffers[i], scene->GetBlades()[j]->GetNumBladesBuffer(), 0, 1, sizeof(BladeDrawIndirect)); + vkCmdDrawIndirect(commandBuffers[i], scene->GetBlades()[j]->GetNumBladesBuffer(), 0, 1, sizeof(BladeDrawIndirect)); } // End render pass @@ -1138,8 +1188,6 @@ void Renderer::Frame() { Renderer::~Renderer() { vkDeviceWaitIdle(logicalDevice); - // TODO: destroy any resources you created - vkFreeCommandBuffers(logicalDevice, graphicsCommandPool, static_cast(commandBuffers.size()), commandBuffers.data()); vkFreeCommandBuffers(logicalDevice, computeCommandPool, 1, &computeCommandBuffer); @@ -1155,6 +1203,7 @@ Renderer::~Renderer() { vkDestroyDescriptorSetLayout(logicalDevice, modelDescriptorSetLayout, nullptr); vkDestroyDescriptorSetLayout(logicalDevice, timeDescriptorSetLayout, nullptr); vkDestroyDescriptorSetLayout(logicalDevice, computeDescriptorSetLayout, nullptr); + vkDestroyDescriptorSetLayout(logicalDevice, grassDescriptorSetLayout, nullptr); vkDestroyDescriptorPool(logicalDevice, descriptorPool, nullptr); diff --git a/src/Renderer.h b/src/Renderer.h index b698e72..b4d4cfc 100644 --- a/src/Renderer.h +++ b/src/Renderer.h @@ -19,6 +19,7 @@ class Renderer { void CreateModelDescriptorSetLayout(); void CreateTimeDescriptorSetLayout(); void CreateComputeDescriptorSetLayout(); + void CreateGrassDescriptorSetLayout(); void CreateDescriptorPool(); diff --git a/src/shaders/compute.comp b/src/shaders/compute.comp index e63edff..51093ef 100644 --- a/src/shaders/compute.comp +++ b/src/shaders/compute.comp @@ -21,11 +21,6 @@ struct Blade { vec4 up; }; -// TODO: Add bindings to: -// 1. Store the input blades ??? store? or read? -// 2. Write out the culled blades -// 3. Write the total number of blades remaining - layout(set = 2, binding = 0) readonly buffer InputGrass { Blade inputBlades[]; } inputBlades; diff --git a/src/shaders/grass.frag b/src/shaders/grass.frag index c7df157..2154f9e 100644 --- a/src/shaders/grass.frag +++ b/src/shaders/grass.frag @@ -7,11 +7,23 @@ layout(set = 0, binding = 0) uniform CameraBufferObject { } camera; // TODO: Declare fragment shader inputs +layout(location = 0) in vec4 v0_in; +layout(location = 1) in vec4 v1_in; +layout(location = 2) in vec4 v2_in; +layout(location = 3) in vec4 up_in; +layout(location = 4) in float u; +layout(location = 5) in float v; layout(location = 0) out vec4 outColor; void main() { // TODO: Compute fragment color + // Define the dark green and light green colors + vec3 darkGreen = vec3(0.0, 0.5, 0.0); // Dark green + vec3 lightGreen = vec3(0.5, 1.0, 0.5); // Light green - outColor = vec4(1.0); + // Interpolate the grass color between darkGreen and lightGreen + vec3 grassColor = mix(darkGreen, lightGreen, v); + + outColor = vec4(grassColor, 1.); } diff --git a/src/shaders/grass.tesc b/src/shaders/grass.tesc index b2131c3..48da506 100644 --- a/src/shaders/grass.tesc +++ b/src/shaders/grass.tesc @@ -8,17 +8,32 @@ layout(set = 0, binding = 0) uniform CameraBufferObject { mat4 proj; } camera; +layout(location = 0) in vec4 v0_in[]; +layout(location = 1) in vec4 v1_in[]; +layout(location = 2) in vec4 v2_in[]; +layout(location = 3) in vec4 up_in[]; + +layout(location = 0) out vec4 v0_out[]; +layout(location = 1) out vec4 v1_out[]; +layout(location = 2) out vec4 v2_out[]; +layout(location = 3) out vec4 up_out[]; + // TODO: Declare tessellation control shader inputs and outputs in gl_PerVertex { vec4 gl_Position; } gl_in[gl_MaxPatchVertices]; + void main() { // Don't move the origin location of the patch gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position; // TODO: Write any shader outputs + vec4 v0_out = v0_in[0]; + vec4 v1_out = v1_in[0]; + vec4 v2_out = v2_in[0]; + vec4 up_out = up_in[0]; // TODO: Set level of tesselation // gl_TessLevelInner[0] = ??? @@ -27,4 +42,10 @@ void main() { // gl_TessLevelOuter[1] = ??? // gl_TessLevelOuter[2] = ??? // gl_TessLevelOuter[3] = ??? + gl_TessLevelInner[0] = 2.0; + gl_TessLevelInner[1] = 2.0; + gl_TessLevelOuter[0] = 2.0; + gl_TessLevelOuter[1] = 2.0; + gl_TessLevelOuter[2] = 2.0; + gl_TessLevelOuter[3] = 2.0; } diff --git a/src/shaders/grass.tese b/src/shaders/grass.tese index 751fff6..1a73068 100644 --- a/src/shaders/grass.tese +++ b/src/shaders/grass.tese @@ -9,10 +9,26 @@ layout(set = 0, binding = 0) uniform CameraBufferObject { } camera; // TODO: Declare tessellation evaluation shader inputs and outputs +layout(location = 0) in vec4 v0_in[]; +layout(location = 1) in vec4 v1_in[]; +layout(location = 2) in vec4 v2_in[]; +layout(location = 3) in vec4 up_in[]; + +layout(location = 0) out vec4 v0_out; +layout(location = 1) out vec4 v1_out; +layout(location = 2) out vec4 v2_out; +layout(location = 3) out vec4 up_out; +layout(location = 4) out float u_out; +layout(location = 5) out float v_out; + void main() { float u = gl_TessCoord.x; float v = gl_TessCoord.y; + u_out = u; + v_out = v; + // TODO: Use u and v to parameterize along the grass blade and output positions for each vertex of the grass blade + gl_Position = camera.proj * camera.view * (gl_in[0].gl_Position + vec4(0.3 * (1.0 - v) * (.5 - u), v, 0.0, 0.0)); } diff --git a/src/shaders/grass.vert b/src/shaders/grass.vert index db9dfe9..2f5d880 100644 --- a/src/shaders/grass.vert +++ b/src/shaders/grass.vert @@ -6,7 +6,15 @@ layout(set = 1, binding = 0) uniform ModelBufferObject { mat4 model; }; -// TODO: Declare vertex shader inputs and outputs +layout(location = 0) in vec4 v0; // Corresponds to Blade::v0 +layout(location = 1) in vec4 v1; // Corresponds to Blade::v1 +layout(location = 2) in vec4 v2; // Corresponds to Blade::v2 +layout(location = 3) in vec4 up; // Corresponds to Blade::up + +layout(location = 0) out vec4 v0_out; +layout(location = 1) out vec4 v1_out; +layout(location = 2) out vec4 v2_out; +layout(location = 3) out vec4 up_out; out gl_PerVertex { vec4 gl_Position; @@ -14,4 +22,9 @@ out gl_PerVertex { void main() { // TODO: Write gl_Position and any other shader outputs + v0_out = v0; + v1_out = v1; + v2_out = v2; + up_out = up; + gl_Position = model * vec4(v0.xyz, 1.); } From 14af33be50c9f867e457e782e7c844434851a0b2 Mon Sep 17 00:00:00 2001 From: Michael Mason Date: Fri, 25 Oct 2024 16:55:04 -0400 Subject: [PATCH 04/10] grass orientation --- src/Renderer.cpp | 2 +- src/shaders/grass.frag | 1 - src/shaders/grass.tesc | 8 ++++---- src/shaders/grass.tese | 18 ++++++++++++++++-- 4 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/Renderer.cpp b/src/Renderer.cpp index 3cce34e..0a5ac0d 100644 --- a/src/Renderer.cpp +++ b/src/Renderer.cpp @@ -1110,7 +1110,7 @@ void Renderer::RecordCommandBuffers() { vkCmdBindIndexBuffer(commandBuffers[i], scene->GetModels()[j]->getIndexBuffer(), 0, VK_INDEX_TYPE_UINT32); // Bind the descriptor set for each model - vkCmdBindDescriptorSets(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipelineLayout, 1, 1, &modelDescriptorSets[j], 0, nullptr); + vkCmdBindDescriptorSets(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipelineLayout, 1, 1, &modelDescriptorSets[j], 0, nullptr); // Draw std::vector indices = scene->GetModels()[j]->getIndices(); diff --git a/src/shaders/grass.frag b/src/shaders/grass.frag index 2154f9e..20192fc 100644 --- a/src/shaders/grass.frag +++ b/src/shaders/grass.frag @@ -17,7 +17,6 @@ layout(location = 5) in float v; layout(location = 0) out vec4 outColor; void main() { - // TODO: Compute fragment color // Define the dark green and light green colors vec3 darkGreen = vec3(0.0, 0.5, 0.0); // Dark green vec3 lightGreen = vec3(0.5, 1.0, 0.5); // Light green diff --git a/src/shaders/grass.tesc b/src/shaders/grass.tesc index 48da506..2c13ac6 100644 --- a/src/shaders/grass.tesc +++ b/src/shaders/grass.tesc @@ -30,10 +30,10 @@ void main() { gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position; // TODO: Write any shader outputs - vec4 v0_out = v0_in[0]; - vec4 v1_out = v1_in[0]; - vec4 v2_out = v2_in[0]; - vec4 up_out = up_in[0]; + v0_out[gl_InvocationID] = v0_in[gl_InvocationID]; + v1_out[gl_InvocationID] = v1_in[gl_InvocationID]; + v2_out[gl_InvocationID] = v2_in[gl_InvocationID]; + up_out[gl_InvocationID] = up_in[gl_InvocationID]; // TODO: Set level of tesselation // gl_TessLevelInner[0] = ??? diff --git a/src/shaders/grass.tese b/src/shaders/grass.tese index 1a73068..52401c9 100644 --- a/src/shaders/grass.tese +++ b/src/shaders/grass.tese @@ -21,6 +21,18 @@ layout(location = 3) out vec4 up_out; layout(location = 4) out float u_out; layout(location = 5) out float v_out; +mat4 rot(vec3 axis, float angle) +{ + axis = normalize(axis); + float s = sin(angle); + float c = cos(angle); + float oc = 1.0 - c; + + return mat4(oc * axis.x * axis.x + c, oc * axis.x * axis.y - axis.z * s, oc * axis.z * axis.x + axis.y * s, 0.0, + oc * axis.x * axis.y + axis.z * s, oc * axis.y * axis.y + c, oc * axis.y * axis.z - axis.x * s, 0.0, + oc * axis.z * axis.x - axis.y * s, oc * axis.y * axis.z + axis.x * s, oc * axis.z * axis.z + c, 0.0, + 0.0, 0.0, 0.0, 1.0); +} void main() { float u = gl_TessCoord.x; @@ -29,6 +41,8 @@ void main() { u_out = u; v_out = v; - // TODO: Use u and v to parameterize along the grass blade and output positions for each vertex of the grass blade - gl_Position = camera.proj * camera.view * (gl_in[0].gl_Position + vec4(0.3 * (1.0 - v) * (.5 - u), v, 0.0, 0.0)); + mat4 rotation = rot(vec3(0, 1, 0), v0_in[0].w); + vec4 pos = gl_in[0].gl_Position + rotation * vec4(0.3 * (1.0 - v) * (.5 - u), v, 0.0, 0.0); + + gl_Position = camera.proj * camera.view * pos; } From 2f0bdd56782d2df7cbbab1419e1fe8a32c97bb98 Mon Sep 17 00:00:00 2001 From: Michael Mason Date: Sat, 26 Oct 2024 19:19:16 -0400 Subject: [PATCH 05/10] some forces implemented --- src/Renderer.cpp | 18 ++++++++------- src/shaders/compute.comp | 49 +++++++++++++++++++++++++++++++++++++--- src/shaders/grass.frag | 7 +----- src/shaders/grass.tesc | 13 ++++++----- src/shaders/grass.tese | 34 +++++++++++++++++++--------- 5 files changed, 87 insertions(+), 34 deletions(-) diff --git a/src/Renderer.cpp b/src/Renderer.cpp index 0a5ac0d..4bad6e2 100644 --- a/src/Renderer.cpp +++ b/src/Renderer.cpp @@ -1033,6 +1033,8 @@ void Renderer::RecordComputeCommandBuffer() { vkCmdDispatch(computeCommandBuffer, NUM_BLADES / WORKGROUP_SIZE, 1, 1); } + vkCmdPipelineBarrier(computeCommandBuffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 0, nullptr); + // ~ End recording ~ if (vkEndCommandBuffer(computeCommandBuffer) != VK_SUCCESS) { throw std::runtime_error("Failed to record compute command buffer"); @@ -1079,19 +1081,19 @@ void Renderer::RecordCommandBuffers() { renderPassInfo.clearValueCount = static_cast(clearValues.size()); renderPassInfo.pClearValues = clearValues.data(); - std::vector barriers(scene->GetBlades().size()); + // ?? It doesn't seem like any of these sync barriers do ANYTHING to fix the grass flickering + + // 1. vkCmdPipelineBarrier(commandBuffers[i], VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 0, nullptr); + + // 2. + /*std::vector barriers(scene->GetBlades().size()); for (uint32_t j = 0; j < barriers.size(); ++j) { - barriers[j].sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER; + barriers[j].sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER; barriers[j].srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT; barriers[j].dstAccessMask = VK_ACCESS_INDIRECT_COMMAND_READ_BIT; - barriers[j].srcQueueFamilyIndex = device->GetQueueIndex(QueueFlags::Compute); - barriers[j].dstQueueFamilyIndex = device->GetQueueIndex(QueueFlags::Graphics); - barriers[j].buffer = scene->GetBlades()[j]->GetNumBladesBuffer(); - barriers[j].offset = 0; - barriers[j].size = sizeof(BladeDrawIndirect); } - vkCmdPipelineBarrier(commandBuffers[i], VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT, 0, 0, nullptr, barriers.size(), barriers.data(), 0, nullptr); + vkCmdPipelineBarrier(commandBuffers[i], VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT, 0, barriers.size(), barriers.data(), 0, nullptr, 0, nullptr);*/ // Bind the camera descriptor set. This is set 0 in all pipelines so it will be inherited vkCmdBindDescriptorSets(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipelineLayout, 0, 1, &cameraDescriptorSet, 0, nullptr); diff --git a/src/shaders/compute.comp b/src/shaders/compute.comp index 51093ef..af51561 100644 --- a/src/shaders/compute.comp +++ b/src/shaders/compute.comp @@ -12,7 +12,7 @@ layout(set = 0, binding = 0) uniform CameraBufferObject { layout(set = 1, binding = 0) uniform Time { float deltaTime; float totalTime; -}; +} time; struct Blade { vec4 v0; @@ -50,6 +50,19 @@ bool inBounds(float value, float bounds) { return (value >= -bounds) && (value <= bounds); } +mat4 rot(vec3 axis, float angle) +{ + axis = normalize(axis); + float s = sin(angle); + float c = cos(angle); + float oc = 1.0 - c; + + return mat4(oc * axis.x * axis.x + c, oc * axis.x * axis.y - axis.z * s, oc * axis.z * axis.x + axis.y * s, 0.0, + oc * axis.x * axis.y + axis.z * s, oc * axis.y * axis.y + c, oc * axis.y * axis.z - axis.x * s, 0.0, + oc * axis.z * axis.x - axis.y * s, oc * axis.y * axis.z + axis.x * s, oc * axis.z * axis.z + c, 0.0, + 0.0, 0.0, 0.0, 1.0); +} + void main() { // Reset the number of blades to 0 if (gl_GlobalInvocationID.x == 0) { @@ -60,13 +73,43 @@ void main() { } barrier(); // Wait till all threads reach this point + uint index = atomicAdd(numBlades.vertexCount, 1); + Blade b = inputBlades.inputBlades[gl_GlobalInvocationID.x]; + // TODO: Apply forces on every blade and update the vertices in the buffer + // gravity + + // gravity magnitude/direction + vec4 D = vec4(0, -1, 0, 1); + + mat4 rotation = rot(vec3(0, 1, 0), b.v0.w); + vec3 f = (rotation * vec4(0, 0, 1, 0)).xyz; + + vec3 gEnvironment = normalize(D.xyz) * D.w; // global gravity force + vec3 gFront = 0.25f * length(gEnvironment) * f; + + vec3 gravity = gEnvironment + gFront; + + // recovery + float stiffness = b.up.w; + vec3 iv2 = b.v0.xyz + (b.up.xyz * b.v1.w); // initial position of v2 + vec3 recovery = (iv2 - b.v2.xyz) * stiffness; + + // wind + float wind; + + // correct position + + + b.v2.xyz += (gravity + recovery) * time.deltaTime; + // TODO: Cull blades that are too far away or not in the camera frustum and write them // to the culled blades buffer // Note: to do this, you will need to use an atomic operation to read and update numBlades.vertexCount // You want to write the visible blades to the buffer without write conflicts between threads - uint index = atomicAdd(numBlades.vertexCount, 1); - culledBlades.culledBlades[index] = inputBlades.inputBlades[gl_GlobalInvocationID.x]; + + + culledBlades.culledBlades[index] = b; } diff --git a/src/shaders/grass.frag b/src/shaders/grass.frag index 20192fc..45a891f 100644 --- a/src/shaders/grass.frag +++ b/src/shaders/grass.frag @@ -7,12 +7,7 @@ layout(set = 0, binding = 0) uniform CameraBufferObject { } camera; // TODO: Declare fragment shader inputs -layout(location = 0) in vec4 v0_in; -layout(location = 1) in vec4 v1_in; -layout(location = 2) in vec4 v2_in; -layout(location = 3) in vec4 up_in; -layout(location = 4) in float u; -layout(location = 5) in float v; +layout(location = 0) in float v; layout(location = 0) out vec4 outColor; diff --git a/src/shaders/grass.tesc b/src/shaders/grass.tesc index 2c13ac6..b46c2e0 100644 --- a/src/shaders/grass.tesc +++ b/src/shaders/grass.tesc @@ -42,10 +42,11 @@ void main() { // gl_TessLevelOuter[1] = ??? // gl_TessLevelOuter[2] = ??? // gl_TessLevelOuter[3] = ??? - gl_TessLevelInner[0] = 2.0; - gl_TessLevelInner[1] = 2.0; - gl_TessLevelOuter[0] = 2.0; - gl_TessLevelOuter[1] = 2.0; - gl_TessLevelOuter[2] = 2.0; - gl_TessLevelOuter[3] = 2.0; + gl_TessLevelInner[0] = 0; + gl_TessLevelInner[1] = 10; + + gl_TessLevelOuter[0] = 10.0; + gl_TessLevelOuter[1] = 2; + gl_TessLevelOuter[2] = 10.0; + gl_TessLevelOuter[3] = 2; } diff --git a/src/shaders/grass.tese b/src/shaders/grass.tese index 52401c9..8b9a07c 100644 --- a/src/shaders/grass.tese +++ b/src/shaders/grass.tese @@ -8,18 +8,12 @@ layout(set = 0, binding = 0) uniform CameraBufferObject { mat4 proj; } camera; -// TODO: Declare tessellation evaluation shader inputs and outputs layout(location = 0) in vec4 v0_in[]; layout(location = 1) in vec4 v1_in[]; layout(location = 2) in vec4 v2_in[]; layout(location = 3) in vec4 up_in[]; -layout(location = 0) out vec4 v0_out; -layout(location = 1) out vec4 v1_out; -layout(location = 2) out vec4 v2_out; -layout(location = 3) out vec4 up_out; -layout(location = 4) out float u_out; -layout(location = 5) out float v_out; +layout(location = 0) out float v_out; mat4 rot(vec3 axis, float angle) { @@ -38,11 +32,29 @@ void main() { float u = gl_TessCoord.x; float v = gl_TessCoord.y; - u_out = u; v_out = v; - mat4 rotation = rot(vec3(0, 1, 0), v0_in[0].w); - vec4 pos = gl_in[0].gl_Position + rotation * vec4(0.3 * (1.0 - v) * (.5 - u), v, 0.0, 0.0); + vec3 v0 = gl_in[0].gl_Position.xyz; + vec3 v1 = v1_in[0].xyz; + vec3 v2 = v2_in[0].xyz; + float width = v2_in[0].w; + float height = v1_in[0].w; - gl_Position = camera.proj * camera.view * pos; + mat4 rotation = rot(vec3(0, 1, 0), v0_in[0].w); + vec3 t1 = (rotation * vec4(1, 0, 0, 0)).xyz; + vec3 a = v0 + v * (v1 - v0); + vec3 b = v1 + v * (v2 - v1); + vec3 c = a + v * (b - a); + vec3 c0 = c - width * t1; + vec3 c1 = c + width * t1; + + // vec3 t0 = normalize(b - a); + // vec3 n = normalize(cross(t0, t1)); + + //vec4 pos = gl_in[0].gl_Position + rotation * vec4(0.3 * (1.0 - v) * (.5 - u), v, 0.0, 0.0); + + float t = u - u * v * v; + vec3 pos = (1. - t) * c0 + t * c1; + + gl_Position = camera.proj * camera.view * vec4(pos, 1); } From 28fd1ef6c40c847eb92c8e7cbe5ed0d3d2f16eea Mon Sep 17 00:00:00 2001 From: Michael Mason Date: Sat, 26 Oct 2024 23:22:02 -0400 Subject: [PATCH 06/10] some wind physics --- src/Blades.h | 2 +- src/Scene.cpp | 2 ++ src/main.cpp | 2 +- src/shaders/compute.comp | 12 +++++------- src/shaders/grass.tese | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Blades.h b/src/Blades.h index 3c8df2a..c5f4734 100644 --- a/src/Blades.h +++ b/src/Blades.h @@ -4,7 +4,7 @@ #include #include "Model.h" -constexpr static unsigned int NUM_BLADES = 1 << 13; // 1 < 13 +constexpr static unsigned int NUM_BLADES = 1 << 14; // 1 < 13 constexpr static float MIN_HEIGHT = 1.3f; constexpr static float MAX_HEIGHT = 2.5f; constexpr static float MIN_WIDTH = 0.1f; diff --git a/src/Scene.cpp b/src/Scene.cpp index 86894f2..b37f007 100644 --- a/src/Scene.cpp +++ b/src/Scene.cpp @@ -1,6 +1,8 @@ #include "Scene.h" #include "BufferUtils.h" +#include + Scene::Scene(Device* device) : device(device) { BufferUtils::CreateBuffer(device, sizeof(Time), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, timeBuffer, timeBufferMemory); vkMapMemory(device->GetVkDevice(), timeBufferMemory, 0, sizeof(Time), 0, &mappedData); diff --git a/src/main.cpp b/src/main.cpp index 6fa280e..87f5baf 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -116,7 +116,7 @@ int main() { grassImageMemory ); - float planeDim = 15.f; + float planeDim = 30.f; float halfWidth = planeDim * 0.5f; Model* plane = new Model(device, transferCommandPool, { diff --git a/src/shaders/compute.comp b/src/shaders/compute.comp index af51561..bf1b783 100644 --- a/src/shaders/compute.comp +++ b/src/shaders/compute.comp @@ -12,7 +12,7 @@ layout(set = 0, binding = 0) uniform CameraBufferObject { layout(set = 1, binding = 0) uniform Time { float deltaTime; float totalTime; -} time; +}; struct Blade { vec4 v0; @@ -83,7 +83,7 @@ void main() { // gravity magnitude/direction vec4 D = vec4(0, -1, 0, 1); - mat4 rotation = rot(vec3(0, 1, 0), b.v0.w); + mat4 rotation = rot(b.up.xyz, b.v0.w); vec3 f = (rotation * vec4(0, 0, 1, 0)).xyz; vec3 gEnvironment = normalize(D.xyz) * D.w; // global gravity force @@ -97,19 +97,17 @@ void main() { vec3 recovery = (iv2 - b.v2.xyz) * stiffness; // wind - float wind; + vec3 wind = 300. * sin(b.v0.x + vec3(totalTime)) * cos(b.v0.z + vec3(totalTime)) * sin(b.v1.w + vec3(totalTime)); // correct position - - b.v2.xyz += (gravity + recovery) * time.deltaTime; + // Not sure why delta time doesn't work, but using deltaTime makes stuttering movement + b.v2.xyz += (gravity + recovery + wind) * 0.001; // TODO: Cull blades that are too far away or not in the camera frustum and write them // to the culled blades buffer // Note: to do this, you will need to use an atomic operation to read and update numBlades.vertexCount // You want to write the visible blades to the buffer without write conflicts between threads - - culledBlades.culledBlades[index] = b; } diff --git a/src/shaders/grass.tese b/src/shaders/grass.tese index 8b9a07c..f87ad34 100644 --- a/src/shaders/grass.tese +++ b/src/shaders/grass.tese @@ -40,7 +40,7 @@ void main() { float width = v2_in[0].w; float height = v1_in[0].w; - mat4 rotation = rot(vec3(0, 1, 0), v0_in[0].w); + mat4 rotation = rot(up_in[0].xyz, v0_in[0].w); vec3 t1 = (rotation * vec4(1, 0, 0, 0)).xyz; vec3 a = v0 + v * (v1 - v0); vec3 b = v1 + v * (v2 - v1); From adb0ea4f351fb66c6e2e1690b04fa5d9a00f52ff Mon Sep 17 00:00:00 2001 From: Michael Mason Date: Thu, 31 Oct 2024 20:44:12 -0400 Subject: [PATCH 07/10] implemented culling --- src/Blades.h | 2 +- src/main.cpp | 2 +- src/shaders/compute.comp | 109 +++++++++++++++++++++++++++++++++------ src/shaders/grass.tese | 2 - 4 files changed, 96 insertions(+), 19 deletions(-) diff --git a/src/Blades.h b/src/Blades.h index c5f4734..f5ed1a2 100644 --- a/src/Blades.h +++ b/src/Blades.h @@ -4,7 +4,7 @@ #include #include "Model.h" -constexpr static unsigned int NUM_BLADES = 1 << 14; // 1 < 13 +constexpr static unsigned int NUM_BLADES = 1 << 16; // 1 < 13 constexpr static float MIN_HEIGHT = 1.3f; constexpr static float MAX_HEIGHT = 2.5f; constexpr static float MIN_WIDTH = 0.1f; diff --git a/src/main.cpp b/src/main.cpp index 87f5baf..ee15de3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -116,7 +116,7 @@ int main() { grassImageMemory ); - float planeDim = 30.f; + float planeDim = 40.f; float halfWidth = planeDim * 0.5f; Model* plane = new Model(device, transferCommandPool, { diff --git a/src/shaders/compute.comp b/src/shaders/compute.comp index bf1b783..af62f6a 100644 --- a/src/shaders/compute.comp +++ b/src/shaders/compute.comp @@ -21,13 +21,13 @@ struct Blade { vec4 up; }; -layout(set = 2, binding = 0) readonly buffer InputGrass { +layout(set = 2, binding = 0) buffer InputGrass { Blade inputBlades[]; -} inputBlades; +}; layout(set = 2, binding = 1) writeonly buffer CulledGrass { Blade culledBlades[]; -} culledBlades; +}; layout(set = 2, binding = 2) buffer NumBlades { uint vertexCount; @@ -63,6 +63,24 @@ mat4 rot(vec3 axis, float angle) 0.0, 0.0, 0.0, 1.0); } +#define TOLERANCE -0.0 + +bool inFrustum(vec3 p) { + vec4 pHom = vec4(p, 1); // homogenous form + vec4 pNor = camera.proj * camera.view * pHom; + float h = pNor.w + TOLERANCE; + + bool vx = p.x > -h && p.x < h; + bool vy = p.y > -h && p.y < h; + bool vz = p.z > -h && p.z < h; + + return vx && vy && vz; +} + + +#define HACK true +#define CULL true + void main() { // Reset the number of blades to 0 if (gl_GlobalInvocationID.x == 0) { @@ -73,41 +91,102 @@ void main() { } barrier(); // Wait till all threads reach this point - uint index = atomicAdd(numBlades.vertexCount, 1); - Blade b = inputBlades.inputBlades[gl_GlobalInvocationID.x]; + Blade b = inputBlades[gl_GlobalInvocationID.x]; + + vec3 v0 = b.v0.xyz; + vec3 v1 = b.v1.xyz; + vec3 v2 = b.v2.xyz; + vec3 up = b.up.xyz; + float orientation = b.v0.w; + float height = b.v1.w; + float width = b.v2.w; + float stiffness = b.up.w; + // TODO: Apply forces on every blade and update the vertices in the buffer // gravity + mat4 rotation = rot(up, orientation); + vec3 f = (rotation * vec4(0, 0, 1, 0)).xyz; + // gravity magnitude/direction vec4 D = vec4(0, -1, 0, 1); - - mat4 rotation = rot(b.up.xyz, b.v0.w); - vec3 f = (rotation * vec4(0, 0, 1, 0)).xyz; - vec3 gEnvironment = normalize(D.xyz) * D.w; // global gravity force vec3 gFront = 0.25f * length(gEnvironment) * f; vec3 gravity = gEnvironment + gFront; // recovery - float stiffness = b.up.w; - vec3 iv2 = b.v0.xyz + (b.up.xyz * b.v1.w); // initial position of v2 - vec3 recovery = (iv2 - b.v2.xyz) * stiffness; + vec3 iv2 = v0 + (up * height); // initial position of v2 + vec3 recovery = (iv2 - width) * stiffness; // wind - vec3 wind = 300. * sin(b.v0.x + vec3(totalTime)) * cos(b.v0.z + vec3(totalTime)) * sin(b.v1.w + vec3(totalTime)); + vec3 wind = 300. * sin(v0.x + vec3(totalTime)) * cos(v0.z + vec3(totalTime)) * sin(height + vec3(totalTime)); // correct position // Not sure why delta time doesn't work, but using deltaTime makes stuttering movement - b.v2.xyz += (gravity + recovery + wind) * 0.001; + // + if (HACK) { + b.v2.xyz += (gravity + recovery + wind) * 0.001; + } + else + { + inputBlades[gl_GlobalInvocationID.x].v2.xyz += (gravity + recovery + wind) * 0.000001; + } + // TODO: Cull blades that are too far away or not in the camera frustum and write them // to the culled blades buffer // Note: to do this, you will need to use an atomic operation to read and update numBlades.vertexCount // You want to write the visible blades to the buffer without write conflicts between threads - culledBlades.culledBlades[index] = b; + // -- Culling -- + + if (CULL) + { + // -- cull based on blade orientation (in relation to camera) -- + vec3 cameraPos = (inverse(camera.view) * vec4(0, 0, 0, 1)).xyz; + + // project camera direction onto grass plane + // direction and distance to camera + vec3 dirCamera = v0 - cameraPos - up * dot(v0 - cameraPos, up); + vec3 dirBlade = normalize((rotation * vec4(1, 0, 0, 0)).xyz); // direction of blade along it's width + + if (0.9 < abs(dot(normalize(dirCamera), dirBlade))) { + return; + } + + // -- cull based on the camera's frustum -- + + // approximate mid point of grass blade + vec3 m = .25 * v0 * .5 * v1 * .25 * v2; + + if (!inFrustum(v0) && !inFrustum(m) && !inFrustum(v1)) { + return; + } + + // --cull based on distance -- + + if (length(dirCamera) > 50.0) { + return; + } + else if (length(dirCamera) > 40.0 && gl_GlobalInvocationID.x % 2 == 0) { + return; + } + else if (length(dirCamera) > 10.0 && gl_GlobalInvocationID.x % 10 == 0) { + return; + } + + } + + uint index = atomicAdd(numBlades.vertexCount, 1); + if (HACK) { + culledBlades[index] = b; + } + else + { + culledBlades[index] = inputBlades[gl_GlobalInvocationID.x]; + } } diff --git a/src/shaders/grass.tese b/src/shaders/grass.tese index f87ad34..337c22b 100644 --- a/src/shaders/grass.tese +++ b/src/shaders/grass.tese @@ -51,8 +51,6 @@ void main() { // vec3 t0 = normalize(b - a); // vec3 n = normalize(cross(t0, t1)); - //vec4 pos = gl_in[0].gl_Position + rotation * vec4(0.3 * (1.0 - v) * (.5 - u), v, 0.0, 0.0); - float t = u - u * v * v; vec3 pos = (1. - t) * c0 + t * c1; From c6226c34b06da0e151f93eedae4a0756bbc9828f Mon Sep 17 00:00:00 2001 From: Michael Mason Date: Thu, 31 Oct 2024 21:49:09 -0400 Subject: [PATCH 08/10] finished --- src/shaders/compute.comp | 65 +++++++++++++++++++++++++++++++--------- 1 file changed, 51 insertions(+), 14 deletions(-) diff --git a/src/shaders/compute.comp b/src/shaders/compute.comp index af62f6a..5b85d9d 100644 --- a/src/shaders/compute.comp +++ b/src/shaders/compute.comp @@ -77,8 +77,32 @@ bool inFrustum(vec3 p) { return vx && vy && vz; } +float hash( in ivec2 p ) // this hash is not production ready, please +{ // replace this by something better + // 2D -> 1D + int n = p.x*3 + p.y*113; + // 1D hash by Hugo Elias + n = (n << 13) ^ n; + n = n * (n * n * 15731 + 789221) + 1376312589; + return -1.0+2.0*float( n & 0x0fffffff)/float(0x0fffffff); +} + +float noise( in vec2 p ) +{ + ivec2 i = ivec2(floor( p )); + vec2 f = fract( p ); + + // cubic interpolant + vec2 u = f*f*(3.0-2.0*f); + + return mix( mix( hash( i + ivec2(0,0) ), + hash( i + ivec2(1,0) ), u.x), + mix( hash( i + ivec2(0,1) ), + hash( i + ivec2(1,1) ), u.x), u.y); +} -#define HACK true + +#define HACK false #define CULL true void main() { @@ -111,7 +135,7 @@ void main() { vec3 f = (rotation * vec4(0, 0, 1, 0)).xyz; // gravity magnitude/direction - vec4 D = vec4(0, -1, 0, 1); + vec4 D = vec4(0, -0.1, 0, 1); vec3 gEnvironment = normalize(D.xyz) * D.w; // global gravity force vec3 gFront = 0.25f * length(gEnvironment) * f; @@ -119,28 +143,41 @@ void main() { // recovery vec3 iv2 = v0 + (up * height); // initial position of v2 - vec3 recovery = (iv2 - width) * stiffness; + vec3 recovery = (iv2 - v2) * stiffness; // wind - vec3 wind = 300. * sin(v0.x + vec3(totalTime)) * cos(v0.z + vec3(totalTime)) * sin(height + vec3(totalTime)); + float n = noise(v0.xz); + vec3 windDir = 4. * sin(n + vec3(totalTime)) * cos(n + vec3(totalTime)); + float fd = 1 - length(dot(normalize(windDir), normalize(v2 - v0))); + float fr = dot((v2 - v0), up) / height; + vec3 wind = windDir * (fd * fr); // correct position // Not sure why delta time doesn't work, but using deltaTime makes stuttering movement - // if (HACK) { b.v2.xyz += (gravity + recovery + wind) * 0.001; } - else - { - inputBlades[gl_GlobalInvocationID.x].v2.xyz += (gravity + recovery + wind) * 0.000001; - } - + else { + //v2 += (gravity + recovery + wind) * deltaTime; + v2 += (gravity + recovery + wind) * deltaTime; + + v2 = v2 - up * min(dot(up, (v2 - v0)), 0.); + float lproj = length(v2 - v0 - up * dot((v2 - v0), up)); + v1 = v0 + height * up * max(1. - (lproj / height), 0.05 * max(lproj / height, 1)); + + float L0 = distance(v2, v0); + float L1 = distance(v1, v0) + distance(v2, v1); + float L = 0.25 * (2 * L0 + 2 * L1); + float r = height/L; - // TODO: Cull blades that are too far away or not in the camera frustum and write them - // to the culled blades buffer - // Note: to do this, you will need to use an atomic operation to read and update numBlades.vertexCount - // You want to write the visible blades to the buffer without write conflicts between threads + v1 = v0 + r * (v1 - v0); + v2 = v1 + r * (v2 - v1); + + inputBlades[gl_GlobalInvocationID.x].v1.xyz = v1; + inputBlades[gl_GlobalInvocationID.x].v2.xyz = v2; + + } // -- Culling -- From 3460c94161d10611f8643639b3eb6bdf7da36938 Mon Sep 17 00:00:00 2001 From: micklemacklemore <56715549+micklemacklemore@users.noreply.github.com> Date: Thu, 31 Oct 2024 22:29:32 -0400 Subject: [PATCH 09/10] Update README.md --- README.md | 54 ++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 48 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 20ee451..e4e62ad 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,54 @@ Vulkan Grass Rendering ================================== -**University of Pennsylvania, CIS 565: GPU Programming and Architecture, Project 5** +https://github.com/user-attachments/assets/a1ca7be8-5007-4135-999c-f40fa03c1052 -* (TODO) YOUR NAME HERE -* Tested on: (TODO) Windows 22, i7-2222 @ 2.22GHz 22GB, GTX 222 222MB (Moore 2222 Lab) +> University of Pennsylvania, CIS 5650: GPU Programming and Architecture, Project 3 - CUDA Path Tracer +> * Michael Mason +> + [Personal Website](https://www.michaelmason.xyz/) +> * Tested on: Windows 11, Ryzen 9 5900HS @ 3.00GHz 16GB, RTX 3080 (Laptop) 8192MB -### (TODO: Your README) +This is a small Vulkan project that implements a grass simulation. -*DO NOT* leave the README to the last minute! It is a crucial part of the -project, and we will not be able to grade you without a good README. +The implementation is based on [Responsive Real-Time Grass Rendering for General 3D Scenes](https://www.cg.tuwien.ac.at/research/publications/2017/JAHRMANN-2017-RRTG/JAHRMANN-2017-RRTG-draft.pdf) by Jahrmann & Wimmer. + +## Physics Sim / Forces + +### Gravity + Recovery + +![Recording 2024-10-31 at 22 18 32](https://github.com/user-attachments/assets/96a571f0-5c65-4d2d-bc6f-f72b95c32ce9) + +### Wind + +![Recording 2024-10-31 at 22 20 15](https://github.com/user-attachments/assets/1cb92ce7-4e38-4c13-bf8f-5f9b2eff712d) + +## Culling Methods + +### Orientation + +![Recording 2024-10-31 at 22 24 01](https://github.com/user-attachments/assets/2515cba0-8f11-4f7b-bb7b-0d3ad1076ec5) + +### Frustum + +![Recording 2024-10-31 at 22 26 36](https://github.com/user-attachments/assets/9fbfd865-eecf-4029-ad20-36e0261bd344) + +### Distance + +![Recording 2024-10-31 at 22 28 39](https://github.com/user-attachments/assets/7fd976c0-e2a6-47be-a788-72a9ee0b03fe) + +## Performance Analysis + + +``` +## README + +* A brief description of the project and the specific features you implemented. +* GIFs of your project in its different stages with the different features being added incrementally. +* A performance analysis (described below). + +### Performance Analysis + +The performance analysis is where you will investigate how... +* Your renderer handles varying numbers of grass blades +* The improvement you get by culling using each of the three culling tests +``` From 3b0b49b40f7f79e6314e79c3cb8694f304f32bb1 Mon Sep 17 00:00:00 2001 From: micklemacklemore <56715549+micklemacklemore@users.noreply.github.com> Date: Thu, 31 Oct 2024 23:42:41 -0400 Subject: [PATCH 10/10] Update README.md --- README.md | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index e4e62ad..53e59d4 100644 --- a/README.md +++ b/README.md @@ -38,17 +38,18 @@ The implementation is based on [Responsive Real-Time Grass Rendering for General ## Performance Analysis +*Lower is Better* -``` -## README +![No Culling, Orientation, Frustum and Distance](https://github.com/user-attachments/assets/5de8f739-49cc-4c7d-829b-6b7c59614f9d) -* A brief description of the project and the specific features you implemented. -* GIFs of your project in its different stages with the different features being added incrementally. -* A performance analysis (described below). +The graph above represents the effect of different culling methods for rendering grass in Vulkan. We measured miliseconds per frame against number of grass blades. -### Performance Analysis +Culling by Orientation: this represents the culling occurs when grass blades (which are infinitely flat) are perpendicular to the camera. In the shader, we cull those grass blades within a certain threshold of 90 degrees -The performance analysis is where you will investigate how... -* Your renderer handles varying numbers of grass blades -* The improvement you get by culling using each of the three culling tests -``` +Culling by Frustum: This represents culling that occurs when grass blades are outside of the camera frustum. In the shader we cull those blades. + +Culling by distance: This represents culling grass blades that are of a certain distance. After 10 units, every 10th blade is culled. After 40 units, every 2nd blade is culled and after 50 units, all blades are culled. + +Without any culling, rendering times increase sharply with blade count, reaching 178.32 milliseconds at 4,194,304 blades. Orientation Culling, which removes blades perpendicular to the camera, provides moderate improvement, reducing the time to 123.23 milliseconds at the highest count. Frustum Culling, which discards blades outside the camera's view, achieves a similar but slightly less effective reduction to 156.2 milliseconds. Distance Culling, however, proves to be the most impactful, reducing the frame time to 75.89 milliseconds by progressively removing distant blades, especially effective in large fields where far-off details contribute minimally to visual quality. + +While distance culling appears to be the winner here, be mindful that the tests done are dependant on the camera's position and orientation to the grass. It's likely more tests are needed to accurately measure performance of culling.