diff --git a/INSTRUCTION.md b/INSTRUCTION.md new file mode 100644 index 0000000..fc0e545 --- /dev/null +++ b/INSTRUCTION.md @@ -0,0 +1,300 @@ +Instructions - Vulkan Grass Rendering +======================== + +This is due **Sunday 11/4, evening at midnight**. + +**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. + +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. + +![](img/grass.gif) ![](img/grass2.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 .gifs are just examples that you +can use as a reference to compare to. Feel free to get creative with your implementations! + +**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 Ottavio before continuing! + +### Contents + +* `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 + +### Installing Vulkan + +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. + +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. + +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! + +### Running the code + +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. + +We recommend starting with trying to display the grass blades without any forces on them before trying to add any forces on the blades themselves. Here is an example of what that may look like: + +![](img/grass_basic.gif) + +### 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-2018/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-2018/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. +* GIFs of your project in its different stages with the different features being added incrementally. +* A performance analysis (described below). + +### Performance Analysis + +The performance analysis is where you will investigate how... +* Your renderer handles varying numbers of grass blades +* The improvement you get by culling using each of the three culling tests + +## 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/README.md b/README.md index fc0e545..a861653 100644 --- a/README.md +++ b/README.md @@ -1,246 +1,71 @@ -Instructions - Vulkan Grass Rendering -======================== +Vulkan Grass Rendering +====================== -This is due **Sunday 11/4, evening at midnight**. +## Demo rendering -**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. +![](img/show.gif) -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. +## Introduction: -![](img/grass.gif) ![](img/grass2.gif) +This project is based on 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) -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 .gifs are just examples that you -can use as a reference to compare to. Feel free to get creative with your implementations! +By using vulkan -- a high-performance realtime 3D graphics API, we can create scenes consist of billions of primitive objects, which makes vulkan a rather suitable API for +realtime grass rendering. According to the paper, I created a compute shader for simulation of various grass physical features like gravity, stiffness, as well as wind force, +and also the validation of grass, including 3 culling operation to enhance performace,a tessellation shader for the geometry detail of a single grass blade, which makes it +tessellated based on a bezier curve, finally, inside the renderer.cpp, I completed the vulkan rendering pipeline. -**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 Ottavio before continuing! +## grass culling: -### 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 +when grass blades are too far away, we will face two problems : +- due to the lower precision of depth values in the distance, zfighting can occur +- blades at high distances are smaller than a pixel, which can cause aliasing artifacts -### Installing Vulkan +besides, sometimes people won't care so much when things are too far away . -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/grassdiscull.gif) -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. +### orientation culling: -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! +as there are no such property as thickness for a grass blade, we can therefore ignore it when it's not facing directly +to the camera, again, if we don't do this aliasing artifacts might occur. -### Running the code +![](img/orientcull.gif) -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. +### view frustum culling: -![](img/cube_demo.png) +we also want to check if a grass blade is inside the view frustum or not, we will have to use three points : v0, a middle point and v2 +, transfrom their coord into NDc space and then compare the boudning of screen plus a centain tolerance... -## Requirements +![](img/viewfrustum.gif) -**Ask on the mailing list for any clarifications.** -In this project, you are given the following code: +## Custom scenes -* 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. +Scenes below show different wind patterns -You need to implement the following features/pipeline stages: +This is the helicopter scene based on the paper. -* 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 +![](img/helicopter.gif) -See below for more guidance. +An interesting scene under different shading method -## Base Code Tour +flat shading|lambert shading +----|--- +![](img/interesting.gif)|![](img/lamb.gif) -Areas that you need to complete are -marked with a `TODO` comment. Functions that are useful -for reference are marked with the comment `CHECKITOUT`. +## Performance Analysis -* `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. +the test is conducted using the helicopter scene, the chart basically shows how rendering time change with respect to numbers of grass blades and different +culling technique -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. +![](img/grass.JPG) -## 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. - -We recommend starting with trying to display the grass blades without any forces on them before trying to add any forces on the blades themselves. Here is an example of what that may look like: - -![](img/grass_basic.gif) - -### 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-2018/Vulkan-Samples/tree/master/samples/5_helloTessellation) -and reading this [tutorial on tessellation](http://in2gpu.com/2014/07/12/tessellation-tutorial-opengl-4-3/). +apparently, with the increasing of grass blade count, rendering time of each frame increases exponentially, +also from the result we can tell that view frustum culling have the best effect but it is based on the viewing position and orientation +, thus I guess it's subject to change very easily once you pan the camera. ## Resources @@ -256,45 +81,3 @@ The following resources may be useful for this project. * [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. -* GIFs of your project in its different stages with the different features being added incrementally. -* A performance analysis (described below). - -### Performance Analysis - -The performance analysis is where you will investigate how... -* Your renderer handles varying numbers of grass blades -* The improvement you get by culling using each of the three culling tests - -## 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/img/grass.JPG b/img/grass.JPG new file mode 100644 index 0000000..37ca139 Binary files /dev/null and b/img/grass.JPG differ diff --git a/img/grassdiscull.gif b/img/grassdiscull.gif new file mode 100644 index 0000000..4ba71ad Binary files /dev/null and b/img/grassdiscull.gif differ diff --git a/img/helicopter.gif b/img/helicopter.gif new file mode 100644 index 0000000..6673ff4 Binary files /dev/null and b/img/helicopter.gif differ diff --git a/img/interesting.gif b/img/interesting.gif new file mode 100644 index 0000000..b90d5ac Binary files /dev/null and b/img/interesting.gif differ diff --git a/img/lamb.gif b/img/lamb.gif new file mode 100644 index 0000000..0d4d8a5 Binary files /dev/null and b/img/lamb.gif differ diff --git a/img/orientcull.gif b/img/orientcull.gif new file mode 100644 index 0000000..a660952 Binary files /dev/null and b/img/orientcull.gif differ diff --git a/img/show.gif b/img/show.gif new file mode 100644 index 0000000..689b383 Binary files /dev/null and b/img/show.gif differ diff --git a/img/viewfrustum.gif b/img/viewfrustum.gif new file mode 100644 index 0000000..9f7edc0 Binary files /dev/null and b/img/viewfrustum.gif differ diff --git a/src/Blades.h b/src/Blades.h index 9bd1eed..80b9e8f 100644 --- a/src/Blades.h +++ b/src/Blades.h @@ -4,11 +4,11 @@ #include #include "Model.h" -constexpr static unsigned int NUM_BLADES = 1 << 13; -constexpr static float MIN_HEIGHT = 1.3f; -constexpr static float MAX_HEIGHT = 2.5f; -constexpr static float MIN_WIDTH = 0.1f; -constexpr static float MAX_WIDTH = 0.14f; +constexpr static unsigned int NUM_BLADES = 1 << 20; +constexpr static float MIN_HEIGHT = 2.3f; +constexpr static float MAX_HEIGHT = 4.5f; +constexpr static float MIN_WIDTH = 0.2f; +constexpr static float MAX_WIDTH = 0.24f; constexpr static float MIN_BEND = 7.0f; constexpr static float MAX_BEND = 13.0f; diff --git a/src/Renderer.cpp b/src/Renderer.cpp index b445d04..dc07995 100644 --- a/src/Renderer.cpp +++ b/src/Renderer.cpp @@ -1,3 +1,7 @@ + + + + #include "Renderer.h" #include "Instance.h" #include "ShaderModule.h" @@ -9,1059 +13,1225 @@ static constexpr unsigned int WORKGROUP_SIZE = 32; Renderer::Renderer(Device* device, SwapChain* swapChain, Scene* scene, Camera* camera) - : device(device), - logicalDevice(device->GetVkDevice()), - swapChain(swapChain), - scene(scene), - camera(camera) { - - CreateCommandPools(); - CreateRenderPass(); - CreateCameraDescriptorSetLayout(); - CreateModelDescriptorSetLayout(); - CreateTimeDescriptorSetLayout(); - CreateComputeDescriptorSetLayout(); - CreateDescriptorPool(); - CreateCameraDescriptorSet(); - CreateModelDescriptorSets(); - CreateGrassDescriptorSets(); - CreateTimeDescriptorSet(); - CreateComputeDescriptorSets(); - CreateFrameResources(); - CreateGraphicsPipeline(); - CreateGrassPipeline(); - CreateComputePipeline(); - RecordCommandBuffers(); - RecordComputeCommandBuffer(); + : device(device), + logicalDevice(device->GetVkDevice()), + swapChain(swapChain), + scene(scene), + camera(camera) { + + CreateCommandPools(); + CreateRenderPass(); + CreateCameraDescriptorSetLayout(); + CreateModelDescriptorSetLayout(); + CreateGrassDescriptorSetLayout(); + CreateTimeDescriptorSetLayout(); + CreateComputeDescriptorSetLayout(); + CreateDescriptorPool(); + CreateCameraDescriptorSet(); + CreateModelDescriptorSets(); + CreateGrassDescriptorSets(); + CreateTimeDescriptorSet(); + CreateComputeDescriptorSets(); + CreateFrameResources(); + CreateGraphicsPipeline(); + CreateGrassPipeline(); + CreateComputePipeline(); + RecordCommandBuffers(); + RecordComputeCommandBuffer(); } void Renderer::CreateCommandPools() { - VkCommandPoolCreateInfo graphicsPoolInfo = {}; - graphicsPoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; - graphicsPoolInfo.queueFamilyIndex = device->GetInstance()->GetQueueFamilyIndices()[QueueFlags::Graphics]; - graphicsPoolInfo.flags = 0; - - if (vkCreateCommandPool(logicalDevice, &graphicsPoolInfo, nullptr, &graphicsCommandPool) != VK_SUCCESS) { - throw std::runtime_error("Failed to create command pool"); - } - - VkCommandPoolCreateInfo computePoolInfo = {}; - computePoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; - computePoolInfo.queueFamilyIndex = device->GetInstance()->GetQueueFamilyIndices()[QueueFlags::Compute]; - computePoolInfo.flags = 0; - - if (vkCreateCommandPool(logicalDevice, &computePoolInfo, nullptr, &computeCommandPool) != VK_SUCCESS) { - throw std::runtime_error("Failed to create command pool"); - } + VkCommandPoolCreateInfo graphicsPoolInfo = {}; + graphicsPoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + graphicsPoolInfo.queueFamilyIndex = device->GetInstance()->GetQueueFamilyIndices()[QueueFlags::Graphics]; + graphicsPoolInfo.flags = 0; + + if (vkCreateCommandPool(logicalDevice, &graphicsPoolInfo, nullptr, &graphicsCommandPool) != VK_SUCCESS) { + throw std::runtime_error("Failed to create command pool"); + } + + VkCommandPoolCreateInfo computePoolInfo = {}; + computePoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + computePoolInfo.queueFamilyIndex = device->GetInstance()->GetQueueFamilyIndices()[QueueFlags::Compute]; + computePoolInfo.flags = 0; + + if (vkCreateCommandPool(logicalDevice, &computePoolInfo, nullptr, &computeCommandPool) != VK_SUCCESS) { + throw std::runtime_error("Failed to create command pool"); + } } void Renderer::CreateRenderPass() { - // Color buffer attachment represented by one of the images from the swap chain - VkAttachmentDescription colorAttachment = {}; - colorAttachment.format = swapChain->GetVkImageFormat(); - colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT; - colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; - colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - - // Create a color attachment reference to be used with subpass - VkAttachmentReference colorAttachmentRef = {}; - colorAttachmentRef.attachment = 0; - colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - - // Depth buffer attachment - VkFormat depthFormat = device->GetInstance()->GetSupportedFormat({ VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT }, VK_IMAGE_TILING_OPTIMAL, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT); - VkAttachmentDescription depthAttachment = {}; - depthAttachment.format = depthFormat; - depthAttachment.samples = VK_SAMPLE_COUNT_1_BIT; - depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - depthAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - - // Create a depth attachment reference - VkAttachmentReference depthAttachmentRef = {}; - depthAttachmentRef.attachment = 1; - depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - - // Create subpass description - VkSubpassDescription subpass = {}; - subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; - subpass.colorAttachmentCount = 1; - subpass.pColorAttachments = &colorAttachmentRef; - subpass.pDepthStencilAttachment = &depthAttachmentRef; - - std::array attachments = { colorAttachment, depthAttachment }; - - // Specify subpass dependency - VkSubpassDependency dependency = {}; - dependency.srcSubpass = VK_SUBPASS_EXTERNAL; - dependency.dstSubpass = 0; - dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dependency.srcAccessMask = 0; - dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - - // Create render pass - VkRenderPassCreateInfo renderPassInfo = {}; - renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; - renderPassInfo.attachmentCount = static_cast(attachments.size()); - renderPassInfo.pAttachments = attachments.data(); - renderPassInfo.subpassCount = 1; - renderPassInfo.pSubpasses = &subpass; - renderPassInfo.dependencyCount = 1; - renderPassInfo.pDependencies = &dependency; - - if (vkCreateRenderPass(logicalDevice, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) { - throw std::runtime_error("Failed to create render pass"); - } + // Color buffer attachment represented by one of the images from the swap chain + VkAttachmentDescription colorAttachment = {}; + colorAttachment.format = swapChain->GetVkImageFormat(); + colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT; + colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + + // Create a color attachment reference to be used with subpass + VkAttachmentReference colorAttachmentRef = {}; + colorAttachmentRef.attachment = 0; + colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + // Depth buffer attachment + VkFormat depthFormat = device->GetInstance()->GetSupportedFormat({ VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT }, VK_IMAGE_TILING_OPTIMAL, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT); + VkAttachmentDescription depthAttachment = {}; + depthAttachment.format = depthFormat; + depthAttachment.samples = VK_SAMPLE_COUNT_1_BIT; + depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + depthAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + // Create a depth attachment reference + VkAttachmentReference depthAttachmentRef = {}; + depthAttachmentRef.attachment = 1; + depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + // Create subpass description + VkSubpassDescription subpass = {}; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.colorAttachmentCount = 1; + subpass.pColorAttachments = &colorAttachmentRef; + subpass.pDepthStencilAttachment = &depthAttachmentRef; + + std::array attachments = { colorAttachment, depthAttachment }; + + // Specify subpass dependency + VkSubpassDependency dependency = {}; + dependency.srcSubpass = VK_SUBPASS_EXTERNAL; + dependency.dstSubpass = 0; + dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependency.srcAccessMask = 0; + dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + + // Create render pass + VkRenderPassCreateInfo renderPassInfo = {}; + renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + renderPassInfo.attachmentCount = static_cast(attachments.size()); + renderPassInfo.pAttachments = attachments.data(); + renderPassInfo.subpassCount = 1; + renderPassInfo.pSubpasses = &subpass; + renderPassInfo.dependencyCount = 1; + renderPassInfo.pDependencies = &dependency; + + if (vkCreateRenderPass(logicalDevice, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) { + throw std::runtime_error("Failed to create render pass"); + } } void Renderer::CreateCameraDescriptorSetLayout() { - // Describe the binding of the descriptor set layout - VkDescriptorSetLayoutBinding uboLayoutBinding = {}; - uboLayoutBinding.binding = 0; - uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - uboLayoutBinding.descriptorCount = 1; - uboLayoutBinding.stageFlags = VK_SHADER_STAGE_ALL; - uboLayoutBinding.pImmutableSamplers = nullptr; - - std::vector bindings = { uboLayoutBinding }; - - // Create the descriptor set layout - VkDescriptorSetLayoutCreateInfo layoutInfo = {}; - layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; - layoutInfo.bindingCount = static_cast(bindings.size()); - layoutInfo.pBindings = bindings.data(); - - if (vkCreateDescriptorSetLayout(logicalDevice, &layoutInfo, nullptr, &cameraDescriptorSetLayout) != VK_SUCCESS) { - throw std::runtime_error("Failed to create descriptor set layout"); - } + // Describe the binding of the descriptor set layout + VkDescriptorSetLayoutBinding uboLayoutBinding = {}; + uboLayoutBinding.binding = 0; + uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + uboLayoutBinding.descriptorCount = 1; + uboLayoutBinding.stageFlags = VK_SHADER_STAGE_ALL; + uboLayoutBinding.pImmutableSamplers = nullptr; + + std::vector bindings = { uboLayoutBinding }; + + // Create the descriptor set layout + VkDescriptorSetLayoutCreateInfo layoutInfo = {}; + layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + layoutInfo.bindingCount = static_cast(bindings.size()); + layoutInfo.pBindings = bindings.data(); + + if (vkCreateDescriptorSetLayout(logicalDevice, &layoutInfo, nullptr, &cameraDescriptorSetLayout) != VK_SUCCESS) { + throw std::runtime_error("Failed to create descriptor set layout"); + } } void Renderer::CreateModelDescriptorSetLayout() { - VkDescriptorSetLayoutBinding uboLayoutBinding = {}; - uboLayoutBinding.binding = 0; - uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - uboLayoutBinding.descriptorCount = 1; - uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; - uboLayoutBinding.pImmutableSamplers = nullptr; - - VkDescriptorSetLayoutBinding samplerLayoutBinding = {}; - samplerLayoutBinding.binding = 1; - samplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - samplerLayoutBinding.descriptorCount = 1; - samplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; - samplerLayoutBinding.pImmutableSamplers = nullptr; - - std::vector bindings = { uboLayoutBinding, samplerLayoutBinding }; - - // Create the descriptor set layout - VkDescriptorSetLayoutCreateInfo layoutInfo = {}; - layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; - layoutInfo.bindingCount = static_cast(bindings.size()); - layoutInfo.pBindings = bindings.data(); - - if (vkCreateDescriptorSetLayout(logicalDevice, &layoutInfo, nullptr, &modelDescriptorSetLayout) != VK_SUCCESS) { - throw std::runtime_error("Failed to create descriptor set layout"); - } + VkDescriptorSetLayoutBinding uboLayoutBinding = {}; + uboLayoutBinding.binding = 0; + uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + uboLayoutBinding.descriptorCount = 1; + uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; + uboLayoutBinding.pImmutableSamplers = nullptr; + + VkDescriptorSetLayoutBinding samplerLayoutBinding = {}; + samplerLayoutBinding.binding = 1; + samplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + samplerLayoutBinding.descriptorCount = 1; + samplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + samplerLayoutBinding.pImmutableSamplers = nullptr; + + std::vector bindings = { uboLayoutBinding, samplerLayoutBinding }; + + // Create the descriptor set layout + VkDescriptorSetLayoutCreateInfo layoutInfo = {}; + layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + layoutInfo.bindingCount = static_cast(bindings.size()); + layoutInfo.pBindings = bindings.data(); + + if (vkCreateDescriptorSetLayout(logicalDevice, &layoutInfo, nullptr, &modelDescriptorSetLayout) != VK_SUCCESS) { + throw std::runtime_error("Failed to create descriptor set layout"); + } } + + void Renderer::CreateTimeDescriptorSetLayout() { - // Describe the binding of the descriptor set layout - VkDescriptorSetLayoutBinding uboLayoutBinding = {}; - uboLayoutBinding.binding = 0; - uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - uboLayoutBinding.descriptorCount = 1; - uboLayoutBinding.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; - uboLayoutBinding.pImmutableSamplers = nullptr; - - std::vector bindings = { uboLayoutBinding }; - - // Create the descriptor set layout - VkDescriptorSetLayoutCreateInfo layoutInfo = {}; - layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; - layoutInfo.bindingCount = static_cast(bindings.size()); - layoutInfo.pBindings = bindings.data(); - - if (vkCreateDescriptorSetLayout(logicalDevice, &layoutInfo, nullptr, &timeDescriptorSetLayout) != VK_SUCCESS) { - throw std::runtime_error("Failed to create descriptor set layout"); - } + // Describe the binding of the descriptor set layout + VkDescriptorSetLayoutBinding uboLayoutBinding = {}; + uboLayoutBinding.binding = 0; + uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + uboLayoutBinding.descriptorCount = 1; + uboLayoutBinding.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + uboLayoutBinding.pImmutableSamplers = nullptr; + + std::vector bindings = { uboLayoutBinding }; + + // Create the descriptor set layout + VkDescriptorSetLayoutCreateInfo layoutInfo = {}; + layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + layoutInfo.bindingCount = static_cast(bindings.size()); + layoutInfo.pBindings = bindings.data(); + + if (vkCreateDescriptorSetLayout(logicalDevice, &layoutInfo, nullptr, &timeDescriptorSetLayout) != VK_SUCCESS) { + throw std::runtime_error("Failed to create descriptor set layout"); + } +} + +void Renderer::CreateGrassDescriptorSetLayout() { + VkDescriptorSetLayoutBinding uboLayoutBinding = {}; + uboLayoutBinding.binding = 0; + uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + uboLayoutBinding.descriptorCount = 1; + uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; + uboLayoutBinding.pImmutableSamplers = nullptr; + std::vector bindings = { uboLayoutBinding }; + // Create the descriptor set layout + VkDescriptorSetLayoutCreateInfo layoutInfo = {}; + layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + layoutInfo.bindingCount = static_cast(bindings.size()); + layoutInfo.pBindings = bindings.data(); + + if (vkCreateDescriptorSetLayout(logicalDevice, &layoutInfo, nullptr, &grassDescriptorSetLayout) != VK_SUCCESS) { + throw std::runtime_error("Failed to create descriptor set layout"); + } } void Renderer::CreateComputeDescriptorSetLayout() { - // TODO: Create the descriptor set layout for the compute pipeline - // Remember this is like a class definition stating why types of information - // will be stored at each binding + + VkDescriptorSetLayoutBinding blades = {}; + blades.binding = 0; + blades.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + blades.descriptorCount = 1; + blades.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + blades.pImmutableSamplers = nullptr; + + VkDescriptorSetLayoutBinding culledBlades = {}; + culledBlades.binding = 1; + culledBlades.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + culledBlades.descriptorCount = 1; + culledBlades.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + culledBlades.pImmutableSamplers = nullptr; + + VkDescriptorSetLayoutBinding numBlades = {}; + numBlades.binding = 2; + numBlades.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + numBlades.descriptorCount = 1; + numBlades.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + numBlades.pImmutableSamplers = nullptr; + + std::vector bindings = { blades, culledBlades, numBlades }; + + // Create the descriptor set layout + VkDescriptorSetLayoutCreateInfo layoutInfo = {}; + layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + layoutInfo.bindingCount = static_cast(bindings.size()); + layoutInfo.pBindings = bindings.data(); + + if (vkCreateDescriptorSetLayout(logicalDevice, &layoutInfo, nullptr, &computeDescriptorSetLayout) != VK_SUCCESS) { + throw std::runtime_error("Failed to create descriptor set layout"); + } } void Renderer::CreateDescriptorPool() { - // Describe which descriptor types that the descriptor sets will contain - std::vector poolSizes = { - // Camera - { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER , 1}, + // 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 - }; + // TODO: Add any additional types and counts of descriptors you will need to allocate + { 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 + + grassDescriptorSets.resize(scene->GetBlades().size()); + + // Describe the desciptor set + VkDescriptorSetLayout layouts[] = { grassDescriptorSetLayout }; + VkDescriptorSetAllocateInfo allocInfo = {}; + allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + allocInfo.descriptorPool = descriptorPool; + allocInfo.descriptorSetCount = static_cast(grassDescriptorSets.size()); + allocInfo.pSetLayouts = layouts; + + // Allocate descriptor sets + if (vkAllocateDescriptorSets(logicalDevice, &allocInfo, grassDescriptorSets.data()) != VK_SUCCESS) { + throw std::runtime_error("Failed to allocate descriptor set"); + } + + std::vector descriptorWrites(grassDescriptorSets.size()); + + for (uint32_t i = 0; i < scene->GetBlades().size(); ++i) { + VkDescriptorBufferInfo modelBufferInfo = {}; + modelBufferInfo.buffer = scene->GetBlades()[i]->GetModelBuffer(); + modelBufferInfo.offset = 0; + modelBufferInfo.range = sizeof(ModelBufferObject); + + descriptorWrites[i + 0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrites[i + 0].dstSet = grassDescriptorSets[i]; + descriptorWrites[i + 0].dstBinding = 0; + descriptorWrites[i + 0].dstArrayElement = 0; + descriptorWrites[i + 0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + descriptorWrites[i + 0].descriptorCount = 1; + descriptorWrites[i + 0].pBufferInfo = &modelBufferInfo; + descriptorWrites[i + 0].pImageInfo = nullptr; + descriptorWrites[i + 0].pTexelBufferView = nullptr; + } + + // Update descriptor sets + vkUpdateDescriptorSets(logicalDevice, static_cast(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr); } void Renderer::CreateTimeDescriptorSet() { - // Describe the desciptor set - VkDescriptorSetLayout layouts[] = { timeDescriptorSetLayout }; - VkDescriptorSetAllocateInfo allocInfo = {}; - allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; - allocInfo.descriptorPool = descriptorPool; - allocInfo.descriptorSetCount = 1; - allocInfo.pSetLayouts = layouts; - - // Allocate descriptor sets - if (vkAllocateDescriptorSets(logicalDevice, &allocInfo, &timeDescriptorSet) != VK_SUCCESS) { - throw std::runtime_error("Failed to allocate descriptor set"); - } - - // Configure the descriptors to refer to buffers - VkDescriptorBufferInfo timeBufferInfo = {}; - timeBufferInfo.buffer = scene->GetTimeBuffer(); - timeBufferInfo.offset = 0; - timeBufferInfo.range = sizeof(Time); - - std::array descriptorWrites = {}; - descriptorWrites[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - descriptorWrites[0].dstSet = timeDescriptorSet; - descriptorWrites[0].dstBinding = 0; - descriptorWrites[0].dstArrayElement = 0; - descriptorWrites[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - descriptorWrites[0].descriptorCount = 1; - descriptorWrites[0].pBufferInfo = &timeBufferInfo; - descriptorWrites[0].pImageInfo = nullptr; - descriptorWrites[0].pTexelBufferView = nullptr; - - // Update descriptor sets - vkUpdateDescriptorSets(logicalDevice, static_cast(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr); + // Describe the desciptor set + VkDescriptorSetLayout layouts[] = { timeDescriptorSetLayout }; + VkDescriptorSetAllocateInfo allocInfo = {}; + allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + allocInfo.descriptorPool = descriptorPool; + allocInfo.descriptorSetCount = 1; + allocInfo.pSetLayouts = layouts; + + // Allocate descriptor sets + if (vkAllocateDescriptorSets(logicalDevice, &allocInfo, &timeDescriptorSet) != VK_SUCCESS) { + throw std::runtime_error("Failed to allocate descriptor set"); + } + + // Configure the descriptors to refer to buffers + VkDescriptorBufferInfo timeBufferInfo = {}; + timeBufferInfo.buffer = scene->GetTimeBuffer(); + timeBufferInfo.offset = 0; + timeBufferInfo.range = sizeof(Time); + + std::array descriptorWrites = {}; + descriptorWrites[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrites[0].dstSet = timeDescriptorSet; + descriptorWrites[0].dstBinding = 0; + descriptorWrites[0].dstArrayElement = 0; + descriptorWrites[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + descriptorWrites[0].descriptorCount = 1; + descriptorWrites[0].pBufferInfo = &timeBufferInfo; + descriptorWrites[0].pImageInfo = nullptr; + descriptorWrites[0].pTexelBufferView = nullptr; + + // Update descriptor sets + vkUpdateDescriptorSets(logicalDevice, static_cast(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr); } void Renderer::CreateComputeDescriptorSets() { - // TODO: Create Descriptor sets for the compute pipeline - // The descriptors should point to Storage buffers which will hold the grass blades, the culled grass blades, and the output number of grass blades + + computeDescriptorSets.resize(scene->GetBlades().size()); + + // Describe the desciptor set + VkDescriptorSetLayout layouts[] = { computeDescriptorSetLayout }; + VkDescriptorSetAllocateInfo allocInfo = {}; + allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + allocInfo.descriptorPool = descriptorPool; + allocInfo.descriptorSetCount = static_cast(computeDescriptorSets.size()); + allocInfo.pSetLayouts = layouts; + + // Allocate descriptor sets + if (vkAllocateDescriptorSets(logicalDevice, &allocInfo, computeDescriptorSets.data()) != VK_SUCCESS) { + throw std::runtime_error("Failed to allocate descriptor set"); + } + + std::vector descriptorWrites(3 * computeDescriptorSets.size()); + + for (uint32_t i = 0; i < scene->GetBlades().size(); ++i) { + //Blades + VkDescriptorBufferInfo bladesBufferInfo = {}; + bladesBufferInfo.buffer = scene->GetBlades()[i]->GetBladesBuffer(); + bladesBufferInfo.offset = 0; + bladesBufferInfo.range = NUM_BLADES * sizeof(Blade); + + //CulledBlades + VkDescriptorBufferInfo culledBaldesBufferInfo = {}; + culledBaldesBufferInfo.buffer = scene->GetBlades()[i]->GetCulledBladesBuffer(); + culledBaldesBufferInfo.offset = 0; + culledBaldesBufferInfo.range = NUM_BLADES * sizeof(Blade); + + //BladesNum + VkDescriptorBufferInfo numBaldesBufferInfo = {}; + numBaldesBufferInfo.buffer = scene->GetBlades()[i]->GetNumBladesBuffer(); + numBaldesBufferInfo.offset = 0; + numBaldesBufferInfo.range = sizeof(BladeDrawIndirect); + + + descriptorWrites[3 * i].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrites[3 * i].dstSet = computeDescriptorSets[i]; + descriptorWrites[3 * i].dstBinding = 0; + descriptorWrites[3 * i].dstArrayElement = 0; + descriptorWrites[3 * i].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + descriptorWrites[3 * i].descriptorCount = 1; + descriptorWrites[3 * i].pBufferInfo = &bladesBufferInfo; + descriptorWrites[3 * i].pImageInfo = nullptr; + descriptorWrites[3 * i].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 = &culledBaldesBufferInfo; + 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 = &numBaldesBufferInfo; + descriptorWrites[3 * i + 2].pImageInfo = nullptr; + descriptorWrites[3 * i + 2].pTexelBufferView = nullptr; + } + + // Update descriptor sets + vkUpdateDescriptorSets(logicalDevice, static_cast(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr); } void Renderer::CreateGraphicsPipeline() { - VkShaderModule vertShaderModule = ShaderModule::Create("shaders/graphics.vert.spv", logicalDevice); - VkShaderModule fragShaderModule = ShaderModule::Create("shaders/graphics.frag.spv", logicalDevice); - - // Assign each shader module to the appropriate stage in the pipeline - VkPipelineShaderStageCreateInfo vertShaderStageInfo = {}; - vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; - vertShaderStageInfo.module = vertShaderModule; - vertShaderStageInfo.pName = "main"; - - VkPipelineShaderStageCreateInfo fragShaderStageInfo = {}; - fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT; - fragShaderStageInfo.module = fragShaderModule; - fragShaderStageInfo.pName = "main"; - - VkPipelineShaderStageCreateInfo shaderStages[] = { vertShaderStageInfo, fragShaderStageInfo }; - - // --- Set up fixed-function stages --- - - // Vertex input - VkPipelineVertexInputStateCreateInfo vertexInputInfo = {}; - vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; - - auto bindingDescription = Vertex::getBindingDescription(); - auto attributeDescriptions = Vertex::getAttributeDescriptions(); - - vertexInputInfo.vertexBindingDescriptionCount = 1; - vertexInputInfo.pVertexBindingDescriptions = &bindingDescription; - vertexInputInfo.vertexAttributeDescriptionCount = static_cast(attributeDescriptions.size()); - vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data(); - - // Input assembly - VkPipelineInputAssemblyStateCreateInfo inputAssembly = {}; - inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; - inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; - inputAssembly.primitiveRestartEnable = VK_FALSE; - - // Viewports and Scissors (rectangles that define in which regions pixels are stored) - VkViewport viewport = {}; - viewport.x = 0.0f; - viewport.y = 0.0f; - viewport.width = static_cast(swapChain->GetVkExtent().width); - viewport.height = static_cast(swapChain->GetVkExtent().height); - viewport.minDepth = 0.0f; - viewport.maxDepth = 1.0f; - - VkRect2D scissor = {}; - scissor.offset = { 0, 0 }; - scissor.extent = swapChain->GetVkExtent(); - - VkPipelineViewportStateCreateInfo viewportState = {}; - viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; - viewportState.viewportCount = 1; - viewportState.pViewports = &viewport; - viewportState.scissorCount = 1; - viewportState.pScissors = &scissor; - - // Rasterizer - VkPipelineRasterizationStateCreateInfo rasterizer = {}; - rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; - rasterizer.depthClampEnable = VK_FALSE; - rasterizer.rasterizerDiscardEnable = VK_FALSE; - rasterizer.polygonMode = VK_POLYGON_MODE_FILL; - rasterizer.lineWidth = 1.0f; - rasterizer.cullMode = VK_CULL_MODE_BACK_BIT; - rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; - rasterizer.depthBiasEnable = VK_FALSE; - rasterizer.depthBiasConstantFactor = 0.0f; - rasterizer.depthBiasClamp = 0.0f; - rasterizer.depthBiasSlopeFactor = 0.0f; - - // Multisampling (turned off here) - VkPipelineMultisampleStateCreateInfo multisampling = {}; - multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; - multisampling.sampleShadingEnable = VK_FALSE; - multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; - multisampling.minSampleShading = 1.0f; - multisampling.pSampleMask = nullptr; - multisampling.alphaToCoverageEnable = VK_FALSE; - multisampling.alphaToOneEnable = VK_FALSE; - - // Depth testing - VkPipelineDepthStencilStateCreateInfo depthStencil = {}; - depthStencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; - depthStencil.depthTestEnable = VK_TRUE; - depthStencil.depthWriteEnable = VK_TRUE; - depthStencil.depthCompareOp = VK_COMPARE_OP_LESS; - depthStencil.depthBoundsTestEnable = VK_FALSE; - depthStencil.minDepthBounds = 0.0f; - depthStencil.maxDepthBounds = 1.0f; - depthStencil.stencilTestEnable = VK_FALSE; - - // Color blending (turned off here, but showing options for learning) - // --> Configuration per attached framebuffer - VkPipelineColorBlendAttachmentState colorBlendAttachment = {}; - colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; - colorBlendAttachment.blendEnable = VK_FALSE; - colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE; - colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO; - colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD; - colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; - colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; - colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD; - - // --> Global color blending settings - VkPipelineColorBlendStateCreateInfo colorBlending = {}; - colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; - colorBlending.logicOpEnable = VK_FALSE; - colorBlending.logicOp = VK_LOGIC_OP_COPY; - colorBlending.attachmentCount = 1; - colorBlending.pAttachments = &colorBlendAttachment; - colorBlending.blendConstants[0] = 0.0f; - colorBlending.blendConstants[1] = 0.0f; - colorBlending.blendConstants[2] = 0.0f; - colorBlending.blendConstants[3] = 0.0f; - - std::vector descriptorSetLayouts = { cameraDescriptorSetLayout, modelDescriptorSetLayout }; - - // Pipeline layout: used to specify uniform values - VkPipelineLayoutCreateInfo pipelineLayoutInfo = {}; - pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; - pipelineLayoutInfo.setLayoutCount = static_cast(descriptorSetLayouts.size()); - pipelineLayoutInfo.pSetLayouts = descriptorSetLayouts.data(); - pipelineLayoutInfo.pushConstantRangeCount = 0; - pipelineLayoutInfo.pPushConstantRanges = 0; - - if (vkCreatePipelineLayout(logicalDevice, &pipelineLayoutInfo, nullptr, &graphicsPipelineLayout) != VK_SUCCESS) { - throw std::runtime_error("Failed to create pipeline layout"); - } - - // --- Create graphics pipeline --- - VkGraphicsPipelineCreateInfo pipelineInfo = {}; - pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; - pipelineInfo.stageCount = 2; - pipelineInfo.pStages = shaderStages; - pipelineInfo.pVertexInputState = &vertexInputInfo; - pipelineInfo.pInputAssemblyState = &inputAssembly; - pipelineInfo.pViewportState = &viewportState; - pipelineInfo.pRasterizationState = &rasterizer; - pipelineInfo.pMultisampleState = &multisampling; - pipelineInfo.pDepthStencilState = &depthStencil; - pipelineInfo.pColorBlendState = &colorBlending; - pipelineInfo.pDynamicState = nullptr; - pipelineInfo.layout = graphicsPipelineLayout; - pipelineInfo.renderPass = renderPass; - pipelineInfo.subpass = 0; - pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; - pipelineInfo.basePipelineIndex = -1; - - if (vkCreateGraphicsPipelines(logicalDevice, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS) { - throw std::runtime_error("Failed to create graphics pipeline"); - } - - vkDestroyShaderModule(logicalDevice, vertShaderModule, nullptr); - vkDestroyShaderModule(logicalDevice, fragShaderModule, nullptr); + VkShaderModule vertShaderModule = ShaderModule::Create("shaders/graphics.vert.spv", logicalDevice); + VkShaderModule fragShaderModule = ShaderModule::Create("shaders/graphics.frag.spv", logicalDevice); + + // Assign each shader module to the appropriate stage in the pipeline + VkPipelineShaderStageCreateInfo vertShaderStageInfo = {}; + vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; + vertShaderStageInfo.module = vertShaderModule; + vertShaderStageInfo.pName = "main"; + + VkPipelineShaderStageCreateInfo fragShaderStageInfo = {}; + fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT; + fragShaderStageInfo.module = fragShaderModule; + fragShaderStageInfo.pName = "main"; + + VkPipelineShaderStageCreateInfo shaderStages[] = { vertShaderStageInfo, fragShaderStageInfo }; + + // --- Set up fixed-function stages --- + + // Vertex input + VkPipelineVertexInputStateCreateInfo vertexInputInfo = {}; + vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + + auto bindingDescription = Vertex::getBindingDescription(); + auto attributeDescriptions = Vertex::getAttributeDescriptions(); + + vertexInputInfo.vertexBindingDescriptionCount = 1; + vertexInputInfo.pVertexBindingDescriptions = &bindingDescription; + vertexInputInfo.vertexAttributeDescriptionCount = static_cast(attributeDescriptions.size()); + vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data(); + + // Input assembly + VkPipelineInputAssemblyStateCreateInfo inputAssembly = {}; + inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + inputAssembly.primitiveRestartEnable = VK_FALSE; + + // Viewports and Scissors (rectangles that define in which regions pixels are stored) + VkViewport viewport = {}; + viewport.x = 0.0f; + viewport.y = 0.0f; + viewport.width = static_cast(swapChain->GetVkExtent().width); + viewport.height = static_cast(swapChain->GetVkExtent().height); + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + + VkRect2D scissor = {}; + scissor.offset = { 0, 0 }; + scissor.extent = swapChain->GetVkExtent(); + + VkPipelineViewportStateCreateInfo viewportState = {}; + viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewportState.viewportCount = 1; + viewportState.pViewports = &viewport; + viewportState.scissorCount = 1; + viewportState.pScissors = &scissor; + + // Rasterizer + VkPipelineRasterizationStateCreateInfo rasterizer = {}; + rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rasterizer.depthClampEnable = VK_FALSE; + rasterizer.rasterizerDiscardEnable = VK_FALSE; + rasterizer.polygonMode = VK_POLYGON_MODE_FILL; + rasterizer.lineWidth = 1.0f; + rasterizer.cullMode = VK_CULL_MODE_BACK_BIT; + rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; + rasterizer.depthBiasEnable = VK_FALSE; + rasterizer.depthBiasConstantFactor = 0.0f; + rasterizer.depthBiasClamp = 0.0f; + rasterizer.depthBiasSlopeFactor = 0.0f; + + // Multisampling (turned off here) + VkPipelineMultisampleStateCreateInfo multisampling = {}; + multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + multisampling.sampleShadingEnable = VK_FALSE; + multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + multisampling.minSampleShading = 1.0f; + multisampling.pSampleMask = nullptr; + multisampling.alphaToCoverageEnable = VK_FALSE; + multisampling.alphaToOneEnable = VK_FALSE; + + // Depth testing + VkPipelineDepthStencilStateCreateInfo depthStencil = {}; + depthStencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; + depthStencil.depthTestEnable = VK_TRUE; + depthStencil.depthWriteEnable = VK_TRUE; + depthStencil.depthCompareOp = VK_COMPARE_OP_LESS; + depthStencil.depthBoundsTestEnable = VK_FALSE; + depthStencil.minDepthBounds = 0.0f; + depthStencil.maxDepthBounds = 1.0f; + depthStencil.stencilTestEnable = VK_FALSE; + + // Color blending (turned off here, but showing options for learning) + // --> Configuration per attached framebuffer + VkPipelineColorBlendAttachmentState colorBlendAttachment = {}; + colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + colorBlendAttachment.blendEnable = VK_FALSE; + colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE; + colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO; + colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD; + colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; + colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; + colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD; + + // --> Global color blending settings + VkPipelineColorBlendStateCreateInfo colorBlending = {}; + colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + colorBlending.logicOpEnable = VK_FALSE; + colorBlending.logicOp = VK_LOGIC_OP_COPY; + colorBlending.attachmentCount = 1; + colorBlending.pAttachments = &colorBlendAttachment; + colorBlending.blendConstants[0] = 0.0f; + colorBlending.blendConstants[1] = 0.0f; + colorBlending.blendConstants[2] = 0.0f; + colorBlending.blendConstants[3] = 0.0f; + + std::vector descriptorSetLayouts = { cameraDescriptorSetLayout, modelDescriptorSetLayout }; + + // Pipeline layout: used to specify uniform values + VkPipelineLayoutCreateInfo pipelineLayoutInfo = {}; + pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipelineLayoutInfo.setLayoutCount = static_cast(descriptorSetLayouts.size()); + pipelineLayoutInfo.pSetLayouts = descriptorSetLayouts.data(); + pipelineLayoutInfo.pushConstantRangeCount = 0; + pipelineLayoutInfo.pPushConstantRanges = 0; + + if (vkCreatePipelineLayout(logicalDevice, &pipelineLayoutInfo, nullptr, &graphicsPipelineLayout) != VK_SUCCESS) { + throw std::runtime_error("Failed to create pipeline layout"); + } + + // --- Create graphics pipeline --- + VkGraphicsPipelineCreateInfo pipelineInfo = {}; + pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + pipelineInfo.stageCount = 2; + pipelineInfo.pStages = shaderStages; + pipelineInfo.pVertexInputState = &vertexInputInfo; + pipelineInfo.pInputAssemblyState = &inputAssembly; + pipelineInfo.pViewportState = &viewportState; + pipelineInfo.pRasterizationState = &rasterizer; + pipelineInfo.pMultisampleState = &multisampling; + pipelineInfo.pDepthStencilState = &depthStencil; + pipelineInfo.pColorBlendState = &colorBlending; + pipelineInfo.pDynamicState = nullptr; + pipelineInfo.layout = graphicsPipelineLayout; + pipelineInfo.renderPass = renderPass; + pipelineInfo.subpass = 0; + pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; + pipelineInfo.basePipelineIndex = -1; + + if (vkCreateGraphicsPipelines(logicalDevice, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS) { + throw std::runtime_error("Failed to create graphics pipeline"); + } + + vkDestroyShaderModule(logicalDevice, vertShaderModule, nullptr); + vkDestroyShaderModule(logicalDevice, fragShaderModule, nullptr); } void Renderer::CreateGrassPipeline() { - // --- Set up programmable shaders --- - VkShaderModule vertShaderModule = ShaderModule::Create("shaders/grass.vert.spv", logicalDevice); - VkShaderModule tescShaderModule = ShaderModule::Create("shaders/grass.tesc.spv", logicalDevice); - VkShaderModule teseShaderModule = ShaderModule::Create("shaders/grass.tese.spv", logicalDevice); - VkShaderModule fragShaderModule = ShaderModule::Create("shaders/grass.frag.spv", logicalDevice); - - // Assign each shader module to the appropriate stage in the pipeline - VkPipelineShaderStageCreateInfo vertShaderStageInfo = {}; - vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; - vertShaderStageInfo.module = vertShaderModule; - vertShaderStageInfo.pName = "main"; - - VkPipelineShaderStageCreateInfo tescShaderStageInfo = {}; - tescShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - tescShaderStageInfo.stage = VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT; - tescShaderStageInfo.module = tescShaderModule; - tescShaderStageInfo.pName = "main"; - - VkPipelineShaderStageCreateInfo teseShaderStageInfo = {}; - teseShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - teseShaderStageInfo.stage = VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT; - teseShaderStageInfo.module = teseShaderModule; - teseShaderStageInfo.pName = "main"; - - VkPipelineShaderStageCreateInfo fragShaderStageInfo = {}; - fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT; - fragShaderStageInfo.module = fragShaderModule; - fragShaderStageInfo.pName = "main"; - - VkPipelineShaderStageCreateInfo shaderStages[] = { vertShaderStageInfo, tescShaderStageInfo, teseShaderStageInfo, fragShaderStageInfo }; - - // --- Set up fixed-function stages --- - - // Vertex input - VkPipelineVertexInputStateCreateInfo vertexInputInfo = {}; - vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; - - auto bindingDescription = Blade::getBindingDescription(); - auto attributeDescriptions = Blade::getAttributeDescriptions(); - - vertexInputInfo.vertexBindingDescriptionCount = 1; - vertexInputInfo.pVertexBindingDescriptions = &bindingDescription; - vertexInputInfo.vertexAttributeDescriptionCount = static_cast(attributeDescriptions.size()); - vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data(); - - // Input Assembly - VkPipelineInputAssemblyStateCreateInfo inputAssembly = {}; - inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; - inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_PATCH_LIST; - inputAssembly.primitiveRestartEnable = VK_FALSE; - - // Viewports and Scissors (rectangles that define in which regions pixels are stored) - VkViewport viewport = {}; - viewport.x = 0.0f; - viewport.y = 0.0f; - viewport.width = static_cast(swapChain->GetVkExtent().width); - viewport.height = static_cast(swapChain->GetVkExtent().height); - viewport.minDepth = 0.0f; - viewport.maxDepth = 1.0f; - - VkRect2D scissor = {}; - scissor.offset = { 0, 0 }; - scissor.extent = swapChain->GetVkExtent(); - - VkPipelineViewportStateCreateInfo viewportState = {}; - viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; - viewportState.viewportCount = 1; - viewportState.pViewports = &viewport; - viewportState.scissorCount = 1; - viewportState.pScissors = &scissor; - - // Rasterizer - VkPipelineRasterizationStateCreateInfo rasterizer = {}; - rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; - rasterizer.depthClampEnable = VK_FALSE; - rasterizer.rasterizerDiscardEnable = VK_FALSE; - rasterizer.polygonMode = VK_POLYGON_MODE_FILL; - rasterizer.lineWidth = 1.0f; - rasterizer.cullMode = VK_CULL_MODE_NONE; - rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; - rasterizer.depthBiasEnable = VK_FALSE; - rasterizer.depthBiasConstantFactor = 0.0f; - rasterizer.depthBiasClamp = 0.0f; - rasterizer.depthBiasSlopeFactor = 0.0f; - - // Multisampling (turned off here) - VkPipelineMultisampleStateCreateInfo multisampling = {}; - multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; - multisampling.sampleShadingEnable = VK_FALSE; - multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; - multisampling.minSampleShading = 1.0f; - multisampling.pSampleMask = nullptr; - multisampling.alphaToCoverageEnable = VK_FALSE; - multisampling.alphaToOneEnable = VK_FALSE; - - // Depth testing - VkPipelineDepthStencilStateCreateInfo depthStencil = {}; - depthStencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; - depthStencil.depthTestEnable = VK_TRUE; - depthStencil.depthWriteEnable = VK_TRUE; - depthStencil.depthCompareOp = VK_COMPARE_OP_LESS; - depthStencil.depthBoundsTestEnable = VK_FALSE; - depthStencil.minDepthBounds = 0.0f; - depthStencil.maxDepthBounds = 1.0f; - depthStencil.stencilTestEnable = VK_FALSE; - - // Color blending (turned off here, but showing options for learning) - // --> Configuration per attached framebuffer - VkPipelineColorBlendAttachmentState colorBlendAttachment = {}; - colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; - colorBlendAttachment.blendEnable = VK_FALSE; - colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE; - colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO; - colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD; - colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; - colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; - colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD; - - // --> Global color blending settings - VkPipelineColorBlendStateCreateInfo colorBlending = {}; - colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; - colorBlending.logicOpEnable = VK_FALSE; - colorBlending.logicOp = VK_LOGIC_OP_COPY; - colorBlending.attachmentCount = 1; - colorBlending.pAttachments = &colorBlendAttachment; - colorBlending.blendConstants[0] = 0.0f; - colorBlending.blendConstants[1] = 0.0f; - colorBlending.blendConstants[2] = 0.0f; - colorBlending.blendConstants[3] = 0.0f; - - std::vector descriptorSetLayouts = { cameraDescriptorSetLayout, modelDescriptorSetLayout }; - - // Pipeline layout: used to specify uniform values - VkPipelineLayoutCreateInfo pipelineLayoutInfo = {}; - pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; - pipelineLayoutInfo.setLayoutCount = static_cast(descriptorSetLayouts.size()); - pipelineLayoutInfo.pSetLayouts = descriptorSetLayouts.data(); - pipelineLayoutInfo.pushConstantRangeCount = 0; - pipelineLayoutInfo.pPushConstantRanges = 0; - - if (vkCreatePipelineLayout(logicalDevice, &pipelineLayoutInfo, nullptr, &grassPipelineLayout) != VK_SUCCESS) { - throw std::runtime_error("Failed to create pipeline layout"); - } - - // Tessellation state - VkPipelineTessellationStateCreateInfo tessellationInfo = {}; - tessellationInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO; - tessellationInfo.pNext = NULL; - tessellationInfo.flags = 0; - tessellationInfo.patchControlPoints = 1; - - // --- Create graphics pipeline --- - VkGraphicsPipelineCreateInfo pipelineInfo = {}; - pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; - pipelineInfo.stageCount = 4; - pipelineInfo.pStages = shaderStages; - pipelineInfo.pVertexInputState = &vertexInputInfo; - pipelineInfo.pInputAssemblyState = &inputAssembly; - pipelineInfo.pViewportState = &viewportState; - pipelineInfo.pRasterizationState = &rasterizer; - pipelineInfo.pMultisampleState = &multisampling; - pipelineInfo.pDepthStencilState = &depthStencil; - pipelineInfo.pColorBlendState = &colorBlending; - pipelineInfo.pTessellationState = &tessellationInfo; - pipelineInfo.pDynamicState = nullptr; - pipelineInfo.layout = grassPipelineLayout; - pipelineInfo.renderPass = renderPass; - pipelineInfo.subpass = 0; - pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; - pipelineInfo.basePipelineIndex = -1; - - if (vkCreateGraphicsPipelines(logicalDevice, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &grassPipeline) != VK_SUCCESS) { - throw std::runtime_error("Failed to create graphics pipeline"); - } - - // No need for the shader modules anymore - vkDestroyShaderModule(logicalDevice, vertShaderModule, nullptr); - vkDestroyShaderModule(logicalDevice, tescShaderModule, nullptr); - vkDestroyShaderModule(logicalDevice, teseShaderModule, nullptr); - vkDestroyShaderModule(logicalDevice, fragShaderModule, nullptr); + // --- Set up programmable shaders --- + VkShaderModule vertShaderModule = ShaderModule::Create("shaders/grass.vert.spv", logicalDevice); + VkShaderModule tescShaderModule = ShaderModule::Create("shaders/grass.tesc.spv", logicalDevice); + VkShaderModule teseShaderModule = ShaderModule::Create("shaders/grass.tese.spv", logicalDevice); + VkShaderModule fragShaderModule = ShaderModule::Create("shaders/grass.frag.spv", logicalDevice); + + // Assign each shader module to the appropriate stage in the pipeline + VkPipelineShaderStageCreateInfo vertShaderStageInfo = {}; + vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; + vertShaderStageInfo.module = vertShaderModule; + vertShaderStageInfo.pName = "main"; + + VkPipelineShaderStageCreateInfo tescShaderStageInfo = {}; + tescShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + tescShaderStageInfo.stage = VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT; + tescShaderStageInfo.module = tescShaderModule; + tescShaderStageInfo.pName = "main"; + + VkPipelineShaderStageCreateInfo teseShaderStageInfo = {}; + teseShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + teseShaderStageInfo.stage = VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT; + teseShaderStageInfo.module = teseShaderModule; + teseShaderStageInfo.pName = "main"; + + VkPipelineShaderStageCreateInfo fragShaderStageInfo = {}; + fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT; + fragShaderStageInfo.module = fragShaderModule; + fragShaderStageInfo.pName = "main"; + + VkPipelineShaderStageCreateInfo shaderStages[] = { vertShaderStageInfo, tescShaderStageInfo, teseShaderStageInfo, fragShaderStageInfo }; + + // --- Set up fixed-function stages --- + + // Vertex input + VkPipelineVertexInputStateCreateInfo vertexInputInfo = {}; + vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + + auto bindingDescription = Blade::getBindingDescription(); + auto attributeDescriptions = Blade::getAttributeDescriptions(); + + vertexInputInfo.vertexBindingDescriptionCount = 1; + vertexInputInfo.pVertexBindingDescriptions = &bindingDescription; + vertexInputInfo.vertexAttributeDescriptionCount = static_cast(attributeDescriptions.size()); + vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data(); + + // Input Assembly + VkPipelineInputAssemblyStateCreateInfo inputAssembly = {}; + inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_PATCH_LIST; + inputAssembly.primitiveRestartEnable = VK_FALSE; + + // Viewports and Scissors (rectangles that define in which regions pixels are stored) + VkViewport viewport = {}; + viewport.x = 0.0f; + viewport.y = 0.0f; + viewport.width = static_cast(swapChain->GetVkExtent().width); + viewport.height = static_cast(swapChain->GetVkExtent().height); + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + + VkRect2D scissor = {}; + scissor.offset = { 0, 0 }; + scissor.extent = swapChain->GetVkExtent(); + + VkPipelineViewportStateCreateInfo viewportState = {}; + viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewportState.viewportCount = 1; + viewportState.pViewports = &viewport; + viewportState.scissorCount = 1; + viewportState.pScissors = &scissor; + + // Rasterizer + VkPipelineRasterizationStateCreateInfo rasterizer = {}; + rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rasterizer.depthClampEnable = VK_FALSE; + rasterizer.rasterizerDiscardEnable = VK_FALSE; + rasterizer.polygonMode = VK_POLYGON_MODE_FILL; + rasterizer.lineWidth = 1.0f; + rasterizer.cullMode = VK_CULL_MODE_NONE; + rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; + rasterizer.depthBiasEnable = VK_FALSE; + rasterizer.depthBiasConstantFactor = 0.0f; + rasterizer.depthBiasClamp = 0.0f; + rasterizer.depthBiasSlopeFactor = 0.0f; + + // Multisampling (turned off here) + VkPipelineMultisampleStateCreateInfo multisampling = {}; + multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + multisampling.sampleShadingEnable = VK_FALSE; + multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + multisampling.minSampleShading = 1.0f; + multisampling.pSampleMask = nullptr; + multisampling.alphaToCoverageEnable = VK_FALSE; + multisampling.alphaToOneEnable = VK_FALSE; + + // Depth testing + VkPipelineDepthStencilStateCreateInfo depthStencil = {}; + depthStencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; + depthStencil.depthTestEnable = VK_TRUE; + depthStencil.depthWriteEnable = VK_TRUE; + depthStencil.depthCompareOp = VK_COMPARE_OP_LESS; + depthStencil.depthBoundsTestEnable = VK_FALSE; + depthStencil.minDepthBounds = 0.0f; + depthStencil.maxDepthBounds = 1.0f; + depthStencil.stencilTestEnable = VK_FALSE; + + // Color blending (turned off here, but showing options for learning) + // --> Configuration per attached framebuffer + VkPipelineColorBlendAttachmentState colorBlendAttachment = {}; + colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + colorBlendAttachment.blendEnable = VK_FALSE; + colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE; + colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO; + colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD; + colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; + colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; + colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD; + + // --> Global color blending settings + VkPipelineColorBlendStateCreateInfo colorBlending = {}; + colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + colorBlending.logicOpEnable = VK_FALSE; + colorBlending.logicOp = VK_LOGIC_OP_COPY; + colorBlending.attachmentCount = 1; + colorBlending.pAttachments = &colorBlendAttachment; + colorBlending.blendConstants[0] = 0.0f; + colorBlending.blendConstants[1] = 0.0f; + colorBlending.blendConstants[2] = 0.0f; + colorBlending.blendConstants[3] = 0.0f; + + std::vector descriptorSetLayouts = { cameraDescriptorSetLayout, grassDescriptorSetLayout }; + + // Pipeline layout: used to specify uniform values + VkPipelineLayoutCreateInfo pipelineLayoutInfo = {}; + pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipelineLayoutInfo.setLayoutCount = static_cast(descriptorSetLayouts.size()); + pipelineLayoutInfo.pSetLayouts = descriptorSetLayouts.data(); + pipelineLayoutInfo.pushConstantRangeCount = 0; + pipelineLayoutInfo.pPushConstantRanges = 0; + + if (vkCreatePipelineLayout(logicalDevice, &pipelineLayoutInfo, nullptr, &grassPipelineLayout) != VK_SUCCESS) { + throw std::runtime_error("Failed to create pipeline layout"); + } + + // Tessellation state + VkPipelineTessellationStateCreateInfo tessellationInfo = {}; + tessellationInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO; + tessellationInfo.pNext = NULL; + tessellationInfo.flags = 0; + tessellationInfo.patchControlPoints = 1; + + // --- Create graphics pipeline --- + VkGraphicsPipelineCreateInfo pipelineInfo = {}; + pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + pipelineInfo.stageCount = 4; + pipelineInfo.pStages = shaderStages; + pipelineInfo.pVertexInputState = &vertexInputInfo; + pipelineInfo.pInputAssemblyState = &inputAssembly; + pipelineInfo.pViewportState = &viewportState; + pipelineInfo.pRasterizationState = &rasterizer; + pipelineInfo.pMultisampleState = &multisampling; + pipelineInfo.pDepthStencilState = &depthStencil; + pipelineInfo.pColorBlendState = &colorBlending; + pipelineInfo.pTessellationState = &tessellationInfo; + pipelineInfo.pDynamicState = nullptr; + pipelineInfo.layout = grassPipelineLayout; + pipelineInfo.renderPass = renderPass; + pipelineInfo.subpass = 0; + pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; + pipelineInfo.basePipelineIndex = -1; + + if (vkCreateGraphicsPipelines(logicalDevice, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &grassPipeline) != VK_SUCCESS) { + throw std::runtime_error("Failed to create graphics pipeline"); + } + + // No need for the shader modules anymore + vkDestroyShaderModule(logicalDevice, vertShaderModule, nullptr); + vkDestroyShaderModule(logicalDevice, tescShaderModule, nullptr); + vkDestroyShaderModule(logicalDevice, teseShaderModule, nullptr); + vkDestroyShaderModule(logicalDevice, fragShaderModule, nullptr); } void Renderer::CreateComputePipeline() { - // Set up programmable shaders - VkShaderModule computeShaderModule = ShaderModule::Create("shaders/compute.comp.spv", logicalDevice); - - VkPipelineShaderStageCreateInfo computeShaderStageInfo = {}; - computeShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - computeShaderStageInfo.stage = VK_SHADER_STAGE_COMPUTE_BIT; - computeShaderStageInfo.module = computeShaderModule; - computeShaderStageInfo.pName = "main"; - - // TODO: Add the compute dsecriptor set layout you create to this list - std::vector descriptorSetLayouts = { cameraDescriptorSetLayout, timeDescriptorSetLayout }; - - // Create pipeline layout - VkPipelineLayoutCreateInfo pipelineLayoutInfo = {}; - pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; - pipelineLayoutInfo.setLayoutCount = static_cast(descriptorSetLayouts.size()); - pipelineLayoutInfo.pSetLayouts = descriptorSetLayouts.data(); - pipelineLayoutInfo.pushConstantRangeCount = 0; - pipelineLayoutInfo.pPushConstantRanges = 0; - - if (vkCreatePipelineLayout(logicalDevice, &pipelineLayoutInfo, nullptr, &computePipelineLayout) != VK_SUCCESS) { - throw std::runtime_error("Failed to create pipeline layout"); - } - - // Create compute pipeline - VkComputePipelineCreateInfo pipelineInfo = {}; - pipelineInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO; - pipelineInfo.stage = computeShaderStageInfo; - pipelineInfo.layout = computePipelineLayout; - pipelineInfo.pNext = nullptr; - pipelineInfo.flags = 0; - pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; - pipelineInfo.basePipelineIndex = -1; - - if (vkCreateComputePipelines(logicalDevice, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &computePipeline) != VK_SUCCESS) { - throw std::runtime_error("Failed to create compute pipeline"); - } - - // No need for shader modules anymore - vkDestroyShaderModule(logicalDevice, computeShaderModule, nullptr); + // Set up programmable shaders + VkShaderModule computeShaderModule = ShaderModule::Create("shaders/compute.comp.spv", logicalDevice); + + VkPipelineShaderStageCreateInfo computeShaderStageInfo = {}; + computeShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + computeShaderStageInfo.stage = VK_SHADER_STAGE_COMPUTE_BIT; + computeShaderStageInfo.module = computeShaderModule; + computeShaderStageInfo.pName = "main"; + + // TODO: Add the compute dsecriptor set layout you create to this list + std::vector descriptorSetLayouts = { cameraDescriptorSetLayout, timeDescriptorSetLayout, computeDescriptorSetLayout }; + + // Create pipeline layout + VkPipelineLayoutCreateInfo pipelineLayoutInfo = {}; + pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipelineLayoutInfo.setLayoutCount = static_cast(descriptorSetLayouts.size()); + pipelineLayoutInfo.pSetLayouts = descriptorSetLayouts.data(); + pipelineLayoutInfo.pushConstantRangeCount = 0; + pipelineLayoutInfo.pPushConstantRanges = 0; + + if (vkCreatePipelineLayout(logicalDevice, &pipelineLayoutInfo, nullptr, &computePipelineLayout) != VK_SUCCESS) { + throw std::runtime_error("Failed to create pipeline layout"); + } + + // Create compute pipeline + VkComputePipelineCreateInfo pipelineInfo = {}; + pipelineInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO; + pipelineInfo.stage = computeShaderStageInfo; + pipelineInfo.layout = computePipelineLayout; + pipelineInfo.pNext = nullptr; + pipelineInfo.flags = 0; + pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; + pipelineInfo.basePipelineIndex = -1; + + if (vkCreateComputePipelines(logicalDevice, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &computePipeline) != VK_SUCCESS) { + throw std::runtime_error("Failed to create compute pipeline"); + } + + // No need for shader modules anymore + vkDestroyShaderModule(logicalDevice, computeShaderModule, nullptr); } void Renderer::CreateFrameResources() { - imageViews.resize(swapChain->GetCount()); - - for (uint32_t i = 0; i < swapChain->GetCount(); i++) { - // --- Create an image view for each swap chain image --- - VkImageViewCreateInfo createInfo = {}; - createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - createInfo.image = swapChain->GetVkImage(i); - - // Specify how the image data should be interpreted - createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; - createInfo.format = swapChain->GetVkImageFormat(); - - // Specify color channel mappings (can be used for swizzling) - createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; - createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; - createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; - createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; - - // Describe the image's purpose and which part of the image should be accessed - createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - createInfo.subresourceRange.baseMipLevel = 0; - createInfo.subresourceRange.levelCount = 1; - createInfo.subresourceRange.baseArrayLayer = 0; - createInfo.subresourceRange.layerCount = 1; - - // Create the image view - if (vkCreateImageView(logicalDevice, &createInfo, nullptr, &imageViews[i]) != VK_SUCCESS) { - throw std::runtime_error("Failed to create image views"); - } - } - - VkFormat depthFormat = device->GetInstance()->GetSupportedFormat({ VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT }, VK_IMAGE_TILING_OPTIMAL, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT); - // CREATE DEPTH IMAGE - Image::Create(device, - swapChain->GetVkExtent().width, - swapChain->GetVkExtent().height, - depthFormat, - VK_IMAGE_TILING_OPTIMAL, - VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, - VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, - depthImage, - depthImageMemory - ); - - depthImageView = Image::CreateView(device, depthImage, depthFormat, VK_IMAGE_ASPECT_DEPTH_BIT); - - // Transition the image for use as depth-stencil - Image::TransitionLayout(device, graphicsCommandPool, depthImage, depthFormat, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); - - - // CREATE FRAMEBUFFERS - framebuffers.resize(swapChain->GetCount()); - for (size_t i = 0; i < swapChain->GetCount(); i++) { - std::vector attachments = { - imageViews[i], - depthImageView - }; - - VkFramebufferCreateInfo framebufferInfo = {}; - framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; - framebufferInfo.renderPass = renderPass; - framebufferInfo.attachmentCount = static_cast(attachments.size()); - framebufferInfo.pAttachments = attachments.data(); - framebufferInfo.width = swapChain->GetVkExtent().width; - framebufferInfo.height = swapChain->GetVkExtent().height; - framebufferInfo.layers = 1; - - if (vkCreateFramebuffer(logicalDevice, &framebufferInfo, nullptr, &framebuffers[i]) != VK_SUCCESS) { - throw std::runtime_error("Failed to create framebuffer"); - } - - } + imageViews.resize(swapChain->GetCount()); + + for (uint32_t i = 0; i < swapChain->GetCount(); i++) { + // --- Create an image view for each swap chain image --- + VkImageViewCreateInfo createInfo = {}; + createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + createInfo.image = swapChain->GetVkImage(i); + + // Specify how the image data should be interpreted + createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + createInfo.format = swapChain->GetVkImageFormat(); + + // Specify color channel mappings (can be used for swizzling) + createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; + + // Describe the image's purpose and which part of the image should be accessed + createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + createInfo.subresourceRange.baseMipLevel = 0; + createInfo.subresourceRange.levelCount = 1; + createInfo.subresourceRange.baseArrayLayer = 0; + createInfo.subresourceRange.layerCount = 1; + + // Create the image view + if (vkCreateImageView(logicalDevice, &createInfo, nullptr, &imageViews[i]) != VK_SUCCESS) { + throw std::runtime_error("Failed to create image views"); + } + } + + VkFormat depthFormat = device->GetInstance()->GetSupportedFormat({ VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT }, VK_IMAGE_TILING_OPTIMAL, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT); + // CREATE DEPTH IMAGE + Image::Create(device, + swapChain->GetVkExtent().width, + swapChain->GetVkExtent().height, + depthFormat, + VK_IMAGE_TILING_OPTIMAL, + VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + depthImage, + depthImageMemory + ); + + depthImageView = Image::CreateView(device, depthImage, depthFormat, VK_IMAGE_ASPECT_DEPTH_BIT); + + // Transition the image for use as depth-stencil + Image::TransitionLayout(device, graphicsCommandPool, depthImage, depthFormat, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); + + + // CREATE FRAMEBUFFERS + framebuffers.resize(swapChain->GetCount()); + for (size_t i = 0; i < swapChain->GetCount(); i++) { + std::vector attachments = { + imageViews[i], + depthImageView + }; + + VkFramebufferCreateInfo framebufferInfo = {}; + framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + framebufferInfo.renderPass = renderPass; + framebufferInfo.attachmentCount = static_cast(attachments.size()); + framebufferInfo.pAttachments = attachments.data(); + framebufferInfo.width = swapChain->GetVkExtent().width; + framebufferInfo.height = swapChain->GetVkExtent().height; + framebufferInfo.layers = 1; + + if (vkCreateFramebuffer(logicalDevice, &framebufferInfo, nullptr, &framebuffers[i]) != VK_SUCCESS) { + throw std::runtime_error("Failed to create framebuffer"); + } + + } } void Renderer::DestroyFrameResources() { - for (size_t i = 0; i < imageViews.size(); i++) { - vkDestroyImageView(logicalDevice, imageViews[i], nullptr); - } + for (size_t i = 0; i < imageViews.size(); i++) { + vkDestroyImageView(logicalDevice, imageViews[i], nullptr); + } - vkDestroyImageView(logicalDevice, depthImageView, nullptr); - vkFreeMemory(logicalDevice, depthImageMemory, nullptr); - vkDestroyImage(logicalDevice, depthImage, nullptr); + vkDestroyImageView(logicalDevice, depthImageView, nullptr); + vkFreeMemory(logicalDevice, depthImageMemory, nullptr); + vkDestroyImage(logicalDevice, depthImage, nullptr); - for (size_t i = 0; i < framebuffers.size(); i++) { - vkDestroyFramebuffer(logicalDevice, framebuffers[i], nullptr); - } + for (size_t i = 0; i < framebuffers.size(); i++) { + vkDestroyFramebuffer(logicalDevice, framebuffers[i], nullptr); + } } void Renderer::RecreateFrameResources() { - vkDestroyPipeline(logicalDevice, graphicsPipeline, nullptr); - vkDestroyPipeline(logicalDevice, grassPipeline, nullptr); - vkDestroyPipelineLayout(logicalDevice, graphicsPipelineLayout, nullptr); - vkDestroyPipelineLayout(logicalDevice, grassPipelineLayout, nullptr); - vkFreeCommandBuffers(logicalDevice, graphicsCommandPool, static_cast(commandBuffers.size()), commandBuffers.data()); - - DestroyFrameResources(); - CreateFrameResources(); - CreateGraphicsPipeline(); - CreateGrassPipeline(); - RecordCommandBuffers(); + vkDestroyPipeline(logicalDevice, graphicsPipeline, nullptr); + vkDestroyPipeline(logicalDevice, grassPipeline, nullptr); + vkDestroyPipelineLayout(logicalDevice, graphicsPipelineLayout, nullptr); + vkDestroyPipelineLayout(logicalDevice, grassPipelineLayout, nullptr); + vkFreeCommandBuffers(logicalDevice, graphicsCommandPool, static_cast(commandBuffers.size()), commandBuffers.data()); + + DestroyFrameResources(); + CreateFrameResources(); + CreateGraphicsPipeline(); + CreateGrassPipeline(); + RecordCommandBuffers(); } void Renderer::RecordComputeCommandBuffer() { - // Specify the command pool and number of buffers to allocate - VkCommandBufferAllocateInfo allocInfo = {}; - allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - allocInfo.commandPool = computeCommandPool; - allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - allocInfo.commandBufferCount = 1; - - if (vkAllocateCommandBuffers(logicalDevice, &allocInfo, &computeCommandBuffer) != VK_SUCCESS) { - throw std::runtime_error("Failed to allocate command buffers"); - } - - VkCommandBufferBeginInfo beginInfo = {}; - beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - beginInfo.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT; - beginInfo.pInheritanceInfo = nullptr; - - // ~ Start recording ~ - if (vkBeginCommandBuffer(computeCommandBuffer, &beginInfo) != VK_SUCCESS) { - throw std::runtime_error("Failed to begin recording compute command buffer"); - } - - // Bind to the compute pipeline - vkCmdBindPipeline(computeCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, computePipeline); - - // Bind camera descriptor set - vkCmdBindDescriptorSets(computeCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, computePipelineLayout, 0, 1, &cameraDescriptorSet, 0, nullptr); - - // Bind descriptor set for time uniforms - vkCmdBindDescriptorSets(computeCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, computePipelineLayout, 1, 1, &timeDescriptorSet, 0, nullptr); - - // TODO: For each group of blades bind its descriptor set and dispatch - - // ~ End recording ~ - if (vkEndCommandBuffer(computeCommandBuffer) != VK_SUCCESS) { - throw std::runtime_error("Failed to record compute command buffer"); - } + // Specify the command pool and number of buffers to allocate + VkCommandBufferAllocateInfo allocInfo = {}; + allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + allocInfo.commandPool = computeCommandPool; + allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + allocInfo.commandBufferCount = 1; + + if (vkAllocateCommandBuffers(logicalDevice, &allocInfo, &computeCommandBuffer) != VK_SUCCESS) { + throw std::runtime_error("Failed to allocate command buffers"); + } + + VkCommandBufferBeginInfo beginInfo = {}; + beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + beginInfo.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT; + beginInfo.pInheritanceInfo = nullptr; + + // ~ Start recording ~ + if (vkBeginCommandBuffer(computeCommandBuffer, &beginInfo) != VK_SUCCESS) { + throw std::runtime_error("Failed to begin recording compute command buffer"); + } + + // Bind to the compute pipeline + vkCmdBindPipeline(computeCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, computePipeline); + + // Bind camera descriptor set + vkCmdBindDescriptorSets(computeCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, computePipelineLayout, 0, 1, &cameraDescriptorSet, 0, nullptr); + + // Bind descriptor set for time uniforms + vkCmdBindDescriptorSets(computeCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, computePipelineLayout, 1, 1, &timeDescriptorSet, 0, nullptr); + + // For each group of blades bind its descriptor set and dispatch + for (int i = 0; i < scene->GetBlades().size(); ++i) { + vkCmdBindDescriptorSets(computeCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, computePipelineLayout, 2, 1, &computeDescriptorSets[i], 0, nullptr); + vkCmdDispatch(computeCommandBuffer, (int)(NUM_BLADES / WORKGROUP_SIZE + 1), 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() }; + //VkBuffer vertexBuffers[] = { scene->GetBlades()[j]->GetBladesBuffer() }; + + VkDeviceSize offsets[] = { 0 }; + + vkCmdBindVertexBuffers(commandBuffers[i], 0, 1, vertexBuffers, offsets); + + // Bind the descriptor set for each grass blades model + vkCmdBindDescriptorSets(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, grassPipelineLayout, 1, 1, &grassDescriptorSets[j], 0, nullptr); + + + 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, modelDescriptorSetLayout, nullptr); + vkDestroyDescriptorSetLayout(logicalDevice, timeDescriptorSetLayout, nullptr); + vkDestroyDescriptorSetLayout(logicalDevice, grassDescriptorSetLayout, nullptr); + vkDestroyDescriptorSetLayout(logicalDevice, computeDescriptorSetLayout, nullptr); - vkDestroyRenderPass(logicalDevice, renderPass, nullptr); - DestroyFrameResources(); - vkDestroyCommandPool(logicalDevice, computeCommandPool, nullptr); - vkDestroyCommandPool(logicalDevice, graphicsCommandPool, 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..4d6e8e8 100644 --- a/src/Renderer.h +++ b/src/Renderer.h @@ -1,3 +1,91 @@ +//#pragma once +// +//#include "Device.h" +//#include "SwapChain.h" +//#include "Scene.h" +//#include "Camera.h" +// +//class Renderer { +//public: +// Renderer() = delete; +// Renderer(Device* device, SwapChain* swapChain, Scene* scene, Camera* camera); +// ~Renderer(); +// +// void CreateCommandPools(); +// +// void CreateRenderPass(); +// +// void CreateCameraDescriptorSetLayout(); +// void CreateModelDescriptorSetLayout(); +// void CreateTimeDescriptorSetLayout(); +// void CreateComputeDescriptorSetLayout(); +// void CreateGrassDescriptorSetLayout(); +// +// void CreateDescriptorPool(); +// +// void CreateCameraDescriptorSet(); +// void CreateModelDescriptorSets(); +// void CreateGrassDescriptorSets(); +// void CreateTimeDescriptorSet(); +// void CreateComputeDescriptorSets(); +// +// void CreateGraphicsPipeline(); +// void CreateGrassPipeline(); +// void CreateComputePipeline(); +// +// void CreateFrameResources(); +// void DestroyFrameResources(); +// void RecreateFrameResources(); +// +// void RecordCommandBuffers(); +// void RecordComputeCommandBuffer(); +// +// void Frame(); +// +//private: +// Device* device; +// VkDevice logicalDevice; +// SwapChain* swapChain; +// Scene* scene; +// Camera* camera; +// +// VkCommandPool graphicsCommandPool; +// VkCommandPool computeCommandPool; +// +// VkRenderPass renderPass; +// +// VkDescriptorSetLayout cameraDescriptorSetLayout; +// VkDescriptorSetLayout modelDescriptorSetLayout; +// VkDescriptorSetLayout timeDescriptorSetLayout; +// VkDescriptorSetLayout computeDescriptorLayout; +// VkDescriptorSetLayout grassDescriptorLayout; +// +// VkDescriptorPool descriptorPool; +// +// VkDescriptorSet cameraDescriptorSet; +// std::vector modelDescriptorSets; +// VkDescriptorSet timeDescriptorSet; +// std::vector computeDescriptorSets; +// std::vector grassDescriptorSets; +// +// VkPipelineLayout graphicsPipelineLayout; +// VkPipelineLayout grassPipelineLayout; +// VkPipelineLayout computePipelineLayout; +// +// VkPipeline graphicsPipeline; +// VkPipeline grassPipeline; +// VkPipeline computePipeline; +// +// std::vector imageViews; +// VkImage depthImage; +// VkDeviceMemory depthImageMemory; +// VkImageView depthImageView; +// std::vector framebuffers; +// +// std::vector commandBuffers; +// VkCommandBuffer computeCommandBuffer; +//}; + #pragma once #include "Device.h" @@ -7,76 +95,82 @@ class Renderer { public: - Renderer() = delete; - Renderer(Device* device, SwapChain* swapChain, Scene* scene, Camera* camera); - ~Renderer(); + Renderer() = delete; + Renderer(Device* device, SwapChain* swapChain, Scene* scene, Camera* camera); + ~Renderer(); + + void CreateCommandPools(); - void CreateCommandPools(); + void CreateRenderPass(); - void CreateRenderPass(); + void CreateCameraDescriptorSetLayout(); + void CreateModelDescriptorSetLayout(); + void CreateGrassDescriptorSetLayout(); + void CreateTimeDescriptorSetLayout(); - void CreateCameraDescriptorSetLayout(); - void CreateModelDescriptorSetLayout(); - void CreateTimeDescriptorSetLayout(); - void CreateComputeDescriptorSetLayout(); + void CreateComputeDescriptorSetLayout(); - void CreateDescriptorPool(); + void CreateDescriptorPool(); - void CreateCameraDescriptorSet(); - void CreateModelDescriptorSets(); - void CreateGrassDescriptorSets(); - void CreateTimeDescriptorSet(); - void CreateComputeDescriptorSets(); + void CreateCameraDescriptorSet(); + void CreateModelDescriptorSets(); + void CreateGrassDescriptorSets(); + void CreateTimeDescriptorSet(); + void CreateComputeDescriptorSets(); - void CreateGraphicsPipeline(); - void CreateGrassPipeline(); - void CreateComputePipeline(); + void CreateGraphicsPipeline(); + void CreateGrassPipeline(); + void CreateComputePipeline(); - void CreateFrameResources(); - void DestroyFrameResources(); - void RecreateFrameResources(); + void CreateFrameResources(); + void DestroyFrameResources(); + void RecreateFrameResources(); - void RecordCommandBuffers(); - void RecordComputeCommandBuffer(); + void RecordCommandBuffers(); + void RecordComputeCommandBuffer(); - void Frame(); + void Frame(); private: - Device* device; - VkDevice logicalDevice; - SwapChain* swapChain; - Scene* scene; - Camera* camera; - - VkCommandPool graphicsCommandPool; - VkCommandPool computeCommandPool; - - VkRenderPass renderPass; - - VkDescriptorSetLayout cameraDescriptorSetLayout; - VkDescriptorSetLayout modelDescriptorSetLayout; - VkDescriptorSetLayout timeDescriptorSetLayout; - - VkDescriptorPool descriptorPool; - - VkDescriptorSet cameraDescriptorSet; - std::vector modelDescriptorSets; - VkDescriptorSet timeDescriptorSet; - - VkPipelineLayout graphicsPipelineLayout; - VkPipelineLayout grassPipelineLayout; - VkPipelineLayout computePipelineLayout; - - VkPipeline graphicsPipeline; - VkPipeline grassPipeline; - VkPipeline computePipeline; - - std::vector imageViews; - VkImage depthImage; - VkDeviceMemory depthImageMemory; - VkImageView depthImageView; - std::vector framebuffers; - - std::vector commandBuffers; - VkCommandBuffer computeCommandBuffer; -}; + Device* device; + VkDevice logicalDevice; + SwapChain* swapChain; + Scene* scene; + Camera* camera; + + VkCommandPool graphicsCommandPool; + VkCommandPool computeCommandPool; + + VkRenderPass renderPass; + + VkDescriptorSetLayout cameraDescriptorSetLayout; + VkDescriptorSetLayout modelDescriptorSetLayout; + VkDescriptorSetLayout grassDescriptorSetLayout; + VkDescriptorSetLayout timeDescriptorSetLayout; + VkDescriptorSetLayout computeDescriptorSetLayout; + + VkDescriptorPool descriptorPool; + + VkDescriptorSet cameraDescriptorSet; + std::vector modelDescriptorSets; + VkDescriptorSet timeDescriptorSet; + std::vector grassDescriptorSets; + std::vector computeDescriptorSets; + + VkPipelineLayout graphicsPipelineLayout; + VkPipelineLayout grassPipelineLayout; + VkPipelineLayout computePipelineLayout; + + VkPipeline graphicsPipeline; + VkPipeline grassPipeline; + VkPipeline computePipeline; + + std::vector imageViews; + VkImage depthImage; + VkDeviceMemory depthImageMemory; + VkImageView depthImageView; + std::vector framebuffers; + + std::vector commandBuffers; + VkCommandBuffer computeCommandBuffer; +}; \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 8bf822b..84dad64 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -67,7 +67,7 @@ namespace { int main() { static constexpr char* applicationName = "Vulkan Grass Rendering"; - InitializeWindow(640, 480, applicationName); + InitializeWindow(1000, 720, applicationName); unsigned int glfwExtensionCount = 0; const char** glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); @@ -116,7 +116,7 @@ int main() { grassImageMemory ); - float planeDim = 15.f; + float planeDim = 25.f; float halfWidth = planeDim * 0.5f; Model* plane = new Model(device, transferCommandPool, { @@ -143,10 +143,19 @@ int main() { glfwSetMouseButtonCallback(GetGLFWWindow(), mouseDownCallback); glfwSetCursorPosCallback(GetGLFWWindow(), mouseMoveCallback); + int start = GetTickCount(); + int frame = 0; + while (!ShouldQuit()) { glfwPollEvents(); scene->UpdateTime(); renderer->Frame(); + frame++; + if (frame == 500) + { + int total = GetTickCount() - start; + printf("ms per frame%f\n", float(total / 500.0)); + } } vkDeviceWaitIdle(device->GetVkDevice()); diff --git a/src/shaders/compute.comp b/src/shaders/compute.comp index 0fd0224..4329820 100644 --- a/src/shaders/compute.comp +++ b/src/shaders/compute.comp @@ -28,25 +28,181 @@ 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; - -bool inBounds(float value, float bounds) { + +layout(set = 2, binding = 0) buffer Blades{ + Blade blades[]; +}; + +layout(set = 2,binding = 1) buffer CulledBlades{ + Blade culledBlades[]; +}; + + layout(set = 2, binding = 2) buffer NumBlades { + uint vertexCount; // Write the number of blades remaining here + uint instanceCount; // = 1 + uint firstVertex; // = 0 + uint firstInstance; // = 0 + } numBlades; + +bool inbounds(float value, float bounds) { return (value >= -bounds) && (value <= bounds); } +float random (vec2 st) { + return fract(sin(dot(st.xy, + vec2(12.9898,78.233)))* + 43758.5453123); +} +//acoording to https://thebookofshaders.com/10/ + 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 + uint index = gl_GlobalInvocationID.x; + + Blade CurBlade = blades[index]; + vec3 CurV0 = CurBlade.v0.xyz; + vec3 CurV1 = CurBlade.v1.xyz; + vec3 CurV2 = CurBlade.v2.xyz; + vec3 CurUp = CurBlade.up.xyz; + + //collision: + vec3 mm = 0.25*CurV0+0.5*CurV1+0.5*CurV2; + + + float bladeheight = CurBlade.v1.w; + float Fangle = CurBlade.v0.w; + //Gravity + //environmental g + vec3 gE = normalize(vec3(0,-1,0)) * 9.8f; + //Front Gravity + vec3 width_dir = (vec3(sin(Fangle), 0, cos(Fangle))); + vec3 front_dir = normalize(cross(CurUp, width_dir)); + vec3 gF = 0.25 * length(gE) * front_dir; + vec3 g = gE + gF; + //Recovery + float stiffness = CurBlade.up.w; + //initial v2 + vec3 iv2 = CurV0 + normalize(CurUp) * bladeheight; + + vec3 r = (iv2 - CurV2) * stiffness ; + + + //normal wind pattern + //vec3 wind_dir = normalize(vec3(1, 0, 1)); + + //center wind pattern + vec3 wind_dir = normalize(-vec3(1,0,1)+CurV0); + + vec3 spcenter = 8*vec3(sin(totalTime),0,cos(totalTime)); + float wind_speed = 5.0; + float wind_power = 10.0; + float waveInterval = 1.2; + //if(length(CurV0-spcenter)<4) + //{ + //wind_dir = vec3(1,0,1); + //wind_power = 500; + //waveInterval = 100; + //} + + vec3 tocent = CurV0-vec3(1,1,1); + vec3 tangent = normalize(cross(tocent,CurUp)); + tangent = tangent-tocent*0.08; + //vec3 wind_dir = tangent; + + //float waveInterval = 1.8; + + float wavecoeff = 1*(cos(( dot(CurV0,wind_dir)- wind_speed * totalTime) / waveInterval) ); + float normalmag = 0; + //Wind + //directional alignment + float fd = 1 - abs(dot(wind_dir, normalize(CurV2 - CurV0))); + //straightness + float fr = dot((CurV2 - CurV0), CurUp) / bladeheight; + + //float wind_power = 5.0*(1-distance(CurV0,vec3(1,1,1))); + vec3 w = wind_dir * wind_power * (normalmag+wavecoeff) * fd * fr; + //Total Force + vec3 tv2 = (g + r + w) * deltaTime; + vec3 fv2 = CurV2 + tv2; + //State Validation + //: v2 must not be pushed beneath the ground, the position of v1 has to + //be set according to the position of v2, and the length of the curve + //must be equal to the height of the blade of grass. + + + + fv2 = fv2 - CurUp * min(dot(CurUp, (fv2 - CurV0)), 0); + + float l_proj = length(fv2 - CurV0 - CurUp * dot((fv2 - CurV0),CurUp)); + + vec3 fv1 = CurV0 + bladeheight * CurUp * max((1 - l_proj / bladeheight), 0.05*max(l_proj / bladeheight, 1)); + + float L0 = distance(fv2, CurV0); + float L1 = distance(fv2, fv1) + distance(fv1, CurV0); + float L = (2.0*L0 + (3.0-1.0)*L1)/(3.0+1.0); + CurBlade.v1.xyz = CurV0 + (bladeheight / L)*(fv1 - CurV0); + CurBlade.v2.xyz = CurBlade.v1.xyz + (bladeheight / L)*(fv2 - fv1); + blades[index] = CurBlade; + // 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 + CurV1 = CurBlade.v1.xyz; + CurV2 = CurBlade.v2.xyz; + //Orientation culling + bool cullorientation = false; + + vec3 viewdir = normalize((inverse(camera.proj*camera.view)*vec4(0,0,1,0)).xyz); + float Epsilon = 0.2; + if(abs(dot(normalize(width_dir), viewdir)) < Epsilon) + cullorientation = true; + + //View-Frustum Culling + bool cullviewfrustum = true; + vec3 midpt = 0.25 * CurV0 + 0.5 * CurV1 + 0.25 * CurV2; + mat4 vp = camera.proj * camera.view; + vec4 ndcV0 = vp * vec4(CurV0, 1.0f); + vec4 ndcV2 = vp * vec4(CurV2, 1.0f); + vec4 ndcmidpt = vp * vec4(midpt, 1.0f); + ndcV0/=ndcV0.w; + ndcV2/=ndcV2.w; + ndcmidpt/=ndcmidpt.w; + float tolerance = 0.2; + + if(inbounds(ndcV0.x,1+tolerance) &&inbounds(ndcV0.y,1+tolerance) || inbounds(ndcV2.x,1+tolerance) && inbounds(ndcV2.y,1+tolerance) + || inbounds(ndcmidpt.x,1+tolerance) && inbounds(ndcmidpt.y,1+tolerance) ) + { + cullviewfrustum = false; + } + //Distance Culling + bool culldis = false; + float far = 50; + float close = 0.1; + float interval = 10; + int count = 5; + vec4 v0CampSpace = camera.view*vec4(CurV0,1.0); + + if(-v0CampSpace.z>far||-v0CampSpace.z1.0) culldis = true; + } + + cullorientation = false; + cullviewfrustum = false; + culldis = false; + + if(!cullorientation && !cullviewfrustum && !culldis){ + culledBlades[atomicAdd(numBlades.vertexCount , 1)] = CurBlade; + } + //culledblades[atomicAdd(numBlades.vertexCount,1)] = blades[gl_GlobalInvocationID.x]; // TODO: Apply forces on every blade and update the vertices in the buffer // TODO: Cull blades that are too far away or not in the camera frustum and write them diff --git a/src/shaders/grass.frag b/src/shaders/grass.frag index c7df157..caa1ddc 100644 --- a/src/shaders/grass.frag +++ b/src/shaders/grass.frag @@ -6,12 +6,31 @@ layout(set = 0, binding = 0) uniform CameraBufferObject { mat4 proj; } camera; +layout(location = 0) in vec4 worldpos; +layout(location = 1) in vec3 worldnormal; +layout(location = 2) in vec2 tex_coords; + + // TODO: Declare fragment shader inputs layout(location = 0) out vec4 outColor; void main() { // TODO: Compute fragment color + vec3 upper_color = vec3(0.3,0.9,0.1); + vec3 lower_color = vec3(0.0,0.4,0.1); + float ambient = 0.2; + vec3 color1 = vec3(0.4, 0.9, 0.3); + vec3 color2 = vec3( 0.2, 0.1, 0.2); + + vec3 diff = mix(color2, color1, tex_coords.y); + vec3 lightDir = normalize(vec3(-1.0, 3.0, -2.0)); + + vec3 lambert = diff *(clamp(dot(worldnormal, lightDir), 0.1, 1.0)+ ambient) ; + + vec3 col1 = vec3( 0.2, 0.1, 0.2); + vec3 col2 = vec3(0.4, 0.9, 0.3); - outColor = vec4(1.0); + outColor = vec4(lambert,1); + //outColor = vec4(mix(col1,col2,tex_coords.y),1.0); } diff --git a/src/shaders/grass.tesc b/src/shaders/grass.tesc index f9ffd07..c40d06a 100644 --- a/src/shaders/grass.tesc +++ b/src/shaders/grass.tesc @@ -8,19 +8,33 @@ layout(set = 0, binding = 0) uniform CameraBufferObject { mat4 proj; } camera; +layout(location = 0) in vec4 tescV1[]; +layout(location = 1) in vec4 tescV2[]; +layout(location = 2) in vec4 tescUp[]; +layout(location = 3) in vec4 tescBitangent[]; + +layout(location = 0) patch out vec4 teseV1; +layout(location = 1) patch out vec4 teseV2; +layout(location = 2) patch out vec4 teseUp; +layout(location = 3) patch out vec4 teseBitangent; + // TODO: Declare tessellation control shader inputs and outputs void main() { // Don't move the origin location of the patch gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position; + teseV1 = tescV1[0]; + teseV2 = tescV2[0]; + teseUp = tescUp[0]; + teseBitangent = tescBitangent[0]; // TODO: Write any shader outputs // 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] = 2.0; + gl_TessLevelInner[1] = 20.0; + gl_TessLevelOuter[0] = 20.0; + gl_TessLevelOuter[1] = 2.0; + gl_TessLevelOuter[2] = 20.0; + gl_TessLevelOuter[3] = 2.0; } diff --git a/src/shaders/grass.tese b/src/shaders/grass.tese index 751fff6..f414f3c 100644 --- a/src/shaders/grass.tese +++ b/src/shaders/grass.tese @@ -8,11 +8,38 @@ layout(set = 0, binding = 0) uniform CameraBufferObject { mat4 proj; } camera; +layout(location = 0) patch in vec4 teseV1; +layout(location = 1) patch in vec4 teseV2; +layout(location = 2) patch in vec4 teseUp; +layout(location = 3) patch in vec4 teseBitangent; + +layout(location = 0) out vec4 pos; +layout(location = 1) out vec3 normal; +layout(location = 2) out vec2 texCoords; + + // TODO: Declare tessellation evaluation shader inputs and outputs void main() { - float u = gl_TessCoord.x; +mat4 vp = camera.proj * camera.view; + float u = gl_TessCoord.x; float v = gl_TessCoord.y; - // TODO: Use u and v to parameterize along the grass blade and output positions for each vertex of the grass blade + vec3 v0 = gl_in[0].gl_Position.xyz; + vec3 a = v0 + v * (teseV1.xyz - v0); + vec3 b = teseV1.xyz + v * (teseV2.xyz - teseV1.xyz); + vec3 c = a + v * (b - a); + vec3 t1 = teseBitangent.xyz; + float w = teseV2.w; + vec3 c0 = c - w * t1 * 0.5; + vec3 c1 = c + w * t1 * 0.5; + vec3 t0 = normalize(b-a); + vec3 n = normalize(cross(t0,t1)); + float threshold = 0.35; + float t = 0.5 + (u - 0.5) * (1 - max(v - threshold, 0)/(1 - threshold)); + vec3 p = (1 - t) * c0 + t * c1; + gl_Position = vp * vec4(p, 1.0); + pos = vec4(p, 1.0); + normal = n; + texCoords = vec2(u,v); } diff --git a/src/shaders/grass.vert b/src/shaders/grass.vert index db9dfe9..3435b5a 100644 --- a/src/shaders/grass.vert +++ b/src/shaders/grass.vert @@ -8,10 +8,31 @@ layout(set = 1, binding = 0) uniform ModelBufferObject { // TODO: Declare vertex shader inputs and outputs +layout(location = 0) in vec4 vsV0; +layout(location = 1) in vec4 vsV1; +layout(location = 2) in vec4 vsV2; +layout(location = 3) in vec4 vsUp; + +layout(location = 0) out vec4 tescV1; +layout(location = 1) out vec4 tescV2; +layout(location = 2) out vec4 tescUp; +layout(location = 3) out vec4 tescBitangent; + out gl_PerVertex { vec4 gl_Position; }; void main() { - // TODO: Write gl_Position and any other shader outputs + gl_Position = model*vsV0; + vec4 world_v1 = model * vec4(vsV1.xyz,1.0); + world_v1 /= world_v1.w; + vec4 world_v2 = model * vec4(vsV2.xyz,1.0); + world_v2 /= world_v2.w; + tescV1 = vec4(world_v1.xyz, vsV1.w); + tescV2 = vec4(world_v2.xyz, vsV2.w); + tescUp.xyz = normalize(vsUp.xyz); + float sin_theta = sin(vsV0.w), cos_theta = cos(vsV0.w); + tescBitangent.xyz = normalize(vec3(sin_theta, 0, cos_theta)); + tescBitangent.w = vsUp.w; + }