diff --git a/INSTRUCTIONS.md b/INSTRUCTIONS.md
new file mode 100644
index 0000000..fc0e545
--- /dev/null
+++ b/INSTRUCTIONS.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.
+
+ 
+
+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.
+
+
+
+## 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:
+
+
+
+### 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.
+
+
+
+### 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/PerformanceAnalysis.xlsx b/PerformanceAnalysis.xlsx
new file mode 100644
index 0000000..5d39853
Binary files /dev/null and b/PerformanceAnalysis.xlsx differ
diff --git a/README.md b/README.md
index fc0e545..2c5b5d1 100644
--- a/README.md
+++ b/README.md
@@ -1,300 +1,231 @@
-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.
-
- 
-
-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.
-
-
-
-## 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:
-
-
-
-### 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.
-
-
-
-### 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.
+Vulkan Grass Rendering
+======================
+
+**University of Pennsylvania, CIS 565: GPU Programming and Architecture, Project 6**
+
+* Angelina Risi
+ * [LinkedIn](www.linkedin.com/in/angelina-risi)
+ * [Twitter](https://twitter.com/Angelina_Risi)
+* Tested on: Windows 10, i7-6700HQ @ 2.60GHz 8GB, GTX 960M 4096MB (Personal Laptop)
+
+
+## Demo
+
+
+
+## Grass Blade Model
+
+
+
+### Bezier Curve & Tessellation
+
+Each grass blade is modelled with a quadratic bezier curve, with two endpoints and a control point. The compute shader uses these points to determine which blades can be seen, culling those out of view, and how natural physical forces affect the blade.
+The grass blade shape is currently a triangle. The blade is transformed from simply three points to a quad mesh using the tessellation shaders. The tessellation control shader generates uv values given inputs on how many points to split the quad into. In the evaluation shader, interpolation of the bezier curve using the v values allows us to define the grass stalk shape. Then we need to determine the point along its width using the u value.
+```cpp
+// calculate vector tangent to face to get width step vector
+vec3 w = normalize(cross(normalize(t_up.xyz), t_norm.xyz))
+ * t_v2.w / 2.0;
+
+// recover base vertex v0
+vec3 t_v0 = gl_in[0].gl_Position.xyz;
+
+// interpolate point on Bezier Curve
+vec3 p1 = ((1 - v) * t_v0) + (v * t_v1.xyz);
+vec3 p2 = ((1 - v) * t_v1.xyz) + (v * t_v2.xyz);
+vec3 q = ((1 - v) * p1) + (v * p2);
+
+// get point along width
+vec3 pos = (q - w*(1-v)) + 2*u*w*(1-v);
+
+// convert to screen space and pass to fragment shader
+gl_Position = camera.proj * camera.view * vec4(pos, 1.0);
+```
+Originally I had it set so the stalk is divided vertically into 5 quads, with the width tapering to the tip. Using slightly more complicated functions one could generate more interesting grass shapes, such as folding the blade along the middle, as some grasses do.
+Recently, I added a simple scaling of tessellation detail level with the inverse of distance from the camera. Detail varies from 1 to 10 evenly between the clipping planes, but it is possible to modify it to work just like the distance culling so that all grass blades beyond a certain distance are simple triangles.
+```cpp
+// vary detail with distance
+vec4 v0 = camera.view * gl_in[gl_InvocationID].gl_Position;
+float z = -v0.z / v0.w;
+z = (z - 0.1)/(100.0 - 0.1);
+z = (1.0 - z);
+
+// Set level of tesselation
+float n = ceil(10.0 * z);
+```
+
+### Color
+
+The uv coordinates for each generated vertex were passed to the fragment shader to help generate the fragment colors. Four colors are defined in the shader: light green, dark green, grey, and white. First, color is interpolated vertically from the v coordinate between dark green at the base and light green at the tip. Then, a grey color is interpolated using the u coordinate between grey at the middle and white at the edges. The final output color is the multiplication of these two colors, giving a nice color gradient across the blade.
+```cpp
+// Define shades of green
+vec4 l_green = vec4(0.1, 0.9, 0.1, 1.0);
+vec4 d_green = vec4(9.0/255.0, 51.0/255.0, 0.0, 1.0);
+
+// grey gradient
+float grey = 0.75;
+float white = 1.0;
+
+// Interpolate green shade
+float v = f_uv.y;
+vec4 c1 = (1-v)*d_green + v * l_green;
+
+// Interpolate brightness
+float u = 2 * abs(f_uv.x - 0.5);
+float c2 = (1-u)*grey + u * white;
+
+// Final color is multiplied
+outColor = (c1 * c2);
+```
+
+## Grass Blade Culling
+
+For practical applications, it is important to discard redundant or unnecessary information. In this case of grass rendering, blades outside the visible area or unable to be seen clearly may be reduced in detail or discarded from the current frame. This process is achieved through multiple culling algorithms in the compute shader. The images provided give extreme examples for visualization of their effects.
+
+### View Frustum Culling
+
+
+
+Grass blades completely outside of the camera's view frustum do not need to be drawn. While they will eventually be culled as they are converted to fragments out of bounds, it will potentially save GPU cycles to remove these blades beforehand.
+The base, tip and approximate midpoint coordinates are converted to screen space using the camera view and projection matrices. Then, they are compared against the screen bounds, +- 1, and the z clipping planes, z = 0 and z = 1 when converted to screen space. Additionally, a tolerance of one half-width unit is used so that partially off-screen blades may still be visible. If any of three tested points are within these bounds, the blade is considered in view and is not culled unless it fails the culling checks.
+
+### Orientation Culling
+
+
+
+The grass blades are currently modelled without a thickness to them, as we only compute 2D tessellation of the blade plane. Thus, if the camera is looking parallel to the blade's width direction, the blade has no thickness to be seen. So that blades less than a pixel wide in screen space are culled, we compare the look vector with the blade width vector using a dot product. If the value is above a threshold, they are nearly the same direction and the blade is culled.
+
+### Distance Culling
+
+
+
+Distant blades would be less detailed and difficult to see when drawn as fragments on the screen, and may be obscured by nearer objects anyway. Since high detail is only necessary for closer blades, we can define a max view distance, beyond which blades are culled, and distance "bins". The blades are binned according to their distance from the camera. Then, the blade index modulo the bin number is used to decide whether it should be culled. This ensures fewer blades are kept if they are further away from the camera.
+For a smoother transition, more bins and smaller step sizes could be used. The max draw distance is the number of bins multiplied by the step size. Currently, it is set to ten bins with a step size of 2.0 units, giving a max draw distance of 20 units.
+
+
+## Physics Modelling
+
+### Gravity
+
+
+
+Gravity is the natural force that attracts masses together. The gravity of the Earth is approximately 9.807 m/s2, which is used as the gravity constant in this code. The overall direction of the gravity is approximately straight down. But to be able to properly model the grass blade folding over instead of the tip moving straight down, we also define a "face" gravity component in the direction of the blade face normal at a quarter of the gravity force.
+```cpp
+float g = 9.807; // 9.807 m/s^2 on Earth
+// Global gravity direction should be straight down
+vec3 gE = vec3(0.0, -g, 0.0);
+
+// "Face" gravity influence, so v2 moves more
+// naturally outward instead of straight down
+vec3 gF = g * norm / 4.0;
+```
+
+### Recovery
+
+
+
+The grass blades have an inherent "elasticity" generating a resistive force opposing changes to its natural position. This is defined by a stiffness factor multiplied by the change in the position of v2.
+
+```cpp
+vec3 R = ((v0 + h * up) - v2) * stiff;
+```
+
+### Wind
+
+
+
+Air naturally circulates due to pressure and heat variations. This disturbance will move the light grass with some force dependent on the wind speed (it's strength) and the direction. If the face of the grass blade is angled compared to the wind direction, the force on the blade is reduced.
+The wind generally has a prevailing direction with some fluctuation in time, and a strength also fluctuating in time. These are modelled with sinusoidal signals of different frequencies.
+```cpp
+float f1 = 0.33;
+
+// set prevailing wind direction to +x
+// shift wind direction in z w/ cos in time
+vec3 w_direc = normalize(vec3(1.0, 0.0, cos(f1 * totalTime + v0.z) / 4.0));
+
+// get adjected face direction approximation
+vec3 dir = normalize(vec3(x, -((v2 - v0).x * x + (v2 - v0).z * z) / (v2 - v0).y, z));
+
+// use direction of blade to adjust wind influence
+// stronger wind expected more momentum in main direction,
+// so use z direction freq^-1 to also adjust strength
+float w_scale = (1.0/ f1) * abs(dot(dir, w_direc));
+float f2 = 1.0;
+vec3 Wind = w_scale * w_direc * (sin(f2 * totalTime + v0.x) + 2.0);
+```
+
+
+Once all the forces are generated, they are applied by multiplying the total force by time, and using the new vector as the shift in the position of v2.
+
+```cpp
+vec3 shift = (R + gF + gE + Wind) * deltaTime;
+
+// adjust v2 position
+v2 += shift;
+```
+
+### Position Validation
+
+
+
+After applying the physical transformations, the grass blade may not be in an acceptable position state. It's tip, v2, may sink below ground, and without adjustment of the control point, v1, the blade will not have consistent length. When properly corrected, the blade looks less hunched over and more consistent in length.
+First, the position of v2 is corrected to be at or above ground level, assuming v0 to be ground.
+```cpp
+// keep v2 from going underground
+float up_dot = dot(up, (v2 - v0));
+v2 = v2 - up * min(up_dot, 0.0);
+```
+Next, we must check the position of v1. If the position of v2 is displaced from the up vector, the position of v1 must also be displaced. Then, we must correct its position again for the correct blade length. We want the length after transformation to be approximately its original height, so we make an approximation of its current length and use the ratio with height to scale a new adjusted position of v1 and v2.
+```cpp
+up_dot = dot(up, (v2 - v0));
+float l_proj = length((v2 - v0) - up * up_dot);
+if (l_proj != 0.0) {
+ v1 = v0 + h * up * max(1.0 - l_proj / h,
+ 0.05 * max (l_proj / h, 1.0));
+
+ float L0 = length(v2 - v0);
+ float L1 = length(v2 - v1) + length(v1 - v0);
+ float n = 2.0; // degree of curve, quadratic
+ float L = (2.0 * L0 + (n - 1.0)* L1) / (n + 1.0);
+
+ float r = h / L;
+
+ vec3 old_v1 = v1;
+
+ v1 = v0 + r*(old_v1 - v0);
+ v2 = v1 + r*(v2 - old_v1);
+}
+```
+Under the current implementation, the blades "bounce" slightly as they are adjusted each frame, assumed due to rounding and approximations leading to oscillations around the desired settling point. Potentially, instead of using the approximation from the paper, we could determine the actual length of the blade. However, this would be more computationally expensive, as we'd have to determine the length between each point on the actual tessellated curve.
+
+
+## Performance Analysis
+
+Performance was measured by extracting the average time per frame. This was done by writing simple wrapper functions to retrieve the timer data from the scene, and measuring the elapsed time for every thousand frames. Then, I took the average of these samples. For scenes that took longer to render, in the interest of time fewer samples were taken.
+
+### Number of Grass Blades
+
+The number of blades first affects the speed of the compute shader's physics and culling pass. Then, having far more blades on-screen affects the vertex and tessellation shading and rasterization steps. On the GPU, the performance effect is not expected to be drastic, such as scaling with N, due to parallelization, but the total number of blades is nonetheless expected to be be a significant contributor to performance.
+
+
+
+From the plot we can see, as expected, a non-linear relationship with blade numbers and time per frame. Although, it is notable that there is a near twice the time per frame to render 1 million blades versus half that amount, indicating the speed becomes more dependent on the number of blades as this number increases.
+Enabling all culling algorithms results in a significant speed boost as well, and is especially useful when there is a larger concentration of grass blades.
+
+### Effects of Culling Techniques
+
+Each culling technique has some compute overhead expected per blade, but is expected to save on future computations and rasterization of unnecessary, non-visible blades.
+
+
+
+The effects of culling are less apparent for each technique for low blade counts, until over 10,000 blades. At that point, there is a notable decrease in time per frame for both the frustum and distance culling techniques. As the scene becomes more and more dense, removing a portion of the distant blades becomes incredibly significant, nearly halving the time. Similarly, depending on the view position many of the blades will lie outside the frustum, so discarding them before continuing through the pipeline saves a large amount of time per frame as well.
+Contrary to what was previously assumed, the orientation culling had little to no effect for smaller blade counts, and the compute overhead was even detrimental for large blade counts. It is possible using a less strict threshold would give better results, but it would result in a less smooth transition of blades coming in and out of viewable orientations.
+
+### Tessellation Level
+
+The tessellation level describes the level of detail in the rendered grass blade. For example, a 100-point curve would be far more detailed and natural looking, but assumed much more computationally expensive to define. Thus, I decided to analyze the performance for varying levels of blade tessellation.
+
+
+
+Similar to expected, there is a trend of performance loss for increased tessellation level. This was tested with 100,000 blades and all culling disabled. If we take the time difference from level 1 for a level of 10 versus level 100, having 10 times more detail only takes about 4 times more extra time, which is still a lot but a reasonable trade-off.
+
+
\ No newline at end of file
diff --git a/bin/Debug/vulkan_grass_rendering.exe b/bin/Debug/vulkan_grass_rendering.exe
new file mode 100644
index 0000000..c6354f9
Binary files /dev/null and b/bin/Debug/vulkan_grass_rendering.exe differ
diff --git a/bin/Debug/vulkan_grass_rendering.ilk b/bin/Debug/vulkan_grass_rendering.ilk
new file mode 100644
index 0000000..d824a20
Binary files /dev/null and b/bin/Debug/vulkan_grass_rendering.ilk differ
diff --git a/bin/Debug/vulkan_grass_rendering.pdb b/bin/Debug/vulkan_grass_rendering.pdb
new file mode 100644
index 0000000..19f24a4
Binary files /dev/null and b/bin/Debug/vulkan_grass_rendering.pdb differ
diff --git a/bin/Release/vulkan_grass_rendering.exe b/bin/Release/vulkan_grass_rendering.exe
new file mode 100644
index 0000000..0a1d150
Binary files /dev/null and b/bin/Release/vulkan_grass_rendering.exe differ
diff --git a/img/CullingPerf.png b/img/CullingPerf.png
new file mode 100644
index 0000000..5e5c48b
Binary files /dev/null and b/img/CullingPerf.png differ
diff --git a/img/Demo_compressed.gif b/img/Demo_compressed.gif
new file mode 100644
index 0000000..75f43b7
Binary files /dev/null and b/img/Demo_compressed.gif differ
diff --git a/img/DistanceCull.gif b/img/DistanceCull.gif
new file mode 100644
index 0000000..e512a8c
Binary files /dev/null and b/img/DistanceCull.gif differ
diff --git a/img/FrustrumCull.PNG b/img/FrustrumCull.PNG
new file mode 100644
index 0000000..9d6886b
Binary files /dev/null and b/img/FrustrumCull.PNG differ
diff --git a/img/FrustrumCull.gif b/img/FrustrumCull.gif
new file mode 100644
index 0000000..7d6cabd
Binary files /dev/null and b/img/FrustrumCull.gif differ
diff --git a/img/Gravity&Recovery1.PNG b/img/Gravity&Recovery1.PNG
new file mode 100644
index 0000000..f672109
Binary files /dev/null and b/img/Gravity&Recovery1.PNG differ
diff --git a/img/Gravity&Recovery2.PNG b/img/Gravity&Recovery2.PNG
new file mode 100644
index 0000000..0ba7ad8
Binary files /dev/null and b/img/Gravity&Recovery2.PNG differ
diff --git a/img/JustGravity.png b/img/JustGravity.png
new file mode 100644
index 0000000..0ba75b4
Binary files /dev/null and b/img/JustGravity.png differ
diff --git a/img/NumBladesPerf.png b/img/NumBladesPerf.png
new file mode 100644
index 0000000..c8ba189
Binary files /dev/null and b/img/NumBladesPerf.png differ
diff --git a/img/OneBlades.PNG b/img/OneBlades.PNG
new file mode 100644
index 0000000..545d038
Binary files /dev/null and b/img/OneBlades.PNG differ
diff --git a/img/OrientationCull.gif b/img/OrientationCull.gif
new file mode 100644
index 0000000..2d7e98f
Binary files /dev/null and b/img/OrientationCull.gif differ
diff --git a/img/Physics.gif b/img/Physics.gif
new file mode 100644
index 0000000..5c8d265
Binary files /dev/null and b/img/Physics.gif differ
diff --git a/img/PosValidated.png b/img/PosValidated.png
new file mode 100644
index 0000000..7f5c22d
Binary files /dev/null and b/img/PosValidated.png differ
diff --git a/img/Something.png b/img/Something.png
new file mode 100644
index 0000000..df46c08
Binary files /dev/null and b/img/Something.png differ
diff --git a/img/SomethingPrettier.PNG b/img/SomethingPrettier.PNG
new file mode 100644
index 0000000..f391d55
Binary files /dev/null and b/img/SomethingPrettier.PNG differ
diff --git a/img/TessPerf.png b/img/TessPerf.png
new file mode 100644
index 0000000..a70f3dc
Binary files /dev/null and b/img/TessPerf.png differ
diff --git a/src/Blades.h b/src/Blades.h
index 9bd1eed..846bd03 100644
--- a/src/Blades.h
+++ b/src/Blades.h
@@ -4,7 +4,7 @@
#include
#include "Model.h"
-constexpr static unsigned int NUM_BLADES = 1 << 13;
+constexpr static unsigned int NUM_BLADES = 1000;
constexpr static float MIN_HEIGHT = 1.3f;
constexpr static float MAX_HEIGHT = 2.5f;
constexpr static float MIN_WIDTH = 0.1f;
diff --git a/src/Renderer.cpp b/src/Renderer.cpp
index b445d04..0a87953 100644
--- a/src/Renderer.cpp
+++ b/src/Renderer.cpp
@@ -21,6 +21,9 @@ Renderer::Renderer(Device* device, SwapChain* swapChain, Scene* scene, Camera* c
CreateModelDescriptorSetLayout();
CreateTimeDescriptorSetLayout();
CreateComputeDescriptorSetLayout();
+
+ CreateGrassDescriptorSetLayout();
+
CreateDescriptorPool();
CreateCameraDescriptorSet();
CreateModelDescriptorSets();
@@ -194,10 +197,64 @@ void Renderer::CreateTimeDescriptorSetLayout() {
}
}
+void Renderer::CreateGrassDescriptorSetLayout() {
+ VkDescriptorSetLayoutBinding bladeLayoutBinding = {};
+ bladeLayoutBinding.binding = 0;
+ bladeLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
+ bladeLayoutBinding.descriptorCount = 1;
+ bladeLayoutBinding.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
+ bladeLayoutBinding.pImmutableSamplers = nullptr;
+
+ std::vector bindings = { bladeLayoutBinding };
+
+ // 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 bladeLayoutBinding = {};
+ bladeLayoutBinding.binding = 0;
+ bladeLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
+ bladeLayoutBinding.descriptorCount = 1;
+ bladeLayoutBinding.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
+ bladeLayoutBinding.pImmutableSamplers = nullptr;
+
+ VkDescriptorSetLayoutBinding culledLayoutBinding = {};
+ culledLayoutBinding.binding = 1;
+ culledLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
+ culledLayoutBinding.descriptorCount = 1;
+ culledLayoutBinding.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
+ culledLayoutBinding.pImmutableSamplers = nullptr;
+
+ VkDescriptorSetLayoutBinding numBladesLayoutBinding = {};
+ numBladesLayoutBinding.binding = 2;
+ numBladesLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
+ numBladesLayoutBinding.descriptorCount = 1;
+ numBladesLayoutBinding.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
+ numBladesLayoutBinding.pImmutableSamplers = nullptr;
+
+ std::vector bindings = { bladeLayoutBinding, culledLayoutBinding, numBladesLayoutBinding };
+
+ // Create the descriptor set layout
+ VkDescriptorSetLayoutCreateInfo layoutInfo = {};
+ layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
+ layoutInfo.bindingCount = static_cast(bindings.size());
+ layoutInfo.pBindings = bindings.data();
+
+ if (vkCreateDescriptorSetLayout(logicalDevice, &layoutInfo, nullptr, &computeDescriptorSetLayout) != VK_SUCCESS) {
+ throw std::runtime_error("Failed to create descriptor set layout");
+ }
}
void Renderer::CreateDescriptorPool() {
@@ -216,6 +273,12 @@ void Renderer::CreateDescriptorPool() {
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER , 1 },
// TODO: Add any additional types and counts of descriptors you will need to allocate
+
+ // compute buffers
+ { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER , 3 * (uint32_t)scene->GetBlades().size() },
+
+ // grass models
+ { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER , (uint32_t)scene->GetBlades().size() },
};
VkDescriptorPoolCreateInfo poolInfo = {};
@@ -320,6 +383,45 @@ void Renderer::CreateModelDescriptorSets() {
void Renderer::CreateGrassDescriptorSets() {
// TODO: Create Descriptor sets for the grass.
// This should involve creating descriptor sets which point to the model matrix of each group of grass blades
+
+ grassDescriptorSets.resize(scene->GetBlades().size());
+
+ 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].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+ descriptorWrites[i].dstSet = grassDescriptorSets[i];
+ descriptorWrites[i].dstBinding = 0;
+ descriptorWrites[i].dstArrayElement = 0;
+ descriptorWrites[i].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+ descriptorWrites[i].descriptorCount = 1;
+ descriptorWrites[i].pBufferInfo = &modelBufferInfo;
+ descriptorWrites[i].pImageInfo = nullptr;
+ descriptorWrites[i].pTexelBufferView = nullptr;
+ }
+
+ // Update descriptor sets
+ vkUpdateDescriptorSets(logicalDevice, static_cast(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr);
+
+
}
void Renderer::CreateTimeDescriptorSet() {
@@ -360,6 +462,73 @@ void Renderer::CreateTimeDescriptorSet() {
void Renderer::CreateComputeDescriptorSets() {
// TODO: Create Descriptor sets for the compute pipeline
// The descriptors should point to Storage buffers which will hold the grass blades, the culled grass blades, and the output number of grass blades
+
+ computeDescriptorSets.resize(scene->GetBlades().size());
+
+ VkDescriptorSetLayout layouts[] = { computeDescriptorSetLayout };
+ VkDescriptorSetAllocateInfo allocInfo = {};
+
+ allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
+ allocInfo.descriptorPool = descriptorPool;
+ allocInfo.descriptorSetCount = static_cast(computeDescriptorSets.size());
+ allocInfo.pSetLayouts = layouts;
+
+ // Allocate descriptor sets
+ if (vkAllocateDescriptorSets(logicalDevice, &allocInfo, computeDescriptorSets.data()) != VK_SUCCESS) {
+ throw std::runtime_error("Failed to allocate descriptor set");
+ }
+
+ std::vector descriptorWrites(3 * computeDescriptorSets.size());
+
+ for (uint32_t i = 0; i < scene->GetBlades().size(); ++i) {
+ VkDescriptorBufferInfo bladeBufferInfo = {};
+ bladeBufferInfo.buffer = scene->GetBlades()[i]->GetBladesBuffer();
+ bladeBufferInfo.offset = 0;
+ bladeBufferInfo.range = NUM_BLADES * sizeof(Blade);
+
+ VkDescriptorBufferInfo culledBufferInfo = {};
+ culledBufferInfo.buffer = scene->GetBlades()[i]->GetCulledBladesBuffer();
+ culledBufferInfo.offset = 0;
+ culledBufferInfo.range = NUM_BLADES * sizeof(Blade);
+
+ VkDescriptorBufferInfo numBladesBufferInfo = {};
+ numBladesBufferInfo.buffer = scene->GetBlades()[i]->GetNumBladesBuffer();
+ numBladesBufferInfo.offset = 0;
+ numBladesBufferInfo.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 = &bladeBufferInfo;
+ 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 = &culledBufferInfo;
+ descriptorWrites[3 * i + 1].pImageInfo = nullptr;
+ descriptorWrites[3 * i + 1].pTexelBufferView = nullptr;
+
+ descriptorWrites[3 * i + 2].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+ descriptorWrites[3 * i + 2].dstSet = computeDescriptorSets[i];
+ descriptorWrites[3 * i + 2].dstBinding = 2;
+ descriptorWrites[3 * i + 2].dstArrayElement = 0;
+ descriptorWrites[3 * i + 2].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
+ descriptorWrites[3 * i + 2].descriptorCount = 1;
+ descriptorWrites[3 * i + 2].pBufferInfo = &numBladesBufferInfo;
+ descriptorWrites[3 * i + 2].pImageInfo = nullptr;
+ descriptorWrites[3 * i + 2].pTexelBufferView = nullptr;
+ }
+
+ // Update descriptor sets
+ vkUpdateDescriptorSets(logicalDevice, static_cast(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr);
}
void Renderer::CreateGraphicsPipeline() {
@@ -654,7 +823,7 @@ void Renderer::CreateGrassPipeline() {
colorBlending.blendConstants[2] = 0.0f;
colorBlending.blendConstants[3] = 0.0f;
- std::vector descriptorSetLayouts = { cameraDescriptorSetLayout, modelDescriptorSetLayout };
+ std::vector descriptorSetLayouts = { cameraDescriptorSetLayout, modelDescriptorSetLayout, grassDescriptorSetLayout };
// Pipeline layout: used to specify uniform values
VkPipelineLayoutCreateInfo pipelineLayoutInfo = {};
@@ -717,7 +886,7 @@ void Renderer::CreateComputePipeline() {
computeShaderStageInfo.pName = "main";
// TODO: Add the compute dsecriptor set layout you create to this list
- std::vector descriptorSetLayouts = { cameraDescriptorSetLayout, timeDescriptorSetLayout };
+ std::vector descriptorSetLayouts = { cameraDescriptorSetLayout, timeDescriptorSetLayout, computeDescriptorSetLayout };
// Create pipeline layout
VkPipelineLayoutCreateInfo pipelineLayoutInfo = {};
@@ -884,6 +1053,11 @@ void Renderer::RecordComputeCommandBuffer() {
vkCmdBindDescriptorSets(computeCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, computePipelineLayout, 1, 1, &timeDescriptorSet, 0, nullptr);
// TODO: For each group of blades bind its descriptor set and dispatch
+ for (uint32_t i = 0; i < scene->GetBlades().size(); ++i) {
+ vkCmdBindDescriptorSets(computeCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, computePipelineLayout, 2, 1, &computeDescriptorSets[i], 0, nullptr);
+ vkCmdDispatch(computeCommandBuffer, ceil(NUM_BLADES / WORKGROUP_SIZE), 1, 1);
+ }
+
// ~ End recording ~
if (vkEndCommandBuffer(computeCommandBuffer) != VK_SUCCESS) {
@@ -976,13 +1150,14 @@ void Renderer::RecordCommandBuffers() {
VkBuffer vertexBuffers[] = { scene->GetBlades()[j]->GetCulledBladesBuffer() };
VkDeviceSize offsets[] = { 0 };
// TODO: Uncomment this when the buffers are populated
- // vkCmdBindVertexBuffers(commandBuffers[i], 0, 1, vertexBuffers, offsets);
+ vkCmdBindVertexBuffers(commandBuffers[i], 0, 1, vertexBuffers, offsets);
// TODO: Bind the descriptor set for each grass blades model
+ vkCmdBindDescriptorSets(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipelineLayout, 1, 1, &modelDescriptorSets[j], 0, nullptr);
// Draw
// TODO: Uncomment this when the buffers are populated
- // vkCmdDrawIndirect(commandBuffers[i], scene->GetBlades()[j]->GetNumBladesBuffer(), 0, 1, sizeof(BladeDrawIndirect));
+ vkCmdDrawIndirect(commandBuffers[i], scene->GetBlades()[j]->GetNumBladesBuffer(), 0, 1, sizeof(BladeDrawIndirect));
}
// End render pass
@@ -1058,6 +1233,9 @@ Renderer::~Renderer() {
vkDestroyDescriptorSetLayout(logicalDevice, modelDescriptorSetLayout, nullptr);
vkDestroyDescriptorSetLayout(logicalDevice, timeDescriptorSetLayout, nullptr);
+ vkDestroyDescriptorSetLayout(logicalDevice, computeDescriptorSetLayout, nullptr);
+ vkDestroyDescriptorSetLayout(logicalDevice, grassDescriptorSetLayout, nullptr);
+
vkDestroyDescriptorPool(logicalDevice, descriptorPool, nullptr);
vkDestroyRenderPass(logicalDevice, renderPass, nullptr);
diff --git a/src/Renderer.h b/src/Renderer.h
index 95e025f..226f54d 100644
--- a/src/Renderer.h
+++ b/src/Renderer.h
@@ -20,6 +20,8 @@ class Renderer {
void CreateTimeDescriptorSetLayout();
void CreateComputeDescriptorSetLayout();
+ void CreateGrassDescriptorSetLayout();
+
void CreateDescriptorPool();
void CreateCameraDescriptorSet();
@@ -56,6 +58,9 @@ class Renderer {
VkDescriptorSetLayout cameraDescriptorSetLayout;
VkDescriptorSetLayout modelDescriptorSetLayout;
VkDescriptorSetLayout timeDescriptorSetLayout;
+
+ VkDescriptorSetLayout computeDescriptorSetLayout;
+ VkDescriptorSetLayout grassDescriptorSetLayout;
VkDescriptorPool descriptorPool;
@@ -63,6 +68,10 @@ class Renderer {
std::vector modelDescriptorSets;
VkDescriptorSet timeDescriptorSet;
+ std::vector computeDescriptorSets;
+ std::vector grassDescriptorSets;
+
+
VkPipelineLayout graphicsPipelineLayout;
VkPipelineLayout grassPipelineLayout;
VkPipelineLayout computePipelineLayout;
diff --git a/src/Scene.cpp b/src/Scene.cpp
index 86894f2..3e87368 100644
--- a/src/Scene.cpp
+++ b/src/Scene.cpp
@@ -38,6 +38,14 @@ VkBuffer Scene::GetTimeBuffer() const {
return timeBuffer;
}
+float Scene::GetDeltaTime() {
+ return time.deltaTime;
+}
+
+float Scene::GetTotalTime() {
+ return time.totalTime;
+}
+
Scene::~Scene() {
vkUnmapMemory(device->GetVkDevice(), timeBufferMemory);
vkDestroyBuffer(device->GetVkDevice(), timeBuffer, nullptr);
diff --git a/src/Scene.h b/src/Scene.h
index 7699d78..06b0556 100644
--- a/src/Scene.h
+++ b/src/Scene.h
@@ -41,5 +41,8 @@ high_resolution_clock::time_point startTime = high_resolution_clock::now();
VkBuffer GetTimeBuffer() const;
+ float GetDeltaTime();
+ float GetTotalTime();
+
void UpdateTime();
};
diff --git a/src/main.cpp b/src/main.cpp
index 8bf822b..694ea6d 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -6,6 +6,10 @@
#include "Scene.h"
#include "Image.h"
+#include
+#include
+using namespace std;
+
Device* device;
SwapChain* swapChain;
Renderer* renderer;
@@ -65,9 +69,13 @@ namespace {
}
}
+
int main() {
static constexpr char* applicationName = "Vulkan Grass Rendering";
- InitializeWindow(640, 480, applicationName);
+
+ int w = 1024;
+ int h = 768;
+ InitializeWindow(w, h, applicationName);
unsigned int glfwExtensionCount = 0;
const char** glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
@@ -90,7 +98,7 @@ int main() {
swapChain = device->CreateSwapChain(surface, 5);
- camera = new Camera(device, 640.f / 480.f);
+ camera = new Camera(device, float(w) / float(h));
VkCommandPoolCreateInfo transferPoolInfo = {};
transferPoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
@@ -143,10 +151,28 @@ int main() {
glfwSetMouseButtonCallback(GetGLFWWindow(), mouseDownCallback);
glfwSetCursorPosCallback(GetGLFWWindow(), mouseMoveCallback);
+ // frame counters
+ int n = 0;
+ float t1 = 0;
+ float t2 = 0;
+
while (!ShouldQuit()) {
glfwPollEvents();
scene->UpdateTime();
+
+ // added printing performance metrics
+ // Print average time per frame to console in ms,
+ // for every 1000 frames (prevents overflow issues)
+ if (n == 1000) {
+ t1 = t2;
+ t2 = scene->GetTotalTime();
+ cout << (t2 - t1) << "\n";
+ n = 0;
+ }
+
renderer->Frame();
+ n++;
+
}
vkDeviceWaitIdle(device->GetVkDevice());
@@ -163,5 +189,7 @@ int main() {
delete device;
delete instance;
DestroyWindow();
+
+ system("pause");
return 0;
}
diff --git a/src/shaders/compute.comp b/src/shaders/compute.comp
index 0fd0224..7dd4c1d 100644
--- a/src/shaders/compute.comp
+++ b/src/shaders/compute.comp
@@ -26,31 +26,244 @@ struct Blade {
// 2. Write out the culled blades
// 3. Write the total number of blades remaining
+layout(set = 2, binding = 0) buffer Blades {
+ Blade blades[];
+};
+
+layout(set = 2, binding = 1) buffer Culled {
+ Blade culled[];
+};
+
// The project is using vkCmdDrawIndirect to use a buffer as the arguments for a draw call
// This is sort of an advanced feature so we've showed you what this buffer should look like
//
-// layout(set = ???, binding = ???) buffer NumBlades {
-// uint vertexCount; // Write the number of blades remaining here
-// uint instanceCount; // = 1
-// uint firstVertex; // = 0
-// uint firstInstance; // = 0
-// } numBlades;
+layout(set = 2, binding = 2) buffer NumBlades {
+ uint vertexCount; // Write the number of blades remaining here
+ uint instanceCount; // = 1
+ uint firstVertex; // = 0
+ uint firstInstance; // = 0
+} numBlades;
bool inBounds(float value, float bounds) {
return (value >= -bounds) && (value <= bounds);
}
+bool bladeInBounds(vec3 v0, vec3 v1, vec3 v2, float w) {
+ float threshold = 1.0;
+
+ vec4 m = vec4((v0 / 4) + (v1 / 2) + (v2 / 4), 1.0);
+
+ // v0
+ vec4 v_v0 = camera.view * vec4(v0, 1.0);
+ v_v0 = camera.proj * v_v0;
+ v_v0 = v_v0 / v_v0.w;
+
+ float tolerance = w / v_v0.w;
+
+ bool y_in = inBounds(v_v0.y, threshold + tolerance);
+ bool x_in = inBounds(v_v0.x, threshold + tolerance);
+ bool z_in = inBounds(v_v0.z - 0.5, threshold / 2.0 + tolerance);
+
+ if (y_in && x_in && z_in) {
+ return true;
+ }
+
+ // v2
+ vec4 v_v2 = camera.proj * camera.view * vec4(v2, 1.0);
+ v_v2 = v_v2 / v_v2.w;
+
+ y_in = inBounds(v_v2.y, threshold + tolerance);
+ x_in = inBounds(v_v2.x, threshold + tolerance);
+ z_in = inBounds(v_v2.z - 0.5, threshold / 2.0 + tolerance);
+
+ if (y_in && x_in && z_in) {
+ return true;
+ }
+
+ // midpoint
+ m = camera.proj * camera.view * m;
+ m = m / m.w;
+
+ y_in = inBounds(m.y, threshold + tolerance);
+ x_in = inBounds(m.x, threshold + tolerance);
+ z_in = inBounds(m.z - 0.5, threshold / 2.0 + tolerance);
+
+ if (y_in && x_in && z_in) {
+ return true;
+ }
+
+ return false;
+}
+
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
- // TODO: Apply forces on every blade and update the vertices in the buffer
+ /* ---------------
+ * Load Blade
+ * ---------------
+ */
+ // Load blade properties
+ // v0 and orientation
+ vec3 v0 = blades[gl_GlobalInvocationID.x].v0.xyz;
+ float face = blades[gl_GlobalInvocationID.x].v0.w;
+
+ // v1 and height
+ vec3 v1 = blades[gl_GlobalInvocationID.x].v1.xyz;
+ float h = blades[gl_GlobalInvocationID.x].v1.w;
+
+ // v2 and width
+ vec3 v2 = blades[gl_GlobalInvocationID.x].v2.xyz;
+ float w = blades[gl_GlobalInvocationID.x].v2.w;
+
+ // up direction and stiffness
+ vec3 up = blades[gl_GlobalInvocationID.x].up.xyz;
+ float stiff = blades[gl_GlobalInvocationID.x].up.w;
+
+ // use direction to get normal vector of face
+ float x = cos(face);
+ float z = sin(face);
+ float y = -(up.x * x + up.z * z) / up.y;
+
+ vec3 norm = normalize(vec3(x,y,z));
+
+ /* Physics:
+ * ---------------
+ * Gravity
+ * ---------------
+ */
+ float g = 9.807; // 9.807 m/s^2 on Earth
+ // Global gravity direction should be straight down
+ vec3 gE = vec3(0.0, -g, 0.0);
+
+ // "Face" gravity influence, so v2 moves more naturally outward instead of straight down
+ vec3 gF = g * norm / 4.0;
+
+ /* ---------------
+ * Recovery
+ * ---------------
+ */
+ vec3 R = ((v0 + h * up) - v2) * stiff;
+
+ /* ---------------
+ * Wind
+ * ---------------
+ */
+ float f1 = 0.33;
+
+ // set prevailing wind direction to +x
+ // shift wind direction in z w/ cos in time
+ vec3 w_direc = normalize(vec3(1.0, 0.0, cos(f1 * totalTime + v0.z) / 4.0));
+
+ // get adjected face direction approximation
+ vec3 dir = normalize(vec3(x, -((v2 - v0).x * x + (v2 - v0).z * z) / (v2 - v0).y, z));
+
+ // use direction of blade to adjust wind influence
+ // stronger wind expected more momentum in main direction,
+ // so use z direction freq^-1 to also adjust strength
+ float w_scale = (1.0/ f1) * abs(dot(dir, w_direc));
+ float f2 = 1.0;
+ vec3 Wind = w_scale * w_direc * (sin(f2 * totalTime + v0.x) + 2.0);
+
+ /* ---------------
+ * Application
+ * ---------------
+ */
+ vec3 shift = (R + gF + gE + Wind) * deltaTime;
+ //vec3 shift = (R + gF + gE) * deltaTime;
+ //vec3 shift = (Wind + R) * deltaTime;
+
+ // adjust v2 position
+ v2 += shift;
+
+ /* ---------------
+ * Validation
+ * ---------------
+ */
+
+ // keep v2 from going underground
+ float up_dot = dot(up, (v2 - v0));
+ v2 = v2 - up * min(up_dot, 0.0);
+
+ // make sure length ~ height
+ up_dot = dot(up, (v2 - v0));
+ float l_proj = length((v2 - v0) - up * up_dot);
+ if (l_proj != 0.0) {
+ v1 = v0 + h * up * max(1.0 - l_proj / h, 0.05 * max (l_proj / h, 1.0));
+
+ float L0 = length(v2 - v0);
+ float L1 = length(v2 - v1) + length(v1 - v0);
+ float n = 2.0; // degree of curve, quadratic
+ float L = (2.0 * L0 + (n - 1.0)* L1) / (n + 1.0);
+
+ float r = h / L;
+
+ vec3 old_v1 = v1;
+
+ v1 = v0 + r*(old_v1 - v0);
+ v2 = v1 + r*(v2 - old_v1);
+ }
+
+ // write updated v1 & v2 to blade data
+ blades[gl_GlobalInvocationID.x].v1 = vec4(v1, h);
+ blades[gl_GlobalInvocationID.x].v2 = vec4(v2, w);
+
+ /* Blade Culling:
+ * ---------------
+ * Frustrum
+ * ---------------
+ */
+ // view frustrum culling with w/2 tolerance
+ bool f_cull_pass = bladeInBounds(v0, v1, v2, w / 2.0);
+
+ /* ---------------
+ * Orientation
+ * ---------------
+ */
+ // get camera forward vector from inverse view matrix, access third row, invert
+ vec4 cam_look = normalize(inverse(camera.view) * vec4(0,0,-1,0));
+
+ bool o_cull_pass = true;
+
+ vec3 tan = normalize(cross(normalize(up), norm));
+ float o_test = dot(vec2(cam_look.x, cam_look.z), vec2(tan.x, tan.z));
+ // if the camera and normal are near perpendicular, cull
+ if (abs(o_test) > 0.9) o_cull_pass = false;
+
+
+ /* ---------------
+ * Distance
+ * ---------------
+ */
+ bool d_cull_pass = true;
+
+ int num_bins = 10;
+ float step = 2.0; // change bin size here
+ vec4 v_v0 = camera.view * vec4(v0, 1.0);
+ z = -v_v0.z;
+
+ // near and far clip planes
+ if (z > 100.0 || z < 0.1) d_cull_pass = false;
+ // cull if beyond furthest bin
+ if (z > (num_bins * step) + 0.1) d_cull_pass = false;
+
+ // select bin, making sure not to divide by zero
+ int bin = int(floor((z - 0.1) / step)) + 1;
+ if (d_cull_pass == true) {
+ if (gl_GlobalInvocationID.x % bin > 1.0) d_cull_pass = false;
+ }
+
+
+ /* ---------------
+ * Write Blade
+ * ---------------
+ */
+ if (f_cull_pass && o_cull_pass && d_cull_pass) {
+ uint idx = atomicAdd(numBlades.vertexCount , 1);
+ culled[idx] = blades[gl_GlobalInvocationID.x];
+ }
- // 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
}
diff --git a/src/shaders/grass.frag b/src/shaders/grass.frag
index c7df157..6745a6b 100644
--- a/src/shaders/grass.frag
+++ b/src/shaders/grass.frag
@@ -8,10 +8,29 @@ layout(set = 0, binding = 0) uniform CameraBufferObject {
// TODO: Declare fragment shader inputs
+layout(location = 0) in vec4 norm;
+layout(location = 1) in vec2 f_uv;
+
layout(location = 0) out vec4 outColor;
void main() {
- // TODO: Compute fragment color
- outColor = vec4(1.0);
+ // Define shades of green
+ vec4 l_green = vec4(0.1, 0.9, 0.1, 1.0);
+ vec4 d_green = vec4(9.0/255.0, 51.0/255.0, 0.0, 1.0);
+
+ // grey gradient
+ float grey = 0.75;
+ float white = 1.0;
+
+ // Interpolate green shade
+ float v = f_uv.y;
+ vec4 c1 = (1-v)*d_green + v * l_green;
+
+ // Interpolate brightness
+ float u = 2 * abs(f_uv.x - 0.5);
+ float c2 = (1-u)*grey + u * white;
+
+ // Final color is multiplied
+ outColor = (c1 * c2);
}
diff --git a/src/shaders/grass.tesc b/src/shaders/grass.tesc
index f9ffd07..d0f7e87 100644
--- a/src/shaders/grass.tesc
+++ b/src/shaders/grass.tesc
@@ -10,17 +10,39 @@ layout(set = 0, binding = 0) uniform CameraBufferObject {
// TODO: Declare tessellation control shader inputs and outputs
+layout (location = 0) in vec3 v_w[];
+layout (location = 1) in vec4 v_v1[];
+layout (location = 2) in vec4 v_v2[];
+layout (location = 3) in vec4 v_up[];
+
+layout (location = 0) patch out vec3 t_w;
+layout (location = 1) patch out vec4 t_v1;
+layout (location = 2) patch out vec4 t_v2;
+layout (location = 3) patch out vec4 t_up;
+
void main() {
// Don't move the origin location of the patch
gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;
- // TODO: Write any shader outputs
+ // Write shader outputs
+ t_w = v_w[0];
+ t_v1 = v_v1[0];
+ t_v2 = v_v2[0];
+ t_up = v_up[0];
+
+ // vary detail with distance
+ vec4 v0 = camera.view * gl_in[gl_InvocationID].gl_Position;
+ float z = -v0.z / v0.w;
+ z = (z - 0.1)/(100.0 - 0.1);
+ z = (1.0 - z);
+
+ // Set level of tesselation
+ float n = ceil(10.0 * z);
- // TODO: Set level of tesselation
- // gl_TessLevelInner[0] = ???
- // gl_TessLevelInner[1] = ???
- // gl_TessLevelOuter[0] = ???
- // gl_TessLevelOuter[1] = ???
- // gl_TessLevelOuter[2] = ???
- // gl_TessLevelOuter[3] = ???
+ gl_TessLevelInner[0] = 1.0;
+ gl_TessLevelInner[1] = n;
+ gl_TessLevelOuter[0] = n;
+ gl_TessLevelOuter[1] = 1.0;
+ gl_TessLevelOuter[2] = n;
+ gl_TessLevelOuter[3] = 1.0;
}
diff --git a/src/shaders/grass.tese b/src/shaders/grass.tese
index 751fff6..47a6f88 100644
--- a/src/shaders/grass.tese
+++ b/src/shaders/grass.tese
@@ -10,9 +10,35 @@ layout(set = 0, binding = 0) uniform CameraBufferObject {
// TODO: Declare tessellation evaluation shader inputs and outputs
+layout (location = 0) patch in vec3 t_w;
+layout (location = 1) patch in vec4 t_v1;
+layout (location = 2) patch in vec4 t_v2;
+layout (location = 3) patch in vec4 t_up;
+
+layout(location = 0) out vec4 norm;
+layout(location = 1) out vec2 f_uv;
+
void main() {
float u = gl_TessCoord.x;
float v = gl_TessCoord.y;
- // TODO: Use u and v to parameterize along the grass blade and output positions for each vertex of the grass blade
+ // Pass uv coordinates for fragment shading
+ f_uv = vec2 (u, v);
+
+ // calculate vector tangent to face to get width step vector
+ vec3 w = t_w * t_v2.w / 2.0;
+
+ // recover base vertex v0
+ vec3 t_v0 = gl_in[0].gl_Position.xyz;
+
+ // interpolate point on Bezier Curve
+ vec3 p1 = ((1 - v) * t_v0) + (v * t_v1.xyz);
+ vec3 p2 = ((1 - v) * t_v1.xyz) + (v * t_v2.xyz);
+ vec3 q = ((1 - v) * p1) + (v * p2);
+
+ // get point along width
+ vec3 pos = (q - w*(1-v)) + 2*u*w*(1-v);
+
+ // convert to screen space and pass to fragment shader
+ gl_Position = camera.proj * camera.view * vec4(pos, 1.0);
}
diff --git a/src/shaders/grass.vert b/src/shaders/grass.vert
index db9dfe9..625f220 100644
--- a/src/shaders/grass.vert
+++ b/src/shaders/grass.vert
@@ -8,10 +8,44 @@ layout(set = 1, binding = 0) uniform ModelBufferObject {
// TODO: Declare vertex shader inputs and outputs
+layout (location = 0) in vec4 v0;
+layout (location = 1) in vec4 v1;
+layout (location = 2) in vec4 v2;
+layout (location = 3) in vec4 up;
+
+layout (location = 0) out vec3 v_w;
+layout (location = 1) out vec4 v_v1;
+layout (location = 2) out vec4 v_v2;
+layout (location = 3) out vec4 v_up;
+
+
+
out gl_PerVertex {
vec4 gl_Position;
};
void main() {
- // TODO: Write gl_Position and any other shader outputs
+ // Write gl_Position and any other shader outputs
+
+ vec4 v = model * vec4(v1.xyz, 1.0);
+ v_v1 = vec4(v.xyz, v1.w);
+
+ v = model * vec4(v2.xyz, 1.0);
+ v_v2 = vec4(v.xyz, v2.w);
+
+ v_up = up;
+
+ // use direction to get normal vector of face
+ float o = v0.w;
+ float x = cos(o);
+ float z = sin(o);
+ float y = -(up.x * x + up.z * z) / up.y;
+
+ vec3 v_norm = normalize(vec3(x,y,z));
+
+ // get vector along width of blade
+ v_w = normalize(cross(normalize(up.xyz), v_norm));
+
+ // save v0 to the position
+ gl_Position = model * vec4(v0.xyz, 1.0);
}