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

Large diffs are not rendered by default.

289 changes: 25 additions & 264 deletions README.md

Large diffs are not rendered by default.

Binary file added bin/Debug/vulkan_grass_rendering.exe
Binary file not shown.
Binary file added bin/Debug/vulkan_grass_rendering.ilk
Binary file not shown.
Binary file added bin/Debug/vulkan_grass_rendering.pdb
Binary file not shown.
Binary file added bin/Release/vulkan_grass_rendering.exe
Binary file not shown.
Binary file added img/cullingTestsGraph.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/numBladesGraph.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/videoScreenshot.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.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
235 changes: 228 additions & 7 deletions src/Renderer.cpp

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions 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,17 @@ class Renderer {

VkDescriptorSetLayout cameraDescriptorSetLayout;
VkDescriptorSetLayout modelDescriptorSetLayout;
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
2 changes: 1 addition & 1 deletion src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ namespace {

int main() {
static constexpr char* applicationName = "Vulkan Grass Rendering";
InitializeWindow(640, 480, applicationName);
InitializeWindow(1200, 800, applicationName);

unsigned int glfwExtensionCount = 0;
const char** glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
Expand Down
139 changes: 129 additions & 10 deletions src/shaders/compute.comp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
#define WORKGROUP_SIZE 32
layout(local_size_x = WORKGROUP_SIZE, local_size_y = 1, local_size_z = 1) in;

#define GRAVITY 9.8

layout(set = 0, binding = 0) uniform CameraBufferObject {
mat4 view;
mat4 proj;
Expand All @@ -27,14 +29,24 @@ struct Blade {
// 3. Write the total number of blades remaining

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

// Store the input blades
layout(set = 2, binding = 0) buffer Blades {
Blade blades[];
};

// Write out the culled blades
layout(set = 2, binding = 1) buffer CulledBlades {
Blade culledBlades[];
};

// Write the total number of blades remaining
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);
Expand All @@ -43,14 +55,121 @@ bool inBounds(float value, float bounds) {
void main() {
// Reset the number of blades to 0
if (gl_GlobalInvocationID.x == 0) {
// numBlades.vertexCount = 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
uint index = gl_GlobalInvocationID.x;
Blade blade = blades[index];

vec3 pos_v0 = blade.v0.xyz;
vec3 pos_v1 = blade.v1.xyz;
vec3 pos_v2 = blade.v2.xyz;
vec3 up = blade.up.xyz;
float theta = blade.v0.w;
float h = blade.v1.w;
float w = blade.v2.w;
float stiffness = blade.up.w;

// Gravity
vec3 orientation = vec3(sin(theta), 0, cos(theta));
vec3 faceDir = normalize(cross(up, orientation));

// D.xyz = direction
// D.w = magnitude of acceleration
vec4 D = vec4(0, -1, 0, GRAVITY);
vec3 gE = normalize(D.xyz) * D.w; // Environmental gravity
vec3 gF = (0.25) * length(gE) * faceDir; // Front gravity
vec3 gravityForce = gE + gF;

// Recovery
vec3 iv2 = pos_v0 + up * h;
vec3 recoveryForce = (iv2 - pos_v2) * stiffness;

// Wind
vec3 windDirection = normalize(vec3(1, 0, 0));
float windStrength = 3.0 * cos(totalTime);
float fr = dot(pos_v2 - pos_v0, up) / h;
float fd = 1.0 - abs(dot(windDirection, normalize(pos_v2 - pos_v0)));
vec3 windForce = windStrength * windDirection * fr * fd;

// Total force
vec3 tv2 = (gravityForce + recoveryForce + windForce) * deltaTime;

// Position of v2
vec3 v2 = pos_v2 + tv2;
v2 = v2 - up * min(dot(up, v2 - pos_v0), 0.0);

// Length from v0 to v2
float l_proj = length(v2 - pos_v0 - up * dot(v2 - pos_v0, up));

// Position of v1
float ratio = l_proj / h;
vec3 v1 = pos_v0 + up * h * max(1.0 - ratio, 0.05 * max(ratio, 1.0));

// Corrected position
float n = 3.0;
float L0 = distance(v2, pos_v0);
float L1 = distance(v2, v1) + distance(v1, pos_v0);
float L = (2.0 * L0 + (n - 1.0) * L1) / (n + 1.0);
ratio = h / L;
vec3 corr_v1 = pos_v0 + ratio * (v1 - pos_v0);
vec3 corr_v2 = corr_v1 + ratio * (v2 - v1);

blade.v1.xyz = corr_v1;
blade.v2.xyz = corr_v2;
blades[index] = blade;

// TODO: 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
}

pos_v0 = blade.v0.xyz;
pos_v1 = blade.v1.xyz;
pos_v2 = blade.v2.xyz;

// Orientation test
mat4 inverseView = inverse(camera.view);
vec3 worldView = (inverseView * vec4(0, 0, 1, 0)).xyz;
vec3 dir_c = normalize(vec3(worldView.x, 0.0, worldView.z));
vec3 dir_b = faceDir;
float threshold = 0.9;
bool orientationTest = threshold > (abs(dot(dir_b, dir_c)));

//View-frustum culling
vec4 proj_v0 = camera.proj * camera.view * vec4(pos_v0, 1.0);
proj_v0 /= proj_v0.w;

vec4 proj_v2 = camera.proj * camera.view * vec4(pos_v2, 1.0);
proj_v2 /= proj_v2.w;

vec3 m = 0.25 * pos_v0 + 0.5 * pos_v1 + 0.25 * pos_v2;
vec4 proj_m = camera.proj * camera.view * vec4(m, 1.0);
proj_m /= proj_m.w;

float tolerance = 1.0;
bool frustrumTest = true;
if( (proj_v0.x >= -tolerance && proj_v0.x <= tolerance ) && (proj_v0.y >= -tolerance && proj_v0.y <= tolerance ) && (proj_v0.z >= 0.0 && proj_v0.z <= 1.0) ||
(proj_v2.x >= -tolerance && proj_v2.x <= tolerance ) && (proj_v2.y >= -tolerance && proj_v2.y <= tolerance ) && (proj_v2.z >= 0.0 && proj_v2.z <= 1.0) ||
(proj_m.x >= -tolerance && proj_m.x <= tolerance) && (proj_m.y >= -tolerance && proj_m.y <= tolerance) && (proj_m.z >= 0.0 && proj_m.z <= 1.0)) {
frustrumTest = false;
}

// Distance test
float near = 0.1;
float far = 100.0;
float depth = (2.0 * near) / (far + near - proj_v0.z * (far - near));

bool distanceTest = true;
if(threshold > depth) {
distanceTest = false;
}

// Add blade if passes all tests
if(!(orientationTest || frustrumTest || distanceTest))
{
culledBlades[atomicAdd(numBlades.vertexCount, 1)] = blade;
}
}
7 changes: 5 additions & 2 deletions src/shaders/grass.frag
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@ layout(set = 0, binding = 0) uniform CameraBufferObject {

// TODO: Declare fragment shader inputs

layout(location = 1) in vec3 Normal;
layout(location = 0) out vec4 outColor;

void main() {
// TODO: Compute fragment color

outColor = vec4(1.0);
vec3 color = vec3(0.1, 0.7, 0.1);
vec3 lightDirection = normalize(vec3(1.0, 5.0, -5.0));
float NdotL = clamp(dot(Normal, lightDirection), 0.1, 1.0);
outColor = vec4(color * NdotL, 1.0);
}
27 changes: 20 additions & 7 deletions src/shaders/grass.tesc
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,31 @@ layout(set = 0, binding = 0) uniform CameraBufferObject {
} camera;

// TODO: Declare tessellation control shader inputs and outputs
layout(location = 0) in vec4 tesc_v1[];
layout(location = 1) in vec4 tesc_v2[];
layout(location = 2) in vec4 tesc_up[];
layout(location = 3) in vec4 tesc_windDir[];

layout(location = 0) patch out vec4 tese_v1;
layout(location = 1) patch out vec4 tese_v2;
layout(location = 2) patch out vec4 tese_up;
layout(location = 3) patch out vec4 tese_windDir;

void main() {
// Don't move the origin location of the patch
gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;

// TODO: Write any shader outputs

tese_v1 = tesc_v1[0];
tese_v2 = tesc_v2[0];
tese_up = tesc_up[0];
tese_windDir = tesc_windDir[0];

// TODO: Set level of tesselation
// gl_TessLevelInner[0] = ???
// gl_TessLevelInner[1] = ???
// gl_TessLevelOuter[0] = ???
// gl_TessLevelOuter[1] = ???
// gl_TessLevelOuter[2] = ???
// gl_TessLevelOuter[3] = ???
gl_TessLevelInner[0] = 1.0; // inner horizontal
gl_TessLevelInner[1] = 1.0; // inner vertical
gl_TessLevelOuter[0] = 5.0; // outer vertical
gl_TessLevelOuter[1] = 1.0; // outer horizontal
gl_TessLevelOuter[2] = 5.0; // outer vertical
gl_TessLevelOuter[3] = 1.0; // outer horizontal
}
29 changes: 29 additions & 0 deletions src/shaders/grass.tese
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,39 @@ layout(set = 0, binding = 0) uniform CameraBufferObject {
} camera;

// TODO: Declare tessellation evaluation shader inputs and outputs
layout(location = 0) patch in vec4 tese_v1;
layout(location = 1) patch in vec4 tese_v2;
layout(location = 2) patch in vec4 tese_up;
layout(location = 3) patch in vec4 tese_windDir;

layout(location = 0) out vec4 Position;
layout(location = 1) out vec3 Normal;
layout(location = 2) out vec3 WindDirection;

void main() {
float u = gl_TessCoord.x;
float v = gl_TessCoord.y;

// TODO: Use u and v to parameterize along the grass blade and output positions for each vertex of the grass blade

// Blade Geometry
vec3 pos_v0 = gl_in[0].gl_Position.xyz;
vec3 a = pos_v0 + v * (tese_v1.xyz - pos_v0);
vec3 b = tese_v1.xyz + v * (tese_v2.xyz - tese_v1.xyz);
vec3 c = a + v * (b - a);
vec3 t1 = 0.5 * tese_v2.w * tese_windDir.xyz;
vec3 c0 = c - t1;
vec3 c1 = c + t1;
vec3 t0 = normalize(b - a);

Normal = normalize(cross(t0, tese_windDir.xyz));
float t = u + 0.5 * v - u * v;
Position.xyz = (1.0 - t) * c0 + t * c1;

// Basic shape
mat4 viewProj = camera.proj * camera.view;
Position = viewProj * vec4(Position.xyz, 1.0);
gl_Position = Position;
Position.w = tese_windDir.w;
WindDirection = tese_windDir.xyz;
}
19 changes: 19 additions & 0 deletions src/shaders/grass.vert
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,30 @@ layout(set = 1, binding = 0) uniform ModelBufferObject {
};

// TODO: Declare vertex shader inputs and outputs
layout(location = 0) in vec4 v0;
layout(location = 1) in vec4 v1;
layout(location = 2) in vec4 v2;
layout(location = 3) in vec4 up;

layout(location = 0) out vec4 tesc_v1;
layout(location = 1) out vec4 tesc_v2;
layout(location = 2) out vec4 tesc_up;
layout(location = 3) out vec4 tesc_windDir;

out gl_PerVertex {
vec4 gl_Position;
};

void main() {
// TODO: Write gl_Position and any other shader outputs
vec4 V0 = model * vec4(v0.xyz, 1.0);
tesc_v1 = vec4((model * vec4(v1.xyz, 1.0)).xyz, v1.w);
tesc_v2 = vec4((model * vec4(v2.xyz, 1.0)).xyz, v2.w);
tesc_up.xyz = normalize(up.xyz);

vec3 orientation = vec3(sin(v0.w), 0, cos(v0.w));
vec3 faceDir = normalize(cross(tesc_up.xyz, orientation));
tesc_windDir.xyz = normalize(cross(faceDir, tesc_up.xyz));
tesc_windDir.w = v1.w * 0.4;
gl_Position = V0;
}