diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1e671b1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +bin +build +.vscode \ No newline at end of file diff --git a/README.md b/README.md index 20ee451..244ea12 100644 --- a/README.md +++ b/README.md @@ -3,10 +3,113 @@ 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) +* Nuofan Xu +* Tested on: Windows 10, AMD Ryzen 3800X @ 32GB, RTX 2080 Super -### (TODO: Your README) +## OverView -*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. +This project implements a grass simulator and renderer using Vulkan. Each grass blade is represented by a quadratic Bezier curve, and physics calculations are performed on these control points in a computing shader. Rendering every grass blade in the scene is computationally expensive and not favorable - luckily grass blade that do not contribute to a frame can be culled in the shader. + +The rendering pipeline can be described as following: +- Visible bezier representaions of grass blades are sent to a grass graphics pipeline +- The Bezier control points are transformed. +- The geometry is created in tesselation shaders +- The grass is shaded in the fragment shader. + +The project uses grass rendering techniques detailed in [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). + +## Grass Simulation + +To simulate an idyllic nature scene in a realistic fashion, besides detailed trees and bushes, as well as a complex water and sky dome simulation, we need a high-quality grass effect. We must be able to cover large areas of the terrain with it, without monopolizing the GPU. The grass should look naturally grown and should wave realistically in the wind. This imposes computational constraints and a balance between perfect quality and fast rendering need to be made. + +Here we use the following physics model. A grass blade is represented by a Bezier curve where the first control point is fixed on the plane, and the physics calculations are performed on the third control point. Along with the three control points, a blade is also defined by an up vector and scalar height, orientation, width, and stiffness coefficient. + +![](img/blade_model.jpg) + +All of the blade data is packed into 4 `vec4`s. The scalar values are packed into the fourth component of the control points and the up vector. + +| x | y | z | w | +| ---- | ---- | ---- | ----------- | +| v0.x | v0.y | v0.z | orientation | +| v1.x | v1.y | v1.z | height | +| v2.x | v2.y | v2.z | width | +| up.x | up.y | up.z | stiffness | + +### Forces per Grass Blade + +Forces are first applied to the `v2` control point. Potential errors are corrected in the state validation stage. The total force applied is the sum of the gravity, recovery, and wind forces. + +#### Gravity + +Gravitational forces applied to each grass blade is a sum of the environmental gravity and the front gravity, which is the gravity with respect to the front facing direction of the blade - computed using the orientation. + +`environmentalGravity = normalize(D.xyz) * D.w` + +`frontGravity = (0.25) * ||environmentalGravity|| * frontFacingDirection` + +`g = environmentalGravity + frontGravity` + +#### Recovery + +The grass blades are treated as springs, and Hooke's law is applied. There is a force that brings the blade back to equilibrium. This force acts in the direction of the original `v2` position, or `iv2`, and is scaled by the stiffness coefficient. The larger the stiffness coefficient, the more the force pushing the blade back to equilibrium. + +`initial_v2 = v0 + up * height` + +`r = (initial_v2 - v2) * stiffness` + +#### Wind + +Wind can be modelled as any function. A touch of randomness in the function allows every individual blade to look as if it reacts independently to forces. The random function being used in this implementation is: + +`vec3 windDirection = normalize(vec3(1, 1, 1));` + +`float windStrength = 10.0* rand(v0.xz) * cos(totalTime);` + +`float fd = 1.0 - abs(dot(windDirection, normalize(v2 - v0)));` + +`float fr = dot(v2 - v0, up) / height;` + +`float theta = fd * fr;` + +`vec3 wind = windStrength * windDirection * theta;` + +### State Validation + +Before `v2` can be translated, the new state must first be corrected for errors. First, `v2` must remain above `v0` because the blade cannot intersect the ground plane. In addition, the system insures that each blade always has a slight curvature, and the length of the Bezier curve is not longer than the fixed blade height. + +## Culling + +In the past, a high-quality grass simulation would have been considered too complex for real-time applications, because simulating thousands upon thousands of waving grass blades can get computationally expensive. With the realization that a detailed modeling of some blades of grass is not as meaningful as some others, we want to cull some of them away. In this project 3 culling techniques are implemented. + +### Orientation culling + +Consider the scenario in which the front face direction of the grass blade is perpendicular to the view vector. Since our grass blades won't have width, we will end up trying to render parts of the grass that are actually smaller than the size of a pixel. This could lead to aliasing artifacts. Therefore blades oriented at angles almost perpendicular to the view vector are culled. + +![](img/orientation_culling.gif) + +The above scene is trying to demonstrate the culling done with different viewing angles and note that as the scene rotates, some of the blades suddenly disappear (when blades are perpendidular to the view) or re-appear, meaning that blades are culled based on their orientation with respect to the camera. + +### View-frustum culling + +Blades that are outside of the view-frustum should be culled because they will never be rendered on screen. This means we can avoid doing computation for all the grass blades outside of the view-frustum. To determine if a blade is in frame, we compare the visibility of the first and last control points and a weighted midpoint instead of `v1` because `v1` does not lie on the curve. + +![](img/view_culling.gif) + +As camera zooms in on the scene, more blades move outside of the viewing frustum and less blades are rendered. + +### Distance culling + +Similar to orientation culling, grass blades at large distances from the camera can be smaller than the size of a pixel and thus can lead to aliasing artifacts. To solve this, we reduce grass density the further we get from the camera. As we zoom out the view, grass blades start to disappear as they will be rendered with size less than a pixel if not culled. + +![](img/distance_culling.gif) + +## Performance Analysis + +![](img/perform_analysis_table.PNG) +![](img/bar.PNG) + +The graph clearly show us that any type of culling can speed up the rendering process, and when three types of culling metthods are combined together, it leads to a substantial performance boost. And this provides us a meaningful way of saving computational power while keeping the rendered scene real, if not better. + +## Feedback + +Any feedback regarding the project is welcome. \ No newline at end of file diff --git a/bin/Release/vulkan_grass_rendering.exe b/bin/Release/vulkan_grass_rendering.exe index f68db3a..1144178 100644 Binary files a/bin/Release/vulkan_grass_rendering.exe and b/bin/Release/vulkan_grass_rendering.exe differ diff --git a/img/bar.PNG b/img/bar.PNG new file mode 100644 index 0000000..c43aa4a Binary files /dev/null and b/img/bar.PNG differ diff --git a/img/distance_culling.gif b/img/distance_culling.gif new file mode 100644 index 0000000..1aadbff Binary files /dev/null and b/img/distance_culling.gif differ diff --git a/img/orientation_culling.gif b/img/orientation_culling.gif new file mode 100644 index 0000000..86c44fe Binary files /dev/null and b/img/orientation_culling.gif differ diff --git a/img/perform_analysis_table.PNG b/img/perform_analysis_table.PNG new file mode 100644 index 0000000..aa3309b Binary files /dev/null and b/img/perform_analysis_table.PNG differ diff --git a/img/view_culling.gif b/img/view_culling.gif new file mode 100644 index 0000000..9aac83b Binary files /dev/null and b/img/view_culling.gif differ diff --git a/src/Renderer.cpp b/src/Renderer.cpp index b445d04..02683a8 100644 --- a/src/Renderer.cpp +++ b/src/Renderer.cpp @@ -9,1059 +9,1202 @@ 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) { - - CreateCommandPools(); - CreateRenderPass(); - CreateCameraDescriptorSetLayout(); - CreateModelDescriptorSetLayout(); - CreateTimeDescriptorSetLayout(); - CreateComputeDescriptorSetLayout(); - CreateDescriptorPool(); - CreateCameraDescriptorSet(); - CreateModelDescriptorSets(); - CreateGrassDescriptorSets(); - CreateTimeDescriptorSet(); - CreateComputeDescriptorSets(); - CreateFrameResources(); - CreateGraphicsPipeline(); - CreateGrassPipeline(); - CreateComputePipeline(); - RecordCommandBuffers(); - RecordComputeCommandBuffer(); + : device(device), + logicalDevice(device->GetVkDevice()), + swapChain(swapChain), + scene(scene), + camera(camera) { + + CreateCommandPools(); + CreateRenderPass(); + CreateCameraDescriptorSetLayout(); + CreateModelDescriptorSetLayout(); + CreateTimeDescriptorSetLayout(); + CreateComputeDescriptorSetLayout(); + CreateDescriptorPool(); + CreateCameraDescriptorSet(); + CreateModelDescriptorSets(); + CreateGrassDescriptorSets(); + CreateTimeDescriptorSet(); + CreateComputeDescriptorSets(); + CreateFrameResources(); + CreateGraphicsPipeline(); + CreateGrassPipeline(); + CreateComputePipeline(); + RecordCommandBuffers(); + RecordComputeCommandBuffer(); } 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) { - throw std::runtime_error("Failed to create command pool"); - } - - VkCommandPoolCreateInfo computePoolInfo = {}; - computePoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; - computePoolInfo.queueFamilyIndex = device->GetInstance()->GetQueueFamilyIndices()[QueueFlags::Compute]; - computePoolInfo.flags = 0; - - if (vkCreateCommandPool(logicalDevice, &computePoolInfo, nullptr, &computeCommandPool) != VK_SUCCESS) { - throw std::runtime_error("Failed to create command pool"); - } + 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) { + throw std::runtime_error("Failed to create command pool"); + } + + VkCommandPoolCreateInfo computePoolInfo = {}; + computePoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + computePoolInfo.queueFamilyIndex = device->GetInstance()->GetQueueFamilyIndices()[QueueFlags::Compute]; + computePoolInfo.flags = 0; + + if (vkCreateCommandPool(logicalDevice, &computePoolInfo, nullptr, &computeCommandPool) != VK_SUCCESS) { + throw std::runtime_error("Failed to create command pool"); + } } void Renderer::CreateRenderPass() { - // Color buffer attachment represented by one of the images from the swap chain - VkAttachmentDescription colorAttachment = {}; - colorAttachment.format = swapChain->GetVkImageFormat(); - colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT; - colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; - colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - - // Create a color attachment reference to be used with subpass - VkAttachmentReference colorAttachmentRef = {}; - colorAttachmentRef.attachment = 0; - 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); - VkAttachmentDescription depthAttachment = {}; - depthAttachment.format = depthFormat; - depthAttachment.samples = VK_SAMPLE_COUNT_1_BIT; - depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - depthAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - - // Create a depth attachment reference - VkAttachmentReference depthAttachmentRef = {}; - depthAttachmentRef.attachment = 1; - depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - - // Create subpass description - VkSubpassDescription subpass = {}; - subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; - subpass.colorAttachmentCount = 1; - subpass.pColorAttachments = &colorAttachmentRef; - subpass.pDepthStencilAttachment = &depthAttachmentRef; - - std::array attachments = { colorAttachment, depthAttachment }; - - // Specify subpass dependency - VkSubpassDependency dependency = {}; - dependency.srcSubpass = VK_SUBPASS_EXTERNAL; - dependency.dstSubpass = 0; - dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dependency.srcAccessMask = 0; - dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - - // Create render pass - VkRenderPassCreateInfo renderPassInfo = {}; - renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; - renderPassInfo.attachmentCount = static_cast(attachments.size()); - renderPassInfo.pAttachments = attachments.data(); - renderPassInfo.subpassCount = 1; - renderPassInfo.pSubpasses = &subpass; - renderPassInfo.dependencyCount = 1; - renderPassInfo.pDependencies = &dependency; - - if (vkCreateRenderPass(logicalDevice, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) { - throw std::runtime_error("Failed to create render pass"); - } + // Color buffer attachment represented by one of the images from the swap chain + VkAttachmentDescription colorAttachment = {}; + colorAttachment.format = swapChain->GetVkImageFormat(); + colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT; + colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + + // Create a color attachment reference to be used with subpass + VkAttachmentReference colorAttachmentRef = {}; + colorAttachmentRef.attachment = 0; + 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); + VkAttachmentDescription depthAttachment = {}; + depthAttachment.format = depthFormat; + depthAttachment.samples = VK_SAMPLE_COUNT_1_BIT; + depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + depthAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + // Create a depth attachment reference + VkAttachmentReference depthAttachmentRef = {}; + depthAttachmentRef.attachment = 1; + depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + // Create subpass description + VkSubpassDescription subpass = {}; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.colorAttachmentCount = 1; + subpass.pColorAttachments = &colorAttachmentRef; + subpass.pDepthStencilAttachment = &depthAttachmentRef; + + std::array attachments = { colorAttachment, depthAttachment }; + + // Specify subpass dependency + VkSubpassDependency dependency = {}; + dependency.srcSubpass = VK_SUBPASS_EXTERNAL; + dependency.dstSubpass = 0; + dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependency.srcAccessMask = 0; + dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + + // Create render pass + VkRenderPassCreateInfo renderPassInfo = {}; + renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + renderPassInfo.attachmentCount = static_cast(attachments.size()); + renderPassInfo.pAttachments = attachments.data(); + renderPassInfo.subpassCount = 1; + renderPassInfo.pSubpasses = &subpass; + renderPassInfo.dependencyCount = 1; + renderPassInfo.pDependencies = &dependency; + + if (vkCreateRenderPass(logicalDevice, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) { + throw std::runtime_error("Failed to create render pass"); + } } void Renderer::CreateCameraDescriptorSetLayout() { - // Describe the binding of the descriptor set layout - VkDescriptorSetLayoutBinding uboLayoutBinding = {}; - uboLayoutBinding.binding = 0; - uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - uboLayoutBinding.descriptorCount = 1; - uboLayoutBinding.stageFlags = VK_SHADER_STAGE_ALL; - 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, &cameraDescriptorSetLayout) != VK_SUCCESS) { - throw std::runtime_error("Failed to create descriptor set layout"); - } + // Describe the binding of the descriptor set layout + VkDescriptorSetLayoutBinding uboLayoutBinding = {}; + uboLayoutBinding.binding = 0; + uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + uboLayoutBinding.descriptorCount = 1; + uboLayoutBinding.stageFlags = VK_SHADER_STAGE_ALL; + 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, &cameraDescriptorSetLayout) != VK_SUCCESS) { + throw std::runtime_error("Failed to create descriptor set layout"); + } } void Renderer::CreateModelDescriptorSetLayout() { - 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; - - VkDescriptorSetLayoutBinding samplerLayoutBinding = {}; - samplerLayoutBinding.binding = 1; - samplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - samplerLayoutBinding.descriptorCount = 1; - samplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; - samplerLayoutBinding.pImmutableSamplers = nullptr; - - std::vector bindings = { uboLayoutBinding, samplerLayoutBinding }; - - // 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, &modelDescriptorSetLayout) != VK_SUCCESS) { - throw std::runtime_error("Failed to create descriptor set layout"); - } + 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; + + VkDescriptorSetLayoutBinding samplerLayoutBinding = {}; + samplerLayoutBinding.binding = 1; + samplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + samplerLayoutBinding.descriptorCount = 1; + samplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + samplerLayoutBinding.pImmutableSamplers = nullptr; + + std::vector bindings = { uboLayoutBinding, samplerLayoutBinding }; + + // 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, &modelDescriptorSetLayout) != VK_SUCCESS) { + throw std::runtime_error("Failed to create descriptor set layout"); + } } void Renderer::CreateTimeDescriptorSetLayout() { - // Describe the binding of the descriptor set layout - VkDescriptorSetLayoutBinding uboLayoutBinding = {}; - uboLayoutBinding.binding = 0; - uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - uboLayoutBinding.descriptorCount = 1; - uboLayoutBinding.stageFlags = VK_SHADER_STAGE_COMPUTE_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, &timeDescriptorSetLayout) != VK_SUCCESS) { - throw std::runtime_error("Failed to create descriptor set layout"); - } + // Describe the binding of the descriptor set layout + VkDescriptorSetLayoutBinding uboLayoutBinding = {}; + uboLayoutBinding.binding = 0; + uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + uboLayoutBinding.descriptorCount = 1; + uboLayoutBinding.stageFlags = VK_SHADER_STAGE_COMPUTE_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, &timeDescriptorSetLayout) != VK_SUCCESS) { + throw std::runtime_error("Failed to create descriptor set layout"); + } } 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 + // 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 + + // Describe the binding of the descriptor set layout + VkDescriptorSetLayoutBinding sboLayoutBinding = {}; + sboLayoutBinding.binding = 0; + sboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + sboLayoutBinding.descriptorCount = 1; + sboLayoutBinding.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + sboLayoutBinding.pImmutableSamplers = nullptr; + + std::vector bindings = { + sboLayoutBinding, + { 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT, nullptr }, + { 2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT, nullptr }, + }; + + // 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() { - // Describe which descriptor types that the descriptor sets will contain - std::vector poolSizes = { - // Camera - { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER , 1}, - - // Models + Blades - { 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()) }, - - // Time (compute) - { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER , 1 }, - - // TODO: Add any additional types and counts of descriptors you will need to allocate - }; - - VkDescriptorPoolCreateInfo poolInfo = {}; - poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; - poolInfo.poolSizeCount = static_cast(poolSizes.size()); - poolInfo.pPoolSizes = poolSizes.data(); - poolInfo.maxSets = 5; - - if (vkCreateDescriptorPool(logicalDevice, &poolInfo, nullptr, &descriptorPool) != VK_SUCCESS) { - throw std::runtime_error("Failed to create descriptor pool"); - } + // Describe which descriptor types that the descriptor sets will contain + std::vector poolSizes = { + // Camera + { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER , 1 }, + + // Models + Blades + { 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()) }, + + // Time (compute) + { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER , 1 }, + + // TODO: Add any additional types and counts of descriptors you will need to allocate + // input blades,culled blades, total blades remaining (compute) + { 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; + + if (vkCreateDescriptorPool(logicalDevice, &poolInfo, nullptr, &descriptorPool) != VK_SUCCESS) { + throw std::runtime_error("Failed to create descriptor pool"); + } } void Renderer::CreateCameraDescriptorSet() { - // Describe the desciptor set - VkDescriptorSetLayout layouts[] = { cameraDescriptorSetLayout }; - VkDescriptorSetAllocateInfo allocInfo = {}; - allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; - allocInfo.descriptorPool = descriptorPool; - allocInfo.descriptorSetCount = 1; - allocInfo.pSetLayouts = layouts; - - // Allocate descriptor sets - if (vkAllocateDescriptorSets(logicalDevice, &allocInfo, &cameraDescriptorSet) != VK_SUCCESS) { - throw std::runtime_error("Failed to allocate descriptor set"); - } - - // Configure the descriptors to refer to buffers - VkDescriptorBufferInfo cameraBufferInfo = {}; - cameraBufferInfo.buffer = camera->GetBuffer(); - cameraBufferInfo.offset = 0; - cameraBufferInfo.range = sizeof(CameraBufferObject); - - std::array descriptorWrites = {}; - descriptorWrites[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - descriptorWrites[0].dstSet = cameraDescriptorSet; - descriptorWrites[0].dstBinding = 0; - descriptorWrites[0].dstArrayElement = 0; - descriptorWrites[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - descriptorWrites[0].descriptorCount = 1; - descriptorWrites[0].pBufferInfo = &cameraBufferInfo; - descriptorWrites[0].pImageInfo = nullptr; - descriptorWrites[0].pTexelBufferView = nullptr; - - // Update descriptor sets - vkUpdateDescriptorSets(logicalDevice, static_cast(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr); + // Describe the desciptor set + VkDescriptorSetLayout layouts[] = { cameraDescriptorSetLayout }; + VkDescriptorSetAllocateInfo allocInfo = {}; + allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + allocInfo.descriptorPool = descriptorPool; + allocInfo.descriptorSetCount = 1; + allocInfo.pSetLayouts = layouts; + + // Allocate descriptor sets + if (vkAllocateDescriptorSets(logicalDevice, &allocInfo, &cameraDescriptorSet) != VK_SUCCESS) { + throw std::runtime_error("Failed to allocate descriptor set"); + } + + // Configure the descriptors to refer to buffers + VkDescriptorBufferInfo cameraBufferInfo = {}; + cameraBufferInfo.buffer = camera->GetBuffer(); + cameraBufferInfo.offset = 0; + cameraBufferInfo.range = sizeof(CameraBufferObject); + + std::array descriptorWrites = {}; + descriptorWrites[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrites[0].dstSet = cameraDescriptorSet; + descriptorWrites[0].dstBinding = 0; + descriptorWrites[0].dstArrayElement = 0; + descriptorWrites[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + descriptorWrites[0].descriptorCount = 1; + descriptorWrites[0].pBufferInfo = &cameraBufferInfo; + descriptorWrites[0].pImageInfo = nullptr; + descriptorWrites[0].pTexelBufferView = nullptr; + + // Update descriptor sets + vkUpdateDescriptorSets(logicalDevice, static_cast(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr); } void Renderer::CreateModelDescriptorSets() { - modelDescriptorSets.resize(scene->GetModels().size()); - - // Describe the desciptor set - VkDescriptorSetLayout layouts[] = { modelDescriptorSetLayout }; - VkDescriptorSetAllocateInfo allocInfo = {}; - allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; - allocInfo.descriptorPool = descriptorPool; - allocInfo.descriptorSetCount = static_cast(modelDescriptorSets.size()); - allocInfo.pSetLayouts = layouts; - - // Allocate descriptor sets - 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) { - VkDescriptorBufferInfo modelBufferInfo = {}; - modelBufferInfo.buffer = scene->GetModels()[i]->GetModelBuffer(); - modelBufferInfo.offset = 0; - modelBufferInfo.range = sizeof(ModelBufferObject); - - // Bind image and sampler resources to the descriptor - VkDescriptorImageInfo imageInfo = {}; - imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - imageInfo.imageView = scene->GetModels()[i]->GetTextureView(); - imageInfo.sampler = scene->GetModels()[i]->GetTextureSampler(); - - descriptorWrites[2 * i + 0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - descriptorWrites[2 * i + 0].dstSet = modelDescriptorSets[i]; - descriptorWrites[2 * i + 0].dstBinding = 0; - descriptorWrites[2 * i + 0].dstArrayElement = 0; - descriptorWrites[2 * i + 0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - descriptorWrites[2 * i + 0].descriptorCount = 1; - descriptorWrites[2 * i + 0].pBufferInfo = &modelBufferInfo; - descriptorWrites[2 * i + 0].pImageInfo = nullptr; - descriptorWrites[2 * i + 0].pTexelBufferView = nullptr; - - descriptorWrites[2 * i + 1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - descriptorWrites[2 * i + 1].dstSet = modelDescriptorSets[i]; - descriptorWrites[2 * i + 1].dstBinding = 1; - descriptorWrites[2 * i + 1].dstArrayElement = 0; - descriptorWrites[2 * i + 1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - descriptorWrites[2 * i + 1].descriptorCount = 1; - descriptorWrites[2 * i + 1].pImageInfo = &imageInfo; - } - - // Update descriptor sets - vkUpdateDescriptorSets(logicalDevice, static_cast(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr); + modelDescriptorSets.resize(scene->GetModels().size()); + + // Describe the desciptor set + VkDescriptorSetLayout layouts[] = { modelDescriptorSetLayout }; + VkDescriptorSetAllocateInfo allocInfo = {}; + allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + allocInfo.descriptorPool = descriptorPool; + allocInfo.descriptorSetCount = static_cast(modelDescriptorSets.size()); + allocInfo.pSetLayouts = layouts; + + // Allocate descriptor sets + 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) { + VkDescriptorBufferInfo modelBufferInfo = {}; + modelBufferInfo.buffer = scene->GetModels()[i]->GetModelBuffer(); + modelBufferInfo.offset = 0; + modelBufferInfo.range = sizeof(ModelBufferObject); + + // Bind image and sampler resources to the descriptor + VkDescriptorImageInfo imageInfo = {}; + imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + imageInfo.imageView = scene->GetModels()[i]->GetTextureView(); + imageInfo.sampler = scene->GetModels()[i]->GetTextureSampler(); + + descriptorWrites[2 * i + 0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrites[2 * i + 0].dstSet = modelDescriptorSets[i]; + descriptorWrites[2 * i + 0].dstBinding = 0; + descriptorWrites[2 * i + 0].dstArrayElement = 0; + descriptorWrites[2 * i + 0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + descriptorWrites[2 * i + 0].descriptorCount = 1; + descriptorWrites[2 * i + 0].pBufferInfo = &modelBufferInfo; + descriptorWrites[2 * i + 0].pImageInfo = nullptr; + descriptorWrites[2 * i + 0].pTexelBufferView = nullptr; + + descriptorWrites[2 * i + 1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrites[2 * i + 1].dstSet = modelDescriptorSets[i]; + descriptorWrites[2 * i + 1].dstBinding = 1; + descriptorWrites[2 * i + 1].dstArrayElement = 0; + descriptorWrites[2 * i + 1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + descriptorWrites[2 * i + 1].descriptorCount = 1; + descriptorWrites[2 * i + 1].pImageInfo = &imageInfo; + } + + // Update descriptor sets + vkUpdateDescriptorSets(logicalDevice, static_cast(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr); } 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 + // 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->GetBlades().size()); + + // Describe the desciptor set + VkDescriptorSetLayout layouts[] = { modelDescriptorSetLayout }; + 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->GetBlades().size(); ++i) { + VkDescriptorBufferInfo grassBufferInfo = {}; + grassBufferInfo.buffer = scene->GetBlades()[i]->GetModelBuffer(); + grassBufferInfo.offset = 0; + grassBufferInfo.range = sizeof(ModelBufferObject); + + descriptorWrites[i + 0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrites[i + 0].dstSet = grassDescriptorSets[i]; + descriptorWrites[i + 0].dstBinding = 0; + descriptorWrites[i + 0].dstArrayElement = 0; + descriptorWrites[i + 0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + descriptorWrites[i + 0].descriptorCount = 1; + descriptorWrites[i + 0].pBufferInfo = &grassBufferInfo; + descriptorWrites[i + 0].pImageInfo = nullptr; + descriptorWrites[i + 0].pTexelBufferView = nullptr; + } + + // Update descriptor sets + vkUpdateDescriptorSets(logicalDevice, static_cast(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr); } void Renderer::CreateTimeDescriptorSet() { - // Describe the desciptor set - VkDescriptorSetLayout layouts[] = { timeDescriptorSetLayout }; - VkDescriptorSetAllocateInfo allocInfo = {}; - allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; - allocInfo.descriptorPool = descriptorPool; - allocInfo.descriptorSetCount = 1; - allocInfo.pSetLayouts = layouts; - - // Allocate descriptor sets - if (vkAllocateDescriptorSets(logicalDevice, &allocInfo, &timeDescriptorSet) != VK_SUCCESS) { - throw std::runtime_error("Failed to allocate descriptor set"); - } - - // Configure the descriptors to refer to buffers - VkDescriptorBufferInfo timeBufferInfo = {}; - timeBufferInfo.buffer = scene->GetTimeBuffer(); - timeBufferInfo.offset = 0; - timeBufferInfo.range = sizeof(Time); - - std::array descriptorWrites = {}; - descriptorWrites[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - descriptorWrites[0].dstSet = timeDescriptorSet; - descriptorWrites[0].dstBinding = 0; - descriptorWrites[0].dstArrayElement = 0; - descriptorWrites[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - descriptorWrites[0].descriptorCount = 1; - descriptorWrites[0].pBufferInfo = &timeBufferInfo; - descriptorWrites[0].pImageInfo = nullptr; - descriptorWrites[0].pTexelBufferView = nullptr; - - // Update descriptor sets - vkUpdateDescriptorSets(logicalDevice, static_cast(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr); + // Describe the desciptor set + VkDescriptorSetLayout layouts[] = { timeDescriptorSetLayout }; + VkDescriptorSetAllocateInfo allocInfo = {}; + allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + allocInfo.descriptorPool = descriptorPool; + allocInfo.descriptorSetCount = 1; + allocInfo.pSetLayouts = layouts; + + // Allocate descriptor sets + if (vkAllocateDescriptorSets(logicalDevice, &allocInfo, &timeDescriptorSet) != VK_SUCCESS) { + throw std::runtime_error("Failed to allocate descriptor set"); + } + + // Configure the descriptors to refer to buffers + VkDescriptorBufferInfo timeBufferInfo = {}; + timeBufferInfo.buffer = scene->GetTimeBuffer(); + timeBufferInfo.offset = 0; + timeBufferInfo.range = sizeof(Time); + + std::array descriptorWrites = {}; + descriptorWrites[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrites[0].dstSet = timeDescriptorSet; + descriptorWrites[0].dstBinding = 0; + descriptorWrites[0].dstArrayElement = 0; + descriptorWrites[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + descriptorWrites[0].descriptorCount = 1; + descriptorWrites[0].pBufferInfo = &timeBufferInfo; + descriptorWrites[0].pImageInfo = nullptr; + descriptorWrites[0].pTexelBufferView = nullptr; + + // Update descriptor sets + vkUpdateDescriptorSets(logicalDevice, static_cast(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr); } 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 + // 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) + { + VkDescriptorBufferInfo inputbladesBufferInfo = {}; + inputbladesBufferInfo.buffer = scene->GetBlades()[i]->GetBladesBuffer(); + inputbladesBufferInfo.offset = 0; + inputbladesBufferInfo.range = NUM_BLADES * sizeof(Blade); + + VkDescriptorBufferInfo culledbladesBufferInfo = {}; + culledbladesBufferInfo.buffer = scene->GetBlades()[i]->GetCulledBladesBuffer(); + culledbladesBufferInfo.offset = 0; + culledbladesBufferInfo.range = NUM_BLADES * sizeof(Blade); + + 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 = &inputbladesBufferInfo; + 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() { - VkShaderModule vertShaderModule = ShaderModule::Create("shaders/graphics.vert.spv", logicalDevice); - VkShaderModule fragShaderModule = ShaderModule::Create("shaders/graphics.frag.spv", logicalDevice); - - // Assign each shader module to the appropriate stage in the pipeline - VkPipelineShaderStageCreateInfo vertShaderStageInfo = {}; - vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; - vertShaderStageInfo.module = vertShaderModule; - vertShaderStageInfo.pName = "main"; - - VkPipelineShaderStageCreateInfo fragShaderStageInfo = {}; - fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT; - fragShaderStageInfo.module = fragShaderModule; - fragShaderStageInfo.pName = "main"; - - VkPipelineShaderStageCreateInfo shaderStages[] = { vertShaderStageInfo, fragShaderStageInfo }; - - // --- Set up fixed-function stages --- - - // Vertex input - VkPipelineVertexInputStateCreateInfo vertexInputInfo = {}; - vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; - - auto bindingDescription = Vertex::getBindingDescription(); - auto attributeDescriptions = Vertex::getAttributeDescriptions(); - - vertexInputInfo.vertexBindingDescriptionCount = 1; - vertexInputInfo.pVertexBindingDescriptions = &bindingDescription; - vertexInputInfo.vertexAttributeDescriptionCount = static_cast(attributeDescriptions.size()); - vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data(); - - // Input assembly - VkPipelineInputAssemblyStateCreateInfo inputAssembly = {}; - inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; - inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; - inputAssembly.primitiveRestartEnable = VK_FALSE; - - // Viewports and Scissors (rectangles that define in which regions pixels are stored) - VkViewport viewport = {}; - viewport.x = 0.0f; - viewport.y = 0.0f; - viewport.width = static_cast(swapChain->GetVkExtent().width); - viewport.height = static_cast(swapChain->GetVkExtent().height); - viewport.minDepth = 0.0f; - viewport.maxDepth = 1.0f; - - VkRect2D scissor = {}; - scissor.offset = { 0, 0 }; - scissor.extent = swapChain->GetVkExtent(); - - VkPipelineViewportStateCreateInfo viewportState = {}; - viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; - viewportState.viewportCount = 1; - viewportState.pViewports = &viewport; - viewportState.scissorCount = 1; - viewportState.pScissors = &scissor; - - // Rasterizer - VkPipelineRasterizationStateCreateInfo rasterizer = {}; - 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.lineWidth = 1.0f; - rasterizer.cullMode = VK_CULL_MODE_BACK_BIT; - rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; - rasterizer.depthBiasEnable = VK_FALSE; - rasterizer.depthBiasConstantFactor = 0.0f; - rasterizer.depthBiasClamp = 0.0f; - rasterizer.depthBiasSlopeFactor = 0.0f; - - // Multisampling (turned off here) - VkPipelineMultisampleStateCreateInfo multisampling = {}; - multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; - multisampling.sampleShadingEnable = VK_FALSE; - multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; - multisampling.minSampleShading = 1.0f; - multisampling.pSampleMask = nullptr; - multisampling.alphaToCoverageEnable = VK_FALSE; - multisampling.alphaToOneEnable = VK_FALSE; - - // Depth testing - VkPipelineDepthStencilStateCreateInfo depthStencil = {}; - depthStencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; - depthStencil.depthTestEnable = VK_TRUE; - depthStencil.depthWriteEnable = VK_TRUE; - depthStencil.depthCompareOp = VK_COMPARE_OP_LESS; - depthStencil.depthBoundsTestEnable = VK_FALSE; - depthStencil.minDepthBounds = 0.0f; - depthStencil.maxDepthBounds = 1.0f; - depthStencil.stencilTestEnable = VK_FALSE; - - // Color blending (turned off here, but showing options for learning) - // --> Configuration per attached framebuffer - VkPipelineColorBlendAttachmentState colorBlendAttachment = {}; - colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; - colorBlendAttachment.blendEnable = VK_FALSE; - colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE; - colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO; - colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD; - colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; - colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; - colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD; - - // --> Global color blending settings - VkPipelineColorBlendStateCreateInfo colorBlending = {}; - colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; - colorBlending.logicOpEnable = VK_FALSE; - colorBlending.logicOp = VK_LOGIC_OP_COPY; - colorBlending.attachmentCount = 1; - colorBlending.pAttachments = &colorBlendAttachment; - colorBlending.blendConstants[0] = 0.0f; - colorBlending.blendConstants[1] = 0.0f; - colorBlending.blendConstants[2] = 0.0f; - colorBlending.blendConstants[3] = 0.0f; - - std::vector descriptorSetLayouts = { cameraDescriptorSetLayout, modelDescriptorSetLayout }; - - // Pipeline layout: used to specify uniform values - VkPipelineLayoutCreateInfo pipelineLayoutInfo = {}; - pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; - pipelineLayoutInfo.setLayoutCount = static_cast(descriptorSetLayouts.size()); - pipelineLayoutInfo.pSetLayouts = descriptorSetLayouts.data(); - pipelineLayoutInfo.pushConstantRangeCount = 0; - pipelineLayoutInfo.pPushConstantRanges = 0; - - if (vkCreatePipelineLayout(logicalDevice, &pipelineLayoutInfo, nullptr, &graphicsPipelineLayout) != VK_SUCCESS) { - throw std::runtime_error("Failed to create pipeline layout"); - } - - // --- Create graphics pipeline --- - VkGraphicsPipelineCreateInfo pipelineInfo = {}; - pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; - pipelineInfo.stageCount = 2; - pipelineInfo.pStages = shaderStages; - pipelineInfo.pVertexInputState = &vertexInputInfo; - pipelineInfo.pInputAssemblyState = &inputAssembly; - pipelineInfo.pViewportState = &viewportState; - pipelineInfo.pRasterizationState = &rasterizer; - pipelineInfo.pMultisampleState = &multisampling; - pipelineInfo.pDepthStencilState = &depthStencil; - pipelineInfo.pColorBlendState = &colorBlending; - pipelineInfo.pDynamicState = nullptr; - pipelineInfo.layout = graphicsPipelineLayout; - pipelineInfo.renderPass = renderPass; - pipelineInfo.subpass = 0; - pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; - pipelineInfo.basePipelineIndex = -1; - - if (vkCreateGraphicsPipelines(logicalDevice, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS) { - throw std::runtime_error("Failed to create graphics pipeline"); - } - - vkDestroyShaderModule(logicalDevice, vertShaderModule, nullptr); - vkDestroyShaderModule(logicalDevice, fragShaderModule, nullptr); + VkShaderModule vertShaderModule = ShaderModule::Create("shaders/graphics.vert.spv", logicalDevice); + VkShaderModule fragShaderModule = ShaderModule::Create("shaders/graphics.frag.spv", logicalDevice); + + // Assign each shader module to the appropriate stage in the pipeline + VkPipelineShaderStageCreateInfo vertShaderStageInfo = {}; + vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; + vertShaderStageInfo.module = vertShaderModule; + vertShaderStageInfo.pName = "main"; + + VkPipelineShaderStageCreateInfo fragShaderStageInfo = {}; + fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT; + fragShaderStageInfo.module = fragShaderModule; + fragShaderStageInfo.pName = "main"; + + VkPipelineShaderStageCreateInfo shaderStages[] = { vertShaderStageInfo, fragShaderStageInfo }; + + // --- Set up fixed-function stages --- + + // Vertex input + VkPipelineVertexInputStateCreateInfo vertexInputInfo = {}; + vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + + auto bindingDescription = Vertex::getBindingDescription(); + auto attributeDescriptions = Vertex::getAttributeDescriptions(); + + vertexInputInfo.vertexBindingDescriptionCount = 1; + vertexInputInfo.pVertexBindingDescriptions = &bindingDescription; + vertexInputInfo.vertexAttributeDescriptionCount = static_cast(attributeDescriptions.size()); + vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data(); + + // Input assembly + VkPipelineInputAssemblyStateCreateInfo inputAssembly = {}; + inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + inputAssembly.primitiveRestartEnable = VK_FALSE; + + // Viewports and Scissors (rectangles that define in which regions pixels are stored) + VkViewport viewport = {}; + viewport.x = 0.0f; + viewport.y = 0.0f; + viewport.width = static_cast(swapChain->GetVkExtent().width); + viewport.height = static_cast(swapChain->GetVkExtent().height); + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + + VkRect2D scissor = {}; + scissor.offset = { 0, 0 }; + scissor.extent = swapChain->GetVkExtent(); + + VkPipelineViewportStateCreateInfo viewportState = {}; + viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewportState.viewportCount = 1; + viewportState.pViewports = &viewport; + viewportState.scissorCount = 1; + viewportState.pScissors = &scissor; + + // Rasterizer + VkPipelineRasterizationStateCreateInfo rasterizer = {}; + 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.lineWidth = 1.0f; + rasterizer.cullMode = VK_CULL_MODE_BACK_BIT; + rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; + rasterizer.depthBiasEnable = VK_FALSE; + rasterizer.depthBiasConstantFactor = 0.0f; + rasterizer.depthBiasClamp = 0.0f; + rasterizer.depthBiasSlopeFactor = 0.0f; + + // Multisampling (turned off here) + VkPipelineMultisampleStateCreateInfo multisampling = {}; + multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + multisampling.sampleShadingEnable = VK_FALSE; + multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + multisampling.minSampleShading = 1.0f; + multisampling.pSampleMask = nullptr; + multisampling.alphaToCoverageEnable = VK_FALSE; + multisampling.alphaToOneEnable = VK_FALSE; + + // Depth testing + VkPipelineDepthStencilStateCreateInfo depthStencil = {}; + depthStencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; + depthStencil.depthTestEnable = VK_TRUE; + depthStencil.depthWriteEnable = VK_TRUE; + depthStencil.depthCompareOp = VK_COMPARE_OP_LESS; + depthStencil.depthBoundsTestEnable = VK_FALSE; + depthStencil.minDepthBounds = 0.0f; + depthStencil.maxDepthBounds = 1.0f; + depthStencil.stencilTestEnable = VK_FALSE; + + // Color blending (turned off here, but showing options for learning) + // --> Configuration per attached framebuffer + VkPipelineColorBlendAttachmentState colorBlendAttachment = {}; + colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + colorBlendAttachment.blendEnable = VK_FALSE; + colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE; + colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO; + colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD; + colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; + colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; + colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD; + + // --> Global color blending settings + VkPipelineColorBlendStateCreateInfo colorBlending = {}; + colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + colorBlending.logicOpEnable = VK_FALSE; + colorBlending.logicOp = VK_LOGIC_OP_COPY; + colorBlending.attachmentCount = 1; + colorBlending.pAttachments = &colorBlendAttachment; + colorBlending.blendConstants[0] = 0.0f; + colorBlending.blendConstants[1] = 0.0f; + colorBlending.blendConstants[2] = 0.0f; + colorBlending.blendConstants[3] = 0.0f; + + std::vector descriptorSetLayouts = { cameraDescriptorSetLayout, modelDescriptorSetLayout }; + + // Pipeline layout: used to specify uniform values + VkPipelineLayoutCreateInfo pipelineLayoutInfo = {}; + pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipelineLayoutInfo.setLayoutCount = static_cast(descriptorSetLayouts.size()); + pipelineLayoutInfo.pSetLayouts = descriptorSetLayouts.data(); + pipelineLayoutInfo.pushConstantRangeCount = 0; + pipelineLayoutInfo.pPushConstantRanges = 0; + + if (vkCreatePipelineLayout(logicalDevice, &pipelineLayoutInfo, nullptr, &graphicsPipelineLayout) != VK_SUCCESS) { + throw std::runtime_error("Failed to create pipeline layout"); + } + + // --- Create graphics pipeline --- + VkGraphicsPipelineCreateInfo pipelineInfo = {}; + pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + pipelineInfo.stageCount = 2; + pipelineInfo.pStages = shaderStages; + pipelineInfo.pVertexInputState = &vertexInputInfo; + pipelineInfo.pInputAssemblyState = &inputAssembly; + pipelineInfo.pViewportState = &viewportState; + pipelineInfo.pRasterizationState = &rasterizer; + pipelineInfo.pMultisampleState = &multisampling; + pipelineInfo.pDepthStencilState = &depthStencil; + pipelineInfo.pColorBlendState = &colorBlending; + pipelineInfo.pDynamicState = nullptr; + pipelineInfo.layout = graphicsPipelineLayout; + pipelineInfo.renderPass = renderPass; + pipelineInfo.subpass = 0; + pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; + pipelineInfo.basePipelineIndex = -1; + + if (vkCreateGraphicsPipelines(logicalDevice, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS) { + throw std::runtime_error("Failed to create graphics pipeline"); + } + + vkDestroyShaderModule(logicalDevice, vertShaderModule, nullptr); + vkDestroyShaderModule(logicalDevice, fragShaderModule, nullptr); } 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); - VkShaderModule teseShaderModule = ShaderModule::Create("shaders/grass.tese.spv", logicalDevice); - VkShaderModule fragShaderModule = ShaderModule::Create("shaders/grass.frag.spv", logicalDevice); - - // Assign each shader module to the appropriate stage in the pipeline - VkPipelineShaderStageCreateInfo vertShaderStageInfo = {}; - vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; - vertShaderStageInfo.module = vertShaderModule; - vertShaderStageInfo.pName = "main"; - - VkPipelineShaderStageCreateInfo tescShaderStageInfo = {}; - tescShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - tescShaderStageInfo.stage = VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT; - tescShaderStageInfo.module = tescShaderModule; - tescShaderStageInfo.pName = "main"; - - VkPipelineShaderStageCreateInfo teseShaderStageInfo = {}; - teseShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - teseShaderStageInfo.stage = VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT; - teseShaderStageInfo.module = teseShaderModule; - teseShaderStageInfo.pName = "main"; - - VkPipelineShaderStageCreateInfo fragShaderStageInfo = {}; - fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT; - fragShaderStageInfo.module = fragShaderModule; - fragShaderStageInfo.pName = "main"; - - VkPipelineShaderStageCreateInfo shaderStages[] = { vertShaderStageInfo, tescShaderStageInfo, teseShaderStageInfo, fragShaderStageInfo }; - - // --- Set up fixed-function stages --- - - // Vertex input - VkPipelineVertexInputStateCreateInfo vertexInputInfo = {}; - vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; - - auto bindingDescription = Blade::getBindingDescription(); - auto attributeDescriptions = Blade::getAttributeDescriptions(); - - vertexInputInfo.vertexBindingDescriptionCount = 1; - vertexInputInfo.pVertexBindingDescriptions = &bindingDescription; - vertexInputInfo.vertexAttributeDescriptionCount = static_cast(attributeDescriptions.size()); - vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data(); - - // Input Assembly - VkPipelineInputAssemblyStateCreateInfo inputAssembly = {}; - inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; - inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_PATCH_LIST; - inputAssembly.primitiveRestartEnable = VK_FALSE; - - // Viewports and Scissors (rectangles that define in which regions pixels are stored) - VkViewport viewport = {}; - viewport.x = 0.0f; - viewport.y = 0.0f; - viewport.width = static_cast(swapChain->GetVkExtent().width); - viewport.height = static_cast(swapChain->GetVkExtent().height); - viewport.minDepth = 0.0f; - viewport.maxDepth = 1.0f; - - VkRect2D scissor = {}; - scissor.offset = { 0, 0 }; - scissor.extent = swapChain->GetVkExtent(); - - VkPipelineViewportStateCreateInfo viewportState = {}; - viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; - viewportState.viewportCount = 1; - viewportState.pViewports = &viewport; - viewportState.scissorCount = 1; - viewportState.pScissors = &scissor; - - // Rasterizer - VkPipelineRasterizationStateCreateInfo rasterizer = {}; - 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.lineWidth = 1.0f; - rasterizer.cullMode = VK_CULL_MODE_NONE; - rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; - rasterizer.depthBiasEnable = VK_FALSE; - rasterizer.depthBiasConstantFactor = 0.0f; - rasterizer.depthBiasClamp = 0.0f; - rasterizer.depthBiasSlopeFactor = 0.0f; - - // Multisampling (turned off here) - VkPipelineMultisampleStateCreateInfo multisampling = {}; - multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; - multisampling.sampleShadingEnable = VK_FALSE; - multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; - multisampling.minSampleShading = 1.0f; - multisampling.pSampleMask = nullptr; - multisampling.alphaToCoverageEnable = VK_FALSE; - multisampling.alphaToOneEnable = VK_FALSE; - - // Depth testing - VkPipelineDepthStencilStateCreateInfo depthStencil = {}; - depthStencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; - depthStencil.depthTestEnable = VK_TRUE; - depthStencil.depthWriteEnable = VK_TRUE; - depthStencil.depthCompareOp = VK_COMPARE_OP_LESS; - depthStencil.depthBoundsTestEnable = VK_FALSE; - depthStencil.minDepthBounds = 0.0f; - depthStencil.maxDepthBounds = 1.0f; - depthStencil.stencilTestEnable = VK_FALSE; - - // Color blending (turned off here, but showing options for learning) - // --> Configuration per attached framebuffer - VkPipelineColorBlendAttachmentState colorBlendAttachment = {}; - colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; - colorBlendAttachment.blendEnable = VK_FALSE; - colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE; - colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO; - colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD; - colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; - colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; - colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD; - - // --> Global color blending settings - VkPipelineColorBlendStateCreateInfo colorBlending = {}; - colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; - colorBlending.logicOpEnable = VK_FALSE; - colorBlending.logicOp = VK_LOGIC_OP_COPY; - colorBlending.attachmentCount = 1; - colorBlending.pAttachments = &colorBlendAttachment; - colorBlending.blendConstants[0] = 0.0f; - colorBlending.blendConstants[1] = 0.0f; - colorBlending.blendConstants[2] = 0.0f; - colorBlending.blendConstants[3] = 0.0f; - - std::vector descriptorSetLayouts = { cameraDescriptorSetLayout, modelDescriptorSetLayout }; - - // Pipeline layout: used to specify uniform values - VkPipelineLayoutCreateInfo pipelineLayoutInfo = {}; - pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; - pipelineLayoutInfo.setLayoutCount = static_cast(descriptorSetLayouts.size()); - pipelineLayoutInfo.pSetLayouts = descriptorSetLayouts.data(); - pipelineLayoutInfo.pushConstantRangeCount = 0; - pipelineLayoutInfo.pPushConstantRanges = 0; - - if (vkCreatePipelineLayout(logicalDevice, &pipelineLayoutInfo, nullptr, &grassPipelineLayout) != VK_SUCCESS) { - throw std::runtime_error("Failed to create pipeline layout"); - } - - // Tessellation state - VkPipelineTessellationStateCreateInfo tessellationInfo = {}; - tessellationInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO; - tessellationInfo.pNext = NULL; - tessellationInfo.flags = 0; - tessellationInfo.patchControlPoints = 1; - - // --- Create graphics pipeline --- - VkGraphicsPipelineCreateInfo pipelineInfo = {}; - pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; - pipelineInfo.stageCount = 4; - pipelineInfo.pStages = shaderStages; - pipelineInfo.pVertexInputState = &vertexInputInfo; - pipelineInfo.pInputAssemblyState = &inputAssembly; - pipelineInfo.pViewportState = &viewportState; - pipelineInfo.pRasterizationState = &rasterizer; - pipelineInfo.pMultisampleState = &multisampling; - pipelineInfo.pDepthStencilState = &depthStencil; - pipelineInfo.pColorBlendState = &colorBlending; - pipelineInfo.pTessellationState = &tessellationInfo; - pipelineInfo.pDynamicState = nullptr; - pipelineInfo.layout = grassPipelineLayout; - pipelineInfo.renderPass = renderPass; - pipelineInfo.subpass = 0; - pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; - pipelineInfo.basePipelineIndex = -1; - - if (vkCreateGraphicsPipelines(logicalDevice, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &grassPipeline) != VK_SUCCESS) { - throw std::runtime_error("Failed to create graphics pipeline"); - } - - // No need for the shader modules anymore - vkDestroyShaderModule(logicalDevice, vertShaderModule, nullptr); - vkDestroyShaderModule(logicalDevice, tescShaderModule, nullptr); - vkDestroyShaderModule(logicalDevice, teseShaderModule, nullptr); - vkDestroyShaderModule(logicalDevice, fragShaderModule, nullptr); + // --- Set up programmable shaders --- + VkShaderModule vertShaderModule = ShaderModule::Create("shaders/grass.vert.spv", logicalDevice); + VkShaderModule tescShaderModule = ShaderModule::Create("shaders/grass.tesc.spv", logicalDevice); + VkShaderModule teseShaderModule = ShaderModule::Create("shaders/grass.tese.spv", logicalDevice); + VkShaderModule fragShaderModule = ShaderModule::Create("shaders/grass.frag.spv", logicalDevice); + + // Assign each shader module to the appropriate stage in the pipeline + VkPipelineShaderStageCreateInfo vertShaderStageInfo = {}; + vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; + vertShaderStageInfo.module = vertShaderModule; + vertShaderStageInfo.pName = "main"; + + VkPipelineShaderStageCreateInfo tescShaderStageInfo = {}; + tescShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + tescShaderStageInfo.stage = VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT; + tescShaderStageInfo.module = tescShaderModule; + tescShaderStageInfo.pName = "main"; + + VkPipelineShaderStageCreateInfo teseShaderStageInfo = {}; + teseShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + teseShaderStageInfo.stage = VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT; + teseShaderStageInfo.module = teseShaderModule; + teseShaderStageInfo.pName = "main"; + + VkPipelineShaderStageCreateInfo fragShaderStageInfo = {}; + fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT; + fragShaderStageInfo.module = fragShaderModule; + fragShaderStageInfo.pName = "main"; + + VkPipelineShaderStageCreateInfo shaderStages[] = { vertShaderStageInfo, tescShaderStageInfo, teseShaderStageInfo, fragShaderStageInfo }; + + // --- Set up fixed-function stages --- + + // Vertex input + VkPipelineVertexInputStateCreateInfo vertexInputInfo = {}; + vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + + auto bindingDescription = Blade::getBindingDescription(); + auto attributeDescriptions = Blade::getAttributeDescriptions(); + + vertexInputInfo.vertexBindingDescriptionCount = 1; + vertexInputInfo.pVertexBindingDescriptions = &bindingDescription; + vertexInputInfo.vertexAttributeDescriptionCount = static_cast(attributeDescriptions.size()); + vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data(); + + // Input Assembly + VkPipelineInputAssemblyStateCreateInfo inputAssembly = {}; + inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_PATCH_LIST; + inputAssembly.primitiveRestartEnable = VK_FALSE; + + // Viewports and Scissors (rectangles that define in which regions pixels are stored) + VkViewport viewport = {}; + viewport.x = 0.0f; + viewport.y = 0.0f; + viewport.width = static_cast(swapChain->GetVkExtent().width); + viewport.height = static_cast(swapChain->GetVkExtent().height); + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + + VkRect2D scissor = {}; + scissor.offset = { 0, 0 }; + scissor.extent = swapChain->GetVkExtent(); + + VkPipelineViewportStateCreateInfo viewportState = {}; + viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewportState.viewportCount = 1; + viewportState.pViewports = &viewport; + viewportState.scissorCount = 1; + viewportState.pScissors = &scissor; + + // Rasterizer + VkPipelineRasterizationStateCreateInfo rasterizer = {}; + 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.lineWidth = 1.0f; + rasterizer.cullMode = VK_CULL_MODE_NONE; + rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; + rasterizer.depthBiasEnable = VK_FALSE; + rasterizer.depthBiasConstantFactor = 0.0f; + rasterizer.depthBiasClamp = 0.0f; + rasterizer.depthBiasSlopeFactor = 0.0f; + + // Multisampling (turned off here) + VkPipelineMultisampleStateCreateInfo multisampling = {}; + multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + multisampling.sampleShadingEnable = VK_FALSE; + multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + multisampling.minSampleShading = 1.0f; + multisampling.pSampleMask = nullptr; + multisampling.alphaToCoverageEnable = VK_FALSE; + multisampling.alphaToOneEnable = VK_FALSE; + + // Depth testing + VkPipelineDepthStencilStateCreateInfo depthStencil = {}; + depthStencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; + depthStencil.depthTestEnable = VK_TRUE; + depthStencil.depthWriteEnable = VK_TRUE; + depthStencil.depthCompareOp = VK_COMPARE_OP_LESS; + depthStencil.depthBoundsTestEnable = VK_FALSE; + depthStencil.minDepthBounds = 0.0f; + depthStencil.maxDepthBounds = 1.0f; + depthStencil.stencilTestEnable = VK_FALSE; + + // Color blending (turned off here, but showing options for learning) + // --> Configuration per attached framebuffer + VkPipelineColorBlendAttachmentState colorBlendAttachment = {}; + colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + colorBlendAttachment.blendEnable = VK_FALSE; + colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE; + colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO; + colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD; + colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; + colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; + colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD; + + // --> Global color blending settings + VkPipelineColorBlendStateCreateInfo colorBlending = {}; + colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + colorBlending.logicOpEnable = VK_FALSE; + colorBlending.logicOp = VK_LOGIC_OP_COPY; + colorBlending.attachmentCount = 1; + colorBlending.pAttachments = &colorBlendAttachment; + colorBlending.blendConstants[0] = 0.0f; + colorBlending.blendConstants[1] = 0.0f; + colorBlending.blendConstants[2] = 0.0f; + colorBlending.blendConstants[3] = 0.0f; + + std::vector descriptorSetLayouts = { cameraDescriptorSetLayout, modelDescriptorSetLayout }; + + // Pipeline layout: used to specify uniform values + VkPipelineLayoutCreateInfo pipelineLayoutInfo = {}; + pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipelineLayoutInfo.setLayoutCount = static_cast(descriptorSetLayouts.size()); + pipelineLayoutInfo.pSetLayouts = descriptorSetLayouts.data(); + pipelineLayoutInfo.pushConstantRangeCount = 0; + pipelineLayoutInfo.pPushConstantRanges = 0; + + if (vkCreatePipelineLayout(logicalDevice, &pipelineLayoutInfo, nullptr, &grassPipelineLayout) != VK_SUCCESS) { + throw std::runtime_error("Failed to create pipeline layout"); + } + + // Tessellation state + VkPipelineTessellationStateCreateInfo tessellationInfo = {}; + tessellationInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO; + tessellationInfo.pNext = NULL; + tessellationInfo.flags = 0; + tessellationInfo.patchControlPoints = 1; + + // --- Create graphics pipeline --- + VkGraphicsPipelineCreateInfo pipelineInfo = {}; + pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + pipelineInfo.stageCount = 4; + pipelineInfo.pStages = shaderStages; + pipelineInfo.pVertexInputState = &vertexInputInfo; + pipelineInfo.pInputAssemblyState = &inputAssembly; + pipelineInfo.pViewportState = &viewportState; + pipelineInfo.pRasterizationState = &rasterizer; + pipelineInfo.pMultisampleState = &multisampling; + pipelineInfo.pDepthStencilState = &depthStencil; + pipelineInfo.pColorBlendState = &colorBlending; + pipelineInfo.pTessellationState = &tessellationInfo; + pipelineInfo.pDynamicState = nullptr; + pipelineInfo.layout = grassPipelineLayout; + pipelineInfo.renderPass = renderPass; + pipelineInfo.subpass = 0; + pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; + pipelineInfo.basePipelineIndex = -1; + + if (vkCreateGraphicsPipelines(logicalDevice, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &grassPipeline) != VK_SUCCESS) { + throw std::runtime_error("Failed to create graphics pipeline"); + } + + // No need for the shader modules anymore + vkDestroyShaderModule(logicalDevice, vertShaderModule, nullptr); + vkDestroyShaderModule(logicalDevice, tescShaderModule, nullptr); + vkDestroyShaderModule(logicalDevice, teseShaderModule, nullptr); + vkDestroyShaderModule(logicalDevice, fragShaderModule, nullptr); } void Renderer::CreateComputePipeline() { - // Set up programmable shaders - VkShaderModule computeShaderModule = ShaderModule::Create("shaders/compute.comp.spv", logicalDevice); - - VkPipelineShaderStageCreateInfo computeShaderStageInfo = {}; - computeShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - computeShaderStageInfo.stage = VK_SHADER_STAGE_COMPUTE_BIT; - computeShaderStageInfo.module = computeShaderModule; - computeShaderStageInfo.pName = "main"; - - // TODO: Add the compute dsecriptor set layout you create to this list - std::vector descriptorSetLayouts = { cameraDescriptorSetLayout, timeDescriptorSetLayout }; - - // Create pipeline layout - VkPipelineLayoutCreateInfo pipelineLayoutInfo = {}; - pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; - pipelineLayoutInfo.setLayoutCount = static_cast(descriptorSetLayouts.size()); - pipelineLayoutInfo.pSetLayouts = descriptorSetLayouts.data(); - pipelineLayoutInfo.pushConstantRangeCount = 0; - pipelineLayoutInfo.pPushConstantRanges = 0; - - if (vkCreatePipelineLayout(logicalDevice, &pipelineLayoutInfo, nullptr, &computePipelineLayout) != VK_SUCCESS) { - throw std::runtime_error("Failed to create pipeline layout"); - } - - // Create compute pipeline - VkComputePipelineCreateInfo pipelineInfo = {}; - pipelineInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO; - pipelineInfo.stage = computeShaderStageInfo; - pipelineInfo.layout = computePipelineLayout; - pipelineInfo.pNext = nullptr; - pipelineInfo.flags = 0; - pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; - pipelineInfo.basePipelineIndex = -1; - - if (vkCreateComputePipelines(logicalDevice, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &computePipeline) != VK_SUCCESS) { - throw std::runtime_error("Failed to create compute pipeline"); - } - - // No need for shader modules anymore - vkDestroyShaderModule(logicalDevice, computeShaderModule, nullptr); + // Set up programmable shaders + VkShaderModule computeShaderModule = ShaderModule::Create("shaders/compute.comp.spv", logicalDevice); + + VkPipelineShaderStageCreateInfo computeShaderStageInfo = {}; + computeShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + computeShaderStageInfo.stage = VK_SHADER_STAGE_COMPUTE_BIT; + 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 + VkPipelineLayoutCreateInfo pipelineLayoutInfo = {}; + pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipelineLayoutInfo.setLayoutCount = static_cast(descriptorSetLayouts.size()); + pipelineLayoutInfo.pSetLayouts = descriptorSetLayouts.data(); + pipelineLayoutInfo.pushConstantRangeCount = 0; + pipelineLayoutInfo.pPushConstantRanges = 0; + + if (vkCreatePipelineLayout(logicalDevice, &pipelineLayoutInfo, nullptr, &computePipelineLayout) != VK_SUCCESS) { + throw std::runtime_error("Failed to create pipeline layout"); + } + + // Create compute pipeline + VkComputePipelineCreateInfo pipelineInfo = {}; + pipelineInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO; + pipelineInfo.stage = computeShaderStageInfo; + pipelineInfo.layout = computePipelineLayout; + pipelineInfo.pNext = nullptr; + pipelineInfo.flags = 0; + pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; + pipelineInfo.basePipelineIndex = -1; + + if (vkCreateComputePipelines(logicalDevice, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &computePipeline) != VK_SUCCESS) { + throw std::runtime_error("Failed to create compute pipeline"); + } + + // No need for shader modules anymore + vkDestroyShaderModule(logicalDevice, computeShaderModule, nullptr); } void Renderer::CreateFrameResources() { - imageViews.resize(swapChain->GetCount()); - - 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; - createInfo.image = swapChain->GetVkImage(i); - - // Specify how the image data should be interpreted - createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; - createInfo.format = swapChain->GetVkImageFormat(); - - // Specify color channel mappings (can be used for swizzling) - createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; - createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; - createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; - createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; - - // Describe the image's purpose and which part of the image should be accessed - createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - createInfo.subresourceRange.baseMipLevel = 0; - createInfo.subresourceRange.levelCount = 1; - createInfo.subresourceRange.baseArrayLayer = 0; - createInfo.subresourceRange.layerCount = 1; - - // Create the image view - 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); - // 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 - ); - - 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++) { - std::vector attachments = { - imageViews[i], - depthImageView - }; - - VkFramebufferCreateInfo framebufferInfo = {}; - framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; - framebufferInfo.renderPass = renderPass; - framebufferInfo.attachmentCount = static_cast(attachments.size()); - framebufferInfo.pAttachments = attachments.data(); - framebufferInfo.width = swapChain->GetVkExtent().width; - framebufferInfo.height = swapChain->GetVkExtent().height; - framebufferInfo.layers = 1; - - if (vkCreateFramebuffer(logicalDevice, &framebufferInfo, nullptr, &framebuffers[i]) != VK_SUCCESS) { - throw std::runtime_error("Failed to create framebuffer"); - } - - } + imageViews.resize(swapChain->GetCount()); + + 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; + createInfo.image = swapChain->GetVkImage(i); + + // Specify how the image data should be interpreted + createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + createInfo.format = swapChain->GetVkImageFormat(); + + // Specify color channel mappings (can be used for swizzling) + createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; + + // Describe the image's purpose and which part of the image should be accessed + createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + createInfo.subresourceRange.baseMipLevel = 0; + createInfo.subresourceRange.levelCount = 1; + createInfo.subresourceRange.baseArrayLayer = 0; + createInfo.subresourceRange.layerCount = 1; + + // Create the image view + 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); + // 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 + ); + + 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++) { + std::vector attachments = { + imageViews[i], + depthImageView + }; + + VkFramebufferCreateInfo framebufferInfo = {}; + framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + framebufferInfo.renderPass = renderPass; + framebufferInfo.attachmentCount = static_cast(attachments.size()); + framebufferInfo.pAttachments = attachments.data(); + framebufferInfo.width = swapChain->GetVkExtent().width; + framebufferInfo.height = swapChain->GetVkExtent().height; + framebufferInfo.layers = 1; + + 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++) { - vkDestroyImageView(logicalDevice, imageViews[i], nullptr); - } + for (size_t i = 0; i < imageViews.size(); i++) { + vkDestroyImageView(logicalDevice, imageViews[i], nullptr); + } - vkDestroyImageView(logicalDevice, depthImageView, nullptr); - vkFreeMemory(logicalDevice, depthImageMemory, nullptr); - vkDestroyImage(logicalDevice, depthImage, nullptr); + vkDestroyImageView(logicalDevice, depthImageView, nullptr); + vkFreeMemory(logicalDevice, depthImageMemory, nullptr); + vkDestroyImage(logicalDevice, depthImage, nullptr); - for (size_t i = 0; i < framebuffers.size(); i++) { - vkDestroyFramebuffer(logicalDevice, framebuffers[i], nullptr); - } + for (size_t i = 0; i < framebuffers.size(); i++) { + vkDestroyFramebuffer(logicalDevice, framebuffers[i], nullptr); + } } void Renderer::RecreateFrameResources() { - vkDestroyPipeline(logicalDevice, graphicsPipeline, nullptr); - vkDestroyPipeline(logicalDevice, grassPipeline, nullptr); - vkDestroyPipelineLayout(logicalDevice, graphicsPipelineLayout, nullptr); - vkDestroyPipelineLayout(logicalDevice, grassPipelineLayout, nullptr); - vkFreeCommandBuffers(logicalDevice, graphicsCommandPool, static_cast(commandBuffers.size()), commandBuffers.data()); - - DestroyFrameResources(); - CreateFrameResources(); - CreateGraphicsPipeline(); - CreateGrassPipeline(); - RecordCommandBuffers(); + vkDestroyPipeline(logicalDevice, graphicsPipeline, nullptr); + vkDestroyPipeline(logicalDevice, grassPipeline, nullptr); + vkDestroyPipelineLayout(logicalDevice, graphicsPipelineLayout, nullptr); + vkDestroyPipelineLayout(logicalDevice, grassPipelineLayout, nullptr); + vkFreeCommandBuffers(logicalDevice, graphicsCommandPool, static_cast(commandBuffers.size()), commandBuffers.data()); + + DestroyFrameResources(); + CreateFrameResources(); + CreateGraphicsPipeline(); + CreateGrassPipeline(); + RecordCommandBuffers(); } void Renderer::RecordComputeCommandBuffer() { - // Specify the command pool and number of buffers to allocate - VkCommandBufferAllocateInfo allocInfo = {}; - allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - allocInfo.commandPool = computeCommandPool; - allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - allocInfo.commandBufferCount = 1; - - if (vkAllocateCommandBuffers(logicalDevice, &allocInfo, &computeCommandBuffer) != VK_SUCCESS) { - throw std::runtime_error("Failed to allocate command buffers"); - } - - 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(computeCommandBuffer, &beginInfo) != VK_SUCCESS) { - throw std::runtime_error("Failed to begin recording compute command buffer"); - } - - // Bind to the compute pipeline - vkCmdBindPipeline(computeCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, computePipeline); - - // Bind camera descriptor set - vkCmdBindDescriptorSets(computeCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, computePipelineLayout, 0, 1, &cameraDescriptorSet, 0, nullptr); - - // 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 - - // ~ End recording ~ - if (vkEndCommandBuffer(computeCommandBuffer) != VK_SUCCESS) { - throw std::runtime_error("Failed to record compute command buffer"); - } + // Specify the command pool and number of buffers to allocate + VkCommandBufferAllocateInfo allocInfo = {}; + allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + allocInfo.commandPool = computeCommandPool; + allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + allocInfo.commandBufferCount = 1; + + if (vkAllocateCommandBuffers(logicalDevice, &allocInfo, &computeCommandBuffer) != VK_SUCCESS) { + throw std::runtime_error("Failed to allocate command buffers"); + } + + 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(computeCommandBuffer, &beginInfo) != VK_SUCCESS) { + throw std::runtime_error("Failed to begin recording compute command buffer"); + } + + // Bind to the compute pipeline + vkCmdBindPipeline(computeCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, computePipeline); + + // Bind camera descriptor set + vkCmdBindDescriptorSets(computeCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, computePipelineLayout, 0, 1, &cameraDescriptorSet, 0, nullptr); + + // 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 (int i = 0; i < computeDescriptorSets.size(); i++) + { + vkCmdBindDescriptorSets(computeCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, computePipelineLayout, 2, 1, + &computeDescriptorSets[i], 0, nullptr); + int groupCountX = int(ceil((NUM_BLADES + WORKGROUP_SIZE - 1) / WORKGROUP_SIZE)); + vkCmdDispatch(computeCommandBuffer, groupCountX, 1, 1); + } + + // ~ End recording ~ + if (vkEndCommandBuffer(computeCommandBuffer) != VK_SUCCESS) { + throw std::runtime_error("Failed to record compute command buffer"); + } } void Renderer::RecordCommandBuffers() { - commandBuffers.resize(swapChain->GetCount()); - - // Specify the command pool and number of buffers to allocate - VkCommandBufferAllocateInfo allocInfo = {}; - allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - allocInfo.commandPool = graphicsCommandPool; - allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - allocInfo.commandBufferCount = static_cast(commandBuffers.size()); - - 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++) { - 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) { - throw std::runtime_error("Failed to begin recording command buffer"); - } - - // Begin the render pass - VkRenderPassBeginInfo renderPassInfo = {}; - renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; - renderPassInfo.renderPass = renderPass; - renderPassInfo.framebuffer = framebuffers[i]; - 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 }; - 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) { - 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; - 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); - - // 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); - - vkCmdBeginRenderPass(commandBuffers[i], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); - - // Bind the graphics pipeline - vkCmdBindPipeline(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline); - - 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 }; - vkCmdBindVertexBuffers(commandBuffers[i], 0, 1, vertexBuffers, offsets); - - 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); - - // Draw - std::vector indices = scene->GetModels()[j]->getIndices(); - vkCmdDrawIndexed(commandBuffers[i], static_cast(indices.size()), 1, 0, 0, 0); - } - - // 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 }; - // 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 - - // Draw - // TODO: Uncomment this when the buffers are populated - // 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) { - throw std::runtime_error("Failed to record command buffer"); - } - } + commandBuffers.resize(swapChain->GetCount()); + + // Specify the command pool and number of buffers to allocate + VkCommandBufferAllocateInfo allocInfo = {}; + allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + allocInfo.commandPool = graphicsCommandPool; + allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + allocInfo.commandBufferCount = static_cast(commandBuffers.size()); + + 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++) { + 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) { + throw std::runtime_error("Failed to begin recording command buffer"); + } + + // Begin the render pass + VkRenderPassBeginInfo renderPassInfo = {}; + renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + renderPassInfo.renderPass = renderPass; + renderPassInfo.framebuffer = framebuffers[i]; + 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 }; + 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) { + 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; + 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); + + // 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); + + vkCmdBeginRenderPass(commandBuffers[i], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); + + // Bind the graphics pipeline + vkCmdBindPipeline(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline); + + 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 }; + vkCmdBindVertexBuffers(commandBuffers[i], 0, 1, vertexBuffers, offsets); + + 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); + + // Draw + std::vector indices = scene->GetModels()[j]->getIndices(); + vkCmdDrawIndexed(commandBuffers[i], static_cast(indices.size()), 1, 0, 0, 0); + } + + // 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 }; + // 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 + vkCmdBindDescriptorSets(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipelineLayout, 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)); + } + + // End render pass + vkCmdEndRenderPass(commandBuffers[i]); + + // ~ End recording ~ + if (vkEndCommandBuffer(commandBuffers[i]) != VK_SUCCESS) { + throw std::runtime_error("Failed to record command buffer"); + } + } } void Renderer::Frame() { - VkSubmitInfo computeSubmitInfo = {}; - computeSubmitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + VkSubmitInfo computeSubmitInfo = {}; + computeSubmitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - computeSubmitInfo.commandBufferCount = 1; - computeSubmitInfo.pCommandBuffers = &computeCommandBuffer; + computeSubmitInfo.commandBufferCount = 1; + computeSubmitInfo.pCommandBuffers = &computeCommandBuffer; - if (vkQueueSubmit(device->GetQueue(QueueFlags::Compute), 1, &computeSubmitInfo, VK_NULL_HANDLE) != VK_SUCCESS) { - throw std::runtime_error("Failed to submit draw command buffer"); - } + 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()) { - RecreateFrameResources(); - return; - } + if (!swapChain->Acquire()) { + RecreateFrameResources(); + return; + } - // Submit the command buffer - VkSubmitInfo submitInfo = {}; - submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + // Submit the command buffer + VkSubmitInfo submitInfo = {}; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - VkSemaphore waitSemaphores[] = { swapChain->GetImageAvailableVkSemaphore() }; - VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT }; - submitInfo.waitSemaphoreCount = 1; - submitInfo.pWaitSemaphores = waitSemaphores; - submitInfo.pWaitDstStageMask = waitStages; + VkSemaphore waitSemaphores[] = { swapChain->GetImageAvailableVkSemaphore() }; + VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT }; + submitInfo.waitSemaphoreCount = 1; + submitInfo.pWaitSemaphores = waitSemaphores; + submitInfo.pWaitDstStageMask = waitStages; - submitInfo.commandBufferCount = 1; - submitInfo.pCommandBuffers = &commandBuffers[swapChain->GetIndex()]; + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &commandBuffers[swapChain->GetIndex()]; - VkSemaphore signalSemaphores[] = { swapChain->GetRenderFinishedVkSemaphore() }; - submitInfo.signalSemaphoreCount = 1; - submitInfo.pSignalSemaphores = signalSemaphores; + VkSemaphore signalSemaphores[] = { swapChain->GetRenderFinishedVkSemaphore() }; + submitInfo.signalSemaphoreCount = 1; + submitInfo.pSignalSemaphores = signalSemaphores; - if (vkQueueSubmit(device->GetQueue(QueueFlags::Graphics), 1, &submitInfo, VK_NULL_HANDLE) != VK_SUCCESS) { - throw std::runtime_error("Failed to submit draw command buffer"); - } + if (vkQueueSubmit(device->GetQueue(QueueFlags::Graphics), 1, &submitInfo, VK_NULL_HANDLE) != VK_SUCCESS) { + throw std::runtime_error("Failed to submit draw command buffer"); + } - if (!swapChain->Present()) { - RecreateFrameResources(); - } + if (!swapChain->Present()) { + RecreateFrameResources(); + } } Renderer::~Renderer() { - vkDeviceWaitIdle(logicalDevice); + vkDeviceWaitIdle(logicalDevice); - // TODO: destroy any resources you created + // TODO: Destroy any resources you created + vkFreeCommandBuffers(logicalDevice, graphicsCommandPool, + static_cast(commandBuffers.size()), commandBuffers.data()); + vkFreeCommandBuffers(logicalDevice, computeCommandPool, 1, &computeCommandBuffer); - 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); + vkDestroyPipeline(logicalDevice, graphicsPipeline, nullptr); + vkDestroyPipeline(logicalDevice, grassPipeline, nullptr); + vkDestroyPipeline(logicalDevice, computePipeline, nullptr); - vkDestroyPipelineLayout(logicalDevice, graphicsPipelineLayout, nullptr); - vkDestroyPipelineLayout(logicalDevice, grassPipelineLayout, 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, cameraDescriptorSetLayout, nullptr); + vkDestroyDescriptorSetLayout(logicalDevice, modelDescriptorSetLayout, nullptr); + vkDestroyDescriptorSetLayout(logicalDevice, timeDescriptorSetLayout, nullptr); + vkDestroyDescriptorSetLayout(logicalDevice, computeDescriptorSetLayout, nullptr); - vkDestroyDescriptorPool(logicalDevice, descriptorPool, nullptr); + vkDestroyDescriptorPool(logicalDevice, descriptorPool, nullptr); - vkDestroyRenderPass(logicalDevice, renderPass, nullptr); - DestroyFrameResources(); - vkDestroyCommandPool(logicalDevice, computeCommandPool, nullptr); - vkDestroyCommandPool(logicalDevice, graphicsCommandPool, nullptr); -} + vkDestroyRenderPass(logicalDevice, renderPass, nullptr); + DestroyFrameResources(); + vkDestroyCommandPool(logicalDevice, computeCommandPool, nullptr); + vkDestroyCommandPool(logicalDevice, graphicsCommandPool, nullptr); +} \ No newline at end of file diff --git a/src/Renderer.h b/src/Renderer.h index 95e025f..d491f9f 100644 --- a/src/Renderer.h +++ b/src/Renderer.h @@ -7,76 +7,79 @@ class Renderer { public: - Renderer() = delete; - Renderer(Device* device, SwapChain* swapChain, Scene* scene, Camera* camera); - ~Renderer(); + Renderer() = delete; + Renderer(Device* device, SwapChain* swapChain, Scene* scene, Camera* camera); + ~Renderer(); - void CreateCommandPools(); + void CreateCommandPools(); - void CreateRenderPass(); + void CreateRenderPass(); - void CreateCameraDescriptorSetLayout(); - void CreateModelDescriptorSetLayout(); - void CreateTimeDescriptorSetLayout(); - void CreateComputeDescriptorSetLayout(); + void CreateCameraDescriptorSetLayout(); + void CreateModelDescriptorSetLayout(); + void CreateTimeDescriptorSetLayout(); + void CreateComputeDescriptorSetLayout(); - void CreateDescriptorPool(); + void CreateDescriptorPool(); - void CreateCameraDescriptorSet(); - void CreateModelDescriptorSets(); - void CreateGrassDescriptorSets(); - void CreateTimeDescriptorSet(); - void CreateComputeDescriptorSets(); + void CreateCameraDescriptorSet(); + void CreateModelDescriptorSets(); + void CreateGrassDescriptorSets(); + void CreateTimeDescriptorSet(); + void CreateComputeDescriptorSets(); - void CreateGraphicsPipeline(); - void CreateGrassPipeline(); - void CreateComputePipeline(); + void CreateGraphicsPipeline(); + void CreateGrassPipeline(); + void CreateComputePipeline(); - void CreateFrameResources(); - void DestroyFrameResources(); - void RecreateFrameResources(); + void CreateFrameResources(); + void DestroyFrameResources(); + void RecreateFrameResources(); - void RecordCommandBuffers(); - void RecordComputeCommandBuffer(); + void RecordCommandBuffers(); + void RecordComputeCommandBuffer(); - void Frame(); + void Frame(); private: - Device* device; - VkDevice logicalDevice; - SwapChain* swapChain; - Scene* scene; - Camera* camera; - - VkCommandPool graphicsCommandPool; - VkCommandPool computeCommandPool; - - VkRenderPass renderPass; - - VkDescriptorSetLayout cameraDescriptorSetLayout; - VkDescriptorSetLayout modelDescriptorSetLayout; - VkDescriptorSetLayout timeDescriptorSetLayout; - - VkDescriptorPool descriptorPool; - - VkDescriptorSet cameraDescriptorSet; - std::vector modelDescriptorSets; - VkDescriptorSet timeDescriptorSet; - - VkPipelineLayout graphicsPipelineLayout; - VkPipelineLayout grassPipelineLayout; - VkPipelineLayout computePipelineLayout; - - VkPipeline graphicsPipeline; - VkPipeline grassPipeline; - VkPipeline computePipeline; - - std::vector imageViews; - VkImage depthImage; - VkDeviceMemory depthImageMemory; - VkImageView depthImageView; - std::vector framebuffers; - - std::vector commandBuffers; - VkCommandBuffer computeCommandBuffer; -}; + Device* device; + VkDevice logicalDevice; + SwapChain* swapChain; + Scene* scene; + Camera* camera; + + VkCommandPool graphicsCommandPool; + VkCommandPool computeCommandPool; + + VkRenderPass renderPass; + + VkDescriptorSetLayout cameraDescriptorSetLayout; + VkDescriptorSetLayout modelDescriptorSetLayout; + VkDescriptorSetLayout timeDescriptorSetLayout; + VkDescriptorSetLayout computeDescriptorSetLayout; + + VkDescriptorPool descriptorPool; + + VkDescriptorSet cameraDescriptorSet; + std::vector modelDescriptorSets; + VkDescriptorSet timeDescriptorSet; + std::vector computeDescriptorSets; + std::vector grassDescriptorSets; + + VkPipelineLayout graphicsPipelineLayout; + VkPipelineLayout grassPipelineLayout; + VkPipelineLayout computePipelineLayout; + + VkPipeline graphicsPipeline; + VkPipeline grassPipeline; + VkPipeline computePipeline; + + std::vector imageViews; + VkImage depthImage; + VkDeviceMemory depthImageMemory; + VkImageView depthImageView; + std::vector framebuffers; + + std::vector commandBuffers; + VkCommandBuffer computeCommandBuffer; +}; \ No newline at end of file diff --git a/src/shaders/compute.comp b/src/shaders/compute.comp index 0fd0224..dda1fc4 100644 --- a/src/shaders/compute.comp +++ b/src/shaders/compute.comp @@ -2,6 +2,8 @@ #extension GL_ARB_separate_shader_objects : enable #define WORKGROUP_SIZE 32 + +#define GRAVITY 9.8 layout(local_size_x = WORKGROUP_SIZE, local_size_y = 1, local_size_z = 1) in; layout(set = 0, binding = 0) uniform CameraBufferObject { @@ -21,11 +23,26 @@ struct Blade { vec4 up; }; +// use sin function +float rand(vec2 co) { + return fract(sin(dot(co, vec2(12.9898, 78.233))) * 43758.5453); +} + // TODO: Add bindings to: // 1. Store the input blades // 2. Write out the culled blades // 3. Write the total number of blades remaining +// input +layout(set = 2, binding = 0) buffer InputBlades { + Blade inputBlades[]; +}; + +// output +layout(set = 2, binding = 1) buffer CulledBlades { + Blade culledBlades[]; +}; + // 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 // @@ -36,6 +53,13 @@ struct Blade { // uint firstInstance; // = 0 // } numBlades; +layout(set = 2, binding = 2) buffer NumBlades { + uint vertexCount; + uint instanceCount; + uint firstVertex; + uint firstInstance; +} numBlades; + bool inBounds(float value, float bounds) { return (value >= -bounds) && (value <= bounds); } @@ -48,9 +72,119 @@ void main() { barrier(); // Wait till all threads reach this point // TODO: Apply forces on every blade and update the vertices in the buffer - + uint index = gl_GlobalInvocationID.x; + Blade blade = inputBlades[index]; // 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 + // blade parameters + vec3 v0 = blade.v0.xyz; + vec3 v1 = blade.v1.xyz; + vec3 v2 = blade.v2.xyz; + vec3 up = blade.up.xyz; + + float angle = blade.v0.w; // orientation of the blade of grass; + // if this angle is zero the thin width portion (non-flat portion) of the grass is facing +ve x. + float height = blade.v1.w; + float width = blade.v2.w; + float stiffness = blade.up.w; + + vec3 side_direction = vec3(cos(angle), 0.0, sin(angle)); + vec3 front_direction = normalize(cross(up, side_direction)); + + // Apply Natural Forces on every blade + // Recovery Force + vec3 initial_v2 = v0 + up * height; + vec3 recovery = (initial_v2 - v2)*stiffness; + + // Gravity Force + vec3 gE = vec3(0.0, -GRAVITY, 0.0); //Environmental Gravity + vec3 gF = 0.25 * GRAVITY * front_direction; //Front Gravity + vec3 gravity = gE+gF; //Total Gravitational Force + + // Wind Force + vec3 windDirection = normalize(vec3(1, 1, 1)); // straight wave + + float windStrength = 10.0* rand(v0.xz) * cos(totalTime); + + float fd = 1.0 - abs(dot(windDirection, normalize(v2 - v0))); + float fr = dot(v2 - v0, up) / height; + float theta = fd * fr; + + vec3 wind = windStrength * windDirection * theta; + + // Resulting Translation due to forces over delta time + vec3 translation_dt = (recovery + wind + gravity) * deltaTime; + + v2 += translation_dt; + + // State Validation + // 1. v2 has to remain above the local plane + v2 -= up * min(dot(up, (v2-v0)), 0.0); + + // 2. grass blade always has a slight curvature + vec3 l_proj = abs( v2-v0 - up * dot((v2-v0), up) ); + v1 = v0 + height*up * max( (1.0 - l_proj)/height, 0.05*max((l_proj/height), 1.0) ); + + // 3. length of Bezier not larger than blade height + float L0 = distance(v0, v2); + float L1 = distance(v0, v1) + distance(v1, v2); + float n = 2.0; + float L = (2.0 * L0 + (n - 1.0) * L1) / (n + 1.0); + float r = height / L; + + // Corrected Values of v1 and v2 + vec3 v1_corrected = v0 + r*(v1-v0); + vec3 v2_corrected = v1_corrected + r*(v2-v1); + + // Update the input blades so the state propogates + inputBlades[index].v1.xyz = v1_corrected; + inputBlades[index].v2.xyz = v2_corrected; + + // Cull Blades + // 1. Orientation culling + mat4 inverseViewMat = inverse(camera.view); + vec3 eye_worldSpace = ( inverseViewMat * vec4(0.0,0.0,0.0,1.0) ).xyz; + vec3 viewDirection = eye_worldSpace - v0; + bool culled_Due_To_Orientaion = dot(viewDirection, front_direction) > 0.8; + + // 2. View-frustum culling + float tolerance = 3.0f; + bool culled_Due_To_Frustum = false; + + vec4 v0_NDC = camera.proj * camera.view * vec4(v0, 1.0); + culled_Due_To_Frustum = ( !inBounds(v0_NDC.x, v0_NDC.w + tolerance) || + !inBounds(v0_NDC.y, v0_NDC.w + tolerance) ); + + if (culled_Due_To_Frustum) + { + vec3 m = 0.25 * v0 + 0.5 * v1 + 0.25 * v2; + vec4 m_NDC = camera.proj * camera.view * vec4(m, 1.0); + culled_Due_To_Frustum = ( !inBounds(m_NDC.x, m_NDC.w + tolerance) || + !inBounds(m_NDC.y, m_NDC.w + tolerance) ); + } + + if (culled_Due_To_Frustum) + { + vec4 v2_NDC = camera.proj * camera.view * vec4(v2, 1.0); + culled_Due_To_Frustum = ( !inBounds(v2_NDC.x, v2_NDC.w + tolerance) || + !inBounds(v2_NDC.y, v2_NDC.w + tolerance) ); + } + + // 3. Distance culling + float projected_distance = length(v0 - eye_worldSpace - up * dot(up, (v0 - eye_worldSpace)) ); + float dmax = 40.0; + float numBuckets = 10.0; + bool culled_Due_To_Distance = mod(index, numBuckets) > floor(numBuckets * (1.0 - projected_distance/dmax) ); + + // Atomic operation to read and update numBlades.vertexCount is required because the compute shader is + // parallezied over the number of grass blades, ie two threads could try to update the numBlades.vertexCount + // at the same time. + // You want to write the visible blades to the buffer without write conflicts between threads + + if(!culled_Due_To_Distance && !culled_Due_To_Frustum && !culled_Due_To_Orientaion) // + { + culledBlades[atomicAdd(numBlades.vertexCount, 1)] = inputBlades[index]; + } } diff --git a/src/shaders/grass.frag b/src/shaders/grass.frag index c7df157..265a371 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 f_normal; +layout(location = 1) in vec3 f_pos_world; layout(location = 0) out vec4 outColor; void main() { - // TODO: Compute fragment color + // TODO: Compute fragment color + vec3 fragColor = vec3(0.0, 1.0, 0.0); + + vec3 darkGreen = vec3(0.0823, 0.3176, 0.0); + vec3 lightGreen = vec3(0.1647, 0.5882, 0.0); + vec3 color = mix(darkGreen, lightGreen, clamp(f_normal.w, 0.0, 1.0)); + + vec3 lightPos = vec3(10.0, 10.0, 10.0); + vec3 lightDir = lightPos - f_pos_world.xyz; + float lambert = clamp(abs(dot(normalize(f_normal.xyz), normalize(lightDir))), 0.0, 1.0); + float ambient = 0.2f; + fragColor = color*ambient + color; outColor = vec4(1.0); } diff --git a/src/shaders/grass.tesc b/src/shaders/grass.tesc index f9ffd07..0dfb583 100644 --- a/src/shaders/grass.tesc +++ b/src/shaders/grass.tesc @@ -9,13 +9,23 @@ layout(set = 0, binding = 0) uniform CameraBufferObject { } camera; // TODO: Declare tessellation control shader inputs and outputs +layout(location = 0) in vec4 v0_tesc[]; +layout(location = 1) in vec4 v1_tesc[]; +layout(location = 2) in vec4 v2_tesc[]; +layout(location = 3) in vec4 v3_tesc[]; + +layout(location = 0) out vec4 v0_tese[]; +layout(location = 1) out vec4 v1_tese[]; +layout(location = 2) out vec4 v2_tese[]; 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 - + v0_tese[gl_InvocationID] = v0_tesc[gl_InvocationID]; + v1_tese[gl_InvocationID] = v1_tesc[gl_InvocationID]; + v2_tese[gl_InvocationID] = v2_tesc[gl_InvocationID]; // TODO: Set level of tesselation // gl_TessLevelInner[0] = ??? // gl_TessLevelInner[1] = ??? @@ -23,4 +33,10 @@ void main() { // gl_TessLevelOuter[1] = ??? // gl_TessLevelOuter[2] = ??? // gl_TessLevelOuter[3] = ??? + gl_TessLevelInner[0] = 1; //horizontal + gl_TessLevelInner[1] = 5; //vertical + gl_TessLevelOuter[0] = 5; //edge 0-3 + gl_TessLevelOuter[1] = 1; //edge 3-2 + gl_TessLevelOuter[2] = 5; //edge 2-1 + gl_TessLevelOuter[3] = 1; //edge 1-0 } diff --git a/src/shaders/grass.tese b/src/shaders/grass.tese index 751fff6..c6ac783 100644 --- a/src/shaders/grass.tese +++ b/src/shaders/grass.tese @@ -9,10 +9,41 @@ layout(set = 0, binding = 0) uniform CameraBufferObject { } camera; // TODO: Declare tessellation evaluation shader inputs and outputs +layout(location = 0) in vec4 v0_tese[]; +layout(location = 1) in vec4 v1_tese[]; +layout(location = 2) in vec4 v2_tese[]; + +layout(location = 0) out vec4 f_normal; +layout(location = 1) out vec3 f_pos_world; 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 a = v0_tese[0].xyz + v * (v1_tese[0].xyz - v0_tese[0].xyz); + vec3 b = v1_tese[0].xyz + v * (v2_tese[0].xyz - v1_tese[0].xyz); + vec3 center = a + v * (b - a); + + float width = v2_tese[0].w; + float angle = v0_tese[0].w; + vec3 bitangent = vec3(cos(angle), 0.0, sin(angle)); + + float scaling = 1.2-v; + vec3 c0 = center - width*scaling * bitangent; + vec3 c1 = center + width*scaling * bitangent; + + vec3 tangent = normalize(b - a); + f_normal.xyz = normalize(bitangent); + f_normal.w = v; //for fragment shading + + //float t = u - u * v * v; // quadratic shape + //float t = u + 0.5*v -u*v; // triangular shape + float tao = 0.75; + float t = 0.5 + (u-0.5)*(1.0 - ( max(v-tao, 0.0) / (1.0-tao)) ); // triangle tip shape + + f_pos_world = mix(c0, c1, t); + + gl_Position = camera.proj * camera.view * vec4(f_pos_world, 1.0); } diff --git a/src/shaders/grass.vert b/src/shaders/grass.vert index db9dfe9..a46c6f1 100644 --- a/src/shaders/grass.vert +++ b/src/shaders/grass.vert @@ -7,6 +7,13 @@ layout(set = 1, binding = 0) uniform ModelBufferObject { }; // TODO: Declare vertex 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 = 0) out vec4 v0_tesc; +layout(location = 1) out vec4 v1_tesc; +layout(location = 2) out vec4 v2_tesc; out gl_PerVertex { vec4 gl_Position; @@ -14,4 +21,9 @@ out gl_PerVertex { void main() { // TODO: Write gl_Position and any other shader outputs + v0_tesc = v0_in; + v1_tesc = v1_in; + v2_tesc = v2_in; + + gl_Position = vec4(v0_in.x, v0_in.y, v0_in.z, 1.0); }