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
296 changes: 36 additions & 260 deletions README.md

Large diffs are not rendered by default.

Binary file added img/DistanceBasedTesselation2pow16.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/DistanceBasedTesselation2pow18.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/DistanceCulling2pow15.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/DistanceCullingDebug.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/NoDistanceBasedTesselation2pow16.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/NoDistanceBasedTesselation2pow18.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/NoDistanceCulling2pow14.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/baseline.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/distanceCulling.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/distanceTessellation.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/normals.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/grassDemoGif.gif
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 << 16;
constexpr static float MIN_HEIGHT = 1.3f;
constexpr static float MAX_HEIGHT = 2.5f;
constexpr static float MIN_WIDTH = 0.1f;
Expand Down
122 changes: 117 additions & 5 deletions src/Renderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,41 @@ void Renderer::CreateComputeDescriptorSetLayout() {
// TODO: Create the descriptor set layout for the compute pipeline
// Remember this is like a class definition stating why types of information
// will be stored at each binding

//input blades
VkDescriptorSetLayoutBinding sboLayoutBinding1 = {};
sboLayoutBinding1.binding = 0;
sboLayoutBinding1.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
sboLayoutBinding1.descriptorCount = 1; //ME-TODO: This will probably need to be at least 2
sboLayoutBinding1.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
sboLayoutBinding1.pImmutableSamplers = nullptr;

//culled blades
VkDescriptorSetLayoutBinding sboLayoutBinding2 = {};
sboLayoutBinding2.binding = 1;
sboLayoutBinding2.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
sboLayoutBinding2.descriptorCount = 1;
sboLayoutBinding2.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
sboLayoutBinding2.pImmutableSamplers = nullptr;

//number of blades
VkDescriptorSetLayoutBinding sboLayoutBinding3 = {};
sboLayoutBinding3.binding = 2;
sboLayoutBinding3.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
sboLayoutBinding3.descriptorCount = 1;
sboLayoutBinding3.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
sboLayoutBinding3.pImmutableSamplers = nullptr;

std::vector<VkDescriptorSetLayoutBinding> bindings = { sboLayoutBinding1, sboLayoutBinding2, sboLayoutBinding3 };

VkDescriptorSetLayoutCreateInfo layoutInfo = {};
layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
layoutInfo.bindingCount = static_cast<uint32_t>(bindings.size());
layoutInfo.pBindings = bindings.data();

if (vkCreateDescriptorSetLayout(logicalDevice, &layoutInfo, nullptr, &computeDescriptorSetLayout) != VK_SUCCESS) {
throw std::runtime_error("Failed to create descriptor set layout");
}
}

void Renderer::CreateDescriptorPool() {
Expand All @@ -216,13 +251,20 @@ void Renderer::CreateDescriptorPool() {
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER , 1 },

// TODO: Add any additional types and counts of descriptors you will need to allocate

//input blades and culled blades
{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER , 2 },

//struct with culled blades data
{VK_DESCRIPTOR_TYPE_STORAGE_BUFFER , 1}

};

VkDescriptorPoolCreateInfo poolInfo = {};
poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
poolInfo.poolSizeCount = static_cast<uint32_t>(poolSizes.size());
poolInfo.pPoolSizes = poolSizes.data();
poolInfo.maxSets = 5;
poolInfo.maxSets = 6;

if (vkCreateDescriptorPool(logicalDevice, &poolInfo, nullptr, &descriptorPool) != VK_SUCCESS) {
throw std::runtime_error("Failed to create descriptor pool");
Expand Down Expand Up @@ -360,6 +402,66 @@ void Renderer::CreateTimeDescriptorSet() {
void Renderer::CreateComputeDescriptorSets() {
// TODO: Create Descriptor sets for the compute pipeline
// The descriptors should point to Storage buffers which will hold the grass blades, the culled grass blades, and the output number of grass blades
VkDescriptorSetLayout layouts[] = { computeDescriptorSetLayout };
VkDescriptorSetAllocateInfo allocInfo = {};
allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
allocInfo.descriptorPool = descriptorPool;
allocInfo.descriptorSetCount = 1;
allocInfo.pSetLayouts = layouts;

//Allocate descriptor sets
if (vkAllocateDescriptorSets(logicalDevice, &allocInfo, &computeDescriptorSet) != VK_SUCCESS) {
throw std::runtime_error("Failed to allocate descriptor set");
}

VkDescriptorBufferInfo computeBufferInfo = {};
Blades* blades = scene->GetBlades().at(0);
computeBufferInfo.buffer = blades->GetBladesBuffer();
computeBufferInfo.offset = 0;
computeBufferInfo.range = NUM_BLADES * sizeof(Blade);

VkDescriptorBufferInfo computeBufferCulledBladesInfo = {};
computeBufferCulledBladesInfo.buffer = blades->GetCulledBladesBuffer();
computeBufferCulledBladesInfo.offset = 0;
computeBufferCulledBladesInfo.range = NUM_BLADES * sizeof(Blade);

VkDescriptorBufferInfo computeBufferNumBladesInfo = {};
computeBufferNumBladesInfo.buffer = blades->GetNumBladesBuffer();
computeBufferNumBladesInfo.offset = 0;
computeBufferNumBladesInfo.range = sizeof(BladeDrawIndirect);

std::array<VkWriteDescriptorSet, 3> descriptorWrites = {};
descriptorWrites[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptorWrites[0].dstSet = computeDescriptorSet;
descriptorWrites[0].dstBinding = 0;
descriptorWrites[0].dstArrayElement = 0;
descriptorWrites[0].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
descriptorWrites[0].descriptorCount = 1;
descriptorWrites[0].pBufferInfo = &computeBufferInfo;
descriptorWrites[0].pImageInfo = nullptr;
descriptorWrites[0].pTexelBufferView = nullptr;

descriptorWrites[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptorWrites[1].dstSet = computeDescriptorSet;
descriptorWrites[1].dstBinding = 1;
descriptorWrites[1].dstArrayElement = 0;
descriptorWrites[1].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
descriptorWrites[1].descriptorCount = 1;
descriptorWrites[1].pBufferInfo = &computeBufferCulledBladesInfo;
descriptorWrites[1].pImageInfo = nullptr;
descriptorWrites[1].pTexelBufferView = nullptr;

descriptorWrites[2].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptorWrites[2].dstSet = computeDescriptorSet;
descriptorWrites[2].dstBinding = 2;
descriptorWrites[2].dstArrayElement = 0;
descriptorWrites[2].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
descriptorWrites[2].descriptorCount = 1;
descriptorWrites[2].pBufferInfo = &computeBufferNumBladesInfo;
descriptorWrites[2].pImageInfo = nullptr;
descriptorWrites[2].pTexelBufferView = nullptr;

vkUpdateDescriptorSets(logicalDevice, static_cast<uint32_t>(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr);
}

void Renderer::CreateGraphicsPipeline() {
Expand Down Expand Up @@ -717,7 +819,7 @@ void Renderer::CreateComputePipeline() {
computeShaderStageInfo.pName = "main";

// TODO: Add the compute dsecriptor set layout you create to this list
std::vector<VkDescriptorSetLayout> descriptorSetLayouts = { cameraDescriptorSetLayout, timeDescriptorSetLayout };
std::vector<VkDescriptorSetLayout> descriptorSetLayouts = { cameraDescriptorSetLayout, timeDescriptorSetLayout, computeDescriptorSetLayout};

// Create pipeline layout
VkPipelineLayoutCreateInfo pipelineLayoutInfo = {};
Expand Down Expand Up @@ -884,6 +986,11 @@ void Renderer::RecordComputeCommandBuffer() {
vkCmdBindDescriptorSets(computeCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, computePipelineLayout, 1, 1, &timeDescriptorSet, 0, nullptr);

// TODO: For each group of blades bind its descriptor set and dispatch
vkCmdBindDescriptorSets(computeCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, computePipelineLayout, 2, 1, &computeDescriptorSet, 0, nullptr);

vkCmdDispatch(computeCommandBuffer, static_cast<uint32_t>(NUM_BLADES / WORKGROUP_SIZE), 1, 1);

//vkCmdDispatch(computeCommandBuffer, 10, 1, 1);

// ~ End recording ~
if (vkEndCommandBuffer(computeCommandBuffer) != VK_SUCCESS) {
Expand Down Expand Up @@ -976,13 +1083,13 @@ void Renderer::RecordCommandBuffers() {
VkBuffer vertexBuffers[] = { scene->GetBlades()[j]->GetCulledBladesBuffer() };
VkDeviceSize offsets[] = { 0 };
// TODO: Uncomment this when the buffers are populated
// vkCmdBindVertexBuffers(commandBuffers[i], 0, 1, vertexBuffers, offsets);
vkCmdBindVertexBuffers(commandBuffers[i], 0, 1, vertexBuffers, offsets);

// TODO: Bind the descriptor set for each grass blades model

// Draw
// TODO: Uncomment this when the buffers are populated
// vkCmdDrawIndirect(commandBuffers[i], scene->GetBlades()[j]->GetNumBladesBuffer(), 0, 1, sizeof(BladeDrawIndirect));
vkCmdDrawIndirect(commandBuffers[i], scene->GetBlades()[j]->GetNumBladesBuffer(), 0, 1, sizeof(BladeDrawIndirect));
}

// End render pass
Expand Down Expand Up @@ -1050,18 +1157,23 @@ Renderer::~Renderer() {
vkDestroyPipeline(logicalDevice, grassPipeline, nullptr);
vkDestroyPipeline(logicalDevice, computePipeline, nullptr);

vkDestroyPipelineLayout(logicalDevice, graphicsPipelineLayout, nullptr);
vkDestroyPipelineLayout(logicalDevice, graphicsPipelineLayout, nullptr);
vkDestroyPipelineLayout(logicalDevice, grassPipelineLayout, nullptr);
vkDestroyPipelineLayout(logicalDevice, computePipelineLayout, nullptr);

vkDestroyDescriptorSetLayout(logicalDevice, cameraDescriptorSetLayout, nullptr);
vkDestroyDescriptorSetLayout(logicalDevice, modelDescriptorSetLayout, nullptr);
vkDestroyDescriptorSetLayout(logicalDevice, timeDescriptorSetLayout, nullptr);

//destroy new descriptorSets
vkDestroyDescriptorSetLayout(logicalDevice, computeDescriptorSetLayout, nullptr);

vkDestroyDescriptorPool(logicalDevice, descriptorPool, nullptr);

vkDestroyRenderPass(logicalDevice, renderPass, nullptr);
DestroyFrameResources();
vkDestroyCommandPool(logicalDevice, computeCommandPool, nullptr);
vkDestroyCommandPool(logicalDevice, graphicsCommandPool, nullptr);


}
4 changes: 4 additions & 0 deletions src/Renderer.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,8 @@ class Renderer {

std::vector<VkCommandBuffer> commandBuffers;
VkCommandBuffer computeCommandBuffer;

//Additional members
VkDescriptorSetLayout computeDescriptorSetLayout;
VkDescriptorSet computeDescriptorSet;
};
113 changes: 97 additions & 16 deletions src/shaders/compute.comp
Original file line number Diff line number Diff line change
Expand Up @@ -21,36 +21,117 @@ struct Blade {
vec4 up;
};

// TODO: Add bindings to:
// 1. Store the input blades
// 2. Write out the culled blades
// 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;
layout(set = 2, binding = 0) buffer InputBlades {
Blade inputBlades[];
};

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

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

bool distanceCulled(float distance, uint index, vec3 pos) {

float closestBucket = 0.0;
float maxDistance = 40.0;
float totalBuckets = 10.0;
float bucket = max(0.0, floor((maxDistance - distance) / (maxDistance / totalBuckets)));
float pdf = abs(sin(dot(pos, vec3(12.9898, 54.3289, 78.233))) * 43758.5453);
pdf = pdf - floor(pdf);
float threshold = bucket / totalBuckets;
threshold = pow(threshold, 2.0);
return pdf > threshold || bucket == 0;
}

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
float time = deltaTime + totalTime;
uint index = gl_GlobalInvocationID.x;

Blade blade = inputBlades[index];

vec4 v0 = blade.v0;
vec4 v1 = blade.v1;
vec4 v2 = blade.v2;
vec4 up = blade.up;

vec3 bladeNormal = vec3(sin(v0.w), 0.0, cos(v0.w));

//recovery force
vec3 recovery = (v0.xyz + v1.w * up.xyz) - v2.xyz;
recovery *= up.w; // multiply by stiffness coefficient

//gravity
vec3 envGravity = 1.0 * (-up.xyz * 9.8);
vec3 frontGravity = .25 * length(envGravity) * bladeNormal;
vec3 gravity = envGravity + frontGravity;

//wind
vec3 windForce = sin(totalTime * 4.0 + abs(cos(v0.x) - cos(v0.z))) * vec3(3.0, 0.0, -3.0);
//vec3 windCenter = 0.5 * vec3(sin(totalTime), 0.0, cos(totalTime));
//vec3 windForce = (-sin(totalTime * 20.0 - length(v0.xyz - windCenter)) + 0.8) * normalize(v0.xyz - windCenter) * 100.0;
float directionAlignment = abs(dot(normalize(windForce), normalize(bladeNormal)));
float heightRatio = dot(v2.xyz - v0.xyz, up.xyz) / v1.w;
float alignmentValue = directionAlignment * heightRatio;
vec3 wind = alignmentValue * windForce;

// TODO: Apply forces on every blade and update the vertices in the buffer
//total the forces
vec3 translation = (recovery + gravity + wind) * deltaTime;

//do initial translation on control point
v2 += vec4(translation, 0.0);

//state validation
v2.xyz = v2.xyz - up.xyz * min(dot(up.xyz, v2.xyz - v0.xyz), 0);

float lProj = length(v2.xyz - v0.xyz - up.xyz * dot(v2.xyz - v0.xyz, up.xyz));
v1.xyz = v0.xyz + v1.w * up.xyz * max(1.0 - lProj / v1.w, 0.05 * max(lProj / v1.w, 1.0));

float L0 = distance(v0.xyz, v2.xyz);
float L1 = distance(v0.xyz, v1.xyz) + distance(v1.xyz, v2.xyz);
float L = (2.0 * L0 + 2.0 * L1) / 4.0;
float r = v1.w / L;
vec3 v1Tmp = v1.xyz;
v1.xyz = v0.xyz + r * (v1.xyz - v0.xyz);
v2.xyz = v1.xyz + r * (v2.xyz - v1Tmp);

//update Blades
inputBlades[index].v1 = v1;
inputBlades[index].v2 = v2;

culledBlades[index] = inputBlades[index];

// TODO: Cull blades that are too far away or not in the camera frustum and write them
// to the culled blades buffer

vec4 clipTest = camera.proj * camera.view * vec4(v0.xyz, 1.0);
bool cullByDistance = distanceCulled(length(vec3(camera.view * vec4(v0.xyz, 1.0))), index, v0.xyz);
clipTest /= clipTest.w;

float normalTest = dot(normalize(vec3(camera.view * vec4(v0.xyz, 1.0))), normalize(vec3(camera.view * vec4(bladeNormal, 0.0))));

if (inBounds(clipTest.x, 1.05) && inBounds(clipTest.y, 1.3) && abs(normalTest) > 0.05 && !cullByDistance) {
culledBlades[atomicAdd(numBlades.vertexCount, 1)] = inputBlades[index];
}

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

//culledBlades.data[index] = inBlades.data[index];


}
4 changes: 3 additions & 1 deletion src/shaders/grass.frag
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ layout(set = 0, binding = 0) uniform CameraBufferObject {

// TODO: Declare fragment shader inputs

layout(location = 0) in float colorHeight;

layout(location = 0) out vec4 outColor;

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

outColor = vec4(1.0);
outColor = colorHeight * vec4(0.0, 1.0, 0.0, 1.0);
}
Loading