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
302 changes: 302 additions & 0 deletions INSTRUCTIONS.md

Large diffs are not rendered by default.

299 changes: 64 additions & 235 deletions README.md

Large diffs are not rendered by default.

Binary file added img/FPSvsCulling.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/FPSvsNumBlades.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/directionalWind.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/distanceCullingVis.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/frustumCullingVis.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/fullGrass.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/moreComplexWind.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/moreComplexWindOverhead.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/orientationCullingVis.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/projectFinishedNoMoreWorkNeeded.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/stillGrass.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/thatSureIsGrass.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/turnTheBassDown.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/uh.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions src/Blades.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ Blades::Blades(Device* device, VkCommandPool commandPool, float planeDim) : Mode
indirectDraw.firstVertex = 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::CreateBufferFromData(device, commandPool, blades.data(), NUM_BLADES * sizeof(Blade), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT, bladesBuffer, bladesBufferMemory);
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
1 change: 1 addition & 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.viewMatrix = glm::lookAt(glm::vec3(16.0f, 16.0f, 10.0f), glm::vec3(0.0f, 1.0f, 0.0f), glm::vec3(0.0f, 1.0f, 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);
cameraBufferObject.projectionMatrix[1][1] *= -1; // y-coordinate is flipped
Expand Down
5 changes: 4 additions & 1 deletion src/Image.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "Device.h"
#include "Instance.h"
#include "BufferUtils.h"
#include <iostream>

void Image::Create(Device* device, uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VkImage& image, VkDeviceMemory& imageMemory) {
// Create Vulkan image
Expand Down Expand Up @@ -200,7 +201,9 @@ void Image::FromFile(Device* device, VkCommandPool commandPool, const char* path
VkDeviceSize imageSize = texWidth * texHeight * 4;

if (!pixels) {
throw std::runtime_error("Failed to load texture image");
//throw std::runtime_error("Failed to load texture image");
std::cout << stbi_failure_reason() << std::endl;
throw std::runtime_error(stbi_failure_reason());
}

// Create staging buffer
Expand Down
211 changes: 194 additions & 17 deletions src/Renderer.cpp

Large diffs are not rendered by default.

8 changes: 7 additions & 1 deletion src/Renderer.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class Renderer {

void CreateCameraDescriptorSetLayout();
void CreateModelDescriptorSetLayout();
void CreateGrassDescriptorSetLayout();
void CreateTimeDescriptorSetLayout();
void CreateComputeDescriptorSetLayout();

Expand Down Expand Up @@ -55,13 +56,18 @@ class Renderer {

VkDescriptorSetLayout cameraDescriptorSetLayout;
VkDescriptorSetLayout modelDescriptorSetLayout;
VkDescriptorSetLayout timeDescriptorSetLayout;
VkDescriptorSetLayout grassDescriptorSetLayout;
VkDescriptorSetLayout timeDescriptorSetLayout;
VkDescriptorSetLayout computeDescriptorSetLayout;


VkDescriptorPool descriptorPool;

VkDescriptorSet cameraDescriptorSet;
std::vector<VkDescriptorSet> modelDescriptorSets;
std::vector<VkDescriptorSet> grassDescriptorSets;
VkDescriptorSet timeDescriptorSet;
std::vector<VkDescriptorSet> computeDescriptorSets;

VkPipelineLayout graphicsPipelineLayout;
VkPipelineLayout grassPipelineLayout;
Expand Down
Binary file added src/images/dirt.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
26 changes: 25 additions & 1 deletion src/main.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include <vulkan/vulkan.h>
#include <sstream>
#include "Instance.h"
#include "Window.h"
#include "Renderer.h"
Expand Down Expand Up @@ -106,7 +107,9 @@ int main() {
VkDeviceMemory grassImageMemory;
Image::FromFile(device,
transferCommandPool,
"images/grass.jpg",
// Note: for some reason, I had to use absolute paths to load any images besides the given one
"images/grass.jpg",
//"C:/Users/caroline/Documents/CIS_565/Project4-Vulkan-Grass-Rendering/src/images/dirt.png",
VK_FORMAT_R8G8B8A8_UNORM,
VK_IMAGE_TILING_OPTIMAL,
VK_IMAGE_USAGE_SAMPLED_BIT,
Expand Down Expand Up @@ -143,8 +146,29 @@ int main() {
glfwSetMouseButtonCallback(GetGLFWWindow(), mouseDownCallback);
glfwSetCursorPosCallback(GetGLFWWindow(), mouseMoveCallback);

double fps = 0;
double timebase = 0;
int frame = 0;

while (!ShouldQuit()) {
glfwPollEvents();

frame++;
double time = glfwGetTime();

if (time - timebase > 1.0) {
fps = frame / (time - timebase);
timebase = time;
frame = 0;
}

std::ostringstream ss;
ss << "[";
ss.precision(1);
ss << std::fixed << fps;
ss << " fps] ";
glfwSetWindowTitle(GetGLFWWindow(), ss.str().c_str());

scene->UpdateTime();
renderer->Frame();
}
Expand Down
187 changes: 175 additions & 12 deletions src/shaders/compute.comp
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
#version 450
#extension GL_ARB_separate_shader_objects : enable

#define PI 3.141592653238

#define ORIENTATION_CULL 1
#define FRUSTUM_CULL 1
#define DISTANCE_CULL 1

#define WORKGROUP_SIZE 32
layout(local_size_x = WORKGROUP_SIZE, local_size_y = 1, local_size_z = 1) in;

Expand All @@ -21,36 +27,193 @@ struct Blade {
vec4 up;
};

// TODO: Add bindings to:
// DONE: Add bindings to:
// 1. Store the input blades
// 2. Write out the culled blades
// 3. Write the total number of blades remaining

layout(set = 2, binding = 0) buffer InBlades {
Blade inBlades[];
};

layout(set = 2, binding = 1) buffer RemainingBlades {
Blade remainingBlades[];
};

// 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 = 2, binding = 2) buffer NumBlades {
uint vertexCount; // Write the number of blades remaining here
uint instanceCount; // = 1
uint firstVertex; // = 0
uint firstInstance; // = 0
} numBlades;

bool inBounds(float value, float bounds) {
return (value >= -bounds) && (value <= bounds);
}

// 2D Random
float random(vec2 p) {
return fract(sin(dot(p.xy, vec2(12.9898,78.233))) * 43758.5453123);
}

// 2D Noise based on Morgan McGuire @morgan3d
// https://www.shadertoy.com/view/4dS3Wd
float noise(vec2 p) {
vec2 i = floor(p);
vec2 f = fract(p);

// Four corners in 2D of a tile
float a = random(i);
float b = random(i + vec2(1.0, 0.0));
float c = random(i + vec2(0.0, 1.0));
float d = random(i + vec2(1.0, 1.0));

// Smooth Interpolation with Cubic Hermine Curve
vec2 u = f * f * (3.0 - 2.0 * f);

// Mix 4 coorners percentages
return mix(a, b, u.x) + (c - a)* u.y * (1.0 - u.x) + (d - b) * u.x * u.y;
}

// https://thebookofshaders.com/13/
#define OCTAVES 6
float fbm(vec2 p) {
float value = 0.0;
float amplitude = .5;
float frequency = 0.;

for (int i = 0; i < OCTAVES; i++) {
value += amplitude * noise(p);
p *= 2.0;
amplitude *= 0.5;
}
return value;
}

void main() {
// Reset the number of blades to 0
if (gl_GlobalInvocationID.x == 0) {
// numBlades.vertexCount = 0;
uint threadIdx = gl_GlobalInvocationID.x;
if (threadIdx == 0) {
numBlades.vertexCount = 0;
}
barrier(); // Wait till all threads reach this point

// TODO: Apply forces on every blade and update the vertices in the buffer
// gather input
vec3 v0 = inBlades[threadIdx].v0.xyz;
vec3 v1 = inBlades[threadIdx].v1.xyz;
vec3 v2 = inBlades[threadIdx].v2.xyz;
vec3 up = inBlades[threadIdx].up.xyz;
float orientation = inBlades[threadIdx].v0.w;
float height = inBlades[threadIdx].v1.w;
float width = inBlades[threadIdx].v2.w;
float stiffness = inBlades[threadIdx].up.w;

vec3 orientationDir = normalize(vec3(cos(orientation), 0.0, sin(orientation))); // pointing along width of blade
vec3 faceDir = normalize(cross(up, orientationDir)); // pointing out from face of blade

// DONE: Apply forces on every blade and update the vertices in the buffer

// gravity force
vec3 gravityDir = vec3(0.f, -1.f, 0.f);
float gravityAcc = 9.81f;
vec3 gE = gravityDir * gravityAcc;
vec3 gF = 0.25 * length(gE) * faceDir;
vec3 gravity = gF + gE;

// recovery force
vec3 iv2 = v0 + height * up;
vec3 recovery = (iv2 - v2) * stiffness;

// TODO: Cull blades that are too far away or not in the camera frustum and write them
// wind force
// playing around with FBM, noise, and time
vec3 windDir = normalize(vec3(sin(totalTime * fbm(0.5 * v0.xz)), 0.5, cos(totalTime * fbm(0.5 * v0.xz))));
//windDir = normalize(vec3(sin(fbm(abs(cos(totalTime)) * 0.5 * v0.xz)), 0.5, cos(fbm(abs(sin(totalTime)) * 0.5 * v0.xz))));

float dirAlignment = 1.0 - abs(dot(windDir, normalize(v2 - v0)));
float heightRatio = dot(v2 - v0, up) / height;
float windAlignment = dirAlignment * heightRatio;
float windScale = 20.f * noise(abs(sin(totalTime)) * v0.xz);
windScale = 50.f * abs(fbm(v0.xz + vec2(totalTime)));
vec3 wind = windScale * windAlignment * windDir;

// apply total force
vec3 tv2 = (gravity + recovery + wind) * 0.01;
v2 += tv2;

// apply correction
v2 = v2 - up * min(dot(up, v2 - v0), 0.0);

float l_proj = length(v2 - v0 - up * dot(v2 - v0, up));
v1 = v0 + height * up * max(1.0 - (l_proj / height), 0.05 * max(l_proj / height, 1.0));

float n = 2.0; // degree of bezier curve
float L0 = distance(v2, v0);
float L1 = distance(v2, v1) + distance(v1, v0);
float L = (2.0 * L0 + (n - 1.0) * L1) / (n + 1.0);
float r = height / L;

vec3 v1_corr = v0 + r * (v1 - v0);
vec3 v2_corr = v1_corr + r * (v2 - v1);

// update values
inBlades[threadIdx].v1 = vec4(v1_corr, height);
inBlades[threadIdx].v2 = vec4(v2_corr, width);

// DONE: 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

bool cull = false;
mat4 invView = inverse(camera.view);

#if ORIENTATION_CULL
// orientation culling
vec3 viewDir = normalize(vec3(camera.view[0][2], camera.view[1][2], camera.view[2][2]));
cull = cull || abs(dot(orientationDir, viewDir)) > 0.9;
#endif // #if ORIENTATION_CULL

#if FRUSTUM_CULL
// view-frustum culling
vec3 m = 0.25 * v0 + 0.5 * v1 + 0.25 * v2; // estimated midpoint of blade

// project v0, v2, and m to NDC
vec4 v0_NDC = camera.proj * camera.view * vec4(v0, 1.0);
vec4 v2_NDC = camera.proj * camera.view * vec4(v2, 1.0);
vec4 m_NDC = camera.proj * camera.view * vec4(m, 1.0);

float t = 0.1; // tolerance
float v0Bounds = v0_NDC.w + t;
float v2Bounds = v2_NDC.w + t;
float mBounds = m_NDC.w + t;
bool v0InBounds = inBounds(v0_NDC.x, v0Bounds) && inBounds(v0_NDC.y, v0Bounds) && inBounds(v0_NDC.z, v0Bounds);
bool v2InBounds = inBounds(v2_NDC.x, v2Bounds) && inBounds(v2_NDC.y, v2Bounds) && inBounds(v2_NDC.z, v2Bounds);
bool mInBounds = inBounds(m_NDC.x, mBounds) && inBounds(m_NDC.y, mBounds) && inBounds(m_NDC.z, mBounds);

// for vis/debug purposes
//if (!v0InBounds && !v2InBounds && !mInBounds) {
// inBlades[threadIdx].v1 += vec4(0.0, 5.0, 0.0, 0.0);
//}

// cull blade if all 3 points are outside frustum
cull = cull || (!v0InBounds && !v2InBounds && !mInBounds);

#endif // #if FRUSTUM_CULL

#if DISTANCE_CULL
// distance culling
vec3 cameraPos = vec3(invView[3][0], invView[3][1], invView[3][2]);
int numBuckets = 3; // number of buckets/levels used in distance culling
float d_max = 50.0; // max distance
float d_proj = length(v0 - cameraPos - up * dot(v0 - cameraPos, up));
cull = cull || (mod(threadIdx, numBuckets) > floor(numBuckets * (1.0 - d_proj / d_max)));

#endif // #if DISTANCE_CULL

// if no culling, increment number of blades and add blade to remaining blades
if (!cull) {
remainingBlades[atomicAdd(numBlades.vertexCount, 1)] = inBlades[threadIdx];
}
}
21 changes: 18 additions & 3 deletions src/shaders/grass.frag
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,27 @@ layout(set = 0, binding = 0) uniform CameraBufferObject {
mat4 proj;
} camera;

// TODO: Declare fragment shader inputs
// DONE: Declare fragment shader inputs
layout(location = 0) in vec2 in_uv;
layout(location = 1) in float in_height;

layout(location = 0) out vec4 outColor;

void main() {
// TODO: Compute fragment color
const float MAX_HEIGHT = 2.5f;

outColor = vec4(1.0);
// DONE: Compute fragment color
float over255 = 1.0 / 255.0;

// green gradient color
vec3 green1 = vec3(16.0, 82.0, 50.0) * over255;
vec3 green2 = vec3(121.0, 161.0, 98.0) * over255;
vec3 green3 = vec3(169.0, 224.0, 81.0) * over255;

float t = (in_uv.y * in_height / MAX_HEIGHT);

vec3 color = mix(mix(green1, green2, t), mix(green2, green3, t), t);
//color = mix(mix(green1, green2, in_uv.y), mix(green2, green3, in_uv.y), in_uv.y);

outColor = vec4(color, 1.0);
}
Loading