diff --git a/README.md b/README.md index a744a2e..4ad5c40 100644 --- a/README.md +++ b/README.md @@ -1,297 +1,36 @@ -Instructions - Vulkan Grass Rendering +University of Pennsylvania, CIS 565: GPU Programming and Architecture, Project 6 Vulkan Grass Rendering ======================== -This is due **Sunday 11/5, evening at midnight**. +* Ziyu Li +* Tested on: Windows 7, Intel Core i7-3840QM @2.80GHz 16GB, Nvidia Quadro K4000M 4GB -**Summary:** -In this project, you will use Vulkan to implement a grass simulator and renderer. You will -use compute shaders to perform physics calculations on Bezier curves that represent individual -grass blades in your application. Since rendering every grass blade on every frame will is fairly -inefficient, you will also use compute shaders to cull grass blades that don't contribute to a given frame. -The remaining blades will be passed to a graphics pipeline, in which you will write several shaders. -You will write a vertex shader to transform Bezier control points, tessellation shaders to dynamically create -the grass geometry from the Bezier curves, and a fragment shader to shade the grass blades. +## Features +#### All Features + - Basic Grass Render + - Gravity Force, Recovery Force, Wind Force + - Orientation Culling, View Frustum Culling, Distance Culling -The base code provided includes all of the basic Vulkan setup, including a compute pipeline that will run your compute -shaders and two graphics pipelines, one for rendering the geometry that grass will be placed on and the other for -rendering the grass itself. Your job will be to write the shaders for the grass graphics pipeline and the compute pipeline, -as well as binding any resources (descriptors) you may need to accomplish the tasks described in this assignment. +#### Summary +In this project, I use Vulkan to implement a grass simulator and renderer. I use compute shaders to perform physics calculations on Bezier curves that represent individual grass blades in my application. Since rendering every grass blade on every frame will is fairly inefficient, I also use compute shaders to cull grass blades that don't contribute to a given frame. The remaining blades will be passed to a graphics pipeline, in which I write several shaders. I write a vertex shader to transform Bezier control points, tessellation shaders to dynamically create the grass geometry from the Bezier curves, and a fragment shader to shade the grass blades. -![](img/grass.gif) -You are not required to use this base code if you don't want -to. You may also change any part of the base code as you please. -**This is YOUR project.** The above .gif is just a simple example that you -can use as a reference to compare to. +## Demo +#### Basic Grass Render -**Important:** -- If you are not in CGGT/DMD, you may replace this project with a GPU compute -project. You MUST get this pre-approved by Austin Eng before continuing! +![](img/grass_s.gif) -### Contents +#### Distance Culling -* `src/` C++/Vulkan source files. - * `shaders/` glsl shader source files - * `images/` images used as textures within graphics pipelines -* `external/` Includes and static libraries for 3rd party libraries. -* `img/` Screenshots and images to use in your READMEs +![](img/grass_sdist.gif) -### Installing Vulkan +## Performance Analysis +#### Benchmark -In order to run a Vulkan project, you first need to download and install the [Vulkan SDK](https://vulkan.lunarg.com/). -Make sure to run the downloaded installed as administrator so that the installer can set the appropriate environment -variables for you. +![](img/grass.PNG) -Once you have done this, you need to make sure your GPU driver supports Vulkan. Download and install a -[Vulkan driver](https://developer.nvidia.com/vulkan-driver) from NVIDIA's website. +![](img/dist.PNG) -Finally, to check that Vulkan is ready for use, go to your Vulkan SDK directory (`C:/VulkanSDK/` unless otherwise specified) -and run the `cube.exe` example within the `Bin` directory. IF you see a rotating gray cube with the LunarG logo, then you -are all set! +![](img/or.PNG) -### Running the code +![](img/view.PNG) -While developing your grass renderer, you will want to keep validation layers enabled so that error checking is turned on. -The project is set up such that when you are in `debug` mode, validation layers are enabled, and when you are in `release` mode, -validation layers are disabled. After building the code, you should be able to run the project without any errors. You will see -a plane with a grass texture on it to begin with. - -![](img/cube_demo.png) - -## Requirements - -**Ask on the mailing list for any clarifications.** - -In this project, you are given the following code: - -* The basic setup for a Vulkan project, including the swapchain, physical device, logical device, and the pipelines described above. -* Structs for some of the uniform buffers you will be using. -* Some buffer creation utility functions. -* A simple interactive camera using the mouse. - -You need to implement the following features/pipeline stages: - -* Compute shader (`shaders/compute.comp`) -* Grass pipeline stages - * Vertex shader (`shaders/grass.vert') - * Tessellation control shader (`shaders/grass.tesc`) - * Tessellation evaluation shader (`shaders/grass.tese`) - * Fragment shader (`shaders/grass.frag`) -* Binding of any extra descriptors you may need - -See below for more guidance. - -## Base Code Tour - -Areas that you need to complete are -marked with a `TODO` comment. Functions that are useful -for reference are marked with the comment `CHECKITOUT`. - -* `src/main.cpp` is the entry point of our application. -* `src/Instance.cpp` sets up the application state, initializes the Vulkan library, and contains functions that will create our -physical and logical device handles. -* `src/Device.cpp` manages the logical device and sets up the queues that our command buffers will be submitted to. -* `src/Renderer.cpp` contains most of the rendering implementation, including Vulkan setup and resource creation. You will -likely have to make changes to this file in order to support changes to your pipelines. -* `src/Camera.cpp` manages the camera state. -* `src/Model.cpp` manages the state of the model that grass will be created on. Currently a plane is hardcoded, but feel free to -update this with arbitrary model loading! -* `src/Blades.cpp` creates the control points corresponding to the grass blades. There are many parameters that you can play with -here that will change the behavior of your rendered grass blades. -* `src/Scene.cpp` manages the scene state, including the model, blades, and simualtion time. -* `src/BufferUtils.cpp` provides helper functions for creating buffers to be used as descriptors. - -We left out descriptions for a couple files that you likely won't have to modify. Feel free to investigate them to understand their -importance within the scope of the project. - -## Grass Rendering - -This project is an implementation of the paper, [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). -Please make sure to use this paper as a primary resource while implementing your grass renderers. It does a great job of explaining -the key algorithms and math you will be using. Below is a brief description of the different components in chronological order of how your renderer will -execute, but feel free to develop the components in whatever order you prefer. - -### Representing Grass as Bezier Curves - -In this project, grass blades will be represented as Bezier curves while performing physics calculations and culling operations. -Each Bezier curve has three control points. -* `v0`: the position of the grass blade on the geomtry -* `v1`: a Bezier curve guide that is always "above" `v0` with respect to the grass blade's up vector (explained soon) -* `v2`: a physical guide for which we simulate forces on - -We also need to store per-blade characteristics that will help us simulate and tessellate our grass blades correctly. -* `up`: the blade's up vector, which corresponds to the normal of the geometry that the grass blade resides on at `v0` -* Orientation: the orientation of the grass blade's face -* Height: the height of the grass blade -* Width: the width of the grass blade's face -* Stiffness coefficient: the stiffness of our grass blade, which will affect the force computations on our blade - -We can pack all this data into four `vec4`s, such that `v0.w` holds orientation, `v1.w` holds height, `v2.w` holds width, and -`up.w` holds the stiffness coefficient. - -![](img/blade_model.jpg) - -### Simulating Forces - -In this project, you will be simulating forces on grass blades while they are still Bezier curves. This will be done in a compute -shader using the compute pipeline that has been created for you. Remember that `v2` is our physical guide, so we will be -applying transformations to `v2` initially, then correcting for potential errors. We will finally update `v1` to maintain the appropriate -length of our grass blade. - -#### Binding Resources - -In order to update the state of your grass blades on every frame, you will need to create a storage buffer to maintain the grass data. -You will also need to pass information about how much time has passed in the simulation and the time since the last frame. To do this, -you can extend or create descriptor sets that will be bound to the compute pipeline. - -#### Gravity - -Given a gravity direction, `D.xyz`, and the magnitude of acceleration, `D.w`, we can compute the environmental gravity in -our scene as `gE = normalize(D.xyz) * D.w`. - -We then determine the contribution of the gravity with respect to the front facing direction of the blade, `f`, -as a term called the "front gravity". Front gravity is computed as `gF = (1/4) * ||gE|| * f`. - -We can then determine the total gravity on the grass blade as `g = gE + gF`. - -#### Recovery - -Recovery corresponds to the counter-force that brings our grass blade back into equilibrium. This is derived in the paper using Hooke's law. -In order to determine the recovery force, we need to compare the current position of `v2` to its original position before -simulation started, `iv2`. At the beginning of our simulation, `v1` and `v2` are initialized to be a distance of the blade height along the `up` vector. - -Once we have `iv2`, we can compute the recovery forces as `r = (iv2 - v2) * stiffness`. - -#### Wind - -In order to simulate wind, you are at liberty to create any wind function you want! In order to have something interesting, -you can make the function depend on the position of `v0` and a function that changes with time. Consider using some combination -of sine or cosine functions. - -Your wind function will determine a wind direction that is affecting the blade, but it is also worth noting that wind has a larger impact on -grass blades whose forward directions are parallel to the wind direction. The paper describes this as a "wind alignment" term. We won't go -over the exact math here, but use the paper as a reference when implementing this. It does a great job of explaining this! - -Once you have a wind direction and a wind alignment term, your total wind force (`w`) will be `windDirection * windAlignment`. - -#### Total force - -We can then determine a translation for `v2` based on the forces as `tv2 = (gravity + recovery + wind) * deltaTime`. However, we can't simply -apply this translation and expect the simulation to be robust. Our forces might push `v2` under the ground! Similarly, moving `v2` but leaving -`v1` in the same position will cause our grass blade to change length, which doesn't make sense. - -Read section 5.2 of the paper in order to learn how to determine the corrected final positions for `v1` and `v2`. - -### Culling tests - -Although we need to simulate forces on every grass blade at every frame, there are many blades that we won't need to render -due to a variety of reasons. Here are some heuristics we can use to cull blades that won't contribute positively to a given frame. - -#### 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. - -In order to remedy this, we can cull these blades! Simply do a dot product test to see if the view vector and front face direction of -the blade are perpendicular. The paper uses a threshold value of `0.9` to cull, but feel free to use what you think looks best. - -#### View-frustum culling - -We also want to cull blades that are outside of the view-frustum, considering they won't show up in the frame anyway. To determine if -a grass blade is in the view-frustum, we want to compare the visibility of three points: `v0, v2, and m`, where `m = (1/4)v0 * (1/2)v1 * (1/4)v2`. -Notice that we aren't using `v1` for the visibility test. This is because the `v1` is a Bezier guide that doesn't represent a position on the grass blade. -We instead use `m` to approximate the midpoint of our Bezier curve. - -If all three points are outside of the view-frustum, we will cull the grass blade. The paper uses a tolerance value for this test so that we are culling -blades a little more conservatively. This can help with cases in which the Bezier curve is technically not visible, but we might be able to see the blade -if we consider its width. - -#### Distance culling - -Similarly to orientation culling, we can end up with grass blades that at large distances are smaller than the size of a pixel. This could lead to additional -artifacts in our renders. In this case, we can cull grass blades as a function of their distance from the camera. - -You are free to define two parameters here. -* A max distance afterwhich all grass blades will be culled. -* A number of buckets to place grass blades between the camera and max distance into. - -Define a function such that the grass blades in the bucket closest to the camera are kept while an increasing number of grass blades -are culled with each farther bucket. - -#### Occlusion culling (extra credit) - -This type of culling only makes sense if our scene has additional objects aside from the plane and the grass blades. We want to cull grass blades that -are occluded by other geometry. Think about how you can use a depth map to accomplish this! - -### Tessellating Bezier curves into grass blades - -In this project, you should pass in each Bezier curve as a single patch to be processed by your grass graphics pipeline. You will tessellate this patch into -a quad with a shape of your choosing (as long as it looks sufficiently like grass of course). The paper has some examples of grass shapes you can use as inspiration. - -In the tessellation control shader, specify the amount of tessellation you want to occur. Remember that you need to provide enough detail to create the curvature of a grass blade. - -The generated vertices will be passed to the tessellation evaluation shader, where you will place the vertices in world space, respecting the width, height, and orientation information -of each blade. Once you have determined the world space position of each vector, make sure to set the output `gl_Position` in clip space! - -** Extra Credit**: Tessellate to varying levels of detail as a function of how far the grass blade is from the camera. For example, if the blade is very far, only generate four vertices in the tessellation control shader. - -To build more intuition on how tessellation works, I highly recommend playing with the [helloTessellation sample](https://github.com/CIS565-Fall-2017/Vulkan-Samples/tree/master/samples/5_helloTessellation) -and reading this [tutorial on tessellation](http://in2gpu.com/2014/07/12/tessellation-tutorial-opengl-4-3/). - -## Resources - -### Links - -The following resources may be useful for this project. - -* [Responsive Real-Time Grass Grass Rendering for General 3D Scenes](https://www.cg.tuwien.ac.at/research/publications/2017/JAHRMANN-2017-RRTG/JAHRMANN-2017-RRTG-draft.pdf) -* [CIS565 Vulkan samples](https://github.com/CIS565-Fall-2017/Vulkan-Samples) -* [Official Vulkan documentation](https://www.khronos.org/registry/vulkan/) -* [Vulkan tutorial](https://vulkan-tutorial.com/) -* [RenderDoc blog on Vulkan](https://renderdoc.org/vulkan-in-30-minutes.html) -* [Tessellation tutorial](http://in2gpu.com/2014/07/12/tessellation-tutorial-opengl-4-3/) - - -## Third-Party Code Policy - -* Use of any third-party code must be approved by asking on our Google Group. -* If it is approved, all students are welcome to use it. Generally, we approve - use of third-party code that is not a core part of the project. For example, - for the path tracer, we would approve using a third-party library for loading - models, but would not approve copying and pasting a CUDA function for doing - refraction. -* Third-party code **MUST** be credited in README.md. -* Using third-party code without its approval, including using another - student's code, is an academic integrity violation, and will, at minimum, - result in you receiving an F for the semester. - - -## README - -* A brief description of the project and the specific features you implemented. -* At least one screenshot of your project running. -* A performance analysis (described below). - -### Performance Analysis - -The performance analysis is where you will investigate how... -* Your renderer handles varying numbers of grass blades -* The improvement you get by culling using each of the three culling tests - -## Submit - -If you have modified any of the `CMakeLists.txt` files at all (aside from the -list of `SOURCE_FILES`), mentions it explicity. -Beware of any build issues discussed on the Google Group. - -Open a GitHub pull request so that we can see that you have finished. -The title should be "Project 6: YOUR NAME". -The template of the comment section of your pull request is attached below, you can do some copy and paste: - -* [Repo Link](https://link-to-your-repo) -* (Briefly) Mentions features that you've completed. Especially those bells and whistles you want to highlight - * Feature 0 - * Feature 1 - * ... -* Feedback on the project itself, if any. diff --git a/bin/Debug/vulkan_grass_rendering.exe b/bin/Debug/vulkan_grass_rendering.exe new file mode 100644 index 0000000..1d83082 Binary files /dev/null and b/bin/Debug/vulkan_grass_rendering.exe differ diff --git a/bin/Debug/vulkan_grass_rendering.ilk b/bin/Debug/vulkan_grass_rendering.ilk new file mode 100644 index 0000000..f5dc0ec Binary files /dev/null and b/bin/Debug/vulkan_grass_rendering.ilk differ diff --git a/bin/Debug/vulkan_grass_rendering.pdb b/bin/Debug/vulkan_grass_rendering.pdb new file mode 100644 index 0000000..72e243a Binary files /dev/null and b/bin/Debug/vulkan_grass_rendering.pdb differ diff --git a/bin/Release/vulkan_grass_rendering.exe b/bin/Release/vulkan_grass_rendering.exe new file mode 100644 index 0000000..75d9989 Binary files /dev/null and b/bin/Release/vulkan_grass_rendering.exe differ diff --git a/img/blade_model.jpg b/img/blade_model.jpg deleted file mode 100644 index b7a0325..0000000 Binary files a/img/blade_model.jpg and /dev/null differ diff --git a/img/cube_demo.png b/img/cube_demo.png deleted file mode 100644 index 21e928e..0000000 Binary files a/img/cube_demo.png and /dev/null differ diff --git a/img/dist.PNG b/img/dist.PNG new file mode 100644 index 0000000..0b61703 Binary files /dev/null and b/img/dist.PNG differ diff --git a/img/grass.PNG b/img/grass.PNG new file mode 100644 index 0000000..73a1f76 Binary files /dev/null and b/img/grass.PNG differ diff --git a/img/grass.gif b/img/grass.gif deleted file mode 100644 index 78f008e..0000000 Binary files a/img/grass.gif and /dev/null differ diff --git a/img/grass_s.gif b/img/grass_s.gif new file mode 100644 index 0000000..70e6c0c Binary files /dev/null and b/img/grass_s.gif differ diff --git a/img/grass_sdist.gif b/img/grass_sdist.gif new file mode 100644 index 0000000..610ab6f Binary files /dev/null and b/img/grass_sdist.gif differ diff --git a/img/or.PNG b/img/or.PNG new file mode 100644 index 0000000..8408b39 Binary files /dev/null and b/img/or.PNG differ diff --git a/img/view.PNG b/img/view.PNG new file mode 100644 index 0000000..d1b7686 Binary files /dev/null and b/img/view.PNG differ diff --git a/src/Blades.h b/src/Blades.h index 9bd1eed..914dc1d 100644 --- a/src/Blades.h +++ b/src/Blades.h @@ -4,7 +4,7 @@ #include #include "Model.h" -constexpr static unsigned int NUM_BLADES = 1 << 13; +constexpr static unsigned int NUM_BLADES = 1 << 14; constexpr static float MIN_HEIGHT = 1.3f; constexpr static float MAX_HEIGHT = 2.5f; constexpr static float MIN_WIDTH = 0.1f; diff --git a/src/Renderer.cpp b/src/Renderer.cpp index b445d04..fa902d3 100644 --- a/src/Renderer.cpp +++ b/src/Renderer.cpp @@ -9,17 +9,21 @@ 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) { + : device(device), + logicalDevice(device->GetVkDevice()), + swapChain(swapChain), + scene(scene), + camera(camera) { CreateCommandPools(); CreateRenderPass(); + CreateCameraDescriptorSetLayout(); CreateModelDescriptorSetLayout(); CreateTimeDescriptorSetLayout(); + + CreateGrassDescriptorSetLayout(); // Grass Layout + CreateComputeDescriptorSetLayout(); CreateDescriptorPool(); CreateCameraDescriptorSet(); @@ -36,1032 +40,1208 @@ Renderer::Renderer(Device* device, SwapChain* swapChain, Scene* scene, Camera* c } 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::CreateGrassDescriptorSetLayout() { + VkDescriptorSetLayoutBinding uboLayoutBinding = {}; + uboLayoutBinding.binding = 0; + uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + uboLayoutBinding.descriptorCount = 1; + uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; + uboLayoutBinding.pImmutableSamplers = nullptr; + + VkDescriptorSetLayoutBinding depthLayoutBinding = {}; + depthLayoutBinding.binding = 1; + depthLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + depthLayoutBinding.descriptorCount = 1; + depthLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + depthLayoutBinding.pImmutableSamplers = nullptr; + + std::vector bindings = { uboLayoutBinding, depthLayoutBinding }; + + // Create the descriptor set layout + VkDescriptorSetLayoutCreateInfo layoutInfo = {}; + layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + layoutInfo.bindingCount = static_cast(bindings.size()); + layoutInfo.pBindings = bindings.data(); + + if (vkCreateDescriptorSetLayout(logicalDevice, &layoutInfo, nullptr, &grassDescriptorSetLayout) != VK_SUCCESS) { + throw std::runtime_error("Failed to create descriptor set layout"); + } } void Renderer::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 + + + VkDescriptorSetLayoutBinding bladesBinding = {}; + bladesBinding.binding = 0; + bladesBinding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + bladesBinding.descriptorCount = 1; + bladesBinding.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + bladesBinding.pImmutableSamplers = nullptr; + + VkDescriptorSetLayoutBinding culledBladesBinding = {}; + culledBladesBinding.binding = 1; + culledBladesBinding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + culledBladesBinding.descriptorCount = 1; + culledBladesBinding.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + culledBladesBinding.pImmutableSamplers = nullptr; + + VkDescriptorSetLayoutBinding numBladesBinding = {}; + numBladesBinding.binding = 2; + numBladesBinding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + numBladesBinding.descriptorCount = 1; + numBladesBinding.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + numBladesBinding.pImmutableSamplers = nullptr; + + std::vector bindings = { bladesBinding, culledBladesBinding, numBladesBinding }; + + 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 compute 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}, + // 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_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()) }, + // Models + Blades + { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER , static_cast(scene->GetModels().size() + scene->GetBlades().size()) }, - // Time (compute) - { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER , 1 }, + // Time (compute) + { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER , 1 }, - // TODO: Add any additional types and counts of descriptors you will need to allocate - }; + // Blades + { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER , 3 * (uint32_t)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; + 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"); - } + 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 + int nBlades = scene->GetBlades().size(); + grassDescriptorSets.resize(nBlades); + + VkDescriptorSetLayout layouts[] = { grassDescriptorSetLayout }; + VkDescriptorSetAllocateInfo allocInfo = {}; + allocInfo.descriptorPool = descriptorPool; + allocInfo.descriptorSetCount = static_cast(grassDescriptorSets.size()); + allocInfo.pSetLayouts = layouts; + allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + + if (vkAllocateDescriptorSets(logicalDevice, &allocInfo, grassDescriptorSets.data()) != VK_SUCCESS) { + throw std::runtime_error("Failed to allocate grass descriptor set"); + } + + std::vector descriptorWrites(grassDescriptorSets.size()); + + for (uint32_t i = 0; i < nBlades; ++i) { + + VkDescriptorBufferInfo modelBufferInfo = {}; + VkDescriptorImageInfo zInfo = {}; + + modelBufferInfo.buffer = scene->GetBlades()[i]->GetModelBuffer(); + modelBufferInfo.offset = 0; + modelBufferInfo.range = sizeof(ModelBufferObject); + //zInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + //zInfo.imageView = depthImageView; + + descriptorWrites[i].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrites[i].dstSet = grassDescriptorSets[i]; + descriptorWrites[i].dstBinding = 0; + descriptorWrites[i].dstArrayElement = 0; + descriptorWrites[i].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + descriptorWrites[i].descriptorCount = 1; + descriptorWrites[i].pBufferInfo = &modelBufferInfo; + descriptorWrites[i].pImageInfo = nullptr; + descriptorWrites[i].pTexelBufferView = nullptr; + } + + // Update descriptor sets + vkUpdateDescriptorSets(logicalDevice, static_cast(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr); } void Renderer::CreateTimeDescriptorSet() { - // 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 + int nBlades = scene->GetBlades().size(); + computeDescriptorSets.resize(nBlades); + + VkDescriptorSetLayout layouts[] = { computeDescriptorSetLayout }; + VkDescriptorSetAllocateInfo allocInfo = {}; + allocInfo.descriptorPool = descriptorPool; + allocInfo.descriptorSetCount = computeDescriptorSets.size(); + allocInfo.pSetLayouts = layouts; + allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + + if (vkAllocateDescriptorSets(logicalDevice, &allocInfo, computeDescriptorSets.data()) != VK_SUCCESS) { + throw std::runtime_error("Failed to allocate compute descriptor set"); + } + + std::vector descriptorWrites(3 * computeDescriptorSets.size()); + + for (uint32_t i = 0; i < nBlades; ++i) { + VkDescriptorBufferInfo bladesInfo = {}; + VkDescriptorBufferInfo culledBaldesInfo = {}; + VkDescriptorBufferInfo numBaldesInfo = {}; + + bladesInfo.buffer = scene->GetBlades()[i]->GetBladesBuffer(); + bladesInfo.offset = 0; + bladesInfo.range = NUM_BLADES * sizeof(Blade); + + culledBaldesInfo.buffer = scene->GetBlades()[i]->GetCulledBladesBuffer(); + culledBaldesInfo.offset = 0; + culledBaldesInfo.range = NUM_BLADES * sizeof(Blade); + + numBaldesInfo.buffer = scene->GetBlades()[i]->GetNumBladesBuffer(); + numBaldesInfo.offset = 0; + numBaldesInfo.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 = &bladesInfo; + 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 = &culledBaldesInfo; + 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 = &numBaldesInfo; + descriptorWrites[3 * i + 2].pImageInfo = nullptr; + descriptorWrites[3 * i + 2].pTexelBufferView = nullptr; + } + + 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.polygonMode = VK_POLYGON_MODE_LINE; + 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.polygonMode = VK_POLYGON_MODE_LINE; + 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"); + } + } + + 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 (size_t i = 0; i < scene->GetBlades().size(); i++) + { + vkCmdBindDescriptorSets(computeCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, computePipelineLayout, 2, 1, &computeDescriptorSets[i], 0, nullptr); + } + + vkCmdDispatch(computeCommandBuffer, (int)ceil(NUM_BLADES / WORKGROUP_SIZE), 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, grassPipelineLayout, 1, 1, &grassDescriptorSets[j], 0, nullptr); + + // Draw + // TODO: Uncomment this when the buffers are populated + vkCmdDrawIndirect(commandBuffers[i], scene->GetBlades()[j]->GetNumBladesBuffer(), 0, 1, sizeof(BladeDrawIndirect)); + } + + // 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); - - vkDestroyPipeline(logicalDevice, graphicsPipeline, nullptr); - vkDestroyPipeline(logicalDevice, grassPipeline, nullptr); - vkDestroyPipeline(logicalDevice, computePipeline, nullptr); + vkFreeCommandBuffers(logicalDevice, graphicsCommandPool, static_cast(commandBuffers.size()), commandBuffers.data()); + vkFreeCommandBuffers(logicalDevice, computeCommandPool, 1, &computeCommandBuffer); - vkDestroyPipelineLayout(logicalDevice, graphicsPipelineLayout, nullptr); - vkDestroyPipelineLayout(logicalDevice, grassPipelineLayout, nullptr); - vkDestroyPipelineLayout(logicalDevice, computePipelineLayout, nullptr); + vkDestroyPipeline(logicalDevice, graphicsPipeline, nullptr); + vkDestroyPipeline(logicalDevice, grassPipeline, nullptr); + vkDestroyPipeline(logicalDevice, computePipeline, nullptr); - vkDestroyDescriptorSetLayout(logicalDevice, cameraDescriptorSetLayout, nullptr); - vkDestroyDescriptorSetLayout(logicalDevice, modelDescriptorSetLayout, nullptr); - vkDestroyDescriptorSetLayout(logicalDevice, timeDescriptorSetLayout, nullptr); + vkDestroyPipelineLayout(logicalDevice, graphicsPipelineLayout, nullptr); + vkDestroyPipelineLayout(logicalDevice, grassPipelineLayout, nullptr); + vkDestroyPipelineLayout(logicalDevice, computePipelineLayout, nullptr); - vkDestroyDescriptorPool(logicalDevice, descriptorPool, nullptr); + vkDestroyDescriptorSetLayout(logicalDevice, cameraDescriptorSetLayout, nullptr); + vkDestroyDescriptorSetLayout(logicalDevice, grassDescriptorSetLayout, nullptr); + vkDestroyDescriptorSetLayout(logicalDevice, modelDescriptorSetLayout, nullptr); + vkDestroyDescriptorSetLayout(logicalDevice, timeDescriptorSetLayout, nullptr); - vkDestroyRenderPass(logicalDevice, renderPass, nullptr); - DestroyFrameResources(); - vkDestroyCommandPool(logicalDevice, computeCommandPool, nullptr); - vkDestroyCommandPool(logicalDevice, graphicsCommandPool, nullptr); -} + vkDestroyDescriptorSetLayout(logicalDevice, computeDescriptorSetLayout, nullptr); + + + vkDestroyDescriptorPool(logicalDevice, descriptorPool, 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..b7a2375 100644 --- a/src/Renderer.h +++ b/src/Renderer.h @@ -19,6 +19,7 @@ class Renderer { void CreateModelDescriptorSetLayout(); void CreateTimeDescriptorSetLayout(); void CreateComputeDescriptorSetLayout(); + void CreateGrassDescriptorSetLayout(); void CreateDescriptorPool(); @@ -56,12 +57,17 @@ class Renderer { VkDescriptorSetLayout cameraDescriptorSetLayout; VkDescriptorSetLayout modelDescriptorSetLayout; VkDescriptorSetLayout timeDescriptorSetLayout; + VkDescriptorSetLayout grassDescriptorSetLayout; + VkDescriptorSetLayout computeDescriptorSetLayout; VkDescriptorPool descriptorPool; VkDescriptorSet cameraDescriptorSet; std::vector modelDescriptorSets; VkDescriptorSet timeDescriptorSet; + std::vector grassDescriptorSets; + std::vector computeDescriptorSets; + VkPipelineLayout graphicsPipelineLayout; VkPipelineLayout grassPipelineLayout; @@ -71,6 +77,8 @@ class Renderer { VkPipeline grassPipeline; VkPipeline computePipeline; + VkFormat depthFormat; + std::vector imageViews; VkImage depthImage; VkDeviceMemory depthImageMemory; diff --git a/src/shaders/compute.comp b/src/shaders/compute.comp index 0fd0224..aee7579 100644 --- a/src/shaders/compute.comp +++ b/src/shaders/compute.comp @@ -21,6 +21,7 @@ struct Blade { vec4 up; }; + // TODO: Add bindings to: // 1. Store the input blades // 2. Write out the culled blades @@ -28,29 +29,139 @@ struct Blade { // The project is using vkCmdDrawIndirect to use a buffer as the arguments for a draw call // This is sort of an advanced feature so we've showed you what this buffer should look like -// -// layout(set = ???, binding = ???) buffer NumBlades { -// uint vertexCount; // Write the number of blades remaining here -// uint instanceCount; // = 1 -// uint firstVertex; // = 0 -// uint firstInstance; // = 0 -// } numBlades; + +layout(set = 2, binding = 2) buffer NumBlades { + uint vertexCount; // Write the number of blades remaining here + uint instanceCount; // = 1 + uint firstVertex; // = 0 + uint firstInstance; // = 0 +} numBlades; + +//input +layout(set = 2, binding = 0) buffer Blades { + Blade blades[]; +}; + +//output +layout(set = 2, binding = 1) buffer CulledBlades { + Blade culledBlades[]; +}; bool inBounds(float value, float bounds) { return (value >= -bounds) && (value <= bounds); } +bool inBound(float value, float a, float b) { + return (value >= a) && (value <= b); +} + +vec3 fp(vec3 p0, vec3 p1, vec3 p2, float v) +{ + return p0 + v*(p1 - p0) + v*(p1 + v*(p2 - p1) - p0 + v*(p1 - p0)); +} + + void main() { - // Reset the number of blades to 0 if (gl_GlobalInvocationID.x == 0) { - // numBlades.vertexCount = 0; + numBlades.vertexCount = 0; } - barrier(); // Wait till all threads reach this point + barrier(); + + uint idx = gl_GlobalInvocationID.x; + + Blade b = blades[idx]; + vec3 up = b.up.xyz; + vec3 p0 = b.v0.xyz; vec3 p1 = b.v1.xyz; vec3 p2 = b.v2.xyz; + vec3 face = normalize(cross(up, vec3(sin(b.v0.w), 0, cos(b.v0.w)))); + + + vec3 g = vec3(0, -9.81, 0); + vec3 gravityForce = face * 0.25 * g + g; + + float h = b.v1.w; + vec3 iv2 = p0 + up * h; + float maxc = 1.8/ min(h, 1.8); + vec3 recoveryForce = (iv2 - p2) * b.up.w * maxc; + + + vec3 windDirection = normalize(vec3(0.5, 0, 0.5)); + //vec3 windDirection = -normalize(p0.xyz); + float m = dot(windDirection, vec3(p0.x, 0.0, p0.z)); + float t = totalTime*3.0; + float s = cos((m + t) / 6.0); + float dirAlign = 1.0 - abs(dot(windDirection ,normalize(p2 - p0))); + vec3 windForce = windDirection * dirAlign * dot(up, p2 - p0) * s * 4.0 / h; + + + vec3 TotalForce = gravityForce + recoveryForce + windForce; + vec3 dF = TotalForce * deltaTime; + vec3 v2 = p2 + dF - min(dot(up, p2 + dF - p0), 0.0) * up; + float lproj_h = length(v2 - p0 - up * dot(v2 - p0, up)) / h; + vec3 v1 = h * up * max(1.0 - lproj_h , max(lproj_h, 1.0) / 20.0) + p0; + + + float _n = 3; + float L0 = length(v2 - p0); + float L1 = length(v1 - p0) + length(v2 - v1); + float L = (L0 * 2.0 + (_n - 1.0) * L1) / (1.0 + _n); + float r = h / L; + b.v1.xyz = p0 + r*(v1 - p0); + b.v2.xyz = b.v1.xyz + r*(v2 - v1); + blades[idx] = b; + + + // Culling + mat4 invView = inverse(camera.view); + mat4 viewProj = camera.proj * camera.view; + vec3 viewVectorWorld = (invView * vec4(0,0,1,0)).xyz; + p0 = b.v0.xyz; p1 = b.v1.xyz; p2 = b.v2.xyz; + + + vec4 projPosV0= viewProj * vec4(p0, 1.0); + vec4 projPosV2= viewProj * vec4(p2, 1.0); + projPosV0 /= projPosV0.w; + projPosV2 /= projPosV2.w; + + vec4 projCenter1 = viewProj * vec4(fp(p0, p1, p2, 0.25), 1.0); + vec4 projCenter3 = viewProj * vec4(fp(p0, p1, p2, 0.75), 1.0); + vec4 projCenter2 = viewProj * vec4(0.25*p0 * 0.5*p1 * 0.25*p2, 1.0); + projCenter1 /= projCenter1.w; + projCenter2 /= projCenter2.w; + projCenter3 /= projCenter3.w; + + float clipVal = 1.3; + float near = 0.1; + float far = 100.0; + + bool mV0x = inBounds(projPosV0.x, clipVal); + bool mV0y = inBounds(projPosV0.y, clipVal); + bool mV0z = inBound(projPosV0.z, 0.0, 1.0); + bool mV0 = mV0x && mV0y && mV0z; + + bool mV2x = inBounds(projPosV2.x, clipVal); + bool mV2y = inBounds(projPosV2.y, clipVal); + bool mV2z = inBound(projPosV2.z, 0.0, 1.0); + bool mV2 = mV2x && mV2y && mV2z; + + bool mP1x = inBounds(projCenter1.x, clipVal); + bool mP1y = inBounds(projCenter1.y, clipVal); + bool mP1z = inBound(projCenter1.z, 0.0, 1.0); + bool mP1 = mP1x && mP1y && mP1z; + + bool mP2x = inBounds(projCenter2.x, clipVal); + bool mP2y = inBounds(projCenter2.y, clipVal); + bool mP2z = inBound(projCenter2.z, 0.0, 1.0); + bool mP2 = mP2x && mP2y && mP2z; + + bool mP3x = inBounds(projCenter3.x, clipVal); + bool mP3y = inBounds(projCenter3.y, clipVal); + bool mP3z = inBound(projCenter3.z, 0.0, 1.0); + bool mP3 = mP3x && mP3y && mP3z; - // TODO: Apply forces on every blade and update the vertices in the buffer + bool culledByFrustum = !(mV0 && mV2 && mP1 && mP2 && mP3); + bool culledByOrientation = abs(dot(normalize(vec3(viewVectorWorld.x, 0.0, viewVectorWorld.z)), face)) < 0.11; + bool culledByDistance = ((2.0 * near) / (far + near - projPosV0.z * (far - near))) > 0.95; - // 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 + if(!culledByOrientation && !culledByFrustum && !culledByDistance) culledBlades[atomicAdd(numBlades.vertexCount , 1)] = b; } + \ No newline at end of file diff --git a/src/shaders/grass.frag b/src/shaders/grass.frag index c7df157..db546cb 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 Position; +layout(location = 1) in vec3 Normal; +layout(location = 2) in vec3 Wind; +layout(location = 3) in vec2 UV; layout(location = 0) out vec4 outColor; void main() { - // TODO: Compute fragment color + vec3 upper = vec3(151.0 / 255.0, 250.0 / 255.0, 86.0 / 255.0); + vec3 lower = vec3(124.0 / 255.0, 214.0 / 255.0, 68.0 / 255.0); + vec3 sun = normalize(vec3(0.0, 5.0, -3.0)); - outColor = vec4(1.0); + vec3 upDark = vec3(105.0 / 255.0, 191.0 / 255.0, 51.0 / 255.0); + vec3 lowDark= vec3(89.0 / 255.0, 168.0 / 255.0, 40.0 / 255.0); + + float NoL = clamp(dot(Normal, sun), 0.3, 1.0); + vec3 mix = mix(lower, upper, UV.y); + + outColor = vec4(mix * NoL, 1.0); } diff --git a/src/shaders/grass.tesc b/src/shaders/grass.tesc index f9ffd07..856f8ce 100644 --- a/src/shaders/grass.tesc +++ b/src/shaders/grass.tesc @@ -9,18 +9,47 @@ layout(set = 0, binding = 0) uniform CameraBufferObject { } camera; // TODO: Declare tessellation control shader inputs and outputs +layout(location = 0) in vec4 tv1[]; +layout(location = 1) in vec4 tv2[]; +layout(location = 2) in vec4 tup[]; +layout(location = 3) in vec4 twd[]; + +layout(location = 0) patch out vec4 _tv1; +layout(location = 1) patch out vec4 _tv2; +layout(location = 2) patch out vec4 _tup; +layout(location = 3) patch out vec4 _twd; + 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 + _tv1 = tv1[0]; + _tv2 = tv2[0]; + _tup = tup[0]; + _twd = twd[0]; + + float near = 0.1; + float far = 25.0; + + float depth = -(camera.view * gl_in[gl_InvocationID].gl_Position).z / (far - near); + depth = clamp(depth, 0.0, 1.0); + + float maxLevel = 5.0; + float minLevel = 1.0; + + depth = depth * depth; + + float level = mix(maxLevel, minLevel, depth); + _twd.w = depth; + // TODO: Set level of tesselation - // gl_TessLevelInner[0] = ??? - // gl_TessLevelInner[1] = ??? - // gl_TessLevelOuter[0] = ??? - // gl_TessLevelOuter[1] = ??? - // gl_TessLevelOuter[2] = ??? - // gl_TessLevelOuter[3] = ??? + gl_TessLevelInner[0] = 1.0; + gl_TessLevelInner[1] = level; + gl_TessLevelOuter[0] = level; + gl_TessLevelOuter[1] = 1.0; + gl_TessLevelOuter[2] = level; + gl_TessLevelOuter[3] = 1.0; } diff --git a/src/shaders/grass.tese b/src/shaders/grass.tese index 751fff6..80bf272 100644 --- a/src/shaders/grass.tese +++ b/src/shaders/grass.tese @@ -10,9 +10,41 @@ layout(set = 0, binding = 0) uniform CameraBufferObject { // TODO: Declare tessellation evaluation shader inputs and outputs +layout(location = 0) patch in vec4 tv1; +layout(location = 1) patch in vec4 tv2; +layout(location = 2) patch in vec4 tup; +layout(location = 3) patch in vec4 twd; + +layout(location = 0) out vec4 Position; +layout(location = 1) out vec3 Normal; +layout(location = 2) out vec3 Wind; +layout(location = 3) out vec2 UV; + void main() { float u = gl_TessCoord.x; float v = gl_TessCoord.y; + mat4 viewProj = camera.proj * camera.view; + vec3 pos = gl_in[0].gl_Position.xyz; + + vec3 t0 = pos + v * (tv1.xyz - pos); + vec3 t1 = tv1.xyz + v * (tv2.xyz - tv1.xyz); + vec3 t2 = t0 + v * (t1 - t0); + + vec3 wbt = 0.5 * twd.xyz * tv2.w; + + float t = 0.5 * v + u - u * v; + vec4 pos_t = vec4(t * (t2 + wbt) + (1.0 - t) * (t2 - wbt), 1.0); + + + Position = viewProj * pos_t; + UV = vec2(u, v); + Normal = normalize(cross(twd.xyz, normalize(t1 - t0))); + Wind = twd.xyz; + + gl_Position = Position; + + Position.w = twd.w; + // TODO: Use u and v to parameterize along the grass blade and output positions for each vertex of the grass blade } diff --git a/src/shaders/grass.vert b/src/shaders/grass.vert index db9dfe9..7f7e949 100644 --- a/src/shaders/grass.vert +++ b/src/shaders/grass.vert @@ -7,6 +7,16 @@ layout(set = 1, binding = 0) uniform ModelBufferObject { }; // TODO: Declare vertex shader inputs and outputs +layout(location = 0) in vec4 v0; +layout(location = 1) in vec4 v1; +layout(location = 2) in vec4 v2; +layout(location = 3) in vec4 up; + +layout(location = 0) out vec4 tv1; +layout(location = 1) out vec4 tv2; +layout(location = 2) out vec4 tup; +layout(location = 3) out vec4 twd; + out gl_PerVertex { vec4 gl_Position; @@ -14,4 +24,17 @@ out gl_PerVertex { void main() { // TODO: Write gl_Position and any other shader outputs + + vec4 _v0 = model * vec4(v0.xyz, 1.0); + vec4 _v1 = model * vec4(v1.xyz, 1.0); + vec4 _v2 = model * vec4(v2.xyz, 1.0); + + tv1 = vec4(_v1.xyz, v1.w); + tv2 = vec4(_v2.xyz, v2.w); + tup = vec4(normalize(up.xyz), tup.w); + + vec3 dir = normalize(cross(tup.xyz, vec3(sin(v0.w), 0, cos(v0.w)))); + twd = vec4(normalize(cross(dir, tup.xyz)), v1.w * 0.4); + + gl_Position = _v0; }