Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
148 changes: 140 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,144 @@
Vulkan Grass Rendering
==================================
<h1 align="center">Vulkan Grass Rendering </h1>

**University of Pennsylvania, CIS 565: GPU Programming and Architecture, Project 5**
<p align="center"><small><b>University of Pennsylvania, CIS 565: GPU Programming and Architecture, Project 5</small></b></p>

* (TODO) YOUR NAME HERE
* Tested on: (TODO) Windows 22, i7-2222 @ 2.22GHz 22GB, GTX 222 222MB (Moore 2222 Lab)
---

### (TODO: Your README)

*DO NOT* leave the README to the last minute! It is a crucial part of the
project, and we will not be able to grade you without a good README.
<p align="center"><img src="img/test.gif" width=500>
</p>

<!--#### Nadine Adnane-->
<!-- * [LinkedIn](https://www.linkedin.com/in/nadnane/)-->

<!--* tested on a laptop with the following specs: -->

<!--* **Machine:** ASUS ROG Zephyrus M16 -->
<!--* **OS:** Windows 11-->
<!--* **Processor:** 12th Gen Intel(R) Core(TM) i9-12900H, 2500 Mhz, 14 Core(s), 20 Logical Processor(s) -->
<!--* **GPU:** NVIDIA GeForce RTX 3070 Ti Laptop GPU-->

## Introduction
This project is my first exploration of the Vulkan API. Vulkan is a graphics and compute API that allows for high-efficiency, cross-platform access to GPUs. Vulkan is currently the industry's only open standard modern GPU API, allowing developers to write applications that are portable to a wide variety of platforms, and it includes the latest graphics technologies such as ray tracing.

In this project, I set out to implement a grass simulator and renderer using Vulkan. The basic idea is to use compute shaders to perform physics calculations on Bezier curves, which are used to represent the individual grass blades of the final scene. 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) by Klemens Jahrmann and Michael Wimmer. Culling and tessellation techniques are employed to optimize the performance of the simulation. Culling is performed on grass blades which are not visible to the camera and thus would be a waste of resources to render. The rest of the blades are rendered by the graphics pipeline, complete with vertex, fragment and tessellation shaders. The vertex shader transforms Bezier control points, the tessellation shaders dynamically create the grass geometry from the Bezier curves, and the fragment shader is used to shade the grass blades.

## Build Instructions
<details><summary>
Click here for details on how to build and run this project.
</summary>
<br></br>

This project was developed using Vulkan SDK version 1.3.296 and Visual Studio 2019, and was tested on a laptop with the following specs:

* **Machine:** ASUS ROG Zephyrus M16
* **OS:** Windows 11
* **Processor:** 12th Gen Intel(R) Core(TM) i9-12900H, 2500 Mhz, 14 Core(s), 20 Logical Processor(s)
* **GPU:** NVIDIA GeForce RTX 3070 Ti Laptop GPU

The project can be built using CMake 3.30.3.
1. Set the source code path to the root folder of the project, "Project5-Vulkan-Grass-Rendering".
2. Then, create a new folder in the root folder of the project called "build".
3. Set the build path to the folder you've just created, "Project5-Vulkan-Grass-Rendering/build"
<p align="center"><img src="img/cmake.png" width=500>
</p>
4. Press "Configure". Select "Visual Studio 16 2019" as the project generator. Then, press "Finish":
<p align="center"><img src="img/cmake2.png" width=500>
</p>

5. Press "Generate". If CMake was successful, you should now see a Visual Studio project, "cis565_project5_vulkan_grass_rendering.sln" in the build folder you created.
<p align="center"><img src="img/cmake3.png" width=300>
</p>
6. Open the project in Visual Studio 2019.
7. In the Solution Explorer, set "vulkan_grass_rendering" as the startup project by right-clicking the project and selecting "Set as Startup Project", as shown below:
<p align="center"><img src="img/visual_studio.png" width=600>
</p>
8. Set the build mode to "Release" and press the green arrow to run the project.
<p align="center"><img src="img/visual_studio2.png" width=300>
</p>


</details>


## Grass Rendering using Bezier Curves
<p align="center"><img src="img/blade_model.jpg" width=300>

Grass blades are modeled as Bezier curves, defined by three control points: v0, v1, and v2. v0 anchors the base of the blade to the ground, v1 influences the blade's curvature above v0, and v2 determines the tip, enabling physics-based transformations like bending from forces such as wind or gravity. Each blade also has attributes like orientation, height, width, up vector, and stiffness, which are stored in four vec4 values. These attributes control the blade’s size, direction, structural integrity, and responsiveness to external forces.

For rendering, each blade is treated as a 2D object in 3D space. The blade's shape is determined by interpolating the control points along a Bezier curve, with vertices calculated using De Casteljau’s algorithm (Jahrmann & Wimmer). This ensures the blade is smoothly aligned to the curve, allowing for realistic movement and behavior in response to dynamic forces.

## Force Simulation
* Gravity
* Gravity combines environmental gravity (scene-wide downward pull) and front-facing gravity (blade-specific).
* Effect: Blades are pulled downward, causing them to squash towards the ground.
* Recovery
* Recovery, based on Hooke's Law, counteracts deformation and restores blades to their initial position.
* Effect: Blades return to their original shape, maintaining structural integrity.
* Wind
* Wind is calculated using a heuristic based on blade position and time, creating a swaying effect.
* Effect: Blades sway depending on wind strength, direction, and alignment. Straighter blades are more affected.

## Culling
Although forces are simulated on every grass blade each frame, many blades don’t need to be rendered due to various factors. Culling optimizes performance by removing non-contributing blades from the render pipeline. Three main culling techniques are implemented in the compute shader:

* #### Orientation culling
* Blades perpendicular to the view vector are culled, as they would appear too thin and create visual artifacts.
* Effect: The thinnest blades are culled based on the camera's perspective.

* #### View-frustum culling
* Blades entirely outside the camera’s view are discarded, based on the visibility of control points (v0, v2) and the midpoint (m).
* Effect: Blades at the edges of the screen space are culled. This threshold is adjustable, ensuring that only visible blades are rendered.

* #### Distance culling
* Blades far from the camera are culled to prevent rendering details that are indistinguishable at a distance.
* Effect: Blades are culled based on their distance from the camera, with adjustable, discrete levels to control which blades are rendered.

## Tessellation
In this project, I also implemented distance-based LOD tessellation. Based on how close a blade of grass is to the camera (within the specified thresholds), the blades are rendered at different levels of detail. Blades which are closer to the camera are rendered with a higher level of detail, while those that are farther away are rendered with a lower level of detail.

## Performance Analysis

### Number of Blades vs. FPS
<p align="center"><img src="img/graph1.png" width=800>

The graph above shows the relationship between the number of grass blades in the scene and the frame rate (frames per second). As expected, as the grass blade count increases, the FPS drops. The effect is most significant as the number of blades becomes very large. Starting out with a blade count 2^6, the FPS is consistently in the 3000s, dropping slightly lower as the blade count is increased to 2^8. The performance hit becomes more drastic as the blade count increases to 2^14 and beyond. At the least, this demonstrates the need for additional techniques to boost the performance of our simulator so that it can handle higher blade counts with ease.

### Culling vs. FPS
<p align="center"><img src="img/graph2.png" width=800>

The graph above showcases the performance boosts granted by the inclusion of grass blade culling in my implementation. For each of the tests, a blade count of 2^15 was used.

As we can see, without any culling (or tessellation), the FPS was consistently around 110.
With only frustum culling, the FPS increased to 180.
Then with distance-based culling, the performance displays a significant boost, with an FPS of 240.
Finally, with all culling options enabled, we can see that the FPS increases to around 700.

This graph demonstrates the significance of the performance boost awarded by the inclusion of various culling techniques.

<!--### Tessellation vs. FPS
<p align="center"><img src="img/graph3.png" width=800>

The graph above displays the effect that tessellation has on the simulator.-->

## Bloopers, Extras, & Final Thoughts
Overall, I really enjoyed implementing this project! I think it was a good way to get my feet wet in terms of working with the Vulkan API for the first time. It was also nice to have the paper as a guide for the implementation.

## Meet the Dev! :wave:
<p align="center">
<img src="img/nadine.png" width=200px>

<p align="center">Hi, I'm Nadine! :)</br>
Questions? Comments? Just want to say hi back?</br>
Contact me here: </br>
<a href="mailto:nadnane@seas.upenn.edu">Email </a> |
<a href="https://www.linkedin.com/in/nadnane/"> LinkedIn </a>
</p>

## References
* [Vulkan.org](https://www.vulkan.org/)
* [NVIDIA Developer - Vulkan](https://developer.nvidia.com/vulkan)
* [IBM - Open Standard vs. Open Source](https://www.ibm.com/think/topics/open-standards-vs-open-source-explanation)
* [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)
* [Official Vulkan documentation](https://www.khronos.org/registry/vulkan/)
* [Tessellation tutorial](https://ogldev.org/www/tutorial30/tutorial30.html)
Binary file added img/cmake.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img/cmake2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img/cmake3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img/graph1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img/graph2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img/init.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img/nadine.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img/test.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img/visual_studio.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img/visual_studio2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion src/Blades.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ 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);
}

Expand Down
2 changes: 1 addition & 1 deletion src/Blades.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#include <array>
#include "Model.h"

constexpr static unsigned int NUM_BLADES = 1 << 13;
constexpr static unsigned int NUM_BLADES = 1 << 15;
constexpr static float MIN_HEIGHT = 1.3f;
constexpr static float MAX_HEIGHT = 2.5f;
constexpr static float MIN_WIDTH = 0.1f;
Expand Down
Loading