diff --git a/Instructions.md b/Instructions.md
new file mode 100644
index 0000000..5884c5f
--- /dev/null
+++ b/Instructions.md
@@ -0,0 +1,295 @@
+Instructions - Vulkan Grass 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.
+
+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 .gif is just a simple example that you
+can use as a reference to compare to.
+
+**Important:**
+- If you are not in CGGT/DMD, you may replace this project with a GPU compute
+project. You MUST get this pre-approved by Austin Eng before continuing!
+
+### 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.
+
+### 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-2017/Vulkan-Samples/tree/master/samples/5_helloTessellation)
+and reading this [tutorial on tessellation](http://in2gpu.com/2014/07/12/tessellation-tutorial-opengl-4-3/).
+
+## Resources
+
+### Links
+
+The following resources may be useful for this project.
+
+* [Responsive Real-Time Grass Grass Rendering for General 3D Scenes](https://www.cg.tuwien.ac.at/research/publications/2017/JAHRMANN-2017-RRTG/JAHRMANN-2017-RRTG-draft.pdf)
+* [CIS565 Vulkan samples](https://github.com/CIS565-Fall-2017/Vulkan-Samples)
+* [Official Vulkan documentation](https://www.khronos.org/registry/vulkan/)
+* [Vulkan tutorial](https://vulkan-tutorial.com/)
+* [RenderDoc blog on Vulkan](https://renderdoc.org/vulkan-in-30-minutes.html)
+* [Tessellation tutorial](http://in2gpu.com/2014/07/12/tessellation-tutorial-opengl-4-3/)
+
+
+## Third-Party Code Policy
+
+* Use of any third-party code must be approved by asking on our Google Group.
+* If it is approved, all students are welcome to use it. Generally, we approve
+ use of third-party code that is not a core part of the project. For example,
+ for the path tracer, we would approve using a third-party library for loading
+ models, but would not approve copying and pasting a CUDA function for doing
+ refraction.
+* Third-party code **MUST** be credited in README.md.
+* Using third-party code without its approval, including using another
+ student's code, is an academic integrity violation, and will, at minimum,
+ result in you receiving an F for the semester.
+
+
+## README
+
+* A brief description of the project and the specific features you implemented.
+* At least one screenshot of your project running.
+* A performance analysis (described below).
+
+### Performance Analysis
+
+The performance analysis is where you will investigate how...
+* Your renderer handles varying numbers of grass blades
+* The improvement you get by culling using each of the three culling tests
+
+## Submit
+
+If you have modified any of the `CMakeLists.txt` files at all (aside from the
+list of `SOURCE_FILES`), mentions it explicity.
+Beware of any build issues discussed on the Google Group.
+
+Open a GitHub pull request so that we can see that you have finished.
+The title should be "Project 6: YOUR NAME".
+The template of the comment section of your pull request is attached below, you can do some copy and paste:
+
+* [Repo Link](https://link-to-your-repo)
+* (Briefly) Mentions features that you've completed. Especially those bells and whistles you want to highlight
+ * Feature 0
+ * Feature 1
+ * ...
+* Feedback on the project itself, if any.
diff --git a/README.md b/README.md
index a744a2e..32a2f11 100644
--- a/README.md
+++ b/README.md
@@ -1,297 +1,73 @@
-Instructions - Vulkan Grass Rendering
-========================
-This is due **Sunday 11/5, evening at midnight**.
+WebGL Clustered Deferred and Forward+ Shading
+======================
-**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.
+**A realtime grass simulation making use of Vulkan's compute, tesselation, and rendering capabilities.**
+**University of Pennsylvania, CIS 565: GPU Programming and Architecture, Project 6**
-
+* Daniel Daley-Mongtomery
+* Tested on: MacBook Pro, OSX 10.12, i7 @ 2.3GHz, 16GB RAM, GT 750M 2048MB (Personal Machine)
-You are not required to use this base code if you don't want
-to. You may also change any part of the base code as you please.
-**This is YOUR project.** The above .gif is just a simple example that you
-can use as a reference to compare to.
+### Demo Link
-**Important:**
-- If you are not in CGGT/DMD, you may replace this project with a GPU compute
-project. You MUST get this pre-approved by Austin Eng before continuing!
+[](https://www.youtube.com/watch?v=rpcss9_z4yU)
-### Contents
+### Implementation
-* `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
+##### 1: Creating the Vulkan Pipelines
+ The word 'Vulkan' has made some waves in the graphics and gaming communities as the next major leap in realtime rendering. The API is *extremely* explicit, allowing developers to specify exactly what they require to perform every task to maximize CPU/GPU balance and utilization. In the context of this project, this meant generating the necessary information about grass blades' positions, rigidity, dimensions, and normals, as well as information about the camera and any geometry.
+
+ This information is bound to customizable Vulkan device buffers and outlined in descriptor sets to specify how much information arrives to which shaders and in what format. This micromanaged paradigm was painful, but not much worse than the contrived texture packing required for [WebGL applications](https://github.com/illDivino/Project5-WebGL-Clustered-Deferred-Forward-Plus). Said and done, I tested a few starting configurations to ensure that my grass was as robust as it should be:
-### 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.
+ After a little bit of experiementation, I settled on the below scene to use in my performance evaluations, as it seemed a representative example of how a grass simulation might be used in a real application. The scene has 219 blades with 7 vertical tesselation subdivions each.
-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!
+##### 2: Compute Shader
+ The physics simulation itself followed the example of [this paper](https://www.cg.tuwien.ac.at/research/publications/2017/JAHRMANN-2017-RRTG/JAHRMANN-2017-RRTG-draft.pdf) paper by Klemens Jahrmann and Michael Wimmer, which stored all information about a blade's height, width, rigidity, orientation, current bend, and normal in four 4D vectors. The curve of the grass is computed as a bezier curve between three points in space, the middle of which exists to account for length conservation.
+
+
+
+Every frame, the tip of the blade is affected by three forces:
+* Wind, which can be generated as an arbitrary vector. I found it worked best as a function of space *and* time, as is found in the demo gif at the top of this document. Wind is also scaled according to the orientation of the blade, such that the orthoganal flat of the balde catches more wind force than the oblique edge.
+* Recovery, represented as the difference between the current and original blade tip location, scaled by a rigidity constant.
+* Gravity, which pulls the blade down along a gravity vector and the blade "forward."
+
+ While a good and relatively inexpensive model, it would become difficult to simulate and render every single one of 221 blades according to this model every frame. To prevent doing useless work, we cull those blades which contribute least to the final image:
-### Running the code
+Distance Culling | Orientation Culling | Frustum Culling
+--- | --- | ---
+Those blades that are within a certain distance *d* of the camera are fully simulated (unless culled by other methods). Between *d* and *2d* only a third of blades are simulated, but all are rendered. Past *3d* no simuation takes place, and at *4d* blades are not even rendered. | Because blades have no thickness, looking at one from the oblique edge might cover less than a pixel in x space, resulting in wasted computation and possibly artifacts. Blades whose forward vector's dot product with the view direction meet a certain threshold are accordingly culled. | Frustum culling simply checks the root, middle, and tip of each blade to ensure that at least one is within the camera frustum. If each of these fall without, there is no reason to compute or render the blade.
-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.
+Those blades that did pass these tests were added to an out buffer and incremented the reported number of blades that made it through. Below are the effects of culling on the test scene pictured above:
-
+
-## Requirements
+ Surprisingly, orientation culling was more effective even than frustum culling for the test scene. While orientation culling's contribution was rather static, however, frustum culling is *extremely* effective for specific situations like looking up or down. As the scene size increases, it can be reasonably assumed that distance culling's effects will increase as well.
-**Ask on the mailing list for any clarifications.**
+##### 3: Tesselation Shaders
-In this project, you are given the following code:
+ Tesselation shaders allow extra geometry to be generated on the fly, saving tons of memory on vertices while still allowing realtime adjustment for cheap. While somewhat niche, they work great for procedural projects like this. Here, I subdivided grass along its length in the tessellation control shader, then tapered, bent, and computed the normal for blades in the tessellation evaluation shader.
-* 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.
+ While we don't have to store the complex geometry between frames, each subdivision in the control shader adds executions to the evaluation and vertex stages. Below is a comparison of cost to quality in the context of my project. Times are for the test scene above, the image is just for clearer visuals.
-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
+I settled on 7 subdivisions for my scene, but depending on context, it seems like many applications could get away with 5.
-See below for more guidance.
+##### 4: Rendering
-## Base Code Tour
+ The grass rendering uses the normal from the tessellation evaluation shader and simple lambert shading. I also generated a rolling plane by [generating a quad from the root positions of every few blades](https://zippy.gfycat.com/InsignificantBlackandwhiteChihuahua.mp4) in each row and column, which maps a repeating grass texture the same color as the blades. Afer all is said and done, the entire system scales as follows:
-Areas that you need to complete are
-marked with a `TODO` comment. Functions that are useful
-for reference are marked with the comment `CHECKITOUT`.
+
-* `src/main.cpp` is the entry point of our application.
-* `src/Instance.cpp` sets up the application state, initializes the Vulkan library, and contains functions that will create our
-physical and logical device handles.
-* `src/Device.cpp` manages the logical device and sets up the queues that our command buffers will be submitted to.
-* `src/Renderer.cpp` contains most of the rendering implementation, including Vulkan setup and resource creation. You will
-likely have to make changes to this file in order to support changes to your pipelines.
-* `src/Camera.cpp` manages the camera state.
-* `src/Model.cpp` manages the state of the model that grass will be created on. Currently a plane is hardcoded, but feel free to
-update this with arbitrary model loading!
-* `src/Blades.cpp` creates the control points corresponding to the grass blades. There are many parameters that you can play with
-here that will change the behavior of your rendered grass blades.
-* `src/Scene.cpp` manages the scene state, including the model, blades, and simualtion time.
-* `src/BufferUtils.cpp` provides helper functions for creating buffers to be used as descriptors.
-
-We left out descriptions for a couple files that you likely won't have to modify. Feel free to investigate them to understand their
-importance within the scope of the project.
-
-## Grass Rendering
-
-This project is an implementation of the paper, [Responsive Real-Time Grass Rendering for General 3D Scenes](https://www.cg.tuwien.ac.at/research/publications/2017/JAHRMANN-2017-RRTG/JAHRMANN-2017-RRTG-draft.pdf).
-Please make sure to use this paper as a primary resource while implementing your grass renderers. It does a great job of explaining
-the key algorithms and math you will be using. Below is a brief description of the different components in chronological order of how your renderer will
-execute, but feel free to develop the components in whatever order you prefer.
-
-### Representing Grass as Bezier Curves
-
-In this project, grass blades will be represented as Bezier curves while performing physics calculations and culling operations.
-Each Bezier curve has three control points.
-* `v0`: the position of the grass blade on the geomtry
-* `v1`: a Bezier curve guide that is always "above" `v0` with respect to the grass blade's up vector (explained soon)
-* `v2`: a physical guide for which we simulate forces on
-
-We also need to store per-blade characteristics that will help us simulate and tessellate our grass blades correctly.
-* `up`: the blade's up vector, which corresponds to the normal of the geometry that the grass blade resides on at `v0`
-* Orientation: the orientation of the grass blade's face
-* Height: the height of the grass blade
-* Width: the width of the grass blade's face
-* Stiffness coefficient: the stiffness of our grass blade, which will affect the force computations on our blade
-
-We can pack all this data into four `vec4`s, such that `v0.w` holds orientation, `v1.w` holds height, `v2.w` holds width, and
-`up.w` holds the stiffness coefficient.
-
-
-
-### Simulating Forces
-
-In this project, you will be simulating forces on grass blades while they are still Bezier curves. This will be done in a compute
-shader using the compute pipeline that has been created for you. Remember that `v2` is our physical guide, so we will be
-applying transformations to `v2` initially, then correcting for potential errors. We will finally update `v1` to maintain the appropriate
-length of our grass blade.
-
-#### Binding Resources
-
-In order to update the state of your grass blades on every frame, you will need to create a storage buffer to maintain the grass data.
-You will also need to pass information about how much time has passed in the simulation and the time since the last frame. To do this,
-you can extend or create descriptor sets that will be bound to the compute pipeline.
-
-#### Gravity
-
-Given a gravity direction, `D.xyz`, and the magnitude of acceleration, `D.w`, we can compute the environmental gravity in
-our scene as `gE = normalize(D.xyz) * D.w`.
-
-We then determine the contribution of the gravity with respect to the front facing direction of the blade, `f`,
-as a term called the "front gravity". Front gravity is computed as `gF = (1/4) * ||gE|| * f`.
-
-We can then determine the total gravity on the grass blade as `g = gE + gF`.
-
-#### Recovery
-
-Recovery corresponds to the counter-force that brings our grass blade back into equilibrium. This is derived in the paper using Hooke's law.
-In order to determine the recovery force, we need to compare the current position of `v2` to its original position before
-simulation started, `iv2`. At the beginning of our simulation, `v1` and `v2` are initialized to be a distance of the blade height along the `up` vector.
-
-Once we have `iv2`, we can compute the recovery forces as `r = (iv2 - v2) * stiffness`.
-
-#### Wind
-
-In order to simulate wind, you are at liberty to create any wind function you want! In order to have something interesting,
-you can make the function depend on the position of `v0` and a function that changes with time. Consider using some combination
-of sine or cosine functions.
-
-Your wind function will determine a wind direction that is affecting the blade, but it is also worth noting that wind has a larger impact on
-grass blades whose forward directions are parallel to the wind direction. The paper describes this as a "wind alignment" term. We won't go
-over the exact math here, but use the paper as a reference when implementing this. It does a great job of explaining this!
-
-Once you have a wind direction and a wind alignment term, your total wind force (`w`) will be `windDirection * windAlignment`.
-
-#### Total force
-
-We can then determine a translation for `v2` based on the forces as `tv2 = (gravity + recovery + wind) * deltaTime`. However, we can't simply
-apply this translation and expect the simulation to be robust. Our forces might push `v2` under the ground! Similarly, moving `v2` but leaving
-`v1` in the same position will cause our grass blade to change length, which doesn't make sense.
-
-Read section 5.2 of the paper in order to learn how to determine the corrected final positions for `v1` and `v2`.
-
-### Culling tests
-
-Although we need to simulate forces on every grass blade at every frame, there are many blades that we won't need to render
-due to a variety of reasons. Here are some heuristics we can use to cull blades that won't contribute positively to a given frame.
-
-#### Orientation culling
-
-Consider the scenario in which the front face direction of the grass blade is perpendicular to the view vector. Since our grass blades
-won't have width, we will end up trying to render parts of the grass that are actually smaller than the size of a pixel. This could
-lead to aliasing artifacts.
-
-In order to remedy this, we can cull these blades! Simply do a dot product test to see if the view vector and front face direction of
-the blade are perpendicular. The paper uses a threshold value of `0.9` to cull, but feel free to use what you think looks best.
-
-#### View-frustum culling
-
-We also want to cull blades that are outside of the view-frustum, considering they won't show up in the frame anyway. To determine if
-a grass blade is in the view-frustum, we want to compare the visibility of three points: `v0, v2, and m`, where `m = (1/4)v0 * (1/2)v1 * (1/4)v2`.
-Notice that we aren't using `v1` for the visibility test. This is because the `v1` is a Bezier guide that doesn't represent a position on the grass blade.
-We instead use `m` to approximate the midpoint of our Bezier curve.
-
-If all three points are outside of the view-frustum, we will cull the grass blade. The paper uses a tolerance value for this test so that we are culling
-blades a little more conservatively. This can help with cases in which the Bezier curve is technically not visible, but we might be able to see the blade
-if we consider its width.
-
-#### Distance culling
-
-Similarly to orientation culling, we can end up with grass blades that at large distances are smaller than the size of a pixel. This could lead to additional
-artifacts in our renders. In this case, we can cull grass blades as a function of their distance from the camera.
-
-You are free to define two parameters here.
-* A max distance afterwhich all grass blades will be culled.
-* A number of buckets to place grass blades between the camera and max distance into.
-
-Define a function such that the grass blades in the bucket closest to the camera are kept while an increasing number of grass blades
-are culled with each farther bucket.
-
-#### Occlusion culling (extra credit)
-
-This type of culling only makes sense if our scene has additional objects aside from the plane and the grass blades. We want to cull grass blades that
-are occluded by other geometry. Think about how you can use a depth map to accomplish this!
-
-### Tessellating Bezier curves into grass blades
-
-In this project, you should pass in each Bezier curve as a single patch to be processed by your grass graphics pipeline. You will tessellate this patch into
-a quad with a shape of your choosing (as long as it looks sufficiently like grass of course). The paper has some examples of grass shapes you can use as inspiration.
-
-In the tessellation control shader, specify the amount of tessellation you want to occur. Remember that you need to provide enough detail to create the curvature of a grass blade.
-
-The generated vertices will be passed to the tessellation evaluation shader, where you will place the vertices in world space, respecting the width, height, and orientation information
-of each blade. Once you have determined the world space position of each vector, make sure to set the output `gl_Position` in clip space!
-
-** Extra Credit**: Tessellate to varying levels of detail as a function of how far the grass blade is from the camera. For example, if the blade is very far, only generate four vertices in the tessellation control shader.
-
-To build more intuition on how tessellation works, I highly recommend playing with the [helloTessellation sample](https://github.com/CIS565-Fall-2017/Vulkan-Samples/tree/master/samples/5_helloTessellation)
-and reading this [tutorial on tessellation](http://in2gpu.com/2014/07/12/tessellation-tutorial-opengl-4-3/).
-
-## Resources
-
-### Links
-
-The following resources may be useful for this project.
+### Credits
* [Responsive Real-Time Grass Grass Rendering for General 3D Scenes](https://www.cg.tuwien.ac.at/research/publications/2017/JAHRMANN-2017-RRTG/JAHRMANN-2017-RRTG-draft.pdf)
* [CIS565 Vulkan samples](https://github.com/CIS565-Fall-2017/Vulkan-Samples)
* [Official Vulkan documentation](https://www.khronos.org/registry/vulkan/)
* [Vulkan tutorial](https://vulkan-tutorial.com/)
-* [RenderDoc blog on Vulkan](https://renderdoc.org/vulkan-in-30-minutes.html)
-* [Tessellation tutorial](http://in2gpu.com/2014/07/12/tessellation-tutorial-opengl-4-3/)
-
-
-## Third-Party Code Policy
-
-* Use of any third-party code must be approved by asking on our Google Group.
-* If it is approved, all students are welcome to use it. Generally, we approve
- use of third-party code that is not a core part of the project. For example,
- for the path tracer, we would approve using a third-party library for loading
- models, but would not approve copying and pasting a CUDA function for doing
- refraction.
-* Third-party code **MUST** be credited in README.md.
-* Using third-party code without its approval, including using another
- student's code, is an academic integrity violation, and will, at minimum,
- result in you receiving an F for the semester.
-
-
-## README
-
-* A brief description of the project and the specific features you implemented.
-* At least one screenshot of your project running.
-* A performance analysis (described below).
-
-### Performance Analysis
-
-The performance analysis is where you will investigate how...
-* Your renderer handles varying numbers of grass blades
-* The improvement you get by culling using each of the three culling tests
-
-## Submit
-
-If you have modified any of the `CMakeLists.txt` files at all (aside from the
-list of `SOURCE_FILES`), mentions it explicity.
-Beware of any build issues discussed on the Google Group.
-
-Open a GitHub pull request so that we can see that you have finished.
-The title should be "Project 6: YOUR NAME".
-The template of the comment section of your pull request is attached below, you can do some copy and paste:
-
-* [Repo Link](https://link-to-your-repo)
-* (Briefly) Mentions features that you've completed. Especially those bells and whistles you want to highlight
- * Feature 0
- * Feature 1
- * ...
-* Feedback on the project itself, if any.
diff --git a/bin/Debug/vulkan_grass_rendering.exe b/bin/Debug/vulkan_grass_rendering.exe
new file mode 100644
index 0000000..9edceea
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..40a5843
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..32e3486
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..971d91a
Binary files /dev/null and b/bin/Release/vulkan_grass_rendering.exe differ
diff --git a/img/1.jpg b/img/1.jpg
new file mode 100644
index 0000000..285aa8f
Binary files /dev/null and b/img/1.jpg differ
diff --git a/img/Capture.PNG b/img/Capture.PNG
new file mode 100644
index 0000000..4419077
Binary files /dev/null and b/img/Capture.PNG differ
diff --git a/img/Culling.png b/img/Culling.png
new file mode 100644
index 0000000..c8c3a1c
Binary files /dev/null and b/img/Culling.png differ
diff --git a/img/NumBlades.png b/img/NumBlades.png
new file mode 100644
index 0000000..40c5a2e
Binary files /dev/null and b/img/NumBlades.png differ
diff --git a/img/Sphere.png b/img/Sphere.png
new file mode 100644
index 0000000..724111a
Binary files /dev/null and b/img/Sphere.png differ
diff --git a/img/demo.gif b/img/demo.gif
new file mode 100644
index 0000000..af14cc6
Binary files /dev/null and b/img/demo.gif differ
diff --git a/img/scene.PNG b/img/scene.PNG
new file mode 100644
index 0000000..71fa48e
Binary files /dev/null and b/img/scene.PNG differ
diff --git a/img/youtubelink.PNG b/img/youtubelink.PNG
new file mode 100644
index 0000000..135c94c
Binary files /dev/null and b/img/youtubelink.PNG differ
diff --git a/src/Blades.cpp b/src/Blades.cpp
index 80e3d76..ea429e4 100644
--- a/src/Blades.cpp
+++ b/src/Blades.cpp
@@ -1,43 +1,61 @@
#include
#include "Blades.h"
+#include
#include "BufferUtils.h"
float generateRandomFloat() {
return rand() / (float)RAND_MAX;
}
-Blades::Blades(Device* device, VkCommandPool commandPool, float planeDim) : Model(device, commandPool, {}, {}) {
+bool bladesComparator(Blade i, Blade j) { return (i.v0.x + i.v0.z < j.v0.x + j.v0.z); }
+
+Blades::Blades(Device* device, VkCommandPool commandPool, float planeDim, std::vector& verts, std::vector& indices) : Model(device, commandPool, {}, {}) {
std::vector blades;
blades.reserve(NUM_BLADES);
+ int index = 0;
+
+ int dim = (int)sqrt(NUM_BLADES);
- for (int i = 0; i < NUM_BLADES; i++) {
- Blade currentBlade = Blade();
+ for (int i = 0; i < dim; i++) {
+ for (int j = 0; j < dim; j++) {
+ Blade currentBlade = Blade();
- glm::vec3 bladeUp(0.0f, 1.0f, 0.0f);
+#if SPHERE
- // Generate positions and direction (v0)
- float x = (generateRandomFloat() - 0.5f) * planeDim;
- float y = 0.0f;
- float z = (generateRandomFloat() - 0.5f) * planeDim;
- float direction = generateRandomFloat() * 2.f * 3.14159265f;
- glm::vec3 bladePosition(x, y, z);
- currentBlade.v0 = glm::vec4(bladePosition, direction);
+ float u = generateRandomFloat() * 2 - 1;
+ float theta = generateRandomFloat() * 2 * M_PI;
+ float x = sqrt(1 - u*u) * cos(theta);
+ float y = sqrt(1 - u*u) * sin(theta) + 4;
+ float z = u;
+ glm::vec3 bladeUp = glm::normalize(glm::vec3(x, y, z) - glm::vec3(0, 4, 0));
+#else
+ float x = (i / sqrt(NUM_BLADES) - 0.5f) * planeDim;
+ float z = (j / sqrt(NUM_BLADES) - 0.5f) * planeDim;
+ float y = cos(z * 0.1) * 4 + cos((x + z)*0.1+M_PI) * 4;
+ glm::vec3 bladeUp = glm::vec3(0, 1, 0);
+#endif
+ float direction = generateRandomFloat() * 2.f * M_PI;
+ glm::vec3 bladePosition(x, y, z);
+ currentBlade.v0 = glm::vec4(bladePosition, direction);
- // Bezier point and height (v1)
- float height = MIN_HEIGHT + (generateRandomFloat() * (MAX_HEIGHT - MIN_HEIGHT));
- currentBlade.v1 = glm::vec4(bladePosition + bladeUp * height, height);
+ // Bezier point and height (v1)
+ float height = MIN_HEIGHT + (generateRandomFloat() * (MAX_HEIGHT - MIN_HEIGHT));
+ currentBlade.v1 = glm::vec4(bladePosition + bladeUp * height, height);
- // Physical model guide and width (v2)
- float width = MIN_WIDTH + (generateRandomFloat() * (MAX_WIDTH - MIN_WIDTH));
- currentBlade.v2 = glm::vec4(bladePosition + bladeUp * height, width);
+ // Physical model guide and width (v2)
+ float width = MIN_WIDTH + (generateRandomFloat() * (MAX_WIDTH - MIN_WIDTH));
+ currentBlade.v2 = glm::vec4(bladePosition + bladeUp * height, width);
- // Up vector and stiffness coefficient (up)
- float stiffness = MIN_BEND + (generateRandomFloat() * (MAX_BEND - MIN_BEND));
- currentBlade.up = glm::vec4(bladeUp, stiffness);
+ // Up vector and stiffness coefficient (up)
+ float stiffness = MIN_BEND + (generateRandomFloat() * (MAX_BEND - MIN_BEND));
+ currentBlade.up = glm::vec4(bladeUp, stiffness);
- blades.push_back(currentBlade);
+ blades.push_back(currentBlade);
+ }
}
+ //std::sort(blades.begin(), blades.end(), bladesComparator);
+
BladeDrawIndirect indirectDraw;
indirectDraw.vertexCount = NUM_BLADES;
indirectDraw.instanceCount = 1;
@@ -47,6 +65,26 @@ Blades::Blades(Device* device, VkCommandPool commandPool, float planeDim) : Mode
BufferUtils::CreateBufferFromData(device, commandPool, blades.data(), NUM_BLADES * sizeof(Blade), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, bladesBuffer, bladesBufferMemory);
BufferUtils::CreateBuffer(device, NUM_BLADES * sizeof(Blade), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, culledBladesBuffer, culledBladesBufferMemory);
BufferUtils::CreateBufferFromData(device, commandPool, &indirectDraw, sizeof(BladeDrawIndirect), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT, numBladesBuffer, numBladesBufferMemory);
+
+ for (int i = 0; i < dim; i++) {
+ for (int j = 0; j < dim; j++) {
+ int index = i * dim + j;
+ Vertex v = Vertex();
+ v.pos = { blades[index].v0.x,blades[index].v0.y,blades[index].v0.z };
+ v.color = { 0,0,0 };
+ v.texCoord = { j*5/(float)dim, i*5/(float)dim };
+ verts.push_back(v);
+
+ if (i != 0 && j != 0) {
+ indices.push_back(index - dim - 1);
+ indices.push_back(index - dim);
+ indices.push_back(index);
+ indices.push_back(index);
+ indices.push_back(index - 1);
+ indices.push_back(index - dim - 1);
+ }
+ }
+ }
}
VkBuffer Blades::GetBladesBuffer() const {
diff --git a/src/Blades.h b/src/Blades.h
index 9bd1eed..6b7d578 100644
--- a/src/Blades.h
+++ b/src/Blades.h
@@ -3,14 +3,15 @@
#include
#include
#include "Model.h"
+#define SPHERE 0;
-constexpr static unsigned int NUM_BLADES = 1 << 13;
+constexpr static unsigned int NUM_BLADES = 1 << 19;
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 float MIN_BEND = 7.0f;
-constexpr static float MAX_BEND = 13.0f;
+constexpr static float MIN_BEND = 5.0f;
+constexpr static float MAX_BEND = 10.0f;
struct Blade {
// Position and direction
@@ -80,7 +81,7 @@ class Blades : public Model {
VkDeviceMemory numBladesBufferMemory;
public:
- Blades(Device* device, VkCommandPool commandPool, float planeDim);
+ Blades(Device* device, VkCommandPool commandPool, float planeDim, std::vector& verts, std::vector& indices);
VkBuffer GetBladesBuffer() const;
VkBuffer GetCulledBladesBuffer() const;
VkBuffer GetNumBladesBuffer() const;
diff --git a/src/Renderer.cpp b/src/Renderer.cpp
index b445d04..dc5bc2a 100644
--- a/src/Renderer.cpp
+++ b/src/Renderer.cpp
@@ -195,8 +195,41 @@ void Renderer::CreateTimeDescriptorSetLayout() {
}
void Renderer::CreateComputeDescriptorSetLayout() {
- // TODO: Create the descriptor set layout for the compute pipeline
+ // DONE: Create the descriptor set layout for the compute pipeline
// Remember this is like a class definition stating why types of information
+ VkDescriptorSetLayoutBinding allBlades = {};
+ allBlades.binding = 0;
+ allBlades.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
+ allBlades.descriptorCount = 1;
+ allBlades.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
+ allBlades.pImmutableSamplers = nullptr;
+
+ VkDescriptorSetLayoutBinding bladesToRender = {};
+ bladesToRender.binding = 1;
+ bladesToRender.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
+ bladesToRender.descriptorCount = 1;
+ bladesToRender.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
+ bladesToRender.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=
+ { allBlades, bladesToRender, 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");
+ }
// will be stored at each binding
}
@@ -215,7 +248,8 @@ void Renderer::CreateDescriptorPool() {
// Time (compute)
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER , 1 },
- // TODO: Add any additional types and counts of descriptors you will need to allocate
+ // DONE: Add any additional types and counts of descriptors you will need to allocate
+ { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, static_cast(3 * scene->GetBlades().size()) }
};
VkDescriptorPoolCreateInfo poolInfo = {};
@@ -318,7 +352,41 @@ void Renderer::CreateModelDescriptorSets() {
}
void Renderer::CreateGrassDescriptorSets() {
- // TODO: Create Descriptor sets for the grass.
+ // DONE: Create Descriptor sets for the grass.
+ grassDescriptorSets.resize(scene->GetBlades().size());
+
+ VkDescriptorSetLayout layouts[] = { modelDescriptorSetLayout }; //same data that the graphics .vert uses
+ VkDescriptorSetAllocateInfo allocInfo = {};
+ allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
+ allocInfo.descriptorPool = descriptorPool;
+ allocInfo.descriptorSetCount = static_cast(scene->GetBlades().size());
+ allocInfo.pSetLayouts = layouts;
+
+ // Allocate descriptor sets
+ if (vkAllocateDescriptorSets(logicalDevice, &allocInfo, grassDescriptorSets.data()) != VK_SUCCESS) {
+ throw std::runtime_error("Failed to allocate descriptor sets for Grass shaders");
+ }
+
+ std::vector descriptorWrites(scene->GetBlades().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;
+ }
+
+ vkUpdateDescriptorSets(logicalDevice, static_cast(scene->GetBlades().size()), descriptorWrites.data(), 0, nullptr);
// This should involve creating descriptor sets which point to the model matrix of each group of grass blades
}
@@ -358,7 +426,73 @@ void Renderer::CreateTimeDescriptorSet() {
}
void Renderer::CreateComputeDescriptorSets() {
- // TODO: Create Descriptor sets for the compute pipeline
+ // DONE: Create Descriptor sets for the compute pipeline
+ 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 (scene->GetBlades().size());
+ allocInfo.pSetLayouts = layouts;
+
+ // Allocate descriptor sets
+ if (vkAllocateDescriptorSets(logicalDevice, &allocInfo, computeDescriptorSets.data()) != VK_SUCCESS) {
+ throw std::runtime_error("Failed to allocate descriptor sets for Compute shaders");
+ }
+
+ std::vector descriptorWrites(3 * scene->GetBlades().size());
+
+ for (uint32_t i = 0; i < scene->GetBlades().size(); i++) {
+
+ VkDescriptorBufferInfo allBladesInfo = {};
+ allBladesInfo.buffer = scene->GetBlades()[i]->GetBladesBuffer();
+ allBladesInfo.offset = 0;
+ allBladesInfo.range = sizeof(Blade) * NUM_BLADES;
+
+ descriptorWrites[3 * i + 0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+ descriptorWrites[3 * i + 0].dstSet = computeDescriptorSets[0];
+ descriptorWrites[3 * i + 0].dstBinding = 0;
+ descriptorWrites[3 * i + 0].dstArrayElement = 0;
+ descriptorWrites[3 * i + 0].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
+ descriptorWrites[3 * i + 0].descriptorCount = 1;
+ descriptorWrites[3 * i + 0].pBufferInfo = &allBladesInfo;
+ descriptorWrites[3 * i + 0].pImageInfo = nullptr;
+ descriptorWrites[3 * i + 0].pTexelBufferView = nullptr;
+
+ VkDescriptorBufferInfo bladesToRenderInfo = {};
+ bladesToRenderInfo.buffer = scene->GetBlades()[i]->GetCulledBladesBuffer();
+ bladesToRenderInfo.offset = 0;
+ bladesToRenderInfo.range = sizeof(Blade) * NUM_BLADES;
+
+ descriptorWrites[3 * i + 1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+ descriptorWrites[3 * i + 1].dstSet = computeDescriptorSets[0];
+ 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 = &bladesToRenderInfo;
+ descriptorWrites[3 * i + 1].pImageInfo = nullptr;
+ descriptorWrites[3 * i + 1].pTexelBufferView = nullptr;
+
+ VkDescriptorBufferInfo numBladesInfo = {};
+ numBladesInfo.buffer = scene->GetBlades()[i]->GetNumBladesBuffer();
+ numBladesInfo.offset = 0;
+ numBladesInfo.range = sizeof(BladeDrawIndirect);
+
+ descriptorWrites[3 * i + 2].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+ descriptorWrites[3 * i + 2].dstSet = computeDescriptorSets[0];
+ 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 = &numBladesInfo;
+ 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);
// The descriptors should point to Storage buffers which will hold the grass blades, the culled grass blades, and the output number of grass blades
}
@@ -716,8 +850,8 @@ void Renderer::CreateComputePipeline() {
computeShaderStageInfo.module = computeShaderModule;
computeShaderStageInfo.pName = "main";
- // TODO: Add the compute dsecriptor set layout you create to this list
- std::vector descriptorSetLayouts = { cameraDescriptorSetLayout, timeDescriptorSetLayout };
+ // DONE: Add the compute dsecriptor set layout you create to this list
+ std::vector descriptorSetLayouts = { cameraDescriptorSetLayout, timeDescriptorSetLayout, computeDescriptorSetLayout };
// Create pipeline layout
VkPipelineLayoutCreateInfo pipelineLayoutInfo = {};
@@ -883,7 +1017,12 @@ void Renderer::RecordComputeCommandBuffer() {
// 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
+ // DONE: 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);
+ uint32_t numBlocksToMakeBlades = (NUM_BLADES + WORKGROUP_SIZE - 1) / WORKGROUP_SIZE;
+ vkCmdDispatch(computeCommandBuffer, numBlocksToMakeBlades, 1, 1);
+ }
// ~ End recording ~
if (vkEndCommandBuffer(computeCommandBuffer) != VK_SUCCESS) {
@@ -926,7 +1065,7 @@ void Renderer::RecordCommandBuffers() {
renderPassInfo.renderArea.extent = swapChain->GetVkExtent();
std::array clearValues = {};
- clearValues[0].color = { 0.0f, 0.0f, 0.0f, 1.0f };
+ clearValues[0].color = { 0.5f, 0.7f, 1.0f, 1.0f };
clearValues[1].depthStencil = { 1.0f, 0 };
renderPassInfo.clearValueCount = static_cast(clearValues.size());
renderPassInfo.pClearValues = clearValues.data();
@@ -975,14 +1114,14 @@ void Renderer::RecordCommandBuffers() {
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);
+ // DONE: Uncomment this when the buffers are populated
+ vkCmdBindVertexBuffers(commandBuffers[i], 0, 1, vertexBuffers, offsets);
- // TODO: Bind the descriptor set for each grass blades model
+ // DONE: Bind the descriptor set for each grass blades model
+ vkCmdBindDescriptorSets(commandBuffers[i], VK_PIPELINE_BIND_POINT_COMPUTE, graphicsPipelineLayout, 1, 1, &grassDescriptorSets[j], 0, nullptr);
- // Draw
- // TODO: Uncomment this when the buffers are populated
- // vkCmdDrawIndirect(commandBuffers[i], scene->GetBlades()[j]->GetNumBladesBuffer(), 0, 1, sizeof(BladeDrawIndirect));
+ // DONE: Uncomment this to Draw when the buffers are populated
+ vkCmdDrawIndirect(commandBuffers[i], scene->GetBlades()[j]->GetNumBladesBuffer(), 0, 1, sizeof(BladeDrawIndirect));
}
// End render pass
@@ -1041,7 +1180,7 @@ void Renderer::Frame() {
Renderer::~Renderer() {
vkDeviceWaitIdle(logicalDevice);
- // TODO: destroy any resources you created
+ // DONE: destroy any resources you created
vkFreeCommandBuffers(logicalDevice, graphicsCommandPool, static_cast(commandBuffers.size()), commandBuffers.data());
vkFreeCommandBuffers(logicalDevice, computeCommandPool, 1, &computeCommandBuffer);
@@ -1057,6 +1196,7 @@ Renderer::~Renderer() {
vkDestroyDescriptorSetLayout(logicalDevice, cameraDescriptorSetLayout, nullptr);
vkDestroyDescriptorSetLayout(logicalDevice, modelDescriptorSetLayout, nullptr);
vkDestroyDescriptorSetLayout(logicalDevice, timeDescriptorSetLayout, nullptr);
+ vkDestroyDescriptorSetLayout(logicalDevice, computeDescriptorSetLayout, nullptr);
vkDestroyDescriptorPool(logicalDevice, descriptorPool, nullptr);
diff --git a/src/Renderer.h b/src/Renderer.h
index 95e025f..8494546 100644
--- a/src/Renderer.h
+++ b/src/Renderer.h
@@ -56,11 +56,14 @@ class Renderer {
VkDescriptorSetLayout cameraDescriptorSetLayout;
VkDescriptorSetLayout modelDescriptorSetLayout;
VkDescriptorSetLayout timeDescriptorSetLayout;
+ VkDescriptorSetLayout computeDescriptorSetLayout;
VkDescriptorPool descriptorPool;
VkDescriptorSet cameraDescriptorSet;
std::vector modelDescriptorSets;
+ std::vector computeDescriptorSets;
+ std::vector grassDescriptorSets;
VkDescriptorSet timeDescriptorSet;
VkPipelineLayout graphicsPipelineLayout;
diff --git a/src/images/grass.jpg b/src/images/grass.jpg
index d8713bd..9d6a89b 100644
Binary files a/src/images/grass.jpg and b/src/images/grass.jpg differ
diff --git a/src/main.cpp b/src/main.cpp
index 8bf822b..afbb986 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(1280, 720, applicationName);
unsigned int glfwExtensionCount = 0;
const char** glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
@@ -90,7 +90,7 @@ int main() {
swapChain = device->CreateSwapChain(surface, 5);
- camera = new Camera(device, 640.f / 480.f);
+ camera = new Camera(device, 1280.0f / 720.0f);
VkCommandPoolCreateInfo transferPoolInfo = {};
transferPoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
@@ -115,21 +115,15 @@ int main() {
grassImage,
grassImageMemory
);
-
- float planeDim = 15.f;
- float halfWidth = planeDim * 0.5f;
- Model* plane = new Model(device, transferCommandPool,
- {
- { { -halfWidth, 0.0f, halfWidth }, { 1.0f, 0.0f, 0.0f },{ 1.0f, 0.0f } },
- { { halfWidth, 0.0f, halfWidth }, { 0.0f, 1.0f, 0.0f },{ 0.0f, 0.0f } },
- { { halfWidth, 0.0f, -halfWidth }, { 0.0f, 0.0f, 1.0f },{ 0.0f, 1.0f } },
- { { -halfWidth, 0.0f, -halfWidth }, { 1.0f, 1.0f, 1.0f },{ 1.0f, 1.0f } }
- },
- { 0, 1, 2, 2, 3, 0 }
- );
- plane->SetTexture(grassImage);
- Blades* blades = new Blades(device, transferCommandPool, planeDim);
+ float planeDim = 100.0f;
+ float halfWidth = planeDim * 0.5f;
+ std::vector planeVertices = std::vector();
+ std::vector planeIndices = std::vector();
+ Blades* blades = new Blades(device, transferCommandPool, planeDim, planeVertices, planeIndices);
+
+ Model* plane = new Model(device, transferCommandPool, planeVertices, planeIndices);
+ plane->SetTexture(grassImage);
vkDestroyCommandPool(device->GetVkDevice(), transferCommandPool, nullptr);
diff --git a/src/shaders/compute.comp b/src/shaders/compute.comp
index 0fd0224..4b97cee 100644
--- a/src/shaders/compute.comp
+++ b/src/shaders/compute.comp
@@ -4,6 +4,8 @@
#define WORKGROUP_SIZE 32
layout(local_size_x = WORKGROUP_SIZE, local_size_y = 1, local_size_z = 1) in;
+
+//outlined in createComputePipeline, filled in RecordComputeCommandBuffers
layout(set = 0, binding = 0) uniform CameraBufferObject {
mat4 view;
mat4 proj;
@@ -21,36 +23,124 @@ struct Blade {
vec4 up;
};
-// TODO: Add bindings to:
+// DONE: Add bindings to:
// 1. Store the input blades
// 2. Write out the culled blades
// 3. Write the total number of blades remaining
// 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) {
- return (value >= -bounds) && (value <= bounds);
+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;
+
+layout(set = 2, binding = 0) buffer AllBlades {
+ Blade allBlades[];
+};
+
+layout(set = 2, binding = 1) buffer BladesToRender {
+ Blade bladesToRender[];
+};
+
+bool inBounds(vec3 value, float bounds) {
+ return (value[0] >= -bounds) && (value[0] <= bounds) ||
+ (value[1] >= -bounds) && (value[1] <= bounds) ||
+ (value[2] >= -bounds) && (value[2] <= bounds);
}
void main() {
+ uint index = gl_GlobalInvocationID.x;
+
// Reset the number of blades to 0
- if (gl_GlobalInvocationID.x == 0) {
- // numBlades.vertexCount = 0;
- }
+ //if (index > numBlades.vertexCount) return;
+ if (index == 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
-
- // TODO: Cull blades that are too far away or not in the camera frustum and write them
+ // DONE: Apply forces on every blade and update the vertices in the buffer
+ // DONE: 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
+ //currently dont cull any blades
+ bool isCulled = false;
+ Blade thisBlade = allBlades[index];
+
+ vec3 v0 = vec3(thisBlade.v0);
+ vec3 v1 = vec3(thisBlade.v1);
+ vec3 v2 = vec3(thisBlade.v2);
+ vec3 vUp = vec3(thisBlade.up);
+ vec3 forward = vec3(cos(thisBlade.v0.w), 0, sin(thisBlade.v0.w));
+ vec3 right = normalize(cross(forward, vUp));
+ forward = -normalize(cross(right, vUp)); //TODO - unnecessarily expensive?
+ float height = thisBlade.v1.w;
+
+ //CULLING
+
+ //orientation
+ mat4 inverseView = inverse(camera.view);
+ vec4 dirc = inverseView * vec4(0.0, 0.0, 1.0, 0.0);
+ isCulled = abs(dot(dirc.xyz, right.xyz)) > 0.7;
+
+ //frustum
+ vec3 m = .25 * v0.xyz + .5 * v1.xyz + .25 * v2.xyz;
+ vec4 rootNDC = (camera.proj * camera.view * vec4(v0, 1.0));
+ vec4 midNDC = (camera.proj * camera.view * vec4(m, 1.0));
+ vec4 tipNDC = (camera.proj * camera.view * vec4(v2, 1.0));
+ isCulled = isCulled || !inBounds(rootNDC.xyz / rootNDC.w, 1.0f) || !inBounds(midNDC.xyz / midNDC.w, 1.0f) || !inBounds(tipNDC.xyz / tipNDC.w, 1.0f);
+
+ //distance
+ float dist = -(camera.view * vec4(v0, 1.0)).z;
+ isCulled = isCulled || dist > 75.0f;
+ if (isCulled && totalTime > 1) return; //let them settle first
+ else if (dist > 50.0f && totalTime > 1) {
+ bladesToRender[atomicAdd(numBlades.vertexCount, 1)] = thisBlade;
+ return;
+ }
+ else if (dist > 25.0f && totalTime > 1 && index % 3 != 0) {
+ bladesToRender[atomicAdd(numBlades.vertexCount, 1)] = thisBlade;
+ return;
+ }
+
+ //FORCES
+
+ //gravity - a quarter of environment grav directed forward
+ vec3 gE = vec3(0.0, -9.81f, 0.0);
+ vec3 gF = 0.25 * 9.81f * forward;
+ vec3 g = gE + gF;
+
+ //recovery - displacement of v2 * stiffness
+ vec3 iv2 = height * vUp + v0;
+ vec3 r = (iv2 - v2) * 1.3f * thisBlade.up.w;
+
+ //wind
+ vec3 windDir = (cos(totalTime + v0.x * 50) + 0.75f) * vec3(1.0f, 0, 0); //overlapping waves
+ //wind alignment
+ float fd = 1 - abs(dot(windDir, right)); //1 in total alignment, 0 in orthogonal case //1 - dot( (windDir * v0) / length(windDir * v0), (v2 - v0) / length(v2 - v0));
+ float fr = dot((v2 - v0), vUp) / height;
+ vec3 w = windDir * fd * fr;
+
+ //state validation
+ vec3 tv2 = deltaTime * (g + r + w);
+ v2 = v2 + tv2;
+ //if (v2.y < 0) v2.y = 0; Not valid with custom normal
+ v2 = v2 - vUp * min(dot(vUp, v2 - v0), 0);
+ //correct length
+ float lproj = length(v2 - v0 - (vUp * dot(v2 - v1, vUp)));
+ v1 = v0 + height * vUp * max(
+ 1-(lproj/height), 0.05f * max(lproj/height, 1)
+ );
+
+ float L0 = distance(v0, v1);
+ float L1 = distance(v0, v1) + distance(v0, v2);
+ float L = (L0 + L1) / 1.5f;
+ float ratio = height / L;
+ v1 = v0 + ratio*(v1 - v0);
+ v2 = v1 + ratio*(v2 - v1);
+ thisBlade.v2 = vec4(v2, thisBlade.v2.w);
+
+ allBlades[index] = thisBlade;
+ bladesToRender[atomicAdd(numBlades.vertexCount, 1)] = thisBlade;
}
diff --git a/src/shaders/grass.frag b/src/shaders/grass.frag
index c7df157..c79fb24 100644
--- a/src/shaders/grass.frag
+++ b/src/shaders/grass.frag
@@ -6,12 +6,15 @@ layout(set = 0, binding = 0) uniform CameraBufferObject {
mat4 proj;
} camera;
-// TODO: Declare fragment shader inputs
+// TODO: Declare fragment shader inputs - normal?
+layout(location = 0) in vec3 normal;
layout(location = 0) out vec4 outColor;
+
void main() {
// TODO: Compute fragment color
-
- outColor = vec4(1.0);
+ float diffuse = 0.33 + clamp(dot(normal, vec3(0, -1, 0)),0,0.67); //lambert at noon
+ vec4 green = vec4(0.5, 0.7, 0.1, 1.0);
+ outColor = diffuse * green;
}
diff --git a/src/shaders/grass.tesc b/src/shaders/grass.tesc
index f9ffd07..84ac0c0 100644
--- a/src/shaders/grass.tesc
+++ b/src/shaders/grass.tesc
@@ -8,19 +8,32 @@ layout(set = 0, binding = 0) uniform CameraBufferObject {
mat4 proj;
} camera;
-// TODO: Declare tessellation control shader inputs and outputs
+// DONE: Declare tessellation control shader inputs and outputs
+layout(location = 0) in vec4[] v0_in;
+layout(location = 1) in vec4[] v1_in;
+layout(location = 2) in vec4[] v2_in;
+layout(location = 3) in vec4[] vUp_in;
+//for .tese
+layout(location = 0) out vec4[] v0_out;
+layout(location = 1) out vec4[] v1_out;
+layout(location = 2) out vec4[] v2_out;
+layout(location = 3) out vec4[] vUp_out;
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
- // TODO: Set level of tesselation
- // gl_TessLevelInner[0] = ???
- // gl_TessLevelInner[1] = ???
- // gl_TessLevelOuter[0] = ???
- // gl_TessLevelOuter[1] = ???
- // gl_TessLevelOuter[2] = ???
- // gl_TessLevelOuter[3] = ???
+ v0_out[gl_InvocationID] = v0_in[gl_InvocationID];
+ v1_out[gl_InvocationID] = v1_in[gl_InvocationID];
+ v2_out[gl_InvocationID] = v2_in[gl_InvocationID];
+ vUp_out[gl_InvocationID] = vUp_in[gl_InvocationID];
+
+ // DONE?: Set level of tesselation
+ gl_TessLevelInner[0] = 1.0;
+ gl_TessLevelInner[1] = 7.0;
+ gl_TessLevelOuter[0] = 7.0;
+ gl_TessLevelOuter[1] = 1.0;
+ gl_TessLevelOuter[2] = 7.0;
+ gl_TessLevelOuter[3] = 1.0;
}
diff --git a/src/shaders/grass.tese b/src/shaders/grass.tese
index 751fff6..ff57f9a 100644
--- a/src/shaders/grass.tese
+++ b/src/shaders/grass.tese
@@ -9,10 +9,40 @@ layout(set = 0, binding = 0) uniform CameraBufferObject {
} camera;
// TODO: Declare tessellation evaluation shader inputs and outputs
+//from .tesc
+layout(location = 0) in vec4[] v0_in;
+layout(location = 1) in vec4[] v1_in;
+layout(location = 2) in vec4[] v2_in;
+layout(location = 3) in vec4[] vUp_in;
+
+layout(location = 0) out vec3 normal;
void main() {
float u = gl_TessCoord.x;
float v = gl_TessCoord.y;
+ float t = u + 0.5 * v - u * v;
+
+ // DONE: Use u and v to parameterize along the grass blade and output positions for each vertex of the grass blade
+ float height = v1_in[0].w;
+ float width = v2_in[0].w;
+ float angle = v0_in[0].w;
+
+ vec3 v0 = vec3(v0_in[0]);
+ vec3 v1 = vec3(v1_in[0]);
+ vec3 v2 = vec3(v2_in[0]);
+ vec3 forward = vec3(cos(angle), 0.0, sin(angle));
+ vec3 right = normalize(cross(forward, vec3(vUp_in[0])));
+ forward = -normalize(cross(right, vec3(vUp_in[0]))); //TODO - unnecessarily expensive?
+
+ vec3 a = v0 + v * (v1 - v0); //amount up
+ vec3 b = v1 + v * (v2 - v1); //amount forward
+ vec3 c = a + v * (b - a);
+ vec3 c0 = c - width * right;
+ vec3 c1 = c + width * right;
+
+ vec3 t0 = normalize(b - a);
+ normal = normalize(cross(t0, right));
+ vec3 pos = (1 - t) * c0 + t * c1;
- // TODO: Use u and v to parameterize along the grass blade and output positions for each vertex of the grass blade
+ gl_Position = camera.proj * camera.view * vec4(pos, 1.0);
}
diff --git a/src/shaders/grass.vert b/src/shaders/grass.vert
index db9dfe9..4363c0d 100644
--- a/src/shaders/grass.vert
+++ b/src/shaders/grass.vert
@@ -6,12 +6,30 @@ layout(set = 1, binding = 0) uniform ModelBufferObject {
mat4 model;
};
-// TODO: Declare vertex shader inputs and outputs
+// DONE: Declare vertex shader inputs and outputs
+
+//coming out of getAttributeDescriptions
+layout(location = 0) in vec4 v0_in;
+layout(location = 1) in vec4 v1_in;
+layout(location = 2) in vec4 v2_in;
+layout(location = 3) in vec4 vUp_in;
+
+//for tesc
+layout(location = 0) out vec4 v0_out;
+layout(location = 1) out vec4 v1_out;
+layout(location = 2) out vec4 v2_out;
+layout(location = 3) out vec4 vUp_out;
out gl_PerVertex {
vec4 gl_Position;
};
void main() {
- // TODO: Write gl_Position and any other shader outputs
+ // DONE: Write gl_Position and any other shader outputs
+ gl_Position = v0_in;
+ //to .tesc
+ v0_out = v0_in;
+ v1_out = v1_in;
+ v2_out = v2_in;
+ vUp_out = vUp_in;
}