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
85 changes: 78 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,83 @@
Vulkan Grass Rendering
==================================
# 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)
- Yu Jiang
- Tested on: Windows 11, Ultra 7 155H @ 3.80 GHz, 32GB RAM, RTX 4060 8192MB (Personal Laptop)

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

*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.
![](img/finalresult.gif)

### Overview

This project is an implementation of [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) in Vulkan, including features of :

- Rendering grass realtimely in vulkan using Bezier curves.
- Simulating **Gravity, Recovery, and Wind** forces in Vulkan compute shaders.
- Dynamic culling blades by **Orientation, Distance and View Frustrum** in Vulkan compute shaders.
- Dynamic LOD by controlling tessellation levels in Vulkan tessellation shaders.

### Features

#### 1. Basic Render without physics

![](img/basic_nophysics_noculling.png)

Implemented the Vulkan render pipeline and the tessellation evaluation shader to compute the generated vertex position according to the paper. I used triangle shape grass in the implementation.

#### 2. Add simulation of forces

![](img/bacisphysics.gif)

Add gravity, recovery and wind forces to the grass. For the wind part I used a simplex noise wind field, the noise function is found from [iq](https://www.shadertoy.com/view/Msf3WH).

#### 3. Orientation Culling

![](img/orientationculling.gif)

This is the effect of orientation culling with `angle_threshold = 0.9`, blades which parallel with the view vector will be culled to ensure very thin blades will not be drawn.

#### 4. View Frustrum Culling

![](img/furstrumculling.gif)

This is the effect of View Frustrum culling, to make the culling visible in the viewport, I used a negative `viewCullingTolerance = -0.1f` to show the results.

#### 5. Distance Culling

![](img/distanceculling.gif)

As camera moves away from the blades, the density of blades will decrease gradually. This is impleneted with deviding distance into 8 buckets and removing different number of blade indices in different buckets.

#### 6. LOD

![](img/LOD.gif)

In this implementation, I changed the value of `gl_TessLevelInner` and `gl_TessLevelOuter` to control the LOD level. In the gif, I used `maxLevelV = 16` and `minLevelV = 2`; `maxLevelH = 4` and `minLevelH = 1`. The levels are computed by `int(round(mix(maxLevelV, minLevelV, clamp(dist / maxDistance, 0.f, 1.f))))` where `dist` is the distance from blade to camera and `maxDistance` is 32 here.

#### 7. Rendering

At last I change the fragment shader and pipeline settings: I used blinn-phong model with varying `ks` based on `uv.y` to make a specular highlight on the grass blades, and I changed the clear color to `{ 0.5f, 0.635f, 0.743f, 1.0f }` to create a blue sky. After that we get the result at the beginning.

### Performance Analysis

#### Performance with Number of Blades

To test performance with number of blades, I fixed resolution to 1600\*1200, and fixed the size of plane with `planeDim = 120.f`, then only change `NUM_BLADES`. And the figure is shown below:

![](img/performance1.png)

Except when number of blades < 2^15 where CPU is somewhat bounded, the fps decreases linearly with the number of blades in the curve. This is the expected result.

#### Performance with Different Optimizations

I tested the fps with different optimizations using 2^19 number of blades, the result is shown below:

![](img/performance2.png)

From the graph we can figure out that Orientation Culling is more about "cutting out blades with alias", so it gives relatively smaller performance gain. While View Frustrum Culling and Distance Culling improves really a lot on FPS, because they culled really many of the blades outside the screen or far away. Combining all three cullings makes the program run 11 times faster than before, which is a really huge improvement! And LOD also introduces about 20% performance gain without significant visual loss.

### References

- [Simplex Noise from iq](https://www.shadertoy.com/view/Msf3WH)
Binary file modified bin/Release/vulkan_grass_rendering.exe
Binary file not shown.
Binary file added img/LOD.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/bacisphysics.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/basic_nophysics_noculling.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/data.xlsx
Binary file not shown.
Binary file added img/distanceculling.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/final.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/finalresult.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/furstrumculling.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/orientationculling.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/performance1.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/performance2.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 << 17;
constexpr static float MIN_HEIGHT = 1.3f;
constexpr static float MAX_HEIGHT = 2.5f;
constexpr static float MIN_WIDTH = 0.1f;
Expand Down
2 changes: 2 additions & 0 deletions src/Camera.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Camera::Camera(Device* device, float aspectRatio) : device(device) {
r = 10.0f;
theta = 0.0f;
phi = 0.0f;
cameraBufferObject.camPos = glm::vec3(0.0f, 1.0f, 10.0f);
cameraBufferObject.viewMatrix = glm::lookAt(glm::vec3(0.0f, 1.0f, 10.0f), glm::vec3(0.0f, 1.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
cameraBufferObject.projectionMatrix = glm::perspective(glm::radians(45.0f), aspectRatio, 0.1f, 100.0f);
cameraBufferObject.projectionMatrix[1][1] *= -1; // y-coordinate is flipped
Expand All @@ -36,6 +37,7 @@ void Camera::UpdateOrbit(float deltaX, float deltaY, float deltaZ) {
glm::mat4 rotation = glm::rotate(glm::mat4(1.0f), radTheta, glm::vec3(0.0f, 1.0f, 0.0f)) * glm::rotate(glm::mat4(1.0f), radPhi, glm::vec3(1.0f, 0.0f, 0.0f));
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.camPos = glm::vec3(finalTransform[3]);
cameraBufferObject.viewMatrix = glm::inverse(finalTransform);

memcpy(mappedData, &cameraBufferObject, sizeof(CameraBufferObject));
Expand Down
1 change: 1 addition & 0 deletions src/Camera.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
struct CameraBufferObject {
glm::mat4 viewMatrix;
glm::mat4 projectionMatrix;
glm::vec3 camPos;
};

class Camera {
Expand Down
18 changes: 16 additions & 2 deletions src/Instance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,18 @@ void Instance::PickPhysicalDevice(std::vector<const char*> deviceExtensions, Que
vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data());

// Evaluate each GPU and check if it is suitable

auto score = [](VkPhysicalDevice dev)->int {
VkPhysicalDeviceProperties props{};
vkGetPhysicalDeviceProperties(dev, &props);
switch (props.deviceType) {
case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: return 3;
case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: return 2;
default: return 1;
}
};
int best = -1;

for (const auto& device : devices) {
bool queueSupport = true;
queueFamilyIndices = checkDeviceQueueSupport(device, requiredQueues, surface);
Expand Down Expand Up @@ -281,12 +293,14 @@ void Instance::PickPhysicalDevice(std::vector<const char*> deviceExtensions, Que
}
}

if (queueSupport &&
int sc = score(device);
if (sc > best && queueSupport &&
checkDeviceExtensionSupport(device, deviceExtensions) &&
(!requiredQueues[QueueFlags::Present] || (!surfaceFormats.empty() && ! presentModes.empty()))
) {
best = sc;
physicalDevice = device;
break;
//break;
}
}

Expand Down
Loading