diff --git a/README.md b/README.md index fc0e545..8872791 100644 --- a/README.md +++ b/README.md @@ -1,300 +1,133 @@ -Instructions - Vulkan Grass Rendering +Vulkan Grass Rendering ======================== -This is due **Sunday 11/4, evening at midnight**. +**University of Pennsylvania, CIS 565: GPU Programming and Architecture, Project 6** -**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. +* Xiao Zhang + * [LinkedIn](https://www.linkedin.com/in/xiao-zhang-674bb8148/) +* Tested on: Windows 10, i7-7700K @ 4.20GHz 16.0GB, GTX 1080 15.96GB (my own PC) -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. +Overview +====================== -![](img/grass.gif) ![](img/grass2.gif) +![](img/0.gif) -You are not required to use this base code if you don't want -to. You may also change any part of the base code as you please. -**This is YOUR project.** The above .gifs are just examples that you -can use as a reference to compare to. Feel free to get creative with your implementations! +Analysis +====================== +* Compute shader WORKGROUP_SIZE is set to (32, 0, 0). -**Important:** -- If you are not in CGGT/DMD, you may replace this project with a GPU compute -project. You MUST get this pre-approved by Ottavio before continuing! +* Rendering time is measured in fps, so higher is better. -### Contents +* Maximum distance for distance culling is 50. -* `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 +* Frustum culling tolerance is 0.9. -### Installing Vulkan +* Orientation culling threshold is 0.2. -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. +* This project is based on the paper [Responsive Real-Time Grass Rendering for General 3D Scenes +](https://www.cg.tuwien.ac.at/research/publications/2017/JAHRMANN-2017-RRTG/JAHRMANN-2017-RRTG-draft.pdf). -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! +## I. Orientation Culling -### Running the code +### overview -While developing your grass renderer, you will want to keep validation layers enabled so that error checking is turned on. -The project is set up such that when you are in `debug` mode, validation layers are enabled, and when you are in `release` mode, -validation layers are disabled. After building the code, you should be able to run the project without any errors. You will see a plane with a grass texture on it to begin with. +![](img/o_culling.JPG) -![](img/cube_demo.png) +### detail -## Requirements +When looking from left or right side of the grass blade, the blade will take less screen pixels almost like a line segment. It is better not to show it because it's not going to make too much difference and also because we want to avoid aliasing caused by the "line segment". The eaisist way to do this is to test the angle between the direction from the grass blade to the camera and the front direction of the grass blade. In practice, I use the dot product to achieve this. If the absolute value of the dot product is smaller than the aforementioned threshold 0.2, then the grass blade is culled. Usually this will do in simple cases but when camera goes over the head and facing downwards to the grass plane, all the grass blades will be culled since the front direction of them are all perpendicular to the viewing direction. To solve this problem, one can use the vector projected by the viewing direction on to the grass plane instead. -**Ask on the mailing list for any clarifications.** +--- -In this project, you are given the following code: +## II. Frustum Culling -* 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. +### overview -You need to implement the following features/pipeline stages: +![](img/f_culling.JPG) -* 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 +### detail -See below for more guidance. +When looking from very close distance, the grass blades outside the camera frustum will now be rendered. To boost performance, we can cull these grass blades in advance. The way I used is transfering the 3 control points of the bezier curve of each grass blade in to culling space, and perform a general frustum culling by checking x, y, z agianst w component. If they are not within the range [w, -w] (assuming w is negative), they are culled. -## 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`. +## III. Distance Culling -* `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. +### overview -We left out descriptions for a couple files that you likely won't have to modify. Feel free to investigate them to understand their -importance within the scope of the project. +![](img/d_culling.JPG) -## Grass Rendering +### detail -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. +When looking from very far distance, the density of the grass doesn't need to be too high because we can hardly tell the difference and also because we want to avoid aliasing caused by a lot of small objects at far distance. The way I do this is to use the distance from the grass blade to the camera as a why to decide what LOD level they should be. Assuming the maximum LOD is n, in LOD m, we want to cull m grass blades out of every n grass blades. This is achieved by take adding the grass index in to the mix. When int(bladeId) % n < int(n * (dproj / dfar)), the grass is culled. dproj is the vector projected by the viewing direction on to the grass plane so that the distance culling is independent to the vertical viewing angle(same reason as orientation culling). dfar is the distance of LOD n. 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) used a different formula which I think is a mistake in editing. -We recommend starting with trying to display the grass blades without any forces on them before trying to add any forces on the blades themselves. Here is an example of what that may look like: +--- -![](img/grass_basic.gif) +## 1. 8192 (2^13) grass blades -### Representing Grass as Bezier Curves +### overview -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 +![](img/a.JPG) -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 +### analysis -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. +This is the bench mark configuration. From the chart, we can see that orientation culling and distance culling works better when the grass is far away from the camera and frustum culling works better when the grass is near to the camera. For orientation culling, this is because the orientation culling depends on the angle between the side direction of the grass and the direction from camera to the grass blade. So when the camera is far from the grass, this angle is smaller, meaning the side of grass is more aligned with the viewing direction, thus it's more likely to be culled. For distance culling, this is because the maximum number of grass blades that can be culled depends on the distance from the grass blade to the camera. So when the camera is far from the grass, more grass will be catogrized in a higher LOD level, thus more grass blade can be culled. For frustum culling, this is simply because none of the grass are outside the camera frustum with the far and mid configuration. However, one interesting thing is that orientation culling and frustum culling in mid distance is the slowest, where they are supposed to be in the middle. One of the reason that I can think of is the grass occupies more screen pixels because of the viewing distance is nearer, which results in longer rendering time. -![](img/blade_model.jpg) +### images -### Simulating Forces +| distance | far | mid | near | +|:-------------------:|:-----------------------:|:-----------------------:|:------------------------:| +| culling off |![](img/1/1_aoff_far.JPG)|![](img/1/1_aoff_mid.JPG)|![](img/1/1_aoff_near.JPG)| +| orientation culling |![](img/1/1_o_far.JPG) |![](img/1/1_o_mid.JPG) |![](img/1/1_o_near.JPG) | +| frustum culling |![](img/1/1_f_far.JPG) |![](img/1/1_f_mid.JPG) |![](img/1/1_f_near.JPG) | +| distance culling |![](img/1/1_d_far.JPG) |![](img/1/1_d_mid.JPG) |![](img/1/1_d_near.JPG) | +| all culling on |![](img/1/1_aon_far.JPG) |![](img/1/1_aon_mid.JPG) |![](img/1/1_aon_near.JPG) | -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 +## 2. 131072 (2^17) grass blades -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. +### overview -#### Gravity +![](img/b.JPG) -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`. +### analysis -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`. +When there are more grass blades, the rendering time is slower. But the pattern remains the same. Orientation culling and distance culling works better when the grass is far away from the camera and frustum culling works better when the grass is near to the camera. -We can then determine the total gravity on the grass blade as `g = gE + gF`. +### images -#### Recovery +| distance | far | mid | near | +|:-------------------:|:-----------------------:|:-----------------------:|:------------------------:| +| culling off |![](img/2/2_aoff_far.JPG)|![](img/2/2_aoff_mid.JPG)|![](img/2/2_aoff_near.JPG)| +| orientation culling |![](img/2/2_o_far.JPG) |![](img/2/2_o_mid.JPG) |![](img/2/2_o_near.JPG) | +| frustum culling |![](img/2/2_f_far.JPG) |![](img/2/2_f_mid.JPG) |![](img/2/2_f_near.JPG) | +| distance culling |![](img/2/2_d_far.JPG) |![](img/2/2_d_mid.JPG) |![](img/2/2_d_near.JPG) | +| all culling on |![](img/2/2_aon_far.JPG) |![](img/2/2_aon_mid.JPG) |![](img/2/2_aon_near.JPG) | -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`. +## 3. 2097152 (2^21) grass blades -#### Wind +### overview -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. +![](img/c.JPG) -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! +### analysis -Once you have a wind direction and a wind alignment term, your total wind force (`w`) will be `windDirection * windAlignment`. +When there are much more grass blades, the rendering time is much slower. But the pattern remains the same. Orientation culling and distance culling works better when the grass is far away from the camera and frustum culling works better when the grass is near to the camera. Among all the three culling options, the performance change in distance culling due to distance is the most significent, even though the performance change for the other two culling options is mellowed out because of the large number of grass blades. This is mostly becuase the distance culling algorithm is the most progressive one, meaning the standard is the lest strict which can result in a lot of grass blades getting culled. -#### Total force +### images -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. +| distance | far | mid | near | +|:-------------------:|:-----------------------:|:-----------------------:|:------------------------:| +| culling off |![](img/3/3_aoff_far.JPG)|![](img/3/3_aoff_mid.JPG)|![](img/3/3_aoff_near.JPG)| +| orientation culling |![](img/3/3_o_far.JPG) |![](img/3/3_o_mid.JPG) |![](img/3/3_o_near.JPG) | +| frustum culling |![](img/3/3_f_far.JPG) |![](img/3/3_f_mid.JPG) |![](img/3/3_f_near.JPG) | +| distance culling |![](img/3/3_d_far.JPG) |![](img/3/3_d_mid.JPG) |![](img/3/3_d_near.JPG) | +| all culling on |![](img/3/3_aon_far.JPG) |![](img/3/3_aon_mid.JPG) |![](img/3/3_aon_near.JPG) | -Read section 5.2 of the paper in order to learn how to determine the corrected final positions for `v1` and `v2`. - -### Culling tests - -Although we need to simulate forces on every grass blade at every frame, there are many blades that we won't need to render -due to a variety of reasons. Here are some heuristics we can use to cull blades that won't contribute positively to a given frame. - -#### Orientation culling - -Consider the scenario in which the front face direction of the grass blade is perpendicular to the view vector. Since our grass blades -won't have width, we will end up trying to render parts of the grass that are actually smaller than the size of a pixel. This could -lead to aliasing artifacts. - -In order to remedy this, we can cull these blades! Simply do a dot product test to see if the view vector and front face direction of -the blade are perpendicular. The paper uses a threshold value of `0.9` to cull, but feel free to use what you think looks best. - -#### View-frustum culling - -We also want to cull blades that are outside of the view-frustum, considering they won't show up in the frame anyway. To determine if -a grass blade is in the view-frustum, we want to compare the visibility of three points: `v0, v2, and m`, where `m = (1/4)v0 * (1/2)v1 * (1/4)v2`. -Notice that we aren't using `v1` for the visibility test. This is because the `v1` is a Bezier guide that doesn't represent a position on the grass blade. -We instead use `m` to approximate the midpoint of our Bezier curve. - -If all three points are outside of the view-frustum, we will cull the grass blade. The paper uses a tolerance value for this test so that we are culling -blades a little more conservatively. This can help with cases in which the Bezier curve is technically not visible, but we might be able to see the blade -if we consider its width. - -#### Distance culling - -Similarly to orientation culling, we can end up with grass blades that at large distances are smaller than the size of a pixel. This could lead to additional -artifacts in our renders. In this case, we can cull grass blades as a function of their distance from the camera. - -You are free to define two parameters here. -* A max distance afterwhich all grass blades will be culled. -* A number of buckets to place grass blades between the camera and max distance into. - -Define a function such that the grass blades in the bucket closest to the camera are kept while an increasing number of grass blades -are culled with each farther bucket. - -#### Occlusion culling (extra credit) - -This type of culling only makes sense if our scene has additional objects aside from the plane and the grass blades. We want to cull grass blades that -are occluded by other geometry. Think about how you can use a depth map to accomplish this! - -### Tessellating Bezier curves into grass blades - -In this project, you should pass in each Bezier curve as a single patch to be processed by your grass graphics pipeline. You will tessellate this patch into -a quad with a shape of your choosing (as long as it looks sufficiently like grass of course). The paper has some examples of grass shapes you can use as inspiration. - -In the tessellation control shader, specify the amount of tessellation you want to occur. Remember that you need to provide enough detail to create the curvature of a grass blade. - -The generated vertices will be passed to the tessellation evaluation shader, where you will place the vertices in world space, respecting the width, height, and orientation information -of each blade. Once you have determined the world space position of each vector, make sure to set the output `gl_Position` in clip space! - -** Extra Credit**: Tessellate to varying levels of detail as a function of how far the grass blade is from the camera. For example, if the blade is very far, only generate four vertices in the tessellation control shader. - -To build more intuition on how tessellation works, I highly recommend playing with the [helloTessellation sample](https://github.com/CIS565-Fall-2018/Vulkan-Samples/tree/master/samples/5_helloTessellation) -and reading this [tutorial on tessellation](http://in2gpu.com/2014/07/12/tessellation-tutorial-opengl-4-3/). - -## Resources - -### Links - -The following resources may be useful for this project. - -* [Responsive Real-Time Grass Grass Rendering for General 3D Scenes](https://www.cg.tuwien.ac.at/research/publications/2017/JAHRMANN-2017-RRTG/JAHRMANN-2017-RRTG-draft.pdf) -* [CIS565 Vulkan samples](https://github.com/CIS565-Fall-2018/Vulkan-Samples) -* [Official Vulkan documentation](https://www.khronos.org/registry/vulkan/) -* [Vulkan tutorial](https://vulkan-tutorial.com/) -* [RenderDoc blog on Vulkan](https://renderdoc.org/vulkan-in-30-minutes.html) -* [Tessellation tutorial](http://in2gpu.com/2014/07/12/tessellation-tutorial-opengl-4-3/) - - -## Third-Party Code Policy - -* Use of any third-party code must be approved by asking on our Google Group. -* If it is approved, all students are welcome to use it. Generally, we approve - use of third-party code that is not a core part of the project. For example, - for the path tracer, we would approve using a third-party library for loading - models, but would not approve copying and pasting a CUDA function for doing - refraction. -* Third-party code **MUST** be credited in README.md. -* Using third-party code without its approval, including using another - student's code, is an academic integrity violation, and will, at minimum, - result in you receiving an F for the semester. - - -## README - -* A brief description of the project and the specific features you implemented. -* GIFs of your project in its different stages with the different features being added incrementally. -* A performance analysis (described below). - -### Performance Analysis - -The performance analysis is where you will investigate how... -* Your renderer handles varying numbers of grass blades -* The improvement you get by culling using each of the three culling tests - -## Submit - -If you have modified any of the `CMakeLists.txt` files at all (aside from the -list of `SOURCE_FILES`), mentions it explicity. -Beware of any build issues discussed on the Google Group. - -Open a GitHub pull request so that we can see that you have finished. -The title should be "Project 6: YOUR NAME". -The template of the comment section of your pull request is attached below, you can do some copy and paste: - -* [Repo Link](https://link-to-your-repo) -* (Briefly) Mentions features that you've completed. Especially those bells and whistles you want to highlight - * Feature 0 - * Feature 1 - * ... -* Feedback on the project itself, if any. +--- diff --git a/bin/Debug/vulkan_grass_rendering.exe b/bin/Debug/vulkan_grass_rendering.exe new file mode 100644 index 0000000..acd11c2 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..71d2ba6 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..f5f6da8 Binary files /dev/null and b/bin/Debug/vulkan_grass_rendering.pdb differ diff --git a/img/0.gif b/img/0.gif new file mode 100644 index 0000000..e8359da Binary files /dev/null and b/img/0.gif differ diff --git a/img/1/1_aoff_far.JPG b/img/1/1_aoff_far.JPG new file mode 100644 index 0000000..05e53fa Binary files /dev/null and b/img/1/1_aoff_far.JPG differ diff --git a/img/1/1_aoff_mid.JPG b/img/1/1_aoff_mid.JPG new file mode 100644 index 0000000..4235700 Binary files /dev/null and b/img/1/1_aoff_mid.JPG differ diff --git a/img/1/1_aoff_near.JPG b/img/1/1_aoff_near.JPG new file mode 100644 index 0000000..63690b3 Binary files /dev/null and b/img/1/1_aoff_near.JPG differ diff --git a/img/1/1_aon_far.JPG b/img/1/1_aon_far.JPG new file mode 100644 index 0000000..188cb3b Binary files /dev/null and b/img/1/1_aon_far.JPG differ diff --git a/img/1/1_aon_mid.JPG b/img/1/1_aon_mid.JPG new file mode 100644 index 0000000..239b886 Binary files /dev/null and b/img/1/1_aon_mid.JPG differ diff --git a/img/1/1_aon_near.JPG b/img/1/1_aon_near.JPG new file mode 100644 index 0000000..0de3dbc Binary files /dev/null and b/img/1/1_aon_near.JPG differ diff --git a/img/1/1_d_far.JPG b/img/1/1_d_far.JPG new file mode 100644 index 0000000..1f74cf9 Binary files /dev/null and b/img/1/1_d_far.JPG differ diff --git a/img/1/1_d_mid.JPG b/img/1/1_d_mid.JPG new file mode 100644 index 0000000..907e3ce Binary files /dev/null and b/img/1/1_d_mid.JPG differ diff --git a/img/1/1_d_near.JPG b/img/1/1_d_near.JPG new file mode 100644 index 0000000..6dfe14f Binary files /dev/null and b/img/1/1_d_near.JPG differ diff --git a/img/1/1_f_far.JPG b/img/1/1_f_far.JPG new file mode 100644 index 0000000..6464053 Binary files /dev/null and b/img/1/1_f_far.JPG differ diff --git a/img/1/1_f_mid.JPG b/img/1/1_f_mid.JPG new file mode 100644 index 0000000..db427ba Binary files /dev/null and b/img/1/1_f_mid.JPG differ diff --git a/img/1/1_f_near.JPG b/img/1/1_f_near.JPG new file mode 100644 index 0000000..bf82a08 Binary files /dev/null and b/img/1/1_f_near.JPG differ diff --git a/img/1/1_o_far.JPG b/img/1/1_o_far.JPG new file mode 100644 index 0000000..852fdc2 Binary files /dev/null and b/img/1/1_o_far.JPG differ diff --git a/img/1/1_o_mid.JPG b/img/1/1_o_mid.JPG new file mode 100644 index 0000000..2e57dc4 Binary files /dev/null and b/img/1/1_o_mid.JPG differ diff --git a/img/1/1_o_near.JPG b/img/1/1_o_near.JPG new file mode 100644 index 0000000..f4fc7f2 Binary files /dev/null and b/img/1/1_o_near.JPG differ diff --git a/img/2/2_aoff_far.JPG b/img/2/2_aoff_far.JPG new file mode 100644 index 0000000..3edf82f Binary files /dev/null and b/img/2/2_aoff_far.JPG differ diff --git a/img/2/2_aoff_mid.JPG b/img/2/2_aoff_mid.JPG new file mode 100644 index 0000000..0120654 Binary files /dev/null and b/img/2/2_aoff_mid.JPG differ diff --git a/img/2/2_aoff_near.JPG b/img/2/2_aoff_near.JPG new file mode 100644 index 0000000..b8c3b8b Binary files /dev/null and b/img/2/2_aoff_near.JPG differ diff --git a/img/2/2_aon_far.JPG b/img/2/2_aon_far.JPG new file mode 100644 index 0000000..ca1c3fd Binary files /dev/null and b/img/2/2_aon_far.JPG differ diff --git a/img/2/2_aon_mid.JPG b/img/2/2_aon_mid.JPG new file mode 100644 index 0000000..1033299 Binary files /dev/null and b/img/2/2_aon_mid.JPG differ diff --git a/img/2/2_aon_near.JPG b/img/2/2_aon_near.JPG new file mode 100644 index 0000000..07527a6 Binary files /dev/null and b/img/2/2_aon_near.JPG differ diff --git a/img/2/2_d_far.JPG b/img/2/2_d_far.JPG new file mode 100644 index 0000000..5b1a086 Binary files /dev/null and b/img/2/2_d_far.JPG differ diff --git a/img/2/2_d_mid.JPG b/img/2/2_d_mid.JPG new file mode 100644 index 0000000..43f837e Binary files /dev/null and b/img/2/2_d_mid.JPG differ diff --git a/img/2/2_d_near.JPG b/img/2/2_d_near.JPG new file mode 100644 index 0000000..10c1322 Binary files /dev/null and b/img/2/2_d_near.JPG differ diff --git a/img/2/2_f_far.JPG b/img/2/2_f_far.JPG new file mode 100644 index 0000000..03993a4 Binary files /dev/null and b/img/2/2_f_far.JPG differ diff --git a/img/2/2_f_mid.JPG b/img/2/2_f_mid.JPG new file mode 100644 index 0000000..64a6474 Binary files /dev/null and b/img/2/2_f_mid.JPG differ diff --git a/img/2/2_f_near.JPG b/img/2/2_f_near.JPG new file mode 100644 index 0000000..e93b704 Binary files /dev/null and b/img/2/2_f_near.JPG differ diff --git a/img/2/2_o_far.JPG b/img/2/2_o_far.JPG new file mode 100644 index 0000000..0c785b2 Binary files /dev/null and b/img/2/2_o_far.JPG differ diff --git a/img/2/2_o_mid.JPG b/img/2/2_o_mid.JPG new file mode 100644 index 0000000..64d1050 Binary files /dev/null and b/img/2/2_o_mid.JPG differ diff --git a/img/2/2_o_near.JPG b/img/2/2_o_near.JPG new file mode 100644 index 0000000..bc04f1b Binary files /dev/null and b/img/2/2_o_near.JPG differ diff --git a/img/3/3_aoff_far.JPG b/img/3/3_aoff_far.JPG new file mode 100644 index 0000000..6d55aac Binary files /dev/null and b/img/3/3_aoff_far.JPG differ diff --git a/img/3/3_aoff_mid.JPG b/img/3/3_aoff_mid.JPG new file mode 100644 index 0000000..646e7b2 Binary files /dev/null and b/img/3/3_aoff_mid.JPG differ diff --git a/img/3/3_aoff_near.JPG b/img/3/3_aoff_near.JPG new file mode 100644 index 0000000..564d395 Binary files /dev/null and b/img/3/3_aoff_near.JPG differ diff --git a/img/3/3_aon_far.JPG b/img/3/3_aon_far.JPG new file mode 100644 index 0000000..63c82ea Binary files /dev/null and b/img/3/3_aon_far.JPG differ diff --git a/img/3/3_aon_mid.JPG b/img/3/3_aon_mid.JPG new file mode 100644 index 0000000..608d707 Binary files /dev/null and b/img/3/3_aon_mid.JPG differ diff --git a/img/3/3_aon_near.JPG b/img/3/3_aon_near.JPG new file mode 100644 index 0000000..108df5d Binary files /dev/null and b/img/3/3_aon_near.JPG differ diff --git a/img/3/3_d_far.JPG b/img/3/3_d_far.JPG new file mode 100644 index 0000000..4e9ee63 Binary files /dev/null and b/img/3/3_d_far.JPG differ diff --git a/img/3/3_d_mid.JPG b/img/3/3_d_mid.JPG new file mode 100644 index 0000000..0de45c8 Binary files /dev/null and b/img/3/3_d_mid.JPG differ diff --git a/img/3/3_d_near.JPG b/img/3/3_d_near.JPG new file mode 100644 index 0000000..46841b2 Binary files /dev/null and b/img/3/3_d_near.JPG differ diff --git a/img/3/3_f_far.JPG b/img/3/3_f_far.JPG new file mode 100644 index 0000000..954e405 Binary files /dev/null and b/img/3/3_f_far.JPG differ diff --git a/img/3/3_f_mid.JPG b/img/3/3_f_mid.JPG new file mode 100644 index 0000000..eab73af Binary files /dev/null and b/img/3/3_f_mid.JPG differ diff --git a/img/3/3_f_near.JPG b/img/3/3_f_near.JPG new file mode 100644 index 0000000..06bcfda Binary files /dev/null and b/img/3/3_f_near.JPG differ diff --git a/img/3/3_o_far.JPG b/img/3/3_o_far.JPG new file mode 100644 index 0000000..0da9fc5 Binary files /dev/null and b/img/3/3_o_far.JPG differ diff --git a/img/3/3_o_mid.JPG b/img/3/3_o_mid.JPG new file mode 100644 index 0000000..2452578 Binary files /dev/null and b/img/3/3_o_mid.JPG differ diff --git a/img/3/3_o_near.JPG b/img/3/3_o_near.JPG new file mode 100644 index 0000000..f1b9a86 Binary files /dev/null and b/img/3/3_o_near.JPG differ diff --git a/img/a.JPG b/img/a.JPG new file mode 100644 index 0000000..20a0f59 Binary files /dev/null and b/img/a.JPG differ diff --git a/img/b.JPG b/img/b.JPG new file mode 100644 index 0000000..11d9a11 Binary files /dev/null and b/img/b.JPG differ diff --git a/img/c.JPG b/img/c.JPG new file mode 100644 index 0000000..03970c4 Binary files /dev/null and b/img/c.JPG differ diff --git a/img/d_culling.JPG b/img/d_culling.JPG new file mode 100644 index 0000000..22888a9 Binary files /dev/null and b/img/d_culling.JPG differ diff --git a/img/f_culling.JPG b/img/f_culling.JPG new file mode 100644 index 0000000..b64a005 Binary files /dev/null and b/img/f_culling.JPG differ diff --git a/img/o_culling.JPG b/img/o_culling.JPG new file mode 100644 index 0000000..b55ff9a Binary files /dev/null and b/img/o_culling.JPG differ diff --git a/src/Blades.cpp b/src/Blades.cpp index 80e3d76..5270728 100644 --- a/src/Blades.cpp +++ b/src/Blades.cpp @@ -19,7 +19,7 @@ Blades::Blades(Device* device, VkCommandPool commandPool, float planeDim) : Mode float x = (generateRandomFloat() - 0.5f) * planeDim; float y = 0.0f; float z = (generateRandomFloat() - 0.5f) * planeDim; - float direction = generateRandomFloat() * 2.f * 3.14159265f; + float direction = generateRandomFloat() * 2.f * 3.14159265f; glm::vec3 bladePosition(x, y, z); currentBlade.v0 = glm::vec4(bladePosition, direction); @@ -45,8 +45,9 @@ Blades::Blades(Device* device, VkCommandPool commandPool, float planeDim) : Mode 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::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); + BufferUtils::CreateBuffer(device, BUCKET_COUNT * sizeof(uint32_t), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, bucketBladeCounts, bucketBladeCountsMemory); } VkBuffer Blades::GetBladesBuffer() const { @@ -61,6 +62,10 @@ VkBuffer Blades::GetNumBladesBuffer() const { return numBladesBuffer; } +VkBuffer Blades::GetBucketBladeCounts() const { + return bucketBladeCounts; +} + Blades::~Blades() { vkDestroyBuffer(device->GetVkDevice(), bladesBuffer, nullptr); vkFreeMemory(device->GetVkDevice(), bladesBufferMemory, nullptr); diff --git a/src/Blades.h b/src/Blades.h index 9bd1eed..79b37f8 100644 --- a/src/Blades.h +++ b/src/Blades.h @@ -5,6 +5,7 @@ #include "Model.h" constexpr static unsigned int NUM_BLADES = 1 << 13; +static constexpr unsigned int BUCKET_COUNT = 50;//not used constexpr static float MIN_HEIGHT = 1.3f; constexpr static float MAX_HEIGHT = 2.5f; constexpr static float MIN_WIDTH = 0.1f; @@ -74,15 +75,18 @@ class Blades : public Model { VkBuffer bladesBuffer; VkBuffer culledBladesBuffer; VkBuffer numBladesBuffer; + VkBuffer bucketBladeCounts; VkDeviceMemory bladesBufferMemory; VkDeviceMemory culledBladesBufferMemory; VkDeviceMemory numBladesBufferMemory; + VkDeviceMemory bucketBladeCountsMemory; public: Blades(Device* device, VkCommandPool commandPool, float planeDim); VkBuffer GetBladesBuffer() const; VkBuffer GetCulledBladesBuffer() const; VkBuffer GetNumBladesBuffer() const; + VkBuffer GetBucketBladeCounts() const; ~Blades(); }; diff --git a/src/Camera.cpp b/src/Camera.cpp index 3afb5b8..cea517a 100644 --- a/src/Camera.cpp +++ b/src/Camera.cpp @@ -41,6 +41,39 @@ void Camera::UpdateOrbit(float deltaX, float deltaY, float deltaZ) { memcpy(mappedData, &cameraBufferObject, sizeof(CameraBufferObject)); } +void Camera::SetOrientationCullingEnabled(float mode) +{ + cameraBufferObject.orientationCullingEnabled = mode; + memcpy(mappedData, &cameraBufferObject, sizeof(CameraBufferObject)); +} + +void Camera::SetFrustumCullingEnabled(float mode) +{ + cameraBufferObject.frustumCullingEnabled = mode; + memcpy(mappedData, &cameraBufferObject, sizeof(CameraBufferObject)); +} + +void Camera::SetDistanceCullingEnabled(float mode) +{ + cameraBufferObject.distanceCullingEnabled = mode; + memcpy(mappedData, &cameraBufferObject, sizeof(CameraBufferObject)); +} + +float Camera::IsOrientationCullingEnabled() +{ + return cameraBufferObject.orientationCullingEnabled; +} + +float Camera::IsFrustumCullingEnabled() +{ + return cameraBufferObject.frustumCullingEnabled; +} + +float Camera::IsDistanceCullingEnabled() +{ + return cameraBufferObject.distanceCullingEnabled; +} + Camera::~Camera() { vkUnmapMemory(device->GetVkDevice(), bufferMemory); vkDestroyBuffer(device->GetVkDevice(), buffer, nullptr); diff --git a/src/Camera.h b/src/Camera.h index 6b10747..eb2fd13 100644 --- a/src/Camera.h +++ b/src/Camera.h @@ -5,8 +5,18 @@ #include "Device.h" struct CameraBufferObject { + CameraBufferObject() + { + orientationCullingEnabled = -1; + frustumCullingEnabled = -1; + distanceCullingEnabled = -1; + } + glm::mat4 viewMatrix; glm::mat4 projectionMatrix; + float orientationCullingEnabled; + float frustumCullingEnabled; + float distanceCullingEnabled; }; class Camera { @@ -20,13 +30,21 @@ class Camera { void* mappedData; - float r, theta, phi; public: + float r, theta, phi; Camera(Device* device, float aspectRatio); ~Camera(); VkBuffer GetBuffer() const; + + void SetOrientationCullingEnabled(float mode); + void SetFrustumCullingEnabled(float mode); + void SetDistanceCullingEnabled(float mode); + + float IsOrientationCullingEnabled(); + float IsFrustumCullingEnabled(); + float IsDistanceCullingEnabled(); void UpdateOrbit(float deltaX, float deltaY, float deltaZ); }; diff --git a/src/Renderer.cpp b/src/Renderer.cpp index b445d04..bafb686 100644 --- a/src/Renderer.cpp +++ b/src/Renderer.cpp @@ -16,21 +16,28 @@ Renderer::Renderer(Device* device, SwapChain* swapChain, Scene* scene, Camera* c camera(camera) { CreateCommandPools(); + CreateRenderPass(); + CreateCameraDescriptorSetLayout(); CreateModelDescriptorSetLayout(); CreateTimeDescriptorSetLayout(); CreateComputeDescriptorSetLayout(); + CreateDescriptorPool(); + CreateCameraDescriptorSet(); CreateModelDescriptorSets(); CreateGrassDescriptorSets(); CreateTimeDescriptorSet(); CreateComputeDescriptorSets(); + CreateFrameResources(); + CreateGraphicsPipeline(); CreateGrassPipeline(); CreateComputePipeline(); + RecordCommandBuffers(); RecordComputeCommandBuffer(); } @@ -149,7 +156,7 @@ void Renderer::CreateModelDescriptorSetLayout() { uboLayoutBinding.binding = 0; uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; uboLayoutBinding.descriptorCount = 1; - uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; + uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_COMPUTE_BIT; uboLayoutBinding.pImmutableSamplers = nullptr; VkDescriptorSetLayoutBinding samplerLayoutBinding = {}; @@ -196,8 +203,52 @@ void Renderer::CreateTimeDescriptorSetLayout() { 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 + // Remember this is like a class definition stating what types of information // will be stored at each binding + + VkDescriptorSetLayoutBinding numBladesSSBOLayoutBinding = {}; + numBladesSSBOLayoutBinding.binding = 0; + numBladesSSBOLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + numBladesSSBOLayoutBinding.descriptorCount = 1; + numBladesSSBOLayoutBinding.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + numBladesSSBOLayoutBinding.pImmutableSamplers = nullptr; + + VkDescriptorSetLayoutBinding bladesSSBOLayoutBinding = {}; + bladesSSBOLayoutBinding.binding = 1; + bladesSSBOLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + bladesSSBOLayoutBinding.descriptorCount = 1;// + bladesSSBOLayoutBinding.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + bladesSSBOLayoutBinding.pImmutableSamplers = nullptr; + + VkDescriptorSetLayoutBinding culledBladesSSBOLayoutBinding = {}; + culledBladesSSBOLayoutBinding.binding = 2; + culledBladesSSBOLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + culledBladesSSBOLayoutBinding.descriptorCount = 1;// + culledBladesSSBOLayoutBinding.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + culledBladesSSBOLayoutBinding.pImmutableSamplers = nullptr; + + VkDescriptorSetLayoutBinding bucketBladeCountsSSBOLayoutBinding = {}; + bucketBladeCountsSSBOLayoutBinding.binding = 3; + bucketBladeCountsSSBOLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + bucketBladeCountsSSBOLayoutBinding.descriptorCount = 1;// + bucketBladeCountsSSBOLayoutBinding.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + bucketBladeCountsSSBOLayoutBinding.pImmutableSamplers = nullptr; + + std::vector bindings = { + numBladesSSBOLayoutBinding, + bladesSSBOLayoutBinding, + culledBladesSSBOLayoutBinding, + bucketBladeCountsSSBOLayoutBinding }; + + // Create the descriptor set layout + VkDescriptorSetLayoutCreateInfo layoutInfo = {}; + layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + layoutInfo.bindingCount = static_cast(bindings.size()); + layoutInfo.pBindings = bindings.data(); + + if (vkCreateDescriptorSetLayout(logicalDevice, &layoutInfo, nullptr, &computeDescriptorSetLayout) != VK_SUCCESS) { + throw std::runtime_error("Failed to create descriptor set layout"); + } } void Renderer::CreateDescriptorPool() { @@ -216,8 +267,20 @@ void Renderer::CreateDescriptorPool() { { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER , 1 }, // TODO: Add any additional types and counts of descriptors you will need to allocate + + // Compute numBlades + Compute blades + Compute culledBlades + Compute bucketBladeCounts + { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER , static_cast(4 * scene->GetBlades().size()) }, + //{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER , static_cast(2 * scene->GetBlades().size() * NUM_BLADES + scene->GetBlades().size()) }, + + // Compute blades + //{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER , static_cast(scene->GetBlades().size()) }, + + // Compute culledBlades + //{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER , static_cast(scene->GetBlades().size()) }, }; + //printf("%d\n", scene->GetBlades().size()); + VkDescriptorPoolCreateInfo poolInfo = {}; poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; poolInfo.poolSizeCount = static_cast(poolSizes.size()); @@ -320,6 +383,48 @@ 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->GetModels().size()); + + // Describe the desciptor set + VkDescriptorSetLayout layouts[] = { modelDescriptorSetLayout }; + VkDescriptorSetAllocateInfo allocInfo = {}; + allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + allocInfo.descriptorPool = descriptorPool; + allocInfo.descriptorSetCount = static_cast(grassDescriptorSets.size()); + allocInfo.pSetLayouts = layouts; + + // Allocate descriptor sets + if (vkAllocateDescriptorSets(logicalDevice, &allocInfo, grassDescriptorSets.data()) != VK_SUCCESS) { + throw std::runtime_error("Failed to allocate descriptor set"); + } + + std::vector descriptorWrites(grassDescriptorSets.size()); + + for (uint32_t i = 0; i < scene->GetBlades().size(); ++i) { + VkDescriptorBufferInfo modelBufferInfo = {}; + modelBufferInfo.buffer = scene->GetBlades()[i]->GetModelBuffer(); + modelBufferInfo.offset = 0; + modelBufferInfo.range = sizeof(ModelBufferObject); + + // Bind image and sampler resources to the descriptor + VkDescriptorImageInfo imageInfo = {}; + imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + imageInfo.imageView = scene->GetBlades()[i]->GetTextureView(); + imageInfo.sampler = scene->GetBlades()[i]->GetTextureSampler(); + + descriptorWrites[i].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrites[i].dstSet = grassDescriptorSets[i]; + descriptorWrites[i].dstBinding = 0; + descriptorWrites[i].dstArrayElement = 0; + descriptorWrites[i].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + descriptorWrites[i].descriptorCount = 1; + descriptorWrites[i].pBufferInfo = &modelBufferInfo; + descriptorWrites[i].pImageInfo = nullptr; + descriptorWrites[i].pTexelBufferView = nullptr; + } + + // Update descriptor sets + vkUpdateDescriptorSets(logicalDevice, static_cast(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr); } void Renderer::CreateTimeDescriptorSet() { @@ -360,6 +465,90 @@ 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(4 * computeDescriptorSets.size()); + + for (uint32_t i = 0; i < scene->GetBlades().size(); ++i) { + VkDescriptorBufferInfo numBladesBufferInfo = {}; + numBladesBufferInfo.buffer = scene->GetBlades()[i]->GetNumBladesBuffer(); + numBladesBufferInfo.offset = 0; + numBladesBufferInfo.range = sizeof(BladeDrawIndirect); + + VkDescriptorBufferInfo bladesBufferInfo = {}; + bladesBufferInfo.buffer = scene->GetBlades()[i]->GetBladesBuffer(); + bladesBufferInfo.offset = 0; + bladesBufferInfo.range = VK_WHOLE_SIZE;// NUM_BLADES * sizeof(Blade); + + VkDescriptorBufferInfo culledBladesBufferInfo = {}; + culledBladesBufferInfo.buffer = scene->GetBlades()[i]->GetCulledBladesBuffer(); + culledBladesBufferInfo.offset = 0; + culledBladesBufferInfo.range = VK_WHOLE_SIZE;// NUM_BLADES * sizeof(Blade); + + VkDescriptorBufferInfo bucketBladeCountsInfo = {}; + bucketBladeCountsInfo.buffer = scene->GetBlades()[i]->GetBucketBladeCounts(); + bucketBladeCountsInfo.offset = 0; + bucketBladeCountsInfo.range = VK_WHOLE_SIZE; + + // Bind SSBO to the descriptor + descriptorWrites[4 * i + 0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrites[4 * i + 0].dstSet = computeDescriptorSets[i]; + descriptorWrites[4 * i + 0].dstBinding = 0; + descriptorWrites[4 * i + 0].dstArrayElement = 0; + descriptorWrites[4 * i + 0].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + descriptorWrites[4 * i + 0].descriptorCount = 1;// + descriptorWrites[4 * i + 0].pBufferInfo = &numBladesBufferInfo; + descriptorWrites[4 * i + 0].pImageInfo = nullptr; + descriptorWrites[4 * i + 0].pTexelBufferView = nullptr; + + descriptorWrites[4 * i + 1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrites[4 * i + 1].dstSet = computeDescriptorSets[i]; + descriptorWrites[4 * i + 1].dstBinding = 1; + descriptorWrites[4 * i + 1].dstArrayElement = 0; + descriptorWrites[4 * i + 1].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + descriptorWrites[4 * i + 1].descriptorCount = 1;// + descriptorWrites[4 * i + 1].pBufferInfo = &bladesBufferInfo; + descriptorWrites[4 * i + 1].pImageInfo = nullptr; + descriptorWrites[4 * i + 1].pTexelBufferView = nullptr; + + descriptorWrites[4 * i + 2].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrites[4 * i + 2].dstSet = computeDescriptorSets[i]; + descriptorWrites[4 * i + 2].dstBinding = 2; + descriptorWrites[4 * i + 2].dstArrayElement = 0; + descriptorWrites[4 * i + 2].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + descriptorWrites[4 * i + 2].descriptorCount = 1;// + descriptorWrites[4 * i + 2].pBufferInfo = &culledBladesBufferInfo; + descriptorWrites[4 * i + 2].pImageInfo = nullptr; + descriptorWrites[4 * i + 2].pTexelBufferView = nullptr; + + descriptorWrites[4 * i + 3].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrites[4 * i + 3].dstSet = computeDescriptorSets[i]; + descriptorWrites[4 * i + 3].dstBinding = 3; + descriptorWrites[4 * i + 3].dstArrayElement = 0; + descriptorWrites[4 * i + 3].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + descriptorWrites[4 * i + 3].descriptorCount = 1;// + descriptorWrites[4 * i + 3].pBufferInfo = &bucketBladeCountsInfo; + descriptorWrites[4 * i + 3].pImageInfo = nullptr; + descriptorWrites[4 * i + 3].pTexelBufferView = nullptr; + } + + // Update descriptor sets + vkUpdateDescriptorSets(logicalDevice, static_cast(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr); + } void Renderer::CreateGraphicsPipeline() { @@ -717,7 +906,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, modelDescriptorSetLayout, timeDescriptorSetLayout, computeDescriptorSetLayout }; // Create pipeline layout VkPipelineLayoutCreateInfo pipelineLayoutInfo = {}; @@ -881,9 +1070,16 @@ void Renderer::RecordComputeCommandBuffer() { vkCmdBindDescriptorSets(computeCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, computePipelineLayout, 0, 1, &cameraDescriptorSet, 0, nullptr); // Bind descriptor set for time uniforms - vkCmdBindDescriptorSets(computeCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, computePipelineLayout, 1, 1, &timeDescriptorSet, 0, nullptr); + vkCmdBindDescriptorSets(computeCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, computePipelineLayout, 2, 1, &timeDescriptorSet, 0, nullptr); // TODO: For each group of blades bind its descriptor set and dispatch + for (uint32_t j = 0; j < scene->GetBlades().size(); ++j) { + vkCmdBindDescriptorSets(computeCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, computePipelineLayout, 3, 1, &computeDescriptorSets[j], 0, nullptr); + vkCmdBindDescriptorSets(computeCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, computePipelineLayout, 1, 1, &grassDescriptorSets[j], 0, nullptr); + int workGroupCount = int(ceil((NUM_BLADES + WORKGROUP_SIZE - 1) / WORKGROUP_SIZE)); + + vkCmdDispatch(computeCommandBuffer, workGroupCount, 1, 1); + } // ~ End recording ~ if (vkEndCommandBuffer(computeCommandBuffer) != VK_SUCCESS) { @@ -976,13 +1172,14 @@ void Renderer::RecordCommandBuffers() { VkBuffer vertexBuffers[] = { scene->GetBlades()[j]->GetCulledBladesBuffer() }; VkDeviceSize offsets[] = { 0 }; // TODO: Uncomment this when the buffers are populated - // vkCmdBindVertexBuffers(commandBuffers[i], 0, 1, vertexBuffers, offsets); + vkCmdBindVertexBuffers(commandBuffers[i], 0, 1, vertexBuffers, offsets); // TODO: Bind the descriptor set for each grass blades model + vkCmdBindDescriptorSets(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, 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 +1254,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..1bde6bf 100644 --- a/src/Renderer.h +++ b/src/Renderer.h @@ -56,12 +56,15 @@ class Renderer { VkDescriptorSetLayout cameraDescriptorSetLayout; VkDescriptorSetLayout modelDescriptorSetLayout; VkDescriptorSetLayout timeDescriptorSetLayout; + VkDescriptorSetLayout computeDescriptorSetLayout;// VkDescriptorPool descriptorPool; VkDescriptorSet cameraDescriptorSet; std::vector modelDescriptorSets; VkDescriptorSet timeDescriptorSet; + std::vector grassDescriptorSets;// + std::vector computeDescriptorSets;// VkPipelineLayout graphicsPipelineLayout; VkPipelineLayout grassPipelineLayout; diff --git a/src/main.cpp b/src/main.cpp index 8bf822b..3cfe697 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,11 +5,18 @@ #include "Camera.h" #include "Scene.h" #include "Image.h" +#include Device* device; SwapChain* swapChain; Renderer* renderer; Camera* camera; +bool record = false; +float record_tick = 1; +float record_totalFPS = 0; +int camera_distance_mode = 0; +const int camera_distance_mode_count = 3; +const int tick_max = 1000; namespace { void resizeCallback(GLFWwindow* window, int width, int height) { @@ -52,6 +59,7 @@ namespace { float deltaY = static_cast((previousY - yPosition) * sensitivity); camera->UpdateOrbit(deltaX, deltaY, 0.0f); + printf("r, theta, phi: %f,%f,%f\n", camera->r, camera->theta, camera->phi); previousX = xPosition; previousY = yPosition; @@ -59,10 +67,54 @@ namespace { double deltaZ = static_cast((previousY - yPosition) * 0.05); camera->UpdateOrbit(0.0f, 0.0f, deltaZ); + printf("r, theta, phi: %f,%f,%f\n", camera->r, camera->theta, camera->phi); previousY = yPosition; } } + + void keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) + { + if (key == GLFW_KEY_1 && action == GLFW_PRESS) + { + camera->SetOrientationCullingEnabled(-1.f*camera->IsOrientationCullingEnabled()); + printf("orientation culling: %f\n", camera->IsOrientationCullingEnabled()); + } + if (key == GLFW_KEY_2 && action == GLFW_PRESS) + { + camera->SetFrustumCullingEnabled(-1.f*camera->IsFrustumCullingEnabled()); + printf("frustum culling: %f\n", camera->IsFrustumCullingEnabled()); + } + if (key == GLFW_KEY_3 && action == GLFW_PRESS) + { + camera->SetDistanceCullingEnabled(-1.f*camera->IsDistanceCullingEnabled()); + printf("distance culling: %f\n", camera->IsDistanceCullingEnabled()); + } + if (key == GLFW_KEY_R && action == GLFW_PRESS) + { + record = !record; + printf("record: %d\n", record); + if (record) + { + record_tick = 0; + record_totalFPS = 0; + } + } + if (key == GLFW_KEY_C && action == GLFW_PRESS) + { + camera_distance_mode = (camera_distance_mode + 1) % camera_distance_mode_count; + if(camera_distance_mode == 0) + camera->r = 50; + if (camera_distance_mode == 1) + camera->r = 20; + if (camera_distance_mode == 2) + camera->r = 5; + camera->theta = 0; + camera->phi = -34; + camera->UpdateOrbit(0.0f, 0.0f, 0.0f); + printf("r, theta, phi: %f,%f,%f\n", camera->r, camera->theta, camera->phi); + } + } } int main() { @@ -142,11 +194,53 @@ int main() { glfwSetWindowSizeCallback(GetGLFWWindow(), resizeCallback); glfwSetMouseButtonCallback(GetGLFWWindow(), mouseDownCallback); glfwSetCursorPosCallback(GetGLFWWindow(), mouseMoveCallback); + glfwSetKeyCallback(GetGLFWWindow(), keyCallback); + std::stringstream ss; + int tick = 0; + float totalFPS = 0; while (!ShouldQuit()) { glfwPollEvents(); scene->UpdateTime(); + + high_resolution_clock::time_point t1 = high_resolution_clock::now(); renderer->Frame(); + high_resolution_clock::time_point t2 = high_resolution_clock::now(); + + duration deltaTime = duration_cast>(t2 - t1); + float FPS = 1.0 / deltaTime.count();//in second + totalFPS += FPS; + tick++; + + if (tick == tick_max) + { + totalFPS = totalFPS / tick_max; + ss << totalFPS << " fps : record"; + + if (record) + { + record_totalFPS += totalFPS; + record_tick++; + ss << "ing"; + } + else + { + float show_record_totalFPS = record_totalFPS / record_tick; + ss << "ed " << show_record_totalFPS << " fps"; + } + + if (record_tick == 10) + { + record = false; + } + + glfwSetWindowTitle(GetGLFWWindow(), ss.str().c_str()); + ss.clear(); + ss.str(""); + tick = 0; + totalFPS = 0; + } + } vkDeviceWaitIdle(device->GetVkDevice()); diff --git a/src/shaders/compute.comp b/src/shaders/compute.comp index 0fd0224..2a87de3 100644 --- a/src/shaders/compute.comp +++ b/src/shaders/compute.comp @@ -1,15 +1,24 @@ #version 450 #extension GL_ARB_separate_shader_objects : enable - +#define BUCKET_COUNT 50 +#define BUCKET_FAR 50 +#define TOLERANCE 0.9 #define WORKGROUP_SIZE 32 layout(local_size_x = WORKGROUP_SIZE, local_size_y = 1, local_size_z = 1) in; layout(set = 0, binding = 0) uniform CameraBufferObject { mat4 view; mat4 proj; + float orientationCullingEnabled; + float frustumCullingEnabled; + float distanceCullingEnabled; } camera; -layout(set = 1, binding = 0) uniform Time { +layout(set = 1, binding = 0) uniform ModelBufferObject { + mat4 model; +}; + +layout(set = 2, binding = 0) uniform Time { float deltaTime; float totalTime; }; @@ -28,29 +37,180 @@ struct Blade { // The project is using vkCmdDrawIndirect to use a buffer as the arguments for a draw call // This is sort of an advanced feature so we've showed you what this buffer should look like -// -// layout(set = ???, binding = ???) buffer NumBlades { -// uint vertexCount; // Write the number of blades remaining here -// uint instanceCount; // = 1 -// uint firstVertex; // = 0 -// uint firstInstance; // = 0 -// } numBlades; + +layout(set = 3, binding = 0) buffer NumBlades { + uint vertexCount; // Write the number of blades remaining here + uint instanceCount;// = 1; + uint firstVertex;// = 0; + uint firstInstance;// = 0; +} numBlades; + +layout(set = 3, binding = 1) buffer Blades { + Blade blades[]; +} blades; + +layout(set = 3, binding = 2) buffer CulledBlades { + Blade culledBlades[]; +} culledBlades; + +// This is not used. Initially, this was used to do bucket based distance culling. +// But the culled blades are not consistent, meaning the ones got binned in the bucket +// during frame 1 may not get binned in the bucket during frame 2, which will result +// in flashing and flickering. So I used the one described in the paper. + +layout(set = 3, binding = 3) buffer BucketBladeCounts { + uint bucketBladeCounts[]; +} bucketBladeCounts; bool inBounds(float value, float bounds) { return (value >= -bounds) && (value <= bounds); } +bool inBounds(vec4 a) +{ + if(inBounds(a.x, abs(a.w) + TOLERANCE) && + inBounds(a.y, abs(a.w) + TOLERANCE) && + inBounds(a.z, abs(a.w) + TOLERANCE)) + return true; + else + return false; +} + +// return [0, 1] +float WindMagnitude(float time, vec2 position2D) +{ + //return sin(time * (position2D.x * position2D.y)); + return sin(time); +} + +// true if culled +bool OrientationCulling(vec3 front, vec3 v0) +{ + vec3 frontInCameraSpace = normalize((camera.view * model * vec4(front, 0.0)).xyz); + vec3 positionInCameraSpace = normalize((camera.view * model * vec4(v0, 1)).xyz); + + if(abs(dot(frontInCameraSpace, -positionInCameraSpace)) < 0.2) + return true; + else + return false; +} + +// true if culled +bool DistanceCulling(vec3 v0, vec3 up, uint bladeId) +{ + vec3 positionInCameraSpace = (camera.view * model * vec4(v0, 1)).xyz; + vec3 upInCameraSpace = normalize((camera.view * model * vec4(up, 0)).xyz); + float dproj = length(positionInCameraSpace - upInCameraSpace * (dot(positionInCameraSpace, upInCameraSpace))); + + if(int(bladeId) % BUCKET_COUNT < int(BUCKET_COUNT * (dproj / BUCKET_FAR))) + return true; + + return false; +} + +// true if culled +bool FrustumCulling(vec3 v0, vec3 v1, vec3 v2) +{ + vec3 m = v0 / 4.0 + v1 / 2.0 + v2 / 4.0; + vec4 m_new = camera.proj * camera.view * model * vec4(m, 1); + vec4 v0_new = camera.proj * camera.view * model * vec4(v0, 1); + vec4 v2_new = camera.proj * camera.view * model * vec4(v2, 1); + if(inBounds(m_new) && inBounds(v0_new) && inBounds(v2_new)) + return false; + else + return true; +} + +// true if culled +bool Culling(Blade blade, uint bladeId) +{ + float direction = blade.v0.w; + vec3 front = vec3(cos(direction), 0, sin(direction)); + + //return false; + + bool result = false; + + if(camera.orientationCullingEnabled > 0) + result = result || OrientationCulling(front, blade.v0.xyz); + + if(camera.frustumCullingEnabled > 0) + result = result || FrustumCulling(blade.v0.xyz, blade.v1.xyz, blade.v2.xyz); + + if(camera.distanceCullingEnabled > 0) + result = result || DistanceCulling(blade.v0.xyz, blade.up.xyz, bladeId); + + return result; +} + void main() { // Reset the number of blades to 0 if (gl_GlobalInvocationID.x == 0) { - // numBlades.vertexCount = 0; + // initialize global variable every frame + numBlades.vertexCount = 0; + numBlades.instanceCount = 1; + numBlades.firstVertex = 0; + numBlades.firstInstance = 0; + } barrier(); // Wait till all threads reach this point // TODO: Apply forces on every blade and update the vertices in the buffer + uint bladeId = gl_GlobalInvocationID.x; + + Blade blade = blades.blades[bladeId]; + float direction = blade.v0.w; + float height = blade.v1.w; + float stiffness = blade.up.w; + vec3 front = vec3(cos(direction), 0, sin(direction)); + vec3 up = blade.up.xyz; + vec3 v0 = blade.v0.xyz; + vec3 v2 = blade.v2.xyz; + + // 1. gravity + vec3 gE = -up * 9.8; + + // 2. front gravity + vec3 gF = 0.25 * length(gE) * front; + + // 3. recovery force + vec3 iv2 = v0 + up * height; + vec3 fR = (iv2 - v2) * stiffness; + + // 4. wind force + vec3 fW = WindMagnitude(totalTime, v0.xz) * vec3(1, 0, 0); + + vec3 tv2 = (fW + fR + gE + gF) * deltaTime; + + v2 += tv2; + + // 1. ground validation + v2 = v2 - up * min(dot(up, (v2 - v0)), 0); + + // 2. calculate v1 + float lproj = length(v2 - v0 - up * dot((v2 - v0), up)); + vec3 v1 = v0 + height * up * max(1.0 - lproj / height, 0.05 * max(lproj / height, 1)); + + // 3. length validation + float L = (2 * length(v2 - v0) + length(v1 - v0) + length(v2 - v1)) / 3; + float gama = height / L; + vec3 v1_new = v0 + gama * (v1 - v0); + vec3 v2_new = v1_new + gama * (v2 - v1); + + blade.v1.xyz = v1_new; + blade.v2.xyz = v2_new; + blades.blades[bladeId] = blade; + // TODO: Cull blades that are too far away or not in the camera frustum and write them // to the culled blades buffer // Note: to do this, you will need to use an atomic operation to read and update numBlades.vertexCount // You want to write the visible blades to the buffer without write conflicts between threads + + if(!Culling(blade, bladeId)) + { + uint culledBladeId = atomicAdd(numBlades.vertexCount, 1); + culledBlades.culledBlades[culledBladeId] = blade; + } + } diff --git a/src/shaders/grass.frag b/src/shaders/grass.frag index c7df157..1422ecc 100644 --- a/src/shaders/grass.frag +++ b/src/shaders/grass.frag @@ -8,10 +8,13 @@ layout(set = 0, binding = 0) uniform CameraBufferObject { // TODO: Declare fragment shader inputs +layout(location = 0) in vec3 normal_FS_IN; + layout(location = 0) out vec4 outColor; void main() { // TODO: Compute fragment color - outColor = vec4(1.0); + // half lambert shading + outColor = vec4(0.2, 0.5, 0.1, 1.0) * ( dot( normal_FS_IN, normalize(vec3(1,1,1)) ) * 0.5 + 0.5 ); } diff --git a/src/shaders/grass.tesc b/src/shaders/grass.tesc index f9ffd07..1f4facd 100644 --- a/src/shaders/grass.tesc +++ b/src/shaders/grass.tesc @@ -10,17 +10,36 @@ layout(set = 0, binding = 0) uniform CameraBufferObject { // TODO: Declare tessellation control shader inputs and outputs +layout(location = 0) in vec4 v0_CS_IN[]; +layout(location = 1) in vec4 v1_CS_IN[]; +layout(location = 2) in vec4 v2_CS_IN[]; +layout(location = 3) in vec4 up_CS_IN[]; + +layout(location = 0) out vec4 v0_ES_IN[]; +layout(location = 1) out vec4 v1_ES_IN[]; +layout(location = 2) out vec4 v2_ES_IN[]; +layout(location = 3) out vec4 up_ES_IN[]; + 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 + // v0_ES_IN[0] = v0_CS_IN[0]; + // v1_ES_IN[0] = v1_CS_IN[0]; + // v2_ES_IN[0] = v2_CS_IN[0]; + // up_ES_IN[0] = up_CS_IN[0]; + // '[]' : tessellation-control per-vertex output l-value must be indexed with gl_InvocationID + v0_ES_IN[gl_InvocationID] = v0_CS_IN[gl_InvocationID]; + v1_ES_IN[gl_InvocationID] = v1_CS_IN[gl_InvocationID]; + v2_ES_IN[gl_InvocationID] = v2_CS_IN[gl_InvocationID]; + up_ES_IN[gl_InvocationID] = up_CS_IN[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] = 4; + gl_TessLevelInner[1] = 4; + gl_TessLevelOuter[0] = 4; + gl_TessLevelOuter[1] = 4; + gl_TessLevelOuter[2] = 4; + gl_TessLevelOuter[3] = 4; } diff --git a/src/shaders/grass.tese b/src/shaders/grass.tese index 751fff6..1a56d99 100644 --- a/src/shaders/grass.tese +++ b/src/shaders/grass.tese @@ -10,9 +10,43 @@ layout(set = 0, binding = 0) uniform CameraBufferObject { // TODO: Declare tessellation evaluation shader inputs and outputs +layout(location = 0) in vec4 v0_ES_IN[]; +layout(location = 1) in vec4 v1_ES_IN[]; +layout(location = 2) in vec4 v2_ES_IN[]; +layout(location = 3) in vec4 up_ES_IN[]; + +layout(location = 0) out vec3 normal_FS_IN; + 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 + + float direction = v0_ES_IN[0].w; + float height = v1_ES_IN[0].w; + float width = v2_ES_IN[0].w; + float stiffness = up_ES_IN[0].w; + + vec3 front = vec3(cos(direction), 0, sin(direction)); + vec3 frontInCameraSpace = normalize((camera.view * vec4(front, 0.0)).xyz); + float normalFlip = sign(frontInCameraSpace.z); + vec3 bitangent = cross(front, up_ES_IN[0].xyz);//perpendicular to front + + vec3 a = v0_ES_IN[0].xyz + v * (v1_ES_IN[0].xyz - v0_ES_IN[0].xyz); + vec3 b = v1_ES_IN[0].xyz + v * (v2_ES_IN[0].xyz - v1_ES_IN[0].xyz); + vec3 c = a + v * (b - a); + vec3 c0 = c - width * bitangent; + vec3 c1 = c + width * bitangent; + vec3 tangent = normalize(b - a); + vec3 n = normalize(cross(tangent, bitangent));//in front direction + + float t = u - u * v * v; + + vec3 pos = (1 - t) * c0 + t * c1; + + normal_FS_IN = normalFlip * n; + + gl_Position = camera.proj * camera.view * vec4(pos, 1.0); + } diff --git a/src/shaders/grass.vert b/src/shaders/grass.vert index db9dfe9..1c68532 100644 --- a/src/shaders/grass.vert +++ b/src/shaders/grass.vert @@ -8,10 +8,27 @@ 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 v0_CS_IN; +layout(location = 1) out vec4 v1_CS_IN; +layout(location = 2) out vec4 v2_CS_IN; +layout(location = 3) out vec4 up_CS_IN; + out gl_PerVertex { vec4 gl_Position; }; void main() { // TODO: Write gl_Position and any other shader outputs + + gl_Position = model * v0; + v0_CS_IN = model * v0; + v1_CS_IN = model * v1; + v2_CS_IN = model * v2; + up_CS_IN = model * up; + }