diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4dd4872 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +bin/* +build/* +bin/Release/vulkan_grass_rendering.exe +bin/* +*.exe diff --git a/README.md b/README.md index 20ee451..2edac5b 100644 --- a/README.md +++ b/README.md @@ -3,10 +3,59 @@ Vulkan Grass Rendering **University of Pennsylvania, CIS 565: GPU Programming and Architecture, Project 5** -* (TODO) YOUR NAME HERE -* Tested on: (TODO) Windows 22, i7-2222 @ 2.22GHz 22GB, GTX 222 222MB (Moore 2222 Lab) +* Richard Chen +* Tested on: Windows 11, i7-10875H @ 2.3GHz 16GB, RTX 2060 MAX-Q 6GB (PC) -### (TODO: Your README) +![](img/vulkan_grass_gif.gif) -*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. +## Overview + +Unlike the ubiquitous OpenGL, Vulkan is a low level graphics API that gives much +more control to the programmer (at the cost of being incredibly verbose). +This comes with the added benefit of having a much higher performance ceiling +and low level graphics APIs like Vulkan, Metal, and DirectX 12 being used by +game engines and where performance is needed. + +In this project, we implemented a grass simulation based on "Responsive +Real-Time Grass Rendering for General 3D Scenes" by Jahrmann and Wimmer in +Vulkan. We used compute shaders to do physics calculations like wind, gravity, +and restoring force. Additionally, tessellation shaders were added to the usual +graphics pipeline of vertex and fragment shaders, where fixed function dedicated +hardware allows for fast and efficient tessellation of scenes. + +## Features + +### Grass +The blades of grass were modelled as Bezier curves. Each blade had v0, v1, and v2 +as the control points and an up vector, and orientation, height, width, and +stiffness as scalar parameters. All of the parameters fit nicely into 4 vec4s. +![](img/blade_model.jpg) + +### Physics +As an approximation, the forces of gravity, wind, and a restoring force were +scaled by the time step and applied to the tip of the grass blade. Then, +corrections were added to +ensure the resulting position was valid, +calculate v1 from v2, +and maintain that grass blade lengths were invariant. +![](img/thick_grass.png) + +### Optimizations + +* Orientation Culling: Grass blades can be approximated as a 2D plane so they +are not rendered when viewed edge-on + +* Distance Culling: When farther away, less grass is rendered until none are +rendered at all + +* View Frustrum Culling: Blades that the outside the camera's field of view are +not rendered as there is no point to doing computations on objects that will +not be seen + +### Other +* Lambertian Shading, where the color intensity is scaled by how directly the +surface faces the camera + +Lambert | Flat +--------|------ +![](img/lambert.png) | ![](img/flat.png) diff --git a/bin/Release/vulkan_grass_rendering.exe b/bin/Release/vulkan_grass_rendering.exe index f68db3a..b0948aa 100644 Binary files a/bin/Release/vulkan_grass_rendering.exe and b/bin/Release/vulkan_grass_rendering.exe differ diff --git a/img/flat.png b/img/flat.png new file mode 100644 index 0000000..62fbfb2 Binary files /dev/null and b/img/flat.png differ diff --git a/img/grass_butts.png b/img/grass_butts.png new file mode 100644 index 0000000..ee4db6f Binary files /dev/null and b/img/grass_butts.png differ diff --git a/img/lambert.png b/img/lambert.png new file mode 100644 index 0000000..fa86a7b Binary files /dev/null and b/img/lambert.png differ diff --git a/img/thick_grass.png b/img/thick_grass.png new file mode 100644 index 0000000..c2a0f97 Binary files /dev/null and b/img/thick_grass.png differ diff --git a/img/vulkan_grass_gif.gif b/img/vulkan_grass_gif.gif new file mode 100644 index 0000000..f544788 Binary files /dev/null and b/img/vulkan_grass_gif.gif differ diff --git a/src/Blades.cpp b/src/Blades.cpp index 80e3d76..124efd7 100644 --- a/src/Blades.cpp +++ b/src/Blades.cpp @@ -2,15 +2,18 @@ #include "Blades.h" #include "BufferUtils.h" -float generateRandomFloat() { +float generateRandomFloat() +{ return rand() / (float)RAND_MAX; } -Blades::Blades(Device* device, VkCommandPool commandPool, float planeDim) : Model(device, commandPool, {}, {}) { +Blades::Blades(Device *device, VkCommandPool commandPool, float planeDim) : Model(device, commandPool, {}, {}) +{ std::vector blades; blades.reserve(NUM_BLADES); - for (int i = 0; i < NUM_BLADES; i++) { + for (int i = 0; i < NUM_BLADES; i++) + { Blade currentBlade = Blade(); glm::vec3 bladeUp(0.0f, 1.0f, 0.0f); @@ -45,23 +48,27 @@ Blades::Blades(Device* device, VkCommandPool commandPool, float planeDim) : Mode 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::CreateBuffer(device, NUM_BLADES * sizeof(Blade), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | 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); } -VkBuffer Blades::GetBladesBuffer() const { +VkBuffer Blades::GetBladesBuffer() const +{ return bladesBuffer; } -VkBuffer Blades::GetCulledBladesBuffer() const { +VkBuffer Blades::GetCulledBladesBuffer() const +{ return culledBladesBuffer; } -VkBuffer Blades::GetNumBladesBuffer() const { +VkBuffer Blades::GetNumBladesBuffer() const +{ return numBladesBuffer; } -Blades::~Blades() { +Blades::~Blades() +{ vkDestroyBuffer(device->GetVkDevice(), bladesBuffer, nullptr); vkFreeMemory(device->GetVkDevice(), bladesBufferMemory, nullptr); vkDestroyBuffer(device->GetVkDevice(), culledBladesBuffer, nullptr); diff --git a/src/Blades.h b/src/Blades.h index 9bd1eed..f2a92a6 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 << 14; constexpr static float MIN_HEIGHT = 1.3f; constexpr static float MAX_HEIGHT = 2.5f; constexpr static float MIN_WIDTH = 0.1f; @@ -12,7 +12,8 @@ constexpr static float MAX_WIDTH = 0.14f; constexpr static float MIN_BEND = 7.0f; constexpr static float MAX_BEND = 13.0f; -struct Blade { +struct Blade +{ // Position and direction glm::vec4 v0; // Bezier point and height @@ -22,7 +23,8 @@ struct Blade { // Up vector and stiffness coefficient glm::vec4 up; - static VkVertexInputBindingDescription getBindingDescription() { + static VkVertexInputBindingDescription getBindingDescription() + { VkVertexInputBindingDescription bindingDescription = {}; bindingDescription.binding = 0; bindingDescription.stride = sizeof(Blade); @@ -31,7 +33,8 @@ struct Blade { return bindingDescription; } - static std::array getAttributeDescriptions() { + static std::array getAttributeDescriptions() + { std::array attributeDescriptions = {}; // v0 @@ -62,14 +65,16 @@ struct Blade { } }; -struct BladeDrawIndirect { +struct BladeDrawIndirect +{ uint32_t vertexCount; uint32_t instanceCount; uint32_t firstVertex; uint32_t firstInstance; }; -class Blades : public Model { +class Blades : public Model +{ private: VkBuffer bladesBuffer; VkBuffer culledBladesBuffer; @@ -80,7 +85,7 @@ class Blades : public Model { VkDeviceMemory numBladesBufferMemory; public: - Blades(Device* device, VkCommandPool commandPool, float planeDim); + Blades(Device *device, VkCommandPool commandPool, float planeDim); VkBuffer GetBladesBuffer() const; VkBuffer GetCulledBladesBuffer() const; VkBuffer GetNumBladesBuffer() const; diff --git a/src/Renderer.cpp b/src/Renderer.cpp index b445d04..b46987a 100644 --- a/src/Renderer.cpp +++ b/src/Renderer.cpp @@ -5,20 +5,23 @@ #include "Blades.h" #include "Camera.h" #include "Image.h" +#include static constexpr unsigned int WORKGROUP_SIZE = 32; -Renderer::Renderer(Device* device, SwapChain* swapChain, Scene* scene, Camera* camera) - : device(device), - logicalDevice(device->GetVkDevice()), - swapChain(swapChain), - scene(scene), - camera(camera) { +Renderer::Renderer(Device *device, SwapChain *swapChain, Scene *scene, Camera *camera) + : device(device), + logicalDevice(device->GetVkDevice()), + swapChain(swapChain), + scene(scene), + camera(camera) +{ CreateCommandPools(); CreateRenderPass(); CreateCameraDescriptorSetLayout(); CreateModelDescriptorSetLayout(); + // CreateGrassDescriptorSetLayout(); CreateTimeDescriptorSetLayout(); CreateComputeDescriptorSetLayout(); CreateDescriptorPool(); @@ -32,16 +35,20 @@ Renderer::Renderer(Device* device, SwapChain* swapChain, Scene* scene, Camera* c CreateGrassPipeline(); CreateComputePipeline(); RecordCommandBuffers(); + std::cout << "REEEEEEEEEEEE" << std::endl; + std::cout << "delay" << std::endl; RecordComputeCommandBuffer(); } -void Renderer::CreateCommandPools() { +void Renderer::CreateCommandPools() +{ VkCommandPoolCreateInfo graphicsPoolInfo = {}; graphicsPoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; graphicsPoolInfo.queueFamilyIndex = device->GetInstance()->GetQueueFamilyIndices()[QueueFlags::Graphics]; graphicsPoolInfo.flags = 0; - if (vkCreateCommandPool(logicalDevice, &graphicsPoolInfo, nullptr, &graphicsCommandPool) != VK_SUCCESS) { + if (vkCreateCommandPool(logicalDevice, &graphicsPoolInfo, nullptr, &graphicsCommandPool) != VK_SUCCESS) + { throw std::runtime_error("Failed to create command pool"); } @@ -50,12 +57,14 @@ void Renderer::CreateCommandPools() { computePoolInfo.queueFamilyIndex = device->GetInstance()->GetQueueFamilyIndices()[QueueFlags::Compute]; computePoolInfo.flags = 0; - if (vkCreateCommandPool(logicalDevice, &computePoolInfo, nullptr, &computeCommandPool) != VK_SUCCESS) { + if (vkCreateCommandPool(logicalDevice, &computePoolInfo, nullptr, &computeCommandPool) != VK_SUCCESS) + { throw std::runtime_error("Failed to create command pool"); } } -void Renderer::CreateRenderPass() { +void Renderer::CreateRenderPass() +{ // Color buffer attachment represented by one of the images from the swap chain VkAttachmentDescription colorAttachment = {}; colorAttachment.format = swapChain->GetVkImageFormat(); @@ -73,7 +82,7 @@ void Renderer::CreateRenderPass() { colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; // Depth buffer attachment - VkFormat depthFormat = device->GetInstance()->GetSupportedFormat({ VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT }, VK_IMAGE_TILING_OPTIMAL, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT); + VkFormat depthFormat = device->GetInstance()->GetSupportedFormat({VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT}, VK_IMAGE_TILING_OPTIMAL, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT); VkAttachmentDescription depthAttachment = {}; depthAttachment.format = depthFormat; depthAttachment.samples = VK_SAMPLE_COUNT_1_BIT; @@ -96,7 +105,7 @@ void Renderer::CreateRenderPass() { subpass.pColorAttachments = &colorAttachmentRef; subpass.pDepthStencilAttachment = &depthAttachmentRef; - std::array attachments = { colorAttachment, depthAttachment }; + std::array attachments = {colorAttachment, depthAttachment}; // Specify subpass dependency VkSubpassDependency dependency = {}; @@ -117,12 +126,14 @@ void Renderer::CreateRenderPass() { renderPassInfo.dependencyCount = 1; renderPassInfo.pDependencies = &dependency; - if (vkCreateRenderPass(logicalDevice, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) { + if (vkCreateRenderPass(logicalDevice, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) + { throw std::runtime_error("Failed to create render pass"); } } -void Renderer::CreateCameraDescriptorSetLayout() { +void Renderer::CreateCameraDescriptorSetLayout() +{ // Describe the binding of the descriptor set layout VkDescriptorSetLayoutBinding uboLayoutBinding = {}; uboLayoutBinding.binding = 0; @@ -131,7 +142,7 @@ void Renderer::CreateCameraDescriptorSetLayout() { uboLayoutBinding.stageFlags = VK_SHADER_STAGE_ALL; uboLayoutBinding.pImmutableSamplers = nullptr; - std::vector bindings = { uboLayoutBinding }; + std::vector bindings = {uboLayoutBinding}; // Create the descriptor set layout VkDescriptorSetLayoutCreateInfo layoutInfo = {}; @@ -139,12 +150,14 @@ void Renderer::CreateCameraDescriptorSetLayout() { layoutInfo.bindingCount = static_cast(bindings.size()); layoutInfo.pBindings = bindings.data(); - if (vkCreateDescriptorSetLayout(logicalDevice, &layoutInfo, nullptr, &cameraDescriptorSetLayout) != VK_SUCCESS) { + if (vkCreateDescriptorSetLayout(logicalDevice, &layoutInfo, nullptr, &cameraDescriptorSetLayout) != VK_SUCCESS) + { throw std::runtime_error("Failed to create descriptor set layout"); } } -void Renderer::CreateModelDescriptorSetLayout() { +void Renderer::CreateModelDescriptorSetLayout() +{ VkDescriptorSetLayoutBinding uboLayoutBinding = {}; uboLayoutBinding.binding = 0; uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; @@ -159,7 +172,7 @@ void Renderer::CreateModelDescriptorSetLayout() { samplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; samplerLayoutBinding.pImmutableSamplers = nullptr; - std::vector bindings = { uboLayoutBinding, samplerLayoutBinding }; + std::vector bindings = {uboLayoutBinding, samplerLayoutBinding}; // Create the descriptor set layout VkDescriptorSetLayoutCreateInfo layoutInfo = {}; @@ -167,12 +180,14 @@ void Renderer::CreateModelDescriptorSetLayout() { layoutInfo.bindingCount = static_cast(bindings.size()); layoutInfo.pBindings = bindings.data(); - if (vkCreateDescriptorSetLayout(logicalDevice, &layoutInfo, nullptr, &modelDescriptorSetLayout) != VK_SUCCESS) { + if (vkCreateDescriptorSetLayout(logicalDevice, &layoutInfo, nullptr, &modelDescriptorSetLayout) != VK_SUCCESS) + { throw std::runtime_error("Failed to create descriptor set layout"); } } -void Renderer::CreateTimeDescriptorSetLayout() { +void Renderer::CreateTimeDescriptorSetLayout() +{ // Describe the binding of the descriptor set layout VkDescriptorSetLayoutBinding uboLayoutBinding = {}; uboLayoutBinding.binding = 0; @@ -181,7 +196,7 @@ void Renderer::CreateTimeDescriptorSetLayout() { uboLayoutBinding.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; uboLayoutBinding.pImmutableSamplers = nullptr; - std::vector bindings = { uboLayoutBinding }; + std::vector bindings = {uboLayoutBinding}; // Create the descriptor set layout VkDescriptorSetLayoutCreateInfo layoutInfo = {}; @@ -189,49 +204,92 @@ void Renderer::CreateTimeDescriptorSetLayout() { layoutInfo.bindingCount = static_cast(bindings.size()); layoutInfo.pBindings = bindings.data(); - if (vkCreateDescriptorSetLayout(logicalDevice, &layoutInfo, nullptr, &timeDescriptorSetLayout) != VK_SUCCESS) { + if (vkCreateDescriptorSetLayout(logicalDevice, &layoutInfo, nullptr, &timeDescriptorSetLayout) != VK_SUCCESS) + { throw std::runtime_error("Failed to create descriptor set layout"); } } -void Renderer::CreateComputeDescriptorSetLayout() { +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 bladesLayoutBinding = {}; + bladesLayoutBinding.binding = 0; + bladesLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + bladesLayoutBinding.descriptorCount = 1; + bladesLayoutBinding.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + bladesLayoutBinding.pImmutableSamplers = nullptr; + + VkDescriptorSetLayoutBinding culledBladesLayoutBinding = {}; + culledBladesLayoutBinding.binding = 1; + culledBladesLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + culledBladesLayoutBinding.descriptorCount = 1; + culledBladesLayoutBinding.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + culledBladesLayoutBinding.pImmutableSamplers = nullptr; + + VkDescriptorSetLayoutBinding numBladesLayoutBinding = {}; + numBladesLayoutBinding.binding = 2; + numBladesLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + numBladesLayoutBinding.descriptorCount = 1; + numBladesLayoutBinding.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + numBladesLayoutBinding.pImmutableSamplers = nullptr; + + std::vector bindings = {bladesLayoutBinding, culledBladesLayoutBinding, numBladesLayoutBinding}; + + // 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, &computeDescriptorSetLayout) != VK_SUCCESS) + { + throw std::runtime_error("Failed to create descriptor set layout"); + } } -void Renderer::CreateDescriptorPool() { +void Renderer::CreateDescriptorPool() +{ // Describe which descriptor types that the descriptor sets will contain std::vector poolSizes = { // Camera - { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER , 1}, + {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1}, // Models + Blades - { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER , static_cast(scene->GetModels().size() + scene->GetBlades().size()) }, + {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, static_cast(scene->GetModels().size() + scene->GetBlades().size())}, // Models + Blades - { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER , static_cast(scene->GetModels().size() + scene->GetBlades().size()) }, + {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, static_cast(scene->GetModels().size() + scene->GetBlades().size())}, // Time (compute) - { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER , 1 }, + {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, static_cast(scene->GetModels().size() + scene->GetBlades().size())}, + // {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, static_cast(scene->GetModels().size() + scene->GetBlades().size())}, + // {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, static_cast(scene->GetModels().size() + scene->GetBlades().size())}, + {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, static_cast(3 * scene->GetBlades().size())}, }; VkDescriptorPoolCreateInfo poolInfo = {}; poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; poolInfo.poolSizeCount = static_cast(poolSizes.size()); poolInfo.pPoolSizes = poolSizes.data(); - poolInfo.maxSets = 5; + poolInfo.maxSets = 7; - if (vkCreateDescriptorPool(logicalDevice, &poolInfo, nullptr, &descriptorPool) != VK_SUCCESS) { + if (vkCreateDescriptorPool(logicalDevice, &poolInfo, nullptr, &descriptorPool) != VK_SUCCESS) + { throw std::runtime_error("Failed to create descriptor pool"); } } -void Renderer::CreateCameraDescriptorSet() { +void Renderer::CreateCameraDescriptorSet() +{ // Describe the desciptor set - VkDescriptorSetLayout layouts[] = { cameraDescriptorSetLayout }; + VkDescriptorSetLayout layouts[] = {cameraDescriptorSetLayout}; VkDescriptorSetAllocateInfo allocInfo = {}; allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; allocInfo.descriptorPool = descriptorPool; @@ -239,7 +297,8 @@ void Renderer::CreateCameraDescriptorSet() { allocInfo.pSetLayouts = layouts; // Allocate descriptor sets - if (vkAllocateDescriptorSets(logicalDevice, &allocInfo, &cameraDescriptorSet) != VK_SUCCESS) { + if (vkAllocateDescriptorSets(logicalDevice, &allocInfo, &cameraDescriptorSet) != VK_SUCCESS) + { throw std::runtime_error("Failed to allocate descriptor set"); } @@ -264,11 +323,12 @@ void Renderer::CreateCameraDescriptorSet() { vkUpdateDescriptorSets(logicalDevice, static_cast(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr); } -void Renderer::CreateModelDescriptorSets() { +void Renderer::CreateModelDescriptorSets() +{ modelDescriptorSets.resize(scene->GetModels().size()); // Describe the desciptor set - VkDescriptorSetLayout layouts[] = { modelDescriptorSetLayout }; + VkDescriptorSetLayout layouts[] = {modelDescriptorSetLayout}; VkDescriptorSetAllocateInfo allocInfo = {}; allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; allocInfo.descriptorPool = descriptorPool; @@ -276,13 +336,15 @@ void Renderer::CreateModelDescriptorSets() { allocInfo.pSetLayouts = layouts; // Allocate descriptor sets - if (vkAllocateDescriptorSets(logicalDevice, &allocInfo, modelDescriptorSets.data()) != VK_SUCCESS) { + if (vkAllocateDescriptorSets(logicalDevice, &allocInfo, modelDescriptorSets.data()) != VK_SUCCESS) + { throw std::runtime_error("Failed to allocate descriptor set"); } std::vector descriptorWrites(2 * modelDescriptorSets.size()); - for (uint32_t i = 0; i < scene->GetModels().size(); ++i) { + for (uint32_t i = 0; i < scene->GetModels().size(); ++i) + { VkDescriptorBufferInfo modelBufferInfo = {}; modelBufferInfo.buffer = scene->GetModels()[i]->GetModelBuffer(); modelBufferInfo.offset = 0; @@ -317,14 +379,52 @@ void Renderer::CreateModelDescriptorSets() { vkUpdateDescriptorSets(logicalDevice, static_cast(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr); } -void Renderer::CreateGrassDescriptorSets() { +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 + // resize + grassDescriptorSets.resize(scene->GetBlades().size()); + // Describe the descriptor set + // VkDescriptorSetLayout layouts[] = {grassDescriptorSetLayout}; + VkDescriptorSetLayout layouts[] = {modelDescriptorSetLayout}; + VkDescriptorSetAllocateInfo allocInfo = {}; + allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + allocInfo.descriptorPool = descriptorPool; + allocInfo.descriptorSetCount = static_cast(scene->GetBlades().size()); + allocInfo.pSetLayouts = layouts; + //Alloc descriptor sets + if (vkAllocateDescriptorSets(logicalDevice, &allocInfo, grassDescriptorSets.data()) != VK_SUCCESS) + { + throw std::runtime_error("Failed to allocate descriptor set"); + } + //descriptor write + std::vector descriptorWrites(grassDescriptorSets.size()); + // loopy boi + for (uint32_t i = 0; i < scene->GetBlades().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() { +void Renderer::CreateTimeDescriptorSet() +{ // Describe the desciptor set - VkDescriptorSetLayout layouts[] = { timeDescriptorSetLayout }; + VkDescriptorSetLayout layouts[] = {timeDescriptorSetLayout}; VkDescriptorSetAllocateInfo allocInfo = {}; allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; allocInfo.descriptorPool = descriptorPool; @@ -332,7 +432,8 @@ void Renderer::CreateTimeDescriptorSet() { allocInfo.pSetLayouts = layouts; // Allocate descriptor sets - if (vkAllocateDescriptorSets(logicalDevice, &allocInfo, &timeDescriptorSet) != VK_SUCCESS) { + if (vkAllocateDescriptorSets(logicalDevice, &allocInfo, &timeDescriptorSet) != VK_SUCCESS) + { throw std::runtime_error("Failed to allocate descriptor set"); } @@ -357,12 +458,83 @@ void Renderer::CreateTimeDescriptorSet() { vkUpdateDescriptorSets(logicalDevice, static_cast(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr); } -void Renderer::CreateComputeDescriptorSets() { +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 + // 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) + { + VkDescriptorBufferInfo bladesBufferInfo = {}; + bladesBufferInfo.buffer = scene->GetBlades()[i]->GetBladesBuffer(); + bladesBufferInfo.offset = 0; + bladesBufferInfo.range = sizeof(Blade) * NUM_BLADES; + + VkDescriptorBufferInfo culledBladesBufferInfo = {}; + culledBladesBufferInfo.buffer = scene->GetBlades()[i]->GetCulledBladesBuffer(); + culledBladesBufferInfo.offset = 0; + culledBladesBufferInfo.range = sizeof(Blade) * NUM_BLADES; + + 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 = &bladesBufferInfo; + descriptorWrites[3 * i + 0].pImageInfo = nullptr; + descriptorWrites[3 * i + 0].pTexelBufferView = nullptr; + + 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 = &culledBladesBufferInfo; + descriptorWrites[3 * i + 1].pImageInfo = nullptr; + descriptorWrites[3 * i + 1].pTexelBufferView = nullptr; + + 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; + descriptorWrites[3 * i + 2].pImageInfo = nullptr; + descriptorWrites[3 * i + 2].pTexelBufferView = nullptr; + } + + // Update descriptor sets + vkUpdateDescriptorSets(logicalDevice, static_cast(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr); } -void Renderer::CreateGraphicsPipeline() { +void Renderer::CreateGraphicsPipeline() +{ VkShaderModule vertShaderModule = ShaderModule::Create("shaders/graphics.vert.spv", logicalDevice); VkShaderModule fragShaderModule = ShaderModule::Create("shaders/graphics.frag.spv", logicalDevice); @@ -379,7 +551,7 @@ void Renderer::CreateGraphicsPipeline() { fragShaderStageInfo.module = fragShaderModule; fragShaderStageInfo.pName = "main"; - VkPipelineShaderStageCreateInfo shaderStages[] = { vertShaderStageInfo, fragShaderStageInfo }; + VkPipelineShaderStageCreateInfo shaderStages[] = {vertShaderStageInfo, fragShaderStageInfo}; // --- Set up fixed-function stages --- @@ -411,7 +583,7 @@ void Renderer::CreateGraphicsPipeline() { viewport.maxDepth = 1.0f; VkRect2D scissor = {}; - scissor.offset = { 0, 0 }; + scissor.offset = {0, 0}; scissor.extent = swapChain->GetVkExtent(); VkPipelineViewportStateCreateInfo viewportState = {}; @@ -480,7 +652,7 @@ void Renderer::CreateGraphicsPipeline() { colorBlending.blendConstants[2] = 0.0f; colorBlending.blendConstants[3] = 0.0f; - std::vector descriptorSetLayouts = { cameraDescriptorSetLayout, modelDescriptorSetLayout }; + std::vector descriptorSetLayouts = {cameraDescriptorSetLayout, modelDescriptorSetLayout}; // Pipeline layout: used to specify uniform values VkPipelineLayoutCreateInfo pipelineLayoutInfo = {}; @@ -490,7 +662,8 @@ void Renderer::CreateGraphicsPipeline() { pipelineLayoutInfo.pushConstantRangeCount = 0; pipelineLayoutInfo.pPushConstantRanges = 0; - if (vkCreatePipelineLayout(logicalDevice, &pipelineLayoutInfo, nullptr, &graphicsPipelineLayout) != VK_SUCCESS) { + if (vkCreatePipelineLayout(logicalDevice, &pipelineLayoutInfo, nullptr, &graphicsPipelineLayout) != VK_SUCCESS) + { throw std::runtime_error("Failed to create pipeline layout"); } @@ -513,7 +686,8 @@ void Renderer::CreateGraphicsPipeline() { pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; pipelineInfo.basePipelineIndex = -1; - if (vkCreateGraphicsPipelines(logicalDevice, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS) { + if (vkCreateGraphicsPipelines(logicalDevice, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS) + { throw std::runtime_error("Failed to create graphics pipeline"); } @@ -521,7 +695,8 @@ void Renderer::CreateGraphicsPipeline() { vkDestroyShaderModule(logicalDevice, fragShaderModule, nullptr); } -void Renderer::CreateGrassPipeline() { +void Renderer::CreateGrassPipeline() +{ // --- Set up programmable shaders --- VkShaderModule vertShaderModule = ShaderModule::Create("shaders/grass.vert.spv", logicalDevice); VkShaderModule tescShaderModule = ShaderModule::Create("shaders/grass.tesc.spv", logicalDevice); @@ -553,7 +728,7 @@ void Renderer::CreateGrassPipeline() { fragShaderStageInfo.module = fragShaderModule; fragShaderStageInfo.pName = "main"; - VkPipelineShaderStageCreateInfo shaderStages[] = { vertShaderStageInfo, tescShaderStageInfo, teseShaderStageInfo, fragShaderStageInfo }; + VkPipelineShaderStageCreateInfo shaderStages[] = {vertShaderStageInfo, tescShaderStageInfo, teseShaderStageInfo, fragShaderStageInfo}; // --- Set up fixed-function stages --- @@ -585,7 +760,7 @@ void Renderer::CreateGrassPipeline() { viewport.maxDepth = 1.0f; VkRect2D scissor = {}; - scissor.offset = { 0, 0 }; + scissor.offset = {0, 0}; scissor.extent = swapChain->GetVkExtent(); VkPipelineViewportStateCreateInfo viewportState = {}; @@ -654,7 +829,9 @@ void Renderer::CreateGrassPipeline() { colorBlending.blendConstants[2] = 0.0f; colorBlending.blendConstants[3] = 0.0f; - std::vector descriptorSetLayouts = { cameraDescriptorSetLayout, modelDescriptorSetLayout }; + //TODO: add grassDescriptionSetLayout???? + // std::vector descriptorSetLayouts = {cameraDescriptorSetLayout, modelDescriptorSetLayout, grassDescriptorSetLayout}; + std::vector descriptorSetLayouts = {cameraDescriptorSetLayout, modelDescriptorSetLayout}; // Pipeline layout: used to specify uniform values VkPipelineLayoutCreateInfo pipelineLayoutInfo = {}; @@ -664,7 +841,8 @@ void Renderer::CreateGrassPipeline() { pipelineLayoutInfo.pushConstantRangeCount = 0; pipelineLayoutInfo.pPushConstantRanges = 0; - if (vkCreatePipelineLayout(logicalDevice, &pipelineLayoutInfo, nullptr, &grassPipelineLayout) != VK_SUCCESS) { + if (vkCreatePipelineLayout(logicalDevice, &pipelineLayoutInfo, nullptr, &grassPipelineLayout) != VK_SUCCESS) + { throw std::runtime_error("Failed to create pipeline layout"); } @@ -695,7 +873,8 @@ void Renderer::CreateGrassPipeline() { pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; pipelineInfo.basePipelineIndex = -1; - if (vkCreateGraphicsPipelines(logicalDevice, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &grassPipeline) != VK_SUCCESS) { + if (vkCreateGraphicsPipelines(logicalDevice, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &grassPipeline) != VK_SUCCESS) + { throw std::runtime_error("Failed to create graphics pipeline"); } @@ -706,7 +885,8 @@ void Renderer::CreateGrassPipeline() { vkDestroyShaderModule(logicalDevice, fragShaderModule, nullptr); } -void Renderer::CreateComputePipeline() { +void Renderer::CreateComputePipeline() +{ // Set up programmable shaders VkShaderModule computeShaderModule = ShaderModule::Create("shaders/compute.comp.spv", logicalDevice); @@ -717,7 +897,8 @@ 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, computeBladesDescriptorSetLayout, computeCulledBladesDescriptorSetLayout, computeNumBladesDescriptorSetLayout}; + std::vector descriptorSetLayouts = {cameraDescriptorSetLayout, timeDescriptorSetLayout, computeDescriptorSetLayout}; // Create pipeline layout VkPipelineLayoutCreateInfo pipelineLayoutInfo = {}; @@ -727,7 +908,8 @@ void Renderer::CreateComputePipeline() { pipelineLayoutInfo.pushConstantRangeCount = 0; pipelineLayoutInfo.pPushConstantRanges = 0; - if (vkCreatePipelineLayout(logicalDevice, &pipelineLayoutInfo, nullptr, &computePipelineLayout) != VK_SUCCESS) { + if (vkCreatePipelineLayout(logicalDevice, &pipelineLayoutInfo, nullptr, &computePipelineLayout) != VK_SUCCESS) + { throw std::runtime_error("Failed to create pipeline layout"); } @@ -741,7 +923,8 @@ void Renderer::CreateComputePipeline() { pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; pipelineInfo.basePipelineIndex = -1; - if (vkCreateComputePipelines(logicalDevice, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &computePipeline) != VK_SUCCESS) { + if (vkCreateComputePipelines(logicalDevice, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &computePipeline) != VK_SUCCESS) + { throw std::runtime_error("Failed to create compute pipeline"); } @@ -749,10 +932,12 @@ void Renderer::CreateComputePipeline() { vkDestroyShaderModule(logicalDevice, computeShaderModule, nullptr); } -void Renderer::CreateFrameResources() { +void Renderer::CreateFrameResources() +{ imageViews.resize(swapChain->GetCount()); - for (uint32_t i = 0; i < swapChain->GetCount(); i++) { + for (uint32_t i = 0; i < swapChain->GetCount(); i++) + { // --- Create an image view for each swap chain image --- VkImageViewCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; @@ -776,37 +961,36 @@ void Renderer::CreateFrameResources() { createInfo.subresourceRange.layerCount = 1; // Create the image view - if (vkCreateImageView(logicalDevice, &createInfo, nullptr, &imageViews[i]) != VK_SUCCESS) { + if (vkCreateImageView(logicalDevice, &createInfo, nullptr, &imageViews[i]) != VK_SUCCESS) + { throw std::runtime_error("Failed to create image views"); } } - VkFormat depthFormat = device->GetInstance()->GetSupportedFormat({ VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT }, VK_IMAGE_TILING_OPTIMAL, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT); + VkFormat depthFormat = device->GetInstance()->GetSupportedFormat({VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT}, VK_IMAGE_TILING_OPTIMAL, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT); // CREATE DEPTH IMAGE Image::Create(device, - swapChain->GetVkExtent().width, - swapChain->GetVkExtent().height, - depthFormat, - VK_IMAGE_TILING_OPTIMAL, - VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, - VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, - depthImage, - depthImageMemory - ); + swapChain->GetVkExtent().width, + swapChain->GetVkExtent().height, + depthFormat, + VK_IMAGE_TILING_OPTIMAL, + VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + depthImage, + depthImageMemory); depthImageView = Image::CreateView(device, depthImage, depthFormat, VK_IMAGE_ASPECT_DEPTH_BIT); - + // Transition the image for use as depth-stencil Image::TransitionLayout(device, graphicsCommandPool, depthImage, depthFormat, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); - // CREATE FRAMEBUFFERS framebuffers.resize(swapChain->GetCount()); - for (size_t i = 0; i < swapChain->GetCount(); i++) { + for (size_t i = 0; i < swapChain->GetCount(); i++) + { std::vector attachments = { imageViews[i], - depthImageView - }; + depthImageView}; VkFramebufferCreateInfo framebufferInfo = {}; framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; @@ -817,15 +1001,17 @@ void Renderer::CreateFrameResources() { framebufferInfo.height = swapChain->GetVkExtent().height; framebufferInfo.layers = 1; - if (vkCreateFramebuffer(logicalDevice, &framebufferInfo, nullptr, &framebuffers[i]) != VK_SUCCESS) { + if (vkCreateFramebuffer(logicalDevice, &framebufferInfo, nullptr, &framebuffers[i]) != VK_SUCCESS) + { throw std::runtime_error("Failed to create framebuffer"); } - } } -void Renderer::DestroyFrameResources() { - for (size_t i = 0; i < imageViews.size(); i++) { +void Renderer::DestroyFrameResources() +{ + for (size_t i = 0; i < imageViews.size(); i++) + { vkDestroyImageView(logicalDevice, imageViews[i], nullptr); } @@ -833,12 +1019,14 @@ void Renderer::DestroyFrameResources() { vkFreeMemory(logicalDevice, depthImageMemory, nullptr); vkDestroyImage(logicalDevice, depthImage, nullptr); - for (size_t i = 0; i < framebuffers.size(); i++) { + for (size_t i = 0; i < framebuffers.size(); i++) + { vkDestroyFramebuffer(logicalDevice, framebuffers[i], nullptr); } } -void Renderer::RecreateFrameResources() { +void Renderer::RecreateFrameResources() +{ vkDestroyPipeline(logicalDevice, graphicsPipeline, nullptr); vkDestroyPipeline(logicalDevice, grassPipeline, nullptr); vkDestroyPipelineLayout(logicalDevice, graphicsPipelineLayout, nullptr); @@ -852,7 +1040,8 @@ void Renderer::RecreateFrameResources() { RecordCommandBuffers(); } -void Renderer::RecordComputeCommandBuffer() { +void Renderer::RecordComputeCommandBuffer() +{ // Specify the command pool and number of buffers to allocate VkCommandBufferAllocateInfo allocInfo = {}; allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; @@ -860,7 +1049,8 @@ void Renderer::RecordComputeCommandBuffer() { allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; allocInfo.commandBufferCount = 1; - if (vkAllocateCommandBuffers(logicalDevice, &allocInfo, &computeCommandBuffer) != VK_SUCCESS) { + if (vkAllocateCommandBuffers(logicalDevice, &allocInfo, &computeCommandBuffer) != VK_SUCCESS) + { throw std::runtime_error("Failed to allocate command buffers"); } @@ -870,7 +1060,8 @@ void Renderer::RecordComputeCommandBuffer() { beginInfo.pInheritanceInfo = nullptr; // ~ Start recording ~ - if (vkBeginCommandBuffer(computeCommandBuffer, &beginInfo) != VK_SUCCESS) { + if (vkBeginCommandBuffer(computeCommandBuffer, &beginInfo) != VK_SUCCESS) + { throw std::runtime_error("Failed to begin recording compute command buffer"); } @@ -885,13 +1076,22 @@ void Renderer::RecordComputeCommandBuffer() { // TODO: For each group of blades bind its descriptor set and dispatch + for (uint32_t i = 0; i < scene->GetBlades().size(); ++i) + { + vkCmdBindDescriptorSets(computeCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, computePipelineLayout, 2, 1, &(computeDescriptorSets[i]), 0, nullptr); + std::cout << "test" << std::endl; + vkCmdDispatch(computeCommandBuffer, (NUM_BLADES) + 1, 1, 1); + } + // ~ End recording ~ - if (vkEndCommandBuffer(computeCommandBuffer) != VK_SUCCESS) { + if (vkEndCommandBuffer(computeCommandBuffer) != VK_SUCCESS) + { throw std::runtime_error("Failed to record compute command buffer"); } } -void Renderer::RecordCommandBuffers() { +void Renderer::RecordCommandBuffers() +{ commandBuffers.resize(swapChain->GetCount()); // Specify the command pool and number of buffers to allocate @@ -901,19 +1101,22 @@ void Renderer::RecordCommandBuffers() { allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; allocInfo.commandBufferCount = static_cast(commandBuffers.size()); - if (vkAllocateCommandBuffers(logicalDevice, &allocInfo, commandBuffers.data()) != VK_SUCCESS) { + if (vkAllocateCommandBuffers(logicalDevice, &allocInfo, commandBuffers.data()) != VK_SUCCESS) + { throw std::runtime_error("Failed to allocate command buffers"); } // Start command buffer recording - for (size_t i = 0; i < commandBuffers.size(); i++) { + for (size_t i = 0; i < commandBuffers.size(); i++) + { VkCommandBufferBeginInfo beginInfo = {}; beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; beginInfo.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT; beginInfo.pInheritanceInfo = nullptr; // ~ Start recording ~ - if (vkBeginCommandBuffer(commandBuffers[i], &beginInfo) != VK_SUCCESS) { + if (vkBeginCommandBuffer(commandBuffers[i], &beginInfo) != VK_SUCCESS) + { throw std::runtime_error("Failed to begin recording command buffer"); } @@ -922,17 +1125,18 @@ void Renderer::RecordCommandBuffers() { renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; renderPassInfo.renderPass = renderPass; renderPassInfo.framebuffer = framebuffers[i]; - renderPassInfo.renderArea.offset = { 0, 0 }; + renderPassInfo.renderArea.offset = {0, 0}; renderPassInfo.renderArea.extent = swapChain->GetVkExtent(); std::array clearValues = {}; - clearValues[0].color = { 0.0f, 0.0f, 0.0f, 1.0f }; - clearValues[1].depthStencil = { 1.0f, 0 }; + clearValues[0].color = {0.0f, 0.0f, 0.0f, 1.0f}; + clearValues[1].depthStencil = {1.0f, 0}; renderPassInfo.clearValueCount = static_cast(clearValues.size()); renderPassInfo.pClearValues = clearValues.data(); std::vector barriers(scene->GetBlades().size()); - for (uint32_t j = 0; j < barriers.size(); ++j) { + for (uint32_t j = 0; j < barriers.size(); ++j) + { barriers[j].sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER; barriers[j].srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT; barriers[j].dstAccessMask = VK_ACCESS_INDIRECT_COMMAND_READ_BIT; @@ -953,10 +1157,11 @@ void Renderer::RecordCommandBuffers() { // Bind the graphics pipeline vkCmdBindPipeline(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline); - for (uint32_t j = 0; j < scene->GetModels().size(); ++j) { + for (uint32_t j = 0; j < scene->GetModels().size(); ++j) + { // Bind the vertex and index buffers - VkBuffer vertexBuffers[] = { scene->GetModels()[j]->getVertexBuffer() }; - VkDeviceSize offsets[] = { 0 }; + VkBuffer vertexBuffers[] = {scene->GetModels()[j]->getVertexBuffer()}; + VkDeviceSize offsets[] = {0}; vkCmdBindVertexBuffers(commandBuffers[i], 0, 1, vertexBuffers, offsets); vkCmdBindIndexBuffer(commandBuffers[i], scene->GetModels()[j]->getIndexBuffer(), 0, VK_INDEX_TYPE_UINT32); @@ -972,30 +1177,34 @@ void Renderer::RecordCommandBuffers() { // Bind the grass pipeline vkCmdBindPipeline(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, grassPipeline); - for (uint32_t j = 0; j < scene->GetBlades().size(); ++j) { - VkBuffer vertexBuffers[] = { scene->GetBlades()[j]->GetCulledBladesBuffer() }; - VkDeviceSize offsets[] = { 0 }; + 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); + vkCmdBindVertexBuffers(commandBuffers[i], 0, 1, vertexBuffers, offsets); // TODO: Bind the descriptor set for each grass blades model + 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 vkCmdEndRenderPass(commandBuffers[i]); // ~ End recording ~ - if (vkEndCommandBuffer(commandBuffers[i]) != VK_SUCCESS) { + if (vkEndCommandBuffer(commandBuffers[i]) != VK_SUCCESS) + { throw std::runtime_error("Failed to record command buffer"); } } } -void Renderer::Frame() { +void Renderer::Frame() +{ VkSubmitInfo computeSubmitInfo = {}; computeSubmitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; @@ -1003,11 +1212,13 @@ void Renderer::Frame() { computeSubmitInfo.commandBufferCount = 1; computeSubmitInfo.pCommandBuffers = &computeCommandBuffer; - if (vkQueueSubmit(device->GetQueue(QueueFlags::Compute), 1, &computeSubmitInfo, VK_NULL_HANDLE) != VK_SUCCESS) { + if (vkQueueSubmit(device->GetQueue(QueueFlags::Compute), 1, &computeSubmitInfo, VK_NULL_HANDLE) != VK_SUCCESS) + { throw std::runtime_error("Failed to submit draw command buffer"); } - if (!swapChain->Acquire()) { + if (!swapChain->Acquire()) + { RecreateFrameResources(); return; } @@ -1016,8 +1227,8 @@ void Renderer::Frame() { VkSubmitInfo submitInfo = {}; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - VkSemaphore waitSemaphores[] = { swapChain->GetImageAvailableVkSemaphore() }; - VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT }; + VkSemaphore waitSemaphores[] = {swapChain->GetImageAvailableVkSemaphore()}; + VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; submitInfo.waitSemaphoreCount = 1; submitInfo.pWaitSemaphores = waitSemaphores; submitInfo.pWaitDstStageMask = waitStages; @@ -1025,27 +1236,31 @@ void Renderer::Frame() { submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = &commandBuffers[swapChain->GetIndex()]; - VkSemaphore signalSemaphores[] = { swapChain->GetRenderFinishedVkSemaphore() }; + VkSemaphore signalSemaphores[] = {swapChain->GetRenderFinishedVkSemaphore()}; submitInfo.signalSemaphoreCount = 1; submitInfo.pSignalSemaphores = signalSemaphores; - if (vkQueueSubmit(device->GetQueue(QueueFlags::Graphics), 1, &submitInfo, VK_NULL_HANDLE) != VK_SUCCESS) { + auto tmp = vkQueueSubmit(device->GetQueue(QueueFlags::Graphics), 1, &submitInfo, VK_NULL_HANDLE); + if (tmp != VK_SUCCESS) + { throw std::runtime_error("Failed to submit draw command buffer"); } - if (!swapChain->Present()) { + if (!swapChain->Present()) + { RecreateFrameResources(); } } -Renderer::~Renderer() { +Renderer::~Renderer() +{ vkDeviceWaitIdle(logicalDevice); // TODO: destroy any resources you created vkFreeCommandBuffers(logicalDevice, graphicsCommandPool, static_cast(commandBuffers.size()), commandBuffers.data()); vkFreeCommandBuffers(logicalDevice, computeCommandPool, 1, &computeCommandBuffer); - + vkDestroyPipeline(logicalDevice, graphicsPipeline, nullptr); vkDestroyPipeline(logicalDevice, grassPipeline, nullptr); vkDestroyPipeline(logicalDevice, computePipeline, nullptr); @@ -1056,6 +1271,7 @@ Renderer::~Renderer() { vkDestroyDescriptorSetLayout(logicalDevice, cameraDescriptorSetLayout, nullptr); vkDestroyDescriptorSetLayout(logicalDevice, modelDescriptorSetLayout, nullptr); + // vkDestroyDescriptorSetLayout(logicalDevice, grassDescriptorSetLayout, nullptr); vkDestroyDescriptorSetLayout(logicalDevice, timeDescriptorSetLayout, nullptr); vkDestroyDescriptorPool(logicalDevice, descriptorPool, nullptr); diff --git a/src/Renderer.h b/src/Renderer.h index 95e025f..676a83e 100644 --- a/src/Renderer.h +++ b/src/Renderer.h @@ -5,10 +5,11 @@ #include "Scene.h" #include "Camera.h" -class Renderer { +class Renderer +{ public: Renderer() = delete; - Renderer(Device* device, SwapChain* swapChain, Scene* scene, Camera* camera); + Renderer(Device *device, SwapChain *swapChain, Scene *scene, Camera *camera); ~Renderer(); void CreateCommandPools(); @@ -17,6 +18,7 @@ class Renderer { void CreateCameraDescriptorSetLayout(); void CreateModelDescriptorSetLayout(); + // void CreateGrassDescriptorSetLayout(); void CreateTimeDescriptorSetLayout(); void CreateComputeDescriptorSetLayout(); @@ -42,11 +44,11 @@ class Renderer { void Frame(); private: - Device* device; + Device *device; VkDevice logicalDevice; - SwapChain* swapChain; - Scene* scene; - Camera* camera; + SwapChain *swapChain; + Scene *scene; + Camera *camera; VkCommandPool graphicsCommandPool; VkCommandPool computeCommandPool; @@ -55,13 +57,24 @@ class Renderer { VkDescriptorSetLayout cameraDescriptorSetLayout; VkDescriptorSetLayout modelDescriptorSetLayout; + // VkDescriptorSetLayout grassDescriptorSetLayout; VkDescriptorSetLayout timeDescriptorSetLayout; - + VkDescriptorSetLayout computeDescriptorSetLayout; + // VkDescriptorSetLayout computeBladesDescriptorSetLayout; + // VkDescriptorSetLayout computeCulledBladesDescriptorSetLayout; + // VkDescriptorSetLayout computeNumBladesDescriptorSetLayout; + VkDescriptorPool descriptorPool; VkDescriptorSet cameraDescriptorSet; std::vector modelDescriptorSets; + // std::vector grassDescriptorSets; VkDescriptorSet timeDescriptorSet; + // std::vector computeBladesDescriptorSets; + // std::vector computeCulledBladesDescriptorSets; + // std::vector computeNumBladesDescriptorSets; + std::vector computeDescriptorSets; + std::vector grassDescriptorSets; VkPipelineLayout graphicsPipelineLayout; VkPipelineLayout grassPipelineLayout; @@ -80,3 +93,5 @@ class Renderer { std::vector commandBuffers; VkCommandBuffer computeCommandBuffer; }; + + diff --git a/src/main.cpp b/src/main.cpp index 8bf822b..c7a981d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -6,14 +6,17 @@ #include "Scene.h" #include "Image.h" -Device* device; -SwapChain* swapChain; -Renderer* renderer; -Camera* camera; - -namespace { - void resizeCallback(GLFWwindow* window, int width, int height) { - if (width == 0 || height == 0) return; +Device *device; +SwapChain *swapChain; +Renderer *renderer; +Camera *camera; + +namespace +{ + void resizeCallback(GLFWwindow *window, int width, int height) + { + if (width == 0 || height == 0) + return; vkDeviceWaitIdle(device->GetVkDevice()); swapChain->Recreate(); @@ -25,28 +28,38 @@ namespace { double previousX = 0.0; double previousY = 0.0; - void mouseDownCallback(GLFWwindow* window, int button, int action, int mods) { - if (button == GLFW_MOUSE_BUTTON_LEFT) { - if (action == GLFW_PRESS) { + void mouseDownCallback(GLFWwindow *window, int button, int action, int mods) + { + if (button == GLFW_MOUSE_BUTTON_LEFT) + { + if (action == GLFW_PRESS) + { leftMouseDown = true; glfwGetCursorPos(window, &previousX, &previousY); } - else if (action == GLFW_RELEASE) { + else if (action == GLFW_RELEASE) + { leftMouseDown = false; } - } else if (button == GLFW_MOUSE_BUTTON_RIGHT) { - if (action == GLFW_PRESS) { + } + else if (button == GLFW_MOUSE_BUTTON_RIGHT) + { + if (action == GLFW_PRESS) + { rightMouseDown = true; glfwGetCursorPos(window, &previousX, &previousY); } - else if (action == GLFW_RELEASE) { + else if (action == GLFW_RELEASE) + { rightMouseDown = false; } } } - void mouseMoveCallback(GLFWwindow* window, double xPosition, double yPosition) { - if (leftMouseDown) { + void mouseMoveCallback(GLFWwindow *window, double xPosition, double yPosition) + { + if (leftMouseDown) + { double sensitivity = 0.5; float deltaX = static_cast((previousX - xPosition) * sensitivity); float deltaY = static_cast((previousY - yPosition) * sensitivity); @@ -55,7 +68,9 @@ namespace { previousX = xPosition; previousY = yPosition; - } else if (rightMouseDown) { + } + else if (rightMouseDown) + { double deltaZ = static_cast((previousY - yPosition) * 0.05); camera->UpdateOrbit(0.0f, 0.0f, deltaZ); @@ -65,21 +80,24 @@ namespace { } } -int main() { - static constexpr char* applicationName = "Vulkan Grass Rendering"; - InitializeWindow(640, 480, applicationName); +int main() +{ + static constexpr char *applicationName = "Vulkan Grass Rendering"; + // InitializeWindow(640, 480, applicationName); + InitializeWindow(1080, 1080, applicationName); unsigned int glfwExtensionCount = 0; - const char** glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); + const char **glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); - Instance* instance = new Instance(applicationName, glfwExtensionCount, glfwExtensions); + Instance *instance = new Instance(applicationName, glfwExtensionCount, glfwExtensions); VkSurfaceKHR surface; - if (glfwCreateWindowSurface(instance->GetVkInstance(), GetGLFWWindow(), nullptr, &surface) != VK_SUCCESS) { + if (glfwCreateWindowSurface(instance->GetVkInstance(), GetGLFWWindow(), nullptr, &surface) != VK_SUCCESS) + { throw std::runtime_error("Failed to create window surface"); } - instance->PickPhysicalDevice({ VK_KHR_SWAPCHAIN_EXTENSION_NAME }, QueueFlagBit::GraphicsBit | QueueFlagBit::TransferBit | QueueFlagBit::ComputeBit | QueueFlagBit::PresentBit, surface); + instance->PickPhysicalDevice({VK_KHR_SWAPCHAIN_EXTENSION_NAME}, QueueFlagBit::GraphicsBit | QueueFlagBit::TransferBit | QueueFlagBit::ComputeBit | QueueFlagBit::PresentBit, surface); VkPhysicalDeviceFeatures deviceFeatures = {}; deviceFeatures.tessellationShader = VK_TRUE; @@ -98,42 +116,39 @@ int main() { transferPoolInfo.flags = 0; VkCommandPool transferCommandPool; - if (vkCreateCommandPool(device->GetVkDevice(), &transferPoolInfo, nullptr, &transferCommandPool) != VK_SUCCESS) { + if (vkCreateCommandPool(device->GetVkDevice(), &transferPoolInfo, nullptr, &transferCommandPool) != VK_SUCCESS) + { throw std::runtime_error("Failed to create command pool"); } VkImage grassImage; VkDeviceMemory grassImageMemory; Image::FromFile(device, - transferCommandPool, - "images/grass.jpg", - VK_FORMAT_R8G8B8A8_UNORM, - VK_IMAGE_TILING_OPTIMAL, - VK_IMAGE_USAGE_SAMPLED_BIT, - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, - grassImage, - grassImageMemory - ); + transferCommandPool, + "images/grass.jpg", + VK_FORMAT_R8G8B8A8_UNORM, + VK_IMAGE_TILING_OPTIMAL, + VK_IMAGE_USAGE_SAMPLED_BIT, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + grassImage, + grassImageMemory); float planeDim = 15.f; float halfWidth = planeDim * 0.5f; - Model* plane = new Model(device, transferCommandPool, - { - { { -halfWidth, 0.0f, halfWidth }, { 1.0f, 0.0f, 0.0f },{ 1.0f, 0.0f } }, - { { halfWidth, 0.0f, halfWidth }, { 0.0f, 1.0f, 0.0f },{ 0.0f, 0.0f } }, - { { halfWidth, 0.0f, -halfWidth }, { 0.0f, 0.0f, 1.0f },{ 0.0f, 1.0f } }, - { { -halfWidth, 0.0f, -halfWidth }, { 1.0f, 1.0f, 1.0f },{ 1.0f, 1.0f } } - }, - { 0, 1, 2, 2, 3, 0 } - ); + Model *plane = new Model(device, transferCommandPool, + {{{-halfWidth, 0.0f, halfWidth}, {1.0f, 0.0f, 0.0f}, {1.0f, 0.0f}}, + {{halfWidth, 0.0f, halfWidth}, {0.0f, 1.0f, 0.0f}, {0.0f, 0.0f}}, + {{halfWidth, 0.0f, -halfWidth}, {0.0f, 0.0f, 1.0f}, {0.0f, 1.0f}}, + {{-halfWidth, 0.0f, -halfWidth}, {1.0f, 1.0f, 1.0f}, {1.0f, 1.0f}}}, + {0, 1, 2, 2, 3, 0}); plane->SetTexture(grassImage); - - Blades* blades = new Blades(device, transferCommandPool, planeDim); + + Blades *blades = new Blades(device, transferCommandPool, planeDim); vkDestroyCommandPool(device->GetVkDevice(), transferCommandPool, nullptr); - Scene* scene = new Scene(device); + Scene *scene = new Scene(device); scene->AddModel(plane); scene->AddBlades(blades); @@ -143,7 +158,8 @@ int main() { glfwSetMouseButtonCallback(GetGLFWWindow(), mouseDownCallback); glfwSetCursorPosCallback(GetGLFWWindow(), mouseMoveCallback); - while (!ShouldQuit()) { + while (!ShouldQuit()) + { glfwPollEvents(); scene->UpdateTime(); renderer->Frame(); diff --git a/src/shaders/compute.comp b/src/shaders/compute.comp index 0fd0224..64f0636 100644 --- a/src/shaders/compute.comp +++ b/src/shaders/compute.comp @@ -28,29 +28,108 @@ struct Blade { // 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 -// -// layout(set = ???, binding = ???) buffer NumBlades { -// uint vertexCount; // Write the number of blades remaining here -// uint instanceCount; // = 1 -// uint firstVertex; // = 0 -// uint firstInstance; // = 0 -// } numBlades; +layout(set = 2, binding = 0) buffer Blades { + Blade data[]; +} bladeData; + +layout(set = 2, binding = 1) buffer CulledBlades { + Blade data[]; +} culledBladeData; + +layout(set = 2, binding = 2) buffer NumBlades { + uint vertexCount; // Write the number of blades remaining here + uint instanceCount; // = 1 + uint firstVertex; // = 0 + uint firstInstance; // = 0 +} numBlades; bool inBounds(float value, float bounds) { return (value >= -bounds) && (value <= bounds); } + void main() { + float toler = 1.0; + float dmax = 40.0; // Reset the number of blades to 0 if (gl_GlobalInvocationID.x == 0) { - // numBlades.vertexCount = 0; + numBlades.vertexCount = 0; } barrier(); // Wait till all threads reach this point // TODO: Apply forces on every blade and update the vertices in the buffer + int idx = int(gl_GlobalInvocationID.x); + vec4 d = vec4(0.0, -1.0, 0.0, 4.9); // gravity + vec3 envGrav = normalize(d.xyz) * d.w; + Blade b = bladeData.data[idx]; + // vec3 frGrav = 0.25 * length(envGrav) * normalize(cross(bladeData.data[idx].up.xyz, bladeData.data[idx].v2.xyz - bladeData.data[idx].v1.xyz)); + vec3 bladeOrient = vec3(cos(b.v0.w), 0.0, sin(b.v0.w)); + vec3 frGrav = 0.25 * length(envGrav) * normalize(cross(bladeOrient, b.up.xyz)); + vec3 totGrav = envGrav + frGrav; + vec3 restF = b.up.w * (b.v0.xyz + b.up.xyz - b.v2.xyz); + // float smallWind = deltaTime * totalTime; + // float smallWind = 2.0 * totalTime; + float smallWind = 0.5 * totalTime; + vec3 wind = 7.0 * vec3(cos(smallWind), 0, 0); + // vec3 wind = 7.0 * vec3(cos(smallWind), 0, sin(smallWind)); + // vec3 wind = vec3(0.0); + + vec4 v0 = b.v0; + vec4 v1 = b.v1; + vec4 v2 = b.v2; + vec4 up = b.up; + v2 = vec4(v2.xyz + deltaTime * (totGrav + restF + wind), v2.w); + v2 = vec4(v2.xyz - (up.xyz * min(dot(up.xyz, v2.xyz - v0.xyz), 0.0)), v2.w); + float lproj = length(v2.xyz - v0.xyz - up.xyz * dot(up.xyz, v2.xyz - v0.xyz)); + v1 = vec4(v0.xyz + v1.w * up.xyz * max(1.0 - lproj / v1.w, 0.05 * max(lproj / v1.w, 1.0)), v1.w); + float approxL = 2 * length(v0.xyz - v2.xyz) + (length(v0.xyz - v1.xyz) + length(v2.xyz - v1.xyz)); + approxL = approxL / 3.0; + float ratio = v1.w / approxL; + vec3 oldv1 = v1.xyz; + v1 = vec4(v0.xyz + ratio * (v1.xyz - v0.xyz), v1.w); + v2 = vec4(v1.xyz + ratio * (v2.xyz - oldv1), v2.w); + bladeData.data[idx].v0 = v0; + bladeData.data[idx].v1 = v1; + bladeData.data[idx].v2 = v2; + bladeData.data[idx].up = up; // 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 + vec3 camEye = inverse(camera.view)[3].xyz; + float cam_grass_align = dot(bladeOrient, normalize(v0.xyz - camEye)); + if (cam_grass_align > 0.9) + { + return; + } + vec4 m = 0.25 * v0 + 0.5 * v1 + 0.25 * v2; + mat4 viewproj = camera.proj * camera.view; + vec4 v0z = viewproj * v0; + vec4 midz = viewproj * vec4(m.xyz, 1.0); + vec4 v2z = viewproj * v2; + float v0h = toler + v0z.w; + float midh = toler + midz.w; + float v2h = toler + v2z.w; + bool keepV0 = (inBounds(v0z.x, v0h) && inBounds(v0z.y, v0h) && inBounds(v0z.z, v0h)); + bool keepMid = (inBounds(midz.x, midh) && inBounds(midz.y, midh) && inBounds(midz.z, midh)); + bool keepV2 = (inBounds(v2z.x, v2h) && inBounds(v2z.y, v2h) && inBounds(v2z.z, v2h)); + if (!keepV0 && !keepMid && !keepV2) + { + return; + } + float dproj = length(v0.xyz - camEye - up.xyz * dot(up.xyz, v0.xyz - camEye)); + int levels = 6; + if (mod(idx, levels) > floor(levels * (1.0 - (dproj / dmax)))) + { + return; + } + + // atomicAdd(numBlades.vertexCount, 1); + // culledBladeData.data[numBlades.vertexCount] = bladeData.data[idx]; + // culledBladeData.data[idx] = bladeData.data[idx]; + // culledBladeData.data[idx] = b; + culledBladeData.data[atomicAdd(numBlades.vertexCount, 1)] = bladeData.data[idx]; } + + diff --git a/src/shaders/grass.frag b/src/shaders/grass.frag index c7df157..1dbb97d 100644 --- a/src/shaders/grass.frag +++ b/src/shaders/grass.frag @@ -10,8 +10,23 @@ layout(set = 0, binding = 0) uniform CameraBufferObject { layout(location = 0) out vec4 outColor; +layout(location = 0) in float lambertCoeff; +layout(location = 1) in vec2 uv; + void main() { // TODO: Compute fragment color - outColor = vec4(1.0); + // outColor = vec4(1.0); + // outColor = lambertCoeff * vec4(0.0, 1.0, 0.0, 0.0); + // vec4 color_top = vec4(100.0/255.0, 240.0/255.0, 90.0/255.0, 1.0); + // vec4 color_bottom = vec4(146.0/255.0, 197.0/255.0, 130.0/255.0, 1.0); + // outColor = lambertCoeff * mix(color_bottom, color_top, uv[1]); + vec4 color_top = vec4(0, 240.0/255.0, 90.0/255.0, 1.0); + vec4 color_bottom = vec4(0, 177.0/255.0, 103.0/255.0, 1.0); + outColor = lambertCoeff * mix(color_bottom, color_top, uv[1]); + // outColor = mix(color_bottom, color_top, uv[1]); + +// vec4 color_top = vec4(0, 0.788, 0.263, 1.0); +// vec4 color_bottom = vec4(0, 0.439, 0.294, 1.0); +// outColor = mix(color_bottom, color_top, uv[1]); } diff --git a/src/shaders/grass.tesc b/src/shaders/grass.tesc index f9ffd07..65d5735 100644 --- a/src/shaders/grass.tesc +++ b/src/shaders/grass.tesc @@ -10,17 +10,31 @@ layout(set = 0, binding = 0) uniform CameraBufferObject { // TODO: Declare tessellation control shader inputs and outputs +layout(location = 0) in vec4 in_v0[]; +layout(location = 1) in vec4 in_v1[]; +layout(location = 2) in vec4 in_v2[]; +layout(location = 3) in vec4 in_up[]; + +layout(location = 0) out vec4 out_v0[]; +layout(location = 1) out vec4 out_v1[]; +layout(location = 2) out vec4 out_v2[]; +layout(location = 3) out vec4 out_up[]; + 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 + out_v0[gl_InvocationID] = in_v0[gl_InvocationID]; + out_v1[gl_InvocationID] = in_v1[gl_InvocationID]; + out_v2[gl_InvocationID] = in_v2[gl_InvocationID]; + out_up[gl_InvocationID] = in_up[gl_InvocationID]; // TODO: Set level of tesselation - // gl_TessLevelInner[0] = ??? - // gl_TessLevelInner[1] = ??? - // gl_TessLevelOuter[0] = ??? - // gl_TessLevelOuter[1] = ??? - // gl_TessLevelOuter[2] = ??? - // gl_TessLevelOuter[3] = ??? + gl_TessLevelInner[0] = 8; + gl_TessLevelInner[1] = 8; + gl_TessLevelOuter[0] = 8; + gl_TessLevelOuter[1] = 8; + gl_TessLevelOuter[2] = 8; + gl_TessLevelOuter[3] = 8; } diff --git a/src/shaders/grass.tese b/src/shaders/grass.tese index 751fff6..34736c0 100644 --- a/src/shaders/grass.tese +++ b/src/shaders/grass.tese @@ -10,9 +10,40 @@ layout(set = 0, binding = 0) uniform CameraBufferObject { // TODO: Declare tessellation evaluation shader inputs and outputs +layout(location = 0) out float lambertCoeff; +layout(location = 1) out vec2 uv; + +layout(location = 0) in vec4 in_v0[]; +layout(location = 1) in vec4 in_v1[]; +layout(location = 2) in vec4 in_v2[]; +layout(location = 3) in vec4 in_up[]; + + void main() { float u = gl_TessCoord.x; float v = gl_TessCoord.y; // TODO: Use u and v to parameterize along the grass blade and output positions for each vertex of the grass blade + vec3 v0 = in_v0[0].xyz; + vec3 v1 = in_v1[0].xyz; + vec3 v2 = in_v2[0].xyz; + vec3 up = in_up[0].xyz; + float width = in_v2[0].w; + float tmp = in_v0[0].w; + vec3 t1 = normalize(vec3(sin(tmp), 0.0, cos(tmp))); + + 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)); + float t = u + 0.5 * v - u * v; + vec4 pos = vec4(mix(c0, c1, t), 1.0); + vec4 posView = camera.view * pos; + + uv = vec2(u,v); + lambertCoeff = abs(dot(normalize(posView), normalize(camera.view * vec4(n, 0.0)))); + gl_Position = camera.proj * camera.view * pos; } diff --git a/src/shaders/grass.vert b/src/shaders/grass.vert index db9dfe9..16fc011 100644 --- a/src/shaders/grass.vert +++ b/src/shaders/grass.vert @@ -6,6 +6,16 @@ layout(set = 1, binding = 0) uniform ModelBufferObject { mat4 model; }; +layout(location = 0) in vec4 in_v0; +layout(location = 1) in vec4 in_v1; +layout(location = 2) in vec4 in_v2; +layout(location = 3) in vec4 in_up; + +layout(location = 0) out vec4 out_v0; +layout(location = 1) out vec4 out_v1; +layout(location = 2) out vec4 out_v2; +layout(location = 3) out vec4 out_up; + // TODO: Declare vertex shader inputs and outputs out gl_PerVertex { @@ -14,4 +24,9 @@ out gl_PerVertex { void main() { // TODO: Write gl_Position and any other shader outputs + gl_Position = model * vec4(in_v0.xyz, 1.0); + out_v0 = model * in_v0; + out_v1 = model * in_v1; + out_v2 = model * in_v2; + out_up = in_up; }