Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Project 5: Zhen Ren #11

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
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
564 changes: 564 additions & 0 deletions .gitignore

Large diffs are not rendered by default.

33 changes: 28 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,33 @@ 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)
* Zhen Ren
* https://www.linkedin.com/in/zhen-ren-837089208/
* Tested on: Windows 11, i9-13900H @ 2.60 GHz 16GB, RTX 4070 Laptop 8GB (Self laptop)

### (TODO: Your README)
![](./img/result.gif)

*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.
### Features
- Grass update and culling using compute shader
- Grass shape generation using tessellation shader
- Grass LOD
- Grass shading

### LOD
![](./img/tess.png)
Use distance based lod to make distant grass have less vertices.

### Grass Highligh
In order to make specular light on grass, I bend the surface normal a little bit. This makes shading result more realistic.
![](./img/normal.png)
![](./img/spec.png)

### Performance

#### Grass Count
![](./img/grassCnt.png)
From the graph, we can find that the frame time generally increases linearly, which is expected.

#### Culling Method
![](./img/culling.png)
The performance of different culling methods depends on the number of grass it can cull out. From the graph, we can see that direction culling and frustum culling have a significant effect. Since most of the grass is within the culling distance, so distance culling is not helpful in this case.
Binary file modified bin/Release/vulkan_grass_rendering.exe
Binary file not shown.
Binary file added img/culling.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/grassCnt.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/normal.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/result.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/spec.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/tess.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
114 changes: 107 additions & 7 deletions src/Blades.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,65 @@
#include "Blades.h"
#include "BufferUtils.h"

float generateRandomFloat() {
return rand() / (float)RAND_MAX;
static std::array<glm::vec2, 15> bladeVertexData =
{
glm::vec2(0.f, 0.f),
glm::vec2(1.f, 0.f),
glm::vec2(0.f, 0.2f),
glm::vec2(1.f, 0.2f),
glm::vec2(0.f, 0.4f),
glm::vec2(1.f, 0.4f),
glm::vec2(0.f, 0.55f),
glm::vec2(1.f, 0.55f),
glm::vec2(0.f, 0.7f),
glm::vec2(1.f, 0.7f),
glm::vec2(0.f, 0.8f),
glm::vec2(1.f, 0.8f),
glm::vec2(0.f, 0.9f),
glm::vec2(1.f, 0.9f),
glm::vec2(0.5f, 1.f),
};

static std::array<uint32_t, 39> bladeIndexData =
{
0, 1, 2,
1, 3, 2,
2, 3, 4,
3, 5, 4,
4, 5, 6,
5, 7, 6,
6, 7, 8,
7, 9, 8,
8, 9, 10,
9, 11, 10,
10, 11, 12,
11, 13, 12,
12, 13, 14
};

glm::vec4 getNearestClumpGrid(glm::vec2 position) {
glm::ivec2 clumpGridID = glm::floor(position / ClumpGridSize);
float minDist = 1000000.0f;
glm::vec4 out;
for (int i = -1; i <= 1; i++) {
for (int j = -1; j <= 1; j++) {
glm::vec2 currGrid = clumpGridID + glm::ivec2(i, j);
glm::vec2 gridOffset = hash22(currGrid) * ClumpGridSize;
glm::vec2 gridCenter = currGrid * ClumpGridSize + gridOffset;
float dist = glm::distance(position, gridCenter);
if (dist < minDist) {
minDist = dist;
out = glm::vec4(currGrid, gridCenter);
}
}
}
return out;
}

Blades::Blades(Device* device, VkCommandPool commandPool, float planeDim) : Model(device, commandPool, {}, {}) {
#define USE_CLUMP 1


Blades::Blades(Device* device, VkCommandPool commandPool, float planeDim, glm::vec3 offset) : Model(device, commandPool, {}, {}) {
std::vector<Blade> blades;
blades.reserve(NUM_BLADES);

Expand All @@ -21,10 +75,30 @@ Blades::Blades(Device* device, VkCommandPool commandPool, float planeDim) : Mode
float z = (generateRandomFloat() - 0.5f) * planeDim;
float direction = generateRandomFloat() * 2.f * 3.14159265f;
glm::vec3 bladePosition(x, y, z);
bladePosition += offset;
glm::vec2 bladeXZPosition(bladePosition.x, bladePosition.z);
glm::vec4 clumpData = getNearestClumpGrid(bladeXZPosition);
float distToCenter = glm::distance(bladeXZPosition, glm::vec2(clumpData.z, clumpData.w));

#if USE_CLUMP
// shift to clump center a little bit
bladeXZPosition = glm::mix(bladeXZPosition, glm::vec2(clumpData.z, clumpData.w), 0.01f);
bladePosition.x = bladeXZPosition.x;
bladePosition.z = bladeXZPosition.y;
bladePosition.y += terrainHeight(bladeXZPosition);

// face to the same direction
float clumpDir = hash32(glm::vec3(clumpData.x, clumpData.y, 0.6f)).x * 2.f * 3.14159265f;
direction = glm::mix(direction, clumpDir, 0.6f);

// face off to center
float offCenterDir = std::atan2(bladePosition.z - clumpData.w, bladePosition.x - clumpData.z) + 0.5f * 3.14159265f;
direction = glm::mix(direction, offCenterDir, 0.4f);
#endif
currentBlade.v0 = glm::vec4(bladePosition, direction);

// Bezier point and height (v1)
float height = MIN_HEIGHT + (generateRandomFloat() * (MAX_HEIGHT - MIN_HEIGHT));
float height = MIN_HEIGHT + (generateRandomFloat() * (MAX_HEIGHT - MIN_HEIGHT)) + 6.f * glm::exp(-0.3f * distToCenter);
currentBlade.v1 = glm::vec4(bladePosition + bladeUp * height, height);

// Physical model guide and width (v2)
Expand All @@ -39,13 +113,18 @@ Blades::Blades(Device* device, VkCommandPool commandPool, float planeDim) : Mode
}

BladeDrawIndirect indirectDraw;
indirectDraw.vertexCount = NUM_BLADES;
/*indirectDraw.vertexCount = NUM_BLADES;
indirectDraw.instanceCount = 1;
indirectDraw.firstVertex = 0;
indirectDraw.firstInstance = 0;
indirectDraw.firstInstance = 0;*/
indirectDraw.indexCount = bladeIndexData.size();
indirectDraw.instanceCount = NUM_BLADES;
indirectDraw.firstIndex = 0;
indirectDraw.vertexOffset = 0;
indirectDraw.firstInstance = 0;

BufferUtils::CreateBufferFromData(device, commandPool, blades.data(), NUM_BLADES * sizeof(Blade), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, bladesBuffer, bladesBufferMemory);
BufferUtils::CreateBuffer(device, NUM_BLADES * sizeof(Blade), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, culledBladesBuffer, culledBladesBufferMemory);
BufferUtils::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 All @@ -69,3 +148,24 @@ Blades::~Blades() {
vkDestroyBuffer(device->GetVkDevice(), numBladesBuffer, nullptr);
vkFreeMemory(device->GetVkDevice(), numBladesBufferMemory, nullptr);
}

VkBuffer Blade::bladeVertexBuffer = 0;
VkBuffer Blade::bladeIndexBuffer = 0;
VkDeviceMemory Blade::bladeVertexBufferMemory = 0;
VkDeviceMemory Blade::bladeIndexBufferMemory = 0;

void Blade::CreateBladeVertexIndexBuffer(Device* device, VkCommandPool commandPool)
{
BufferUtils::CreateBufferFromData(device, commandPool, bladeVertexData.data(),
bladeVertexData.size() * sizeof(glm::vec2), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, Blade::bladeVertexBuffer, Blade::bladeVertexBufferMemory);
BufferUtils::CreateBufferFromData(device, commandPool, bladeIndexData.data(),
bladeIndexData.size() * sizeof(uint32_t), VK_BUFFER_USAGE_INDEX_BUFFER_BIT, Blade::bladeIndexBuffer, Blade::bladeIndexBufferMemory);
}

void Blade::DestroyBladeVertexIndexBuffer(Device* device)
{
vkDestroyBuffer(device->GetVkDevice(), bladeVertexBuffer, nullptr);
vkFreeMemory(device->GetVkDevice(), bladeVertexBufferMemory, nullptr);
vkDestroyBuffer(device->GetVkDevice(), bladeIndexBuffer, nullptr);
vkFreeMemory(device->GetVkDevice(), bladeIndexBufferMemory, nullptr);
}
46 changes: 34 additions & 12 deletions src/Blades.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@
#include <array>
#include "Model.h"

constexpr static unsigned int NUM_BLADES = 1 << 13;
constexpr static float MIN_HEIGHT = 1.3f;
constexpr static float MAX_HEIGHT = 2.5f;
constexpr static float MIN_WIDTH = 0.1f;
constexpr static float MAX_WIDTH = 0.14f;
constexpr static float MIN_BEND = 7.0f;
constexpr static float MAX_BEND = 13.0f;
constexpr static unsigned int NUM_BLADES = 1 << 8;
constexpr static float MIN_HEIGHT = 6.3f;
constexpr static float MAX_HEIGHT = 8.5f;
constexpr static float MIN_WIDTH = 0.28f;
constexpr static float MAX_WIDTH = 0.32f;
constexpr static float MIN_BEND = 1.5f;
constexpr static float MAX_BEND = 2.5f;

constexpr static float ClumpGridSize = 10.0f;


struct Blade {
// Position and direction
Expand Down Expand Up @@ -60,13 +63,32 @@ struct Blade {

return attributeDescriptions;
}

static VkBuffer bladeVertexBuffer;
static VkBuffer bladeIndexBuffer;

static VkDeviceMemory bladeVertexBufferMemory;
static VkDeviceMemory bladeIndexBufferMemory;

static void CreateBladeVertexIndexBuffer(Device* device, VkCommandPool commandPool);
static void DestroyBladeVertexIndexBuffer(Device* device);
static VkBuffer GetBladeVertexBuffer() { return bladeVertexBuffer; }
static VkBuffer GetBladeIndexBuffer() { return bladeIndexBuffer; }
};

//struct BladeDrawIndirect {
// uint32_t vertexCount;
// uint32_t instanceCount;
// uint32_t firstVertex;
// uint32_t firstInstance;
//};

struct BladeDrawIndirect {
uint32_t vertexCount;
uint32_t instanceCount;
uint32_t firstVertex;
uint32_t firstInstance;
uint32_t indexCount;
uint32_t instanceCount;
uint32_t firstIndex;
int32_t vertexOffset;
uint32_t firstInstance;
};

class Blades : public Model {
Expand All @@ -80,7 +102,7 @@ class Blades : public Model {
VkDeviceMemory numBladesBufferMemory;

public:
Blades(Device* device, VkCommandPool commandPool, float planeDim);
Blades(Device* device, VkCommandPool commandPool, float planeDim, glm::vec3 offset = glm::vec3(0));
VkBuffer GetBladesBuffer() const;
VkBuffer GetCulledBladesBuffer() const;
VkBuffer GetNumBladesBuffer() const;
Expand Down
4 changes: 3 additions & 1 deletion src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ file(GLOB IMAGES
${CMAKE_CURRENT_SOURCE_DIR}/images/*.jpg
${CMAKE_CURRENT_SOURCE_DIR}/images/*.png
${CMAKE_CURRENT_SOURCE_DIR}/images/*.bmp
${CMAKE_CURRENT_SOURCE_DIR}/images/*.obj
)

foreach(IMAGE ${IMAGES})
Expand Down Expand Up @@ -54,4 +55,5 @@ target_include_directories(vulkan_grass_rendering PRIVATE
${STB_INCLUDE_DIR}
)

InternalTarget("" vulkan_grass_rendering)
InternalTarget("Vulkan Grass Rendering" vulkan_grass_rendering)

42 changes: 36 additions & 6 deletions src/Camera.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,21 @@

Camera::Camera(Device* device, float aspectRatio) : device(device) {
r = 10.0f;
theta = 0.0f;
theta = 00.0f;
phi = 0.0f;
cameraBufferObject.viewMatrix = glm::lookAt(glm::vec3(0.0f, 1.0f, 10.0f), glm::vec3(0.0f, 1.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
cameraBufferObject.projectionMatrix = glm::perspective(glm::radians(45.0f), aspectRatio, 0.1f, 100.0f);
glm::vec3 eye = glm::vec3(0.0f, 30.0f, 50.0f);
center = glm::vec3(100.0f, 10.0f, 100.0f);
r = glm::distance(eye, center);
theta = glm::degrees(glm::atan((eye.z - center.z) / (eye.x - center.x))) + 180;
phi = -glm::degrees(glm::asin((eye.y - center.y) / r));

cameraBufferObject.viewMatrix = glm::lookAt(eye, center, glm::vec3(0.0f, 1.0f, 0.0f));
cameraBufferObject.projectionMatrix = glm::perspective(glm::radians(45.0f), aspectRatio, 0.1f, 500.0f);
cameraBufferObject.projectionMatrix[1][1] *= -1; // y-coordinate is flipped
cameraBufferObject.viewMatrixInverse = glm::inverse(cameraBufferObject.viewMatrix);
//cameraBufferObject.projectionMatrixInverse = glm::inverse(glm::perspective(glm::radians(45.0f), aspectRatio, 0.1f, 500.0f));
cameraBufferObject.projectionMatrixInverse = glm::inverse(cameraBufferObject.projectionMatrix);
cameraBufferObject.eye = glm::vec4(eye, 1.f);

BufferUtils::CreateBuffer(device, sizeof(CameraBufferObject), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, buffer, bufferMemory);
vkMapMemory(device->GetVkDevice(), bufferMemory, 0, sizeof(CameraBufferObject), 0, &mappedData);
Expand All @@ -28,19 +38,39 @@ VkBuffer Camera::GetBuffer() const {
void Camera::UpdateOrbit(float deltaX, float deltaY, float deltaZ) {
theta += deltaX;
phi += deltaY;
r = glm::clamp(r - deltaZ, 1.0f, 50.0f);
r = glm::clamp(r - deltaZ, 1.0f, 400.0f);

float radTheta = glm::radians(theta);
float radPhi = glm::radians(phi);

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));

glm::mat4 finalTransform = glm::translate(glm::mat4(1.0f), center) *
rotation * glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, r));
cameraBufferObject.eye = finalTransform[3];
cameraBufferObject.viewMatrix = glm::inverse(finalTransform);
cameraBufferObject.viewMatrixInverse = glm::inverse(cameraBufferObject.viewMatrix);

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

void Camera::UpdatePosition(float deltaX, float deltaY, float deltaZ)
{
glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f);
glm::vec3 forward = glm::normalize(center - glm::vec3(cameraBufferObject.eye));
glm::vec3 right = glm::normalize(glm::cross(forward, up));
up = glm::cross(right, forward);

glm::vec3 eye = glm::vec3(cameraBufferObject.eye);
glm::vec3 offset = deltaX * right + deltaY * up;;
eye += offset;
center += offset;
cameraBufferObject.eye = glm::vec4(eye, 1.0f);
cameraBufferObject.viewMatrix = glm::lookAt(eye, center, glm::vec3(0.0f, 1.0f, 0.0f));
cameraBufferObject.viewMatrixInverse = glm::inverse(cameraBufferObject.viewMatrix);

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

Camera::~Camera() {
vkUnmapMemory(device->GetVkDevice(), bufferMemory);
vkDestroyBuffer(device->GetVkDevice(), buffer, nullptr);
Expand Down
5 changes: 5 additions & 0 deletions src/Camera.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
struct CameraBufferObject {
glm::mat4 viewMatrix;
glm::mat4 projectionMatrix;
glm::mat4 viewMatrixInverse;
glm::mat4 projectionMatrixInverse;
glm::vec4 eye;
};

class Camera {
Expand All @@ -21,6 +24,7 @@ class Camera {
void* mappedData;

float r, theta, phi;
glm::vec3 center = glm::vec3(0, 1, 0);

public:
Camera(Device* device, float aspectRatio);
Expand All @@ -29,4 +33,5 @@ class Camera {
VkBuffer GetBuffer() const;

void UpdateOrbit(float deltaX, float deltaY, float deltaZ);
void UpdatePosition(float deltaX, float deltaY, float deltaZ);
};
4 changes: 2 additions & 2 deletions src/Image.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
#include "Instance.h"
#include "BufferUtils.h"

void Image::Create(Device* device, uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VkImage& image, VkDeviceMemory& imageMemory) {
void Image::Create(Device* device, uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VkImage& image, VkDeviceMemory& imageMemory, VkSampleCountFlagBits sampleCnt) {
// Create Vulkan image
VkImageCreateInfo imageInfo = {};
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
Expand All @@ -20,7 +20,7 @@ void Image::Create(Device* device, uint32_t width, uint32_t height, VkFormat for
imageInfo.tiling = tiling;
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
imageInfo.usage = usage;
imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
imageInfo.samples = sampleCnt;
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;

if (vkCreateImage(device->GetVkDevice(), &imageInfo, nullptr, &image) != VK_SUCCESS) {
Expand Down
2 changes: 1 addition & 1 deletion src/Image.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

namespace Image {

void Create(Device* device, uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VkImage& image, VkDeviceMemory& imageMemory);
void Create(Device* device, uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VkImage& image, VkDeviceMemory& imageMemory, VkSampleCountFlagBits sampleCnt = VK_SAMPLE_COUNT_1_BIT);
void TransitionLayout(Device* device, VkCommandPool commandPool, VkImage image, VkFormat format, VkImageLayout oldLayout, VkImageLayout newLayout);
VkImageView CreateView(Device* device, VkImage image, VkFormat format, VkImageAspectFlags aspectFlags);
void CopyFromBuffer(Device* device, VkCommandPool commandPool, VkBuffer buffer, VkImage& image, uint32_t width, uint32_t height);
Expand Down
Loading