diff --git a/README.md b/README.md
index 47a8c52..99168b1 100644
--- a/README.md
+++ b/README.md
@@ -1,302 +1,213 @@
-Instructions - Vulkan Grass Rendering
-========================
+# Vulkan Grass Rendering
+### Grace Gilbert, University of Pennsylvania, CIS 565: GPU Programming and Architecture
-This is due **Wednesday 10/9, evening at midnight**.
+
+
+
-**QUICK NOTE**: Please use `git clone --recursive` when cloning this repo as there are submodules which need to be cloned as well.
+* Grace Gilbert
+ * gracelgilbert.com
+* Tested on: Windows 10, i9-9900K @ 3.60GHz 64GB, GeForce RTX 2080 40860MB
-**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.
+## Overview
+In this project I implemented a grass simulator and renderer using Vulkan. Each blade of grass is represented by a Bezier curve, which gets tessellated and shaped into a blade. I use a compute shader to perform physics calculations on the blades, adjusting the Bezier curve to apply gravity, an elastic recovery force, and wind. In this compute shader, I also cull blades to improve efficiency and remove some aliasing and flickering in the render.
-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.
+## Resources
+* [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/)
- 
+## Demos
+Light Breeze | Sinusoidal Wind | Radial Winds
+:-------------------------:|:-------------------------:|:-------------------------:
+|  |
-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!
+The first example has relatively stiff blades of grass and a light breeze applied. The wind distribution is purely based off of a Fractal Brownian Motion (FBM) noise function.
-**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 Shehzan or one of the TAs before continuing!
+The second is less stiff grass with sinusoidal functions added together to create the wind distribution. Some FBM noise is layered on top of the sinusoidal functions to add a more natural feel. For this example, I tried imitating a gif I had found online of grass:
-### 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
+The final example contains 4 times as many blades as the first two, and the wind is distributed according to a radiating function. This is somewhat of a helicopter effect, as if a helicopter is landing in the center of the radiating wind.
-### Installing Vulkan
+## Implementation
+### Pipeline Setup
+This project utilizes the Vulkan pipeline. Much of this pipeline was set up in the base code, getting to the point of a textured ground plane rendering with a simple, moveable camera setup. To render grass, I set up an additional grass descriptor set layout and descriptor set, as well as a compute descriptor set to pass in the appropriate data into the compute shader.
-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.
+The grass descriptor set passes the model matrix for each group of grass blades into the grass rendering pipeline. This model matrix transforms all of the blades, ensuring that the patch of grass blades is located in the correct position.
-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.
+The compute descriptor set passes in three buffers to the compute shader. The first of these buffers is the input blades data, which holds the control points for each blade's Bezier curve, as well as the blades height, width, orientation, stiffness, and the up vector of the surface it sits on. This data is what gets modified to apply physics to the blades. Next is another buffer of blades which will contain the blades that remain after culling. This is the buffer whose data gets passed into the render pipeline, as these are the blades to be rendered. Initially, prior to implementing culling, this buffer just copied the input blades data. Finally, the compute shader takes in a buffer that stores the number of remaining blades. Each non-culled blade increments this number using an atomic add. This number ensures that we render the right number of blades each frame.
-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!
+The overall path of the pipeline is first the compute shader, where physics and culling are performed. The output data then goes to the grass vertex shader, which transforms the control points with the blade's model matrix. Then the tessellation control shader determines how finely to tesselate the blade. The tessellation evaluation shader shapes the tessellated blade. Finally, the fragment shader shades the blades, which are then rendered to the screen.
-### Running the code
+### Blade Tessellation and Rendering
+Each blade of grass is represented as a Bezier curve with three control points, `v0`, `v1`, and `v2`, where `v0` is the base position of the blade. `v1` always lies directly above `v0`, and `v2` is what we apply forces to to animate the grass. There is also an up vector, defining the up direcction of the surface the blades sit on. The vectors are all 4D vectors, where the 4th element holds another piece of information: height, width, stiffness, and orientation. The following image represents the blade model described:
-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.
+
-
+The vectors get passed into the vertex shader, where they are transformed into model space using the model matrix of the group of blades. The base position, `v0`, gets passed as the gl_Position into the tessellation control shader. The control shader sets the tessellation levels and passes along the control points and gl_Position into the tessellation evaluation shader. The tessellation levels control how subdivided the tessellation will be, which affects how smooth the curves in the grass blades will be.
-## Requirements
+The tessellation evaluation shader is where we shape the blades to curve along their Bezier curve and to have a grass blade shape along their width. To shape the blade along the Bezier curve defined by the control points, we perform a series of interpolations, following De Casteljau's algorithm:
-**Ask on the mailing list for any clarifications.**
+
-In this project, you are given the following code:
+We then use these interpolated values to find the final position of our current point in the tessellation:
-* 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:
+The t value in the above equation determines the shape of the blade of grass, acheiving any of the shapes pictures below:
-* 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.
+Finally, this position is projected into screen space with the view projection matrix.
-## Base Code Tour
+The last step of the blade rendering is the fragment shader. This shader applies a gradient to the blades, making them dark green at their base and light green at the tips. This shading uses the height at the current portion of the blade. This height is a mix between the world space height and the individual blade space height. The world space height ensures that parts of the blades at the same height will have similar luminance. The blade space height ensures that even shorter blades still have some light green at their tips. The mixing of these two height spaces gives a more natural, varied look to the grass.
-Areas that you need to complete are
-marked with a `TODO` comment. Functions that are useful
-for reference are marked with the comment `CHECKITOUT`.
+Simply rendering the blades of grass gives the following results:
-* `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.
+Quadratic Blades | Triangular Blades
+:-------------------------:|:-------------------------:
+| 
-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.
+I initially tried using lambertian shading based on the angle of the blade of grass, but this caused the color distrubition to be too varied. Because each blade is a flat plane, the orientation dramatically affects the lighting on it, causing all blades to have different tones of green, which does not look realistic.
-## 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.
+Something I added for visual appeal was I changed the background color of the window to a sky blue, and changed the base texture from grass to dirt to place the grass in a more realistic setting.
-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:
+### Physics
+The process of applying physics to the blades of grass involves calculating all forces that act upon the blades, and then updating the Bezier control points according to these forces. We are applying forces onto the third control point, `v2`, however there are measures we must take to ensure that when we apply the forces, we maintain certain properties: the blade must not fall through the ground plane, the length of the curved blade must be the same as the height of the blade to preserve length, and `v1` must be updated in relation to `v2`.
-
+We start by finding the updated position for `v2`, ensuring that it is above ground:
-### 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
+To find the updated position of `v1`, which must always be directly above the base of the blade, we find the length of the projection of the blade onto the ground and use this to find `v1`:
-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.
+
-
+The final step is to ensure that the length of the curved blade is not longer than the height of the straight blade. We first evaluation the length of the Bezier curve:
-### 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.
+Finally, we use this length and the height of the blade to set the final updated control points:
-#### 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.
+Below I describe how we calculate each of the forces that act on the blades of grass.
#### 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`.
+To apply gravity onto the grass blades, we start with a direction and magnitude for gravity, which we use to find the environmental gravity:
+```
+GravtityDirection = (0, -1, 0)
+GravityMagnitude = 9.8
+GravityEnvironmental = (0, -9.8, 0)
+```
+Because blades are represented as flat planes, they can only fall in one direction, the direction that they are facing. To find this direction, I converted the orientation of the blade into a direction vector with simple trigonometry:
+```
+BladeFacingDirection = (cos(orientation), 0, sin(orientation))
+```
+We then calculate how much gravity contributes with respect to this facing direction:
+```
+GravityFront = (1/4) * length(GravityEnvironmental) * BladeFacingDirection
+```
+Finally, the total force of gravity is the sum of the environmental gravity and the front facing gravity:
+```
+GravityForce = GravityEnvironmental + GravityFront
+```
+
+With just gravity applied, the blades fall to the ground (the blades fall after a few seconds):
+
+
#### Recovery
+Blades of grass have some elastic stiffness to them, allowing them to remain mostly upright even in the presence of gravity. To represent this, we add a recovery force to counter the gravitational force. We use Hooke's law to simulate this.
-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
+The recovery force is found by comparing the current position of `v2`, the third control point, to its original position at the start of the simulation, `iv2`. Knowing both `v2` and `iv2`, the recovery force is as follows:
+```
+r = (iv2 - v2) * BladeStiffness.
+```
-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.
+With gravity and recovery only, the blades reach a state of equilibrium and do not move from this position:
-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
+#### Wind
+The final force is the wind force. The wind force on each blade is determined by a function of the position of the base of each blade, wi(v0). This function defines the direction and strength of the wind at each point in space. For my wind functions, I used combinations of sinusoidal function and FBM noise. The noise breaks up some of the sinusoidal patterns in the wind.
-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.
+We next need to see how the wind affects the blade based on its the blade's orientation. Note that a wind force acting perpendicular to the front face of the blade will have no affect on that blade, as it can only bend along its front face. The affect of the wind becomes stronger as the direction and the front face become more aligned. The calculation for the final wind force is the following:
-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.
+With a light wind distribution purely based on and FBM noise function, the result of all three forces combined looks as follows:
-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
+### Culling
+There are many blades of grass that we in fact do not have to render due to various reasons. We still must apply physics to all blades, as the physics may move an unrendered blade into a state where it must be rendered, but we can cut down the cost of rendering many blades using the following culling techniques.
-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.
+#### Orientation
+Some blades are angled in such a way that they appear very thin in camera. If the line of sight from the camera to that blade is perpendicular or nearly perpendicular to the front facing direction of the blade, the blade will appear as a very thing line. We can cull these blades, as they are nearly invisible. In addition to adding efficiency, this culling will reduce some flickering on screen. The thin blades may be smaller than a single pixel width, cuasing flickering as the line of the blade's edge flickers in and out of view. As few to none of the blades will be perfectly perpendicular to the camera view direction, we use a threshold to cull out these blades. The culling process looks like the following, where dirc is the front facing direction of the blade and dirb is the direction from the camera to the blade:
-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.
+The following illustrates orientation culling using an extreme threshold to make the effect visible. Only blades that are very strongly facing the camera will remain unculled:
-#### 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!
+#### Frustum
+Not all blades are within sight of the camera at all times. There is no need to render blades that the camera cannot see, so we can cull these blades. To do this, we check the visibility of three points on the blade, `v0`, `v2`, and `m`, which is a midpoint on the curve calculated as `m = (1/4)v0 + (1/2)v1 + (1/4)v2`. If all three points are not visible, then we can cull the blade.
-### Tessellating Bezier curves into grass blades
+To determine if a point is visible on camera, we project it into screen space and see if it falls within the bounds of the screen, with a small tolerance, as we want some buffer area around the screen to prevent seeing the blades disappear as the move off screen, as the curve may be off screen, but the blades have width, so their geometry might still be on screen.
-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 following illustrates frustum culling with a negative tolerance, culling blades that fall just within the frustum in order to make the effect visible. Important to notice is that the blades at and behind the far clip plane are being culled, so as the camera moves in and out, the cutoff line changes. Also notice that at the edges, we can see blades flicker in and out of the screen, as they get culled when they move too far out.
-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.
+#### Distance
+Like with orientation culling, if a blade is very far fromm the camera, it may appear very small on screen, causing rendering artifacts. To reduce these artifacts, we cull blades based on their distance from camera. Rather than a sharp cutoff where blades go from being rendered to being culled a certain distance, we step the culling gradually in regions. We define a maximum distance and a number of regions. As the regions get further from camera, we cull increasingly more of the blades in that region. In the first region, no blades are culled, and in the last region all blades are culled, giving a smooth transition of blade density as we move farther from camera.
-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/).
+Distance from camera is calculated as follows:
-## Resources
+
-### Links
+Culling is determined according to this distance and the defined regions:
-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/)
+
+The following illustrates distance culling with extreme, quick density falloff to see the effect. The maximum distance is 50, so the blade density drops fairly close to camera, and there are 10 groups to illustrate the regions of density falloff:
-## 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.
+## Performance Analysis
+To analyze performance, I compared the per-frame render time for different numbers of blades and with different culling optimizations turned on. I kept the camera the same for all tests. Note that for distance culling, I adjusted the maximum depth so that it the culling would actually affect the scene at the tested camera angle.
+2^10 and 2^13 Blades | 2^15 and 2^18 Blades | 2^21 and 2^24 Blades
+:-------------------------:|:-------------------------:|:-------------------------:
+|  |
-## README
+The results indicate that with a small number of blades, the culling optimizations are not extremely effective. With 2^10 blades, orientation and distance culling actually slow down the rendering. However, with more blades, even just 2^13, any of the optimizations speed up the render, and for all tested number of blades, all optimizations together sped up the render time.
-* 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).
+Generally, distance culling sped up the render the most significantly. However, this may not be the most telling result, as I adjusted distance culling to have an effect with the current camera position, so it may be optimizating more than it might if the regions were set differently. One reason distance culling may have a stronger impact is because more of the blades farther back fall into the viewing frustum. So if we can cull a lot of the ones that are in the frustum but far back, this may cull more than simple culling the ones outside the frustum altogether.
-### Performance Analysis
+## Bloopers
+In the process of trying to get the blades to render, I was indexing into the input arrays in the tessellation evaluation shader incorrectly. This caused all of my blades of grass to render as one single blade.
-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
+In fixing this same error, I lowered the number of blades to see what was happening, and got this image:
-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:
+Another error that produced an interesting result was accidentally using the length of `v0` in my force application. This caused very large forces to be applied when `v0` was very close to the origin, giving this image:
-* [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..3ad9c28
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..49ab65d
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..4ba9735
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
index f68db3a..40e9d0b 100644
Binary files a/bin/Release/vulkan_grass_rendering.exe and b/bin/Release/vulkan_grass_rendering.exe differ
diff --git a/img/AllForces.gif b/img/AllForces.gif
new file mode 100644
index 0000000..0003af1
Binary files /dev/null and b/img/AllForces.gif differ
diff --git a/img/BezierEquations.PNG b/img/BezierEquations.PNG
new file mode 100644
index 0000000..7ef7824
Binary files /dev/null and b/img/BezierEquations.PNG differ
diff --git a/img/BezierPositionEquation.PNG b/img/BezierPositionEquation.PNG
new file mode 100644
index 0000000..5af9b10
Binary files /dev/null and b/img/BezierPositionEquation.PNG differ
diff --git a/img/BladeShapes.PNG b/img/BladeShapes.PNG
new file mode 100644
index 0000000..8f15dd7
Binary files /dev/null and b/img/BladeShapes.PNG differ
diff --git a/img/CurveUpdateStage1.PNG b/img/CurveUpdateStage1.PNG
new file mode 100644
index 0000000..a8ad103
Binary files /dev/null and b/img/CurveUpdateStage1.PNG differ
diff --git a/img/CurveUpdateStage2.PNG b/img/CurveUpdateStage2.PNG
new file mode 100644
index 0000000..3bd2127
Binary files /dev/null and b/img/CurveUpdateStage2.PNG differ
diff --git a/img/CurveUpdateStage3.PNG b/img/CurveUpdateStage3.PNG
new file mode 100644
index 0000000..1eb13d0
Binary files /dev/null and b/img/CurveUpdateStage3.PNG differ
diff --git a/img/CurveUpdateStage4.PNG b/img/CurveUpdateStage4.PNG
new file mode 100644
index 0000000..65742e1
Binary files /dev/null and b/img/CurveUpdateStage4.PNG differ
diff --git a/img/CurveUpdateStage5.PNG b/img/CurveUpdateStage5.PNG
new file mode 100644
index 0000000..2f7a646
Binary files /dev/null and b/img/CurveUpdateStage5.PNG differ
diff --git a/img/DistanceCalculationForCulling.PNG b/img/DistanceCalculationForCulling.PNG
new file mode 100644
index 0000000..ea433d3
Binary files /dev/null and b/img/DistanceCalculationForCulling.PNG differ
diff --git a/img/DistanceCulling.PNG b/img/DistanceCulling.PNG
new file mode 100644
index 0000000..253b38a
Binary files /dev/null and b/img/DistanceCulling.PNG differ
diff --git a/img/DistanceCullingVideo.gif b/img/DistanceCullingVideo.gif
new file mode 100644
index 0000000..9b5b636
Binary files /dev/null and b/img/DistanceCullingVideo.gif differ
diff --git a/img/FinalWindForce.PNG b/img/FinalWindForce.PNG
new file mode 100644
index 0000000..c40b85e
Binary files /dev/null and b/img/FinalWindForce.PNG differ
diff --git a/img/FrustumCulling.PNG b/img/FrustumCulling.PNG
new file mode 100644
index 0000000..fb41884
Binary files /dev/null and b/img/FrustumCulling.PNG differ
diff --git a/img/FrustumCullingVideo.gif b/img/FrustumCullingVideo.gif
new file mode 100644
index 0000000..bb9e0a3
Binary files /dev/null and b/img/FrustumCullingVideo.gif differ
diff --git a/img/GravityAndRecovery.PNG b/img/GravityAndRecovery.PNG
new file mode 100644
index 0000000..8793800
Binary files /dev/null and b/img/GravityAndRecovery.PNG differ
diff --git a/img/GravityRecoveryOnly.PNG b/img/GravityRecoveryOnly.PNG
new file mode 100644
index 0000000..d3f90be
Binary files /dev/null and b/img/GravityRecoveryOnly.PNG differ
diff --git a/img/LambertianShading.PNG b/img/LambertianShading.PNG
new file mode 100644
index 0000000..61bc6f2
Binary files /dev/null and b/img/LambertianShading.PNG differ
diff --git a/img/LightBreeze.gif b/img/LightBreeze.gif
new file mode 100644
index 0000000..fcb9e54
Binary files /dev/null and b/img/LightBreeze.gif differ
diff --git a/img/OrientationCulling.PNG b/img/OrientationCulling.PNG
new file mode 100644
index 0000000..d4a90df
Binary files /dev/null and b/img/OrientationCulling.PNG differ
diff --git a/img/OrientationCullingVideo.gif b/img/OrientationCullingVideo.gif
new file mode 100644
index 0000000..9071051
Binary files /dev/null and b/img/OrientationCullingVideo.gif differ
diff --git a/img/PerformanceChart12.PNG b/img/PerformanceChart12.PNG
new file mode 100644
index 0000000..975d2dd
Binary files /dev/null and b/img/PerformanceChart12.PNG differ
diff --git a/img/PerformanceChart34.PNG b/img/PerformanceChart34.PNG
new file mode 100644
index 0000000..1b3f70d
Binary files /dev/null and b/img/PerformanceChart34.PNG differ
diff --git a/img/PerformanceChart56.PNG b/img/PerformanceChart56.PNG
new file mode 100644
index 0000000..fab405a
Binary files /dev/null and b/img/PerformanceChart56.PNG differ
diff --git a/img/PerformanceLargerBladeNumber.PNG b/img/PerformanceLargerBladeNumber.PNG
new file mode 100644
index 0000000..d12b6d7
Binary files /dev/null and b/img/PerformanceLargerBladeNumber.PNG differ
diff --git a/img/PerformanceSmallerBladeNumber.PNG b/img/PerformanceSmallerBladeNumber.PNG
new file mode 100644
index 0000000..41cdf74
Binary files /dev/null and b/img/PerformanceSmallerBladeNumber.PNG differ
diff --git a/img/RadialWind.gif b/img/RadialWind.gif
new file mode 100644
index 0000000..4f1b903
Binary files /dev/null and b/img/RadialWind.gif differ
diff --git a/img/ReferenceGrass.gif b/img/ReferenceGrass.gif
new file mode 100644
index 0000000..d01b879
Binary files /dev/null and b/img/ReferenceGrass.gif differ
diff --git a/img/StillBlades.PNG b/img/StillBlades.PNG
new file mode 100644
index 0000000..9263cc6
Binary files /dev/null and b/img/StillBlades.PNG differ
diff --git a/img/TallCentralGrass.PNG b/img/TallCentralGrass.PNG
new file mode 100644
index 0000000..0431b80
Binary files /dev/null and b/img/TallCentralGrass.PNG differ
diff --git a/img/TesselationIndexingBlooper.PNG b/img/TesselationIndexingBlooper.PNG
new file mode 100644
index 0000000..329a0dd
Binary files /dev/null and b/img/TesselationIndexingBlooper.PNG differ
diff --git a/img/TriangleBlades.PNG b/img/TriangleBlades.PNG
new file mode 100644
index 0000000..48052e6
Binary files /dev/null and b/img/TriangleBlades.PNG differ
diff --git a/img/WindCalculations.PNG b/img/WindCalculations.PNG
new file mode 100644
index 0000000..2498255
Binary files /dev/null and b/img/WindCalculations.PNG differ
diff --git a/img/gravityOnly.gif b/img/gravityOnly.gif
new file mode 100644
index 0000000..05406e7
Binary files /dev/null and b/img/gravityOnly.gif differ
diff --git a/img/singleBlade.PNG b/img/singleBlade.PNG
new file mode 100644
index 0000000..66d9224
Binary files /dev/null and b/img/singleBlade.PNG differ
diff --git a/img/stillGrass.PNG b/img/stillGrass.PNG
new file mode 100644
index 0000000..f01060d
Binary files /dev/null and b/img/stillGrass.PNG differ
diff --git a/img/video1.gif b/img/video1.gif
new file mode 100644
index 0000000..6522eb8
Binary files /dev/null and b/img/video1.gif differ
diff --git a/img/video2.gif b/img/video2.gif
new file mode 100644
index 0000000..cecb832
Binary files /dev/null and b/img/video2.gif differ
diff --git a/src/Blades.cpp b/src/Blades.cpp
index 80e3d76..3a0ce55 100644
--- a/src/Blades.cpp
+++ b/src/Blades.cpp
@@ -44,8 +44,8 @@ Blades::Blades(Device* device, VkCommandPool commandPool, float planeDim) : Mode
indirectDraw.firstVertex = 0;
indirectDraw.firstInstance = 0;
- 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, blades.data(), NUM_BLADES * sizeof(Blade), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT, bladesBuffer, bladesBufferMemory);
+ BufferUtils::CreateBuffer(device, NUM_BLADES * sizeof(Blade), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_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);
}
diff --git a/src/Blades.h b/src/Blades.h
index 9bd1eed..2bcae8c 100644
--- a/src/Blades.h
+++ b/src/Blades.h
@@ -4,13 +4,13 @@
#include
#include "Model.h"
-constexpr static unsigned int NUM_BLADES = 1 << 13;
+constexpr static unsigned int NUM_BLADES = 1 << 13; // originally 13
constexpr static float MIN_HEIGHT = 1.3f;
constexpr static float MAX_HEIGHT = 2.5f;
-constexpr static float MIN_WIDTH = 0.1f;
-constexpr static float MAX_WIDTH = 0.14f;
-constexpr static float MIN_BEND = 7.0f;
-constexpr static float MAX_BEND = 13.0f;
+constexpr static float MIN_WIDTH = 0.06f;
+constexpr static float MAX_WIDTH = 0.1f;
+constexpr static float MIN_BEND = 5.0f;
+constexpr static float MAX_BEND = 11.0f;
struct Blade {
// Position and direction
diff --git a/src/Camera.cpp b/src/Camera.cpp
index 3afb5b8..729684f 100644
--- a/src/Camera.cpp
+++ b/src/Camera.cpp
@@ -13,7 +13,7 @@ Camera::Camera(Device* device, float aspectRatio) : device(device) {
theta = 0.0f;
phi = 0.0f;
cameraBufferObject.viewMatrix = glm::lookAt(glm::vec3(0.0f, 1.0f, 10.0f), glm::vec3(0.0f, 1.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
- cameraBufferObject.projectionMatrix = glm::perspective(glm::radians(45.0f), aspectRatio, 0.1f, 100.0f);
+ cameraBufferObject.projectionMatrix = glm::perspective(glm::radians(45.0f), aspectRatio, 0.1f, 50.0f);
cameraBufferObject.projectionMatrix[1][1] *= -1; // y-coordinate is flipped
BufferUtils::CreateBuffer(device, sizeof(CameraBufferObject), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, buffer, bufferMemory);
diff --git a/src/Renderer.cpp b/src/Renderer.cpp
index b445d04..30d3f9c 100644
--- a/src/Renderer.cpp
+++ b/src/Renderer.cpp
@@ -19,6 +19,7 @@ Renderer::Renderer(Device* device, SwapChain* swapChain, Scene* scene, Camera* c
CreateRenderPass();
CreateCameraDescriptorSetLayout();
CreateModelDescriptorSetLayout();
+ CreateGrassDescriptorSetLayout();
CreateTimeDescriptorSetLayout();
CreateComputeDescriptorSetLayout();
CreateDescriptorPool();
@@ -172,6 +173,27 @@ void Renderer::CreateModelDescriptorSetLayout() {
}
}
+void Renderer::CreateGrassDescriptorSetLayout() {
+ VkDescriptorSetLayoutBinding uboLayoutBinding = {};
+ uboLayoutBinding.binding = 0;
+ uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+ uboLayoutBinding.descriptorCount = 1;
+ uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
+ uboLayoutBinding.pImmutableSamplers = nullptr;
+
+ std::vector bindings = { uboLayoutBinding };
+
+ // Create the descriptor set layout
+ VkDescriptorSetLayoutCreateInfo layoutInfo = {};
+ layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
+ layoutInfo.bindingCount = static_cast(bindings.size());
+ layoutInfo.pBindings = bindings.data();
+
+ if (vkCreateDescriptorSetLayout(logicalDevice, &layoutInfo, nullptr, &grassDescriptorSetLayout) != VK_SUCCESS) {
+ throw std::runtime_error("Failed to create descriptor set layout");
+ }
+}
+
void Renderer::CreateTimeDescriptorSetLayout() {
// Describe the binding of the descriptor set layout
VkDescriptorSetLayoutBinding uboLayoutBinding = {};
@@ -198,6 +220,41 @@ 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 sboLayoutBindingInput = {};
+ sboLayoutBindingInput.binding = 0;
+ sboLayoutBindingInput.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
+ sboLayoutBindingInput.descriptorCount = 1;
+ sboLayoutBindingInput.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
+ sboLayoutBindingInput.pImmutableSamplers = nullptr;
+
+ VkDescriptorSetLayoutBinding sboLayoutBindingCulled = {};
+ sboLayoutBindingCulled.binding = 1;
+ sboLayoutBindingCulled.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
+ sboLayoutBindingCulled.descriptorCount = 1; // NOTE: if things aren't working, check if this should be 2 and 3 instead of all 1
+ sboLayoutBindingCulled.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
+ sboLayoutBindingCulled.pImmutableSamplers = nullptr;
+
+ VkDescriptorSetLayoutBinding sboLayoutBindingNum = {};
+ sboLayoutBindingNum.binding = 2;
+ sboLayoutBindingNum.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
+ sboLayoutBindingNum.descriptorCount = 1;
+ sboLayoutBindingNum.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
+ sboLayoutBindingNum.pImmutableSamplers = nullptr;
+
+ std::vector bindings = { sboLayoutBindingInput, sboLayoutBindingCulled, sboLayoutBindingNum };
+
+ // 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() {
@@ -207,7 +264,7 @@ void Renderer::CreateDescriptorPool() {
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER , 1},
// Models + Blades
- { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER , static_cast(scene->GetModels().size() + scene->GetBlades().size()) },
+ { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER , static_cast(scene->GetModels().size()) },
// Models + Blades
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER , static_cast(scene->GetModels().size() + scene->GetBlades().size()) },
@@ -215,6 +272,8 @@ void Renderer::CreateDescriptorPool() {
// Time (compute)
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER , 1 },
+ { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER , static_cast(3 * scene->GetBlades().size()) },
+
// TODO: Add any additional types and counts of descriptors you will need to allocate
};
@@ -320,6 +379,42 @@ 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());
+
+ // Describe the desciptor set
+ VkDescriptorSetLayout layouts[] = { grassDescriptorSetLayout };
+ VkDescriptorSetAllocateInfo allocInfo = {};
+ allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
+ allocInfo.descriptorPool = descriptorPool;
+ allocInfo.descriptorSetCount = static_cast(grassDescriptorSets.size());
+ allocInfo.pSetLayouts = layouts;
+
+ // Allocate descriptor sets
+ if (vkAllocateDescriptorSets(logicalDevice, &allocInfo, grassDescriptorSets.data()) != VK_SUCCESS) {
+ throw std::runtime_error("Failed to allocate descriptor set");
+ }
+
+ std::vector descriptorWrites(grassDescriptorSets.size());
+
+ for (uint32_t i = 0; i < scene->GetBlades().size(); ++i) {
+ VkDescriptorBufferInfo grassBufferInfo = {};
+ grassBufferInfo.buffer = scene->GetBlades()[i]->GetModelBuffer();
+ grassBufferInfo.offset = 0;
+ grassBufferInfo.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 = &grassBufferInfo;
+ 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 +455,77 @@ 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());
+
+ // Describe the desciptor set
+ VkDescriptorSetLayout layouts[] = { computeDescriptorSetLayout };
+ VkDescriptorSetAllocateInfo allocInfo = {};
+ allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
+ allocInfo.descriptorPool = descriptorPool;
+ allocInfo.descriptorSetCount = static_cast(computeDescriptorSets.size());
+ allocInfo.pSetLayouts = layouts;
+
+ // Allocate descriptor sets
+ if (vkAllocateDescriptorSets(logicalDevice, &allocInfo, computeDescriptorSets.data()) != VK_SUCCESS) {
+ throw std::runtime_error("Failed to allocate descriptor set");
+ }
+
+ std::vector descriptorWrites(3 * computeDescriptorSets.size());
+
+ for (uint32_t i = 0; i < scene->GetBlades().size(); ++i) {
+ VkDescriptorBufferInfo bladesBufferInfo = {};
+ bladesBufferInfo.buffer = scene->GetBlades()[i]->GetBladesBuffer();
+ bladesBufferInfo.offset = 0;
+ bladesBufferInfo.range = NUM_BLADES * sizeof(Blade);
+
+ descriptorWrites[3 * i + 0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+ descriptorWrites[3 * i + 0].dstSet = computeDescriptorSets[i];
+ descriptorWrites[3 * i + 0].dstBinding = 0;
+ descriptorWrites[3 * i + 0].dstArrayElement = 0;
+ descriptorWrites[3 * i + 0].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
+ descriptorWrites[3 * i + 0].descriptorCount = 1;
+ descriptorWrites[3 * i + 0].pBufferInfo = &bladesBufferInfo;
+ descriptorWrites[3 * i + 0].pImageInfo = nullptr;
+ descriptorWrites[3 * i + 0].pTexelBufferView = nullptr;
+
+
+
+ VkDescriptorBufferInfo cullBufferInfo = {};
+ cullBufferInfo.buffer = scene->GetBlades()[i]->GetCulledBladesBuffer();
+ cullBufferInfo.offset = 0;
+ cullBufferInfo.range = NUM_BLADES * sizeof(Blade);
+
+ 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 = &cullBufferInfo;
+ descriptorWrites[3 * i + 1].pImageInfo = nullptr;
+ descriptorWrites[3 * i + 1].pTexelBufferView = nullptr;
+
+
+
+ VkDescriptorBufferInfo numBladesBufferInfo = {};
+ numBladesBufferInfo.buffer = scene->GetBlades()[i]->GetNumBladesBuffer();
+ numBladesBufferInfo.offset = 0;
+ numBladesBufferInfo.range = sizeof(BladeDrawIndirect);
+
+ 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() {
@@ -608,7 +774,7 @@ void Renderer::CreateGrassPipeline() {
rasterizer.depthBiasConstantFactor = 0.0f;
rasterizer.depthBiasClamp = 0.0f;
rasterizer.depthBiasSlopeFactor = 0.0f;
-
+
// Multisampling (turned off here)
VkPipelineMultisampleStateCreateInfo multisampling = {};
multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
@@ -654,7 +820,7 @@ void Renderer::CreateGrassPipeline() {
colorBlending.blendConstants[2] = 0.0f;
colorBlending.blendConstants[3] = 0.0f;
- std::vector descriptorSetLayouts = { cameraDescriptorSetLayout, modelDescriptorSetLayout };
+ std::vector descriptorSetLayouts = { cameraDescriptorSetLayout, grassDescriptorSetLayout }; // NOTE: This was originally modelDescriptorSetLayout, not grass.
// Pipeline layout: used to specify uniform values
VkPipelineLayoutCreateInfo pipelineLayoutInfo = {};
@@ -717,7 +883,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 +1050,13 @@ 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 (int i = 0; i < scene->GetBlades().size(); ++i) {
+ vkCmdBindDescriptorSets(computeCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, computePipelineLayout, 2 + i, 1, &computeDescriptorSets[i], 0, nullptr);
+ vkCmdDispatch(computeCommandBuffer, (int)ceil((NUM_BLADES + WORKGROUP_SIZE - 1) / WORKGROUP_SIZE), 1, 1);
+ }
+
+
+
// ~ End recording ~
if (vkEndCommandBuffer(computeCommandBuffer) != VK_SUCCESS) {
@@ -926,7 +1099,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.721f, 0.8f, 0.841f, 1.0f };
clearValues[1].depthStencil = { 1.0f, 0 };
renderPassInfo.clearValueCount = static_cast(clearValues.size());
renderPassInfo.pClearValues = clearValues.data();
@@ -976,13 +1149,15 @@ 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, grassPipelineLayout, 1, 1, &grassDescriptorSets[j], 0, nullptr);
+
// Draw
// TODO: Uncomment this when the buffers are populated
- // vkCmdDrawIndirect(commandBuffers[i], scene->GetBlades()[j]->GetNumBladesBuffer(), 0, 1, sizeof(BladeDrawIndirect));
+ vkCmdDrawIndirect(commandBuffers[i], scene->GetBlades()[j]->GetNumBladesBuffer(), 0, 1, sizeof(BladeDrawIndirect));
}
// End render pass
@@ -1057,6 +1232,8 @@ Renderer::~Renderer() {
vkDestroyDescriptorSetLayout(logicalDevice, cameraDescriptorSetLayout, nullptr);
vkDestroyDescriptorSetLayout(logicalDevice, modelDescriptorSetLayout, nullptr);
vkDestroyDescriptorSetLayout(logicalDevice, timeDescriptorSetLayout, nullptr);
+ vkDestroyDescriptorSetLayout(logicalDevice, grassDescriptorSetLayout, nullptr);
+
vkDestroyDescriptorPool(logicalDevice, descriptorPool, nullptr);
diff --git a/src/Renderer.h b/src/Renderer.h
index 95e025f..255d4c5 100644
--- a/src/Renderer.h
+++ b/src/Renderer.h
@@ -17,6 +17,8 @@ class Renderer {
void CreateCameraDescriptorSetLayout();
void CreateModelDescriptorSetLayout();
+ void CreateGrassDescriptorSetLayout();
+
void CreateTimeDescriptorSetLayout();
void CreateComputeDescriptorSetLayout();
@@ -55,12 +57,16 @@ class Renderer {
VkDescriptorSetLayout cameraDescriptorSetLayout;
VkDescriptorSetLayout modelDescriptorSetLayout;
+ VkDescriptorSetLayout grassDescriptorSetLayout;
+ VkDescriptorSetLayout computeDescriptorSetLayout;
VkDescriptorSetLayout timeDescriptorSetLayout;
VkDescriptorPool descriptorPool;
VkDescriptorSet cameraDescriptorSet;
std::vector modelDescriptorSets;
+ std::vector grassDescriptorSets;
+ std::vector computeDescriptorSets;
VkDescriptorSet timeDescriptorSet;
VkPipelineLayout graphicsPipelineLayout;
diff --git a/src/Window.cpp b/src/Window.cpp
index a365dc9..374f1a0 100644
--- a/src/Window.cpp
+++ b/src/Window.cpp
@@ -21,8 +21,10 @@ void InitializeWindow(int width, int height, const char* name) {
}
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
+
window = glfwCreateWindow(width, height, name, nullptr, nullptr);
+
if (!window) {
fprintf(stderr, "Failed to initialize GLFW window\n");
glfwTerminate();
diff --git a/src/images/dirt.jpg b/src/images/dirt.jpg
new file mode 100644
index 0000000..fbd7d1a
Binary files /dev/null and b/src/images/dirt.jpg differ
diff --git a/src/main.cpp b/src/main.cpp
index 8bf822b..1e7a127 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -5,6 +5,7 @@
#include "Camera.h"
#include "Scene.h"
#include "Image.h"
+#include
Device* device;
SwapChain* swapChain;
@@ -106,7 +107,7 @@ int main() {
VkDeviceMemory grassImageMemory;
Image::FromFile(device,
transferCommandPool,
- "images/grass.jpg",
+ "images/dirt.jpg",
VK_FORMAT_R8G8B8A8_UNORM,
VK_IMAGE_TILING_OPTIMAL,
VK_IMAGE_USAGE_SAMPLED_BIT,
@@ -146,7 +147,10 @@ int main() {
while (!ShouldQuit()) {
glfwPollEvents();
scene->UpdateTime();
+ double previousTime = glfwGetTime();
renderer->Frame();
+ double currentTime = glfwGetTime();
+ std::cout << currentTime - previousTime << std::endl;
}
vkDeviceWaitIdle(device->GetVkDevice());
diff --git a/src/shaders/compute.comp b/src/shaders/compute.comp
index 0fd0224..b37c701 100644
--- a/src/shaders/compute.comp
+++ b/src/shaders/compute.comp
@@ -21,36 +21,196 @@ struct Blade {
vec4 up;
};
+layout(set = 2, binding = 0) buffer InputBlades {
+ Blade inputBlades[];
+};
+
+layout(set = 2, binding = 1) buffer RemainingBlades {
+ Blade remainingBlades[];
+};
+
+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;
+
+
+
// TODO: 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);
}
+float random1( vec2 p , vec2 seed) {
+ return fract(sin(dot(p + seed, vec2(127.1, 311.7))) * 43758.5453);
+}
+
+float interpNoise2d(float x, float y) {
+ float intX = floor(x);
+ float fractX = fract(x);
+ float intY = floor(y);
+ float fractY = fract(y);
+
+ float v1 = random1(vec2(intX, intY), vec2(1.f, 1.f));
+ float v2 = random1(vec2(intX + 1.f, intY), vec2(1.f, 1.f));
+ float v3 = random1(vec2(intX, intY + 1.f), vec2(1.f, 1.f));
+ float v4 = random1(vec2(intX + 1.f, intY + 1.f), vec2(1.f, 1.f));
+
+ float i1 = mix(v1, v2, fractX);
+ float i2 = mix(v3, v4, fractX);
+ return mix(i1, i2, fractY);
+ return 2.0;
+}
+
+float fbm(float x, float y, float height, float xScale, float yScale) {
+ float total = 0.f;
+ float persistence = 0.5f;
+ int octaves = 4;
+ float freq = 2.0;
+ float amp = 1.0;
+ for (int i = 0; i < octaves; i++) {
+ // total += interpNoise2d( (x / xScale) * freq, (y / yScale) * freq) * amp;
+ total += interpNoise2d( (x / xScale) * freq, (y / yScale) * freq) * amp;
+ freq *= 2.0;
+ amp *= persistence;
+ }
+ return height * total;
+}
+
+
+
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
+ // 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
// 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
+ vec4 currv0 = inputBlades[gl_GlobalInvocationID.x].v0;
+ vec4 currv1 = inputBlades[gl_GlobalInvocationID.x].v1;
+ vec4 currv2 = inputBlades[gl_GlobalInvocationID.x].v2;
+ vec4 currup = inputBlades[gl_GlobalInvocationID.x].up;
+
+ vec3 v0 = vec3(currv0);
+ vec3 v1 = vec3(currv1);
+ vec3 v2 = vec3(currv2);
+ vec3 up = vec3(currup);
+ float direction = currv0.w;
+ float height = currv1.w;
+ float width = currv2.w;
+ float stiffness = currup.w;
+
+
+ vec3 D = vec3(0.0, -1.0, 0.0);
+ float gMag = 9.8;
+ vec3 gE = normalize(D) * gMag;
+ vec3 f = normalize(vec3(cos(direction), 0.f, sin(direction)));
+ vec3 gF = (1.f/4.f) * length(gE) * f;
+ vec3 g = gE + gF;
+
+ vec3 iv2 = v0 + height * normalize(up);
+ vec3 r = (iv2 - v2) * stiffness;
+
+
+ float currFBM = fbm(v0.x, v0.z, 2.0, 1.0, 1.0);
+// vec3 noise = vec3(fbm(sin(v0.x / 2.0 + totalTime / 10.0 + 1.f), cos(v0.z / 2.f + totalTime / 10.0 + 4.f), 1.0, 1.0, 1.0),
+// fbm(cos(v0.x / 2.0 + totalTime / 10.0 + 2.f), sin(v0.z / 2.f + totalTime / 10.0 + 5.f), 1.0, 1.0, 1.0),
+// fbm(sin(v0.x / 2.0 + totalTime / 10.0 + 3.f), cos(v0.z / 2.f + totalTime / 10.0 + 6.f), 1.0, 1.0, 1.0)) - vec3(1.f);
+// vec3 wi = 23.f * vec3(sin((v0.x * 1.3 + 30.f) * 0.6 + totalTime / 1.4), 0.f, 0.5 * cos((v0.z * 1.2 + 2.5f) * 0.2 + totalTime / 1.8f)) +
+// 28.f * vec3(cos(v0.z * 0.8 * 0.5 + totalTime / 1.2f + 5.0), 0.f, 0.5 * sin(v0.x * 0.3 * 0.9 + totalTime / 1.5f));
+// float tanTheta = abs(sin(totalTime));
+// float currRatio = (v0.z / length(v0));
+// float radialScale = clamp(abs(tanTheta - currRatio), 0.f, 1.f);
+ vec3 wi = 30.f * (sin(length(v0) * 1.5 - totalTime * 6.0) + 1.0) *
+ vec3(normalize(v0).x,
+ 0.f,
+ normalize(v0).z);
+// wi = (wi + 40.f * noise) / 1.0;
+// wi = 20.f * noise;
+
+ float fd = 1.f - abs(dot(normalize(wi), normalize(v2 - v0)));
+ float fr = dot(v2 - v0, up) / height;
+ float theta = fd * fr;
+
+ vec3 w = wi * theta;
+
+ vec3 tv2 = (g + r + w) * deltaTime;
+
+ v2 += tv2;
+
+ v2 = v2 - up * min(up * (v2 - v0), 0.f);
+
+ float lproj = length(v2 - v0 - up * ((v2 - v0) * up));
+
+ v1 = v0 + height * up * max(1 - lproj/height, 0.05 * max(lproj/height, 1.f));
+
+ int n = 2;
+ float L0 = length(v2 - v0);
+ float L1 = length(v2 - v1) + length(v1 - v0);
+ float L = (2 * L0 + (n - 1) * L1) / (n + 1);
+
+ float rfloat = height / L;
+ inputBlades[gl_GlobalInvocationID.x].v1 = vec4(v0 + rfloat * (v1 - v0), height);
+ inputBlades[gl_GlobalInvocationID.x].v2 = vec4(inputBlades[gl_GlobalInvocationID.x].v1.xyz + rfloat * (v2 - v1), width);
+
+
+ bool cullBlade = false;
+ vec3 dirc = f;
+ vec4 cameraPos = inverse(camera.view) * vec4(0, 0, 0, 1);
+ vec3 dirb = normalize(cameraPos.xyz - v0);
+ float orientationValue = abs(dot(dirc, dirb));
+ if (orientationValue > 0.95) {
+ cullBlade = true;
+ }
+
+ float tolerance = 0.2;
+ vec3 m = (1.f/4.f) * v0 + (1.f/2.f) * v1 + (1.f/4.f) * v2;
+
+ vec4 v0Prime = camera.proj * camera.view * vec4(v0, 1.f);
+ float hv0 = v0Prime.w + tolerance;
+ bool vv0 = (v0Prime.x >= -hv0) && (v0Prime.x <= hv0) &&
+ (v0Prime.y >= -hv0) && (v0Prime.y <= hv0) &&
+ (v0Prime.z >= -hv0) && (v0Prime.z <= hv0);
+
+
+ vec4 mPrime = camera.proj * camera.view * vec4(m, 1.f);
+ float hm = mPrime.w + tolerance;
+ bool vm = (mPrime.x >= -hm) && (mPrime.x <= hm) &&
+ (mPrime.y >= -hm) && (mPrime.y <= hm) &&
+ (mPrime.z >= -hm) && (mPrime.z <= hm);
+
+ vec4 v2Prime = camera.proj * camera.view * vec4(v2, 1.f);
+ float hv2 = v2Prime.w + tolerance;
+ bool vv2 = (v2Prime.x >= -hv2) && (v2Prime.x <= hv2) &&
+ (v2Prime.y >= -hv2) && (v2Prime.y <= hv2) &&
+ (v2Prime.z >= -hv2) && (v2Prime.z <= hv2);
+ if (!vv0 && !vm && !vv2) {
+ cullBlade = true;
+ }
+
+ float dproj = length(v0 - cameraPos.xyz - up * dot(v0 - cameraPos.xyz, up));
+ int numIntervals = 5;
+ float dmax = 80.f;
+ for (int i = 0; i < numIntervals; ++i) {
+ uint modVal = gl_GlobalInvocationID.x - i * (gl_GlobalInvocationID.x / i);
+ if (modVal < floor(i * ((dproj / dmax)))) {
+ cullBlade = true;
+ }
+ }
+
+
+ if (!cullBlade) {
+ remainingBlades[atomicAdd(numBlades.vertexCount, 1)] = inputBlades[gl_GlobalInvocationID.x];
+ }
}
diff --git a/src/shaders/grass.frag b/src/shaders/grass.frag
index c7df157..00b1bb7 100644
--- a/src/shaders/grass.frag
+++ b/src/shaders/grass.frag
@@ -7,11 +7,16 @@ layout(set = 0, binding = 0) uniform CameraBufferObject {
} camera;
// TODO: Declare fragment shader inputs
+layout(location = 0) in vec4 normal;
+layout(location = 1) in vec4 lightDir;
+layout(location = 2) in float height;
layout(location = 0) out vec4 outColor;
void main() {
// TODO: Compute fragment color
+ float ambient = 0.15;
+ vec4 color = vec4(0.45f, 0.6f, 0.45f, 1.f);
- outColor = vec4(1.0);
+ outColor = color * clamp((ambient + height), 0.f, 1.f);
}
diff --git a/src/shaders/grass.tesc b/src/shaders/grass.tesc
index f9ffd07..7f05c59 100644
--- a/src/shaders/grass.tesc
+++ b/src/shaders/grass.tesc
@@ -10,17 +10,31 @@ layout(set = 0, binding = 0) uniform CameraBufferObject {
// TODO: Declare tessellation control shader inputs and outputs
+layout(location = 0) in vec4[] in_v0;
+layout(location = 1) in vec4[] in_v1;
+layout(location = 2) in vec4[] in_v2;
+layout(location = 3) in vec4[] in_up;
+
+layout(location = 0) out vec4[] out_v0;
+layout(location = 1) out vec4[] out_v1;
+layout(location = 2) out vec4[] out_v2;
+layout(location = 3) out vec4[] out_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
+ out_v0[gl_InvocationID] = in_v0[gl_InvocationID];
+ out_v1[gl_InvocationID] = in_v1[gl_InvocationID];
+ out_v2[gl_InvocationID] = in_v2[gl_InvocationID];
+ out_up[gl_InvocationID] = in_up[gl_InvocationID];
// 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] = 8;
+ gl_TessLevelInner[1] = 8;
+ gl_TessLevelOuter[0] = 8;
+ gl_TessLevelOuter[1] = 8;
+ gl_TessLevelOuter[2] = 8;
+ gl_TessLevelOuter[3] = 8;
}
diff --git a/src/shaders/grass.tese b/src/shaders/grass.tese
index 751fff6..e93e30e 100644
--- a/src/shaders/grass.tese
+++ b/src/shaders/grass.tese
@@ -9,10 +9,53 @@ layout(set = 0, binding = 0) uniform CameraBufferObject {
} camera;
// TODO: Declare tessellation evaluation shader inputs and outputs
+layout(location = 0) in vec4[] in_v0;
+layout(location = 1) in vec4[] in_v1;
+layout(location = 2) in vec4[] in_v2;
+layout(location = 3) in vec4[] in_up;
+
+layout(location = 0) out vec4 normal;
+layout(location = 1) out vec4 lightDir;
+layout(location = 2) out float height;
+
+const float PI = 3.141592654;
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
+ // Read in inputs
+ vec4 currv0 = in_v0[0];
+ vec4 currv1 = in_v1[0];
+ vec4 currv2 = in_v2[0];
+ vec4 currUp = in_up[0];
+
+ // Get vec3 parts of inputs
+ vec3 v0 = vec3(currv0);
+ vec3 v1 = vec3(currv1);
+ vec3 v2 = vec3(currv2);
+ vec3 up = vec3(currUp);
+
+ // Convert orientation into direction
+ vec3 t1 = normalize(vec3(cos(currv0.w + PI), 0.f, sin(currv0.w + PI)));
+
+ // Perform interpolations to calculate position
+ vec3 a = v0 + v * (v1 - v0);
+ vec3 b = v1 + v * (v2 - v1);
+ vec3 c = a + v * (b - a);
+ vec3 c0 = c - currv2.w * t1;
+ vec3 c1 = c + currv2.w * t1;
+ vec3 t0 = (b - a) / (length(b - a));
+ vec3 n = cross(t0, t1) / length(cross(t0, t1));
+
+ normal = normalize(vec4(n, 0.f));
+ float t = u - u * v * v;
+// t = u + 0.5 * v - u * v;
+
+ vec3 pos = (1.f - t) * c0 + t * c1;
+
+ // Set transformed position and output data
+ gl_Position = camera.proj * camera.view * vec4(pos, 1.0);
+ lightDir = normalize(gl_Position - vec4(0.0, 5.0, 0.0, 1.0));
+ height = mix(v, pos.y / 4.0, 0.8);
}
diff --git a/src/shaders/grass.vert b/src/shaders/grass.vert
index db9dfe9..52181c4 100644
--- a/src/shaders/grass.vert
+++ b/src/shaders/grass.vert
@@ -1,4 +1,3 @@
-
#version 450
#extension GL_ARB_separate_shader_objects : enable
@@ -7,6 +6,16 @@ layout(set = 1, binding = 0) uniform ModelBufferObject {
};
// TODO: Declare vertex shader inputs and outputs
+layout(location = 0) in vec4 v0;
+layout(location = 1) in vec4 v1;
+layout(location = 2) in vec4 v2;
+layout(location = 3) in vec4 up;
+
+layout(location = 0) out vec4 out_v0;
+layout(location = 1) out vec4 out_v1;
+layout(location = 2) out vec4 out_v2;
+layout(location = 3) out vec4 out_up;
+
out gl_PerVertex {
vec4 gl_Position;
@@ -14,4 +23,11 @@ out gl_PerVertex {
void main() {
// TODO: Write gl_Position and any other shader outputs
+ // Transform all vectors by model matrix
+ out_v0 = vec4((model * vec4(v0.xyz, 1.f)).xyz, v0.w);
+ out_v1 = vec4((model * vec4(v1.xyz, 1.f)).xyz, v1.w);
+ out_v2 = vec4((model * vec4(v2.xyz, 1.f)).xyz, v2.w);
+ out_up = vec4((model * vec4(up.xyz, 1.f)).xyz, up.w);
+
+ gl_Position = model * vec4(v0.xyz, 1.0);
}