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
562 changes: 562 additions & 0 deletions .gitignore

Large diffs are not rendered by default.

92 changes: 87 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,92 @@ Vulkan Grass Rendering

**University of Pennsylvania, CIS 565: GPU Programming and Architecture, Project 5**

* (TODO) YOUR NAME HERE
* Tested on: (TODO) Windows 22, i7-2222 @ 2.22GHz 22GB, GTX 222 222MB (Moore 2222 Lab)
* Han Yang
* [LinkedIn](https://www.linkedin.com/in/han-yang-0031231a3/), [personal website](https://bdwhst.wixsite.com/portfolio), etc.
* Tested on: Windows 11, i9-12900HX @ 2.30GHz 16GB, RTX4080 laptop 12GB

### (TODO: Your README)
## Final Result

![](./img/grass-finished.gif)

## Principles and Analysis

In our model, each individual blade of grass is simulated using a dedicated GPU thread, ensuring a rich, dynamic appearance to the grassy landscape.

### Geometry

The geometry of each blade is foundational to its visual representation:

- **Control Points:** Every blade of grass is constructed using three pivotal control points. These points define the shape and curvature of the blade.

- **Tessellation:**
- Leveraging tessellation, we dynamically generate a multitude of discrete points.
- These points are derived from the bezier curve established by the control points, ensuring the blade's realistic, curved appearance.
- The tessellated structure grants the flexibility of having varying levels of detail, potentially boosting performance without significantly compromising visual fidelity.

### Simulation

Our grass simulation, essential for a lifelike depiction, is grounded in three primary forces:

1. **Gravity Force:**
- An omnipresent downward force, gravity ensures each blade has a natural 'droop', reflecting the real-world pull.
- The strength of the gravity force can be modulated to simulate conditions like a waterlogged field or a dry, arid landscape.
2. **Wind Force:**
- This dynamic force introduces motion, causing the grass to sway and rustle.
- By altering the direction and magnitude of the wind force, various atmospheric conditions, from a gentle breeze to a raging storm, can be emulated.
- Here I used Perlin noise to simulate the wind field
3. **Recovery Force:**
- After any perturbation, such as a strong gust of wind, the recovery force works to restore the grass blade to its original stance.
- It ensures that after any disturbance, the grass doesn't remain permanently deformed.

After we computed the new position by using these forces, we need a validation stage to ensure we get a robust and valid simulation for the control points.

### Culling

Culling is a crucial optimization technique, ensuring only relevant blades are rendered:

#### Direction Culling

- **Parallel Check:** If a blade's orientation is almost parallel to the viewer's line of sight, it's likely to contribute negligibly to the scene.
- Such blades are culled to improve performance.

![](./img/direction_culling.gif)

#### View Frustum Culling

- **Visibility Check:** The viewer's field of vision, or frustum, is a defined volume. Blades of grass outside this volume are not visible.
- By checking a few key points on each blade against this volume, we can determine its visibility.
- Blades found outside are culled, ensuring computational resources are focused only on visible entities.

![](./img/viewfrustrum_culling.gif)

#### Distance Culling

- **Proximity Based:** Beyond a certain distance, blades become virtually indistinguishable to the viewer.
- We set a threshold distance, beyond which blades are culled.
- **Fading Mechanism:**
- Instead of abruptly culling blades at the threshold, a fading mechanism is used.
- We cull a percentage of blades according to their projected distance to the camera.

![](./img/dist_culling.gif)

### Distance Based Tessellation

We set the tessellation level of the blade according to its distance to the camera, the further it gets, the less tessellation level will it be.

![](./img/dist_based_tess.png)

### Performance

We tested the program under different conditions, and recorded average FPS for each case. Here we set max tessellation level to 10, min tessellation level to 2, and resolution to 800x800.

| Condition \ Num of Blades | 1<<14 | 1<<15 | 1<<16 | 1<<17 | 1<<18 | 1<<19 |
| -------------------------------------------- | ----- | ----- | ----- | ----- | ----- | ----- |
| With culling and distance based tessellation | 3334 | 2132 | 1124 | 598 | 311 | 160 |
| With culling | 2720 | 1461 | 779 | 375 | 183 | 111 |
| Naïve | 1107 | 542 | 289 | 141 | 74 | 38 |

![](./img/perf.png)

We can see that both culling and distance based tessellation can greatly improve our program's FPS. The improvement of culling is roughly 150%, and the improvement of distance based tessellation is roughly 40%.

*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.
Binary file removed bin/Release/vulkan_grass_rendering.exe
Binary file not shown.
Binary file added img/direction_culling.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/dist_based_tess.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/dist_culling.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/grass-finished.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/perf.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/viewfrustrum_culling.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 8 additions & 2 deletions src/Blades.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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::CreateBufferFromData(device, commandPool, &indirectDraw, sizeof(BladeDrawIndirect), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT, numBladesBuffer, numBladesBufferMemory);
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::CreateBuffer(device, sizeof(BladeDrawIndirect), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, numBladesBuffer, numBladesBufferMemory);
BufferUtils::CopyToHostVisibleMemory(device, &indirectDraw, numBladesBufferMemory, sizeof(indirectDraw));
}

VkBuffer Blades::GetBladesBuffer() const {
Expand All @@ -61,6 +62,11 @@ VkBuffer Blades::GetNumBladesBuffer() const {
return numBladesBuffer;
}

VkDeviceMemory Blades::GetNumBladesMemory() const
{
return numBladesBufferMemory;
}

Blades::~Blades() {
vkDestroyBuffer(device->GetVkDevice(), bladesBuffer, nullptr);
vkFreeMemory(device->GetVkDevice(), bladesBufferMemory, nullptr);
Expand Down
16 changes: 13 additions & 3 deletions src/Blades.h
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
#pragma once
#include <vulkan/vulkan.h>
#define GLM_FORCE_DEFAULT_ALIGNED_GENTYPES
#include <glm/glm.hpp>
#include <array>
#include "Model.h"

constexpr static unsigned int NUM_BLADES = 1 << 13;
constexpr static unsigned int NUM_BLADES = 1 << 16;
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_WIDTH = 0.08f;
constexpr static float MAX_WIDTH = 0.1f;
constexpr static float MIN_BEND = 7.0f;
constexpr static float MAX_BEND = 13.0f;

Expand Down Expand Up @@ -69,6 +70,14 @@ struct BladeDrawIndirect {
uint32_t firstInstance;
};

struct ComputePushConstant {
glm::vec4 G;
glm::vec4 wind_Params;//time frequency, position freq,
int numBlades;
float maxCullDist;
int numCullLevels;
};

class Blades : public Model {
private:
VkBuffer bladesBuffer;
Expand All @@ -84,5 +93,6 @@ class Blades : public Model {
VkBuffer GetBladesBuffer() const;
VkBuffer GetCulledBladesBuffer() const;
VkBuffer GetNumBladesBuffer() const;
VkDeviceMemory GetNumBladesMemory() const;
~Blades();
};
8 changes: 8 additions & 0 deletions src/BufferUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,3 +90,11 @@ void BufferUtils::CreateBufferFromData(Device* device, VkCommandPool commandPool
vkDestroyBuffer(device->GetVkDevice(), stagingBuffer, nullptr);
vkFreeMemory(device->GetVkDevice(), stagingBufferMemory, nullptr);
}

void BufferUtils::CopyToHostVisibleMemory(Device* device, void* srcData, VkDeviceMemory dstMemory, VkDeviceSize size)
{
void* data;
vkMapMemory(device->GetVkDevice(), dstMemory, 0, size, 0, &data);
memcpy(data, srcData, static_cast<size_t>(size));
vkUnmapMemory(device->GetVkDevice(), dstMemory);
}
1 change: 1 addition & 0 deletions src/BufferUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ namespace BufferUtils {
void CreateBuffer(Device* device, VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory);
void CopyBuffer(Device* device, VkCommandPool commandPool, VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size);
void CreateBufferFromData(Device* device, VkCommandPool commandPool, void* bufferData, VkDeviceSize bufferSize, VkBufferUsageFlags bufferUsage, VkBuffer& buffer, VkDeviceMemory& bufferMemory);
void CopyToHostVisibleMemory(Device* device, void* srcData, VkDeviceMemory dstMemory, VkDeviceSize size);
}
1 change: 0 additions & 1 deletion src/Camera.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ void Camera::UpdateOrbit(float deltaX, float deltaY, float deltaZ) {
glm::mat4 finalTransform = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f)) * rotation * glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 1.0f, r));

cameraBufferObject.viewMatrix = glm::inverse(finalTransform);

memcpy(mappedData, &cameraBufferObject, sizeof(CameraBufferObject));
}

Expand Down
2 changes: 1 addition & 1 deletion src/Camera.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@

#pragma once

#define GLM_FORCE_DEFAULT_ALIGNED_GENTYPES
#include <glm/glm.hpp>
#include "Device.h"

Expand Down
Loading