diff --git a/README.md b/README.md index 20ee451..1d99cde 100644 --- a/README.md +++ b/README.md @@ -3,10 +3,41 @@ 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) +![](img/cover.gif) +An implementation of Vulkan grass Rendering based on [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). Including bezier curve grass rendering and blade culling. +* Tom Donnelly + * [LinkedIn](https://www.linkedin.com/in/the-tom-donnelly/) +* Tested on: Windows 11, AMD Ryzen 9 5900X, NVIDIA GeForce RTX 3070 (Personal Desktop) -### (TODO: Your README) +### Implementation +The grass blades were initially implemented with no forces or culling, resulting in a standing blade. +![](img/grass_up.png) +Forces were then added to the blades: +Wind, made using a sin function: +![](img/wind.gif) +Gravity: +![](img/gravity.png) +Recovery, based on blade stiffness: +![](img/recovery.png) -*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. +Culling methods were then added to increase performance: +Orientation Culling: +![](img/o_cull.gif) +Frustrum Culling: +![](img/f_cull.gif) +Distance Culling: +![](img/d_cull.gif) + + +### Analysis +Number of grass blades and culling techniques were analyzed. + +| 256 Blades | 16384 Blades | 1048576 Blades | +| ---------- | ------------ | -------------- | +|![](img/8.gif) |![](img/13.gif) | ![](img/20.gif) | + +![](img/num_blades.png) +Extremely high FPS were achieved until around ~260000 blades were simulated at which point the performance drastically decreased. +![](img/Culling.png) +Distance culling had the most impact on performance which was surprising as I suspected orientation culling may be the most beneficial. Its likely that distance culling is less noticeable and most beneficial as it slightly decreases the number of blades at distances based on bins. + diff --git a/img/13.gif b/img/13.gif new file mode 100644 index 0000000..44de37b Binary files /dev/null and b/img/13.gif differ diff --git a/img/20.gif b/img/20.gif new file mode 100644 index 0000000..4e9cb61 Binary files /dev/null and b/img/20.gif differ diff --git a/img/8.gif b/img/8.gif new file mode 100644 index 0000000..b431213 Binary files /dev/null and b/img/8.gif differ diff --git a/img/Culling.png b/img/Culling.png new file mode 100644 index 0000000..0a4cd2e Binary files /dev/null and b/img/Culling.png differ diff --git a/img/cover.gif b/img/cover.gif new file mode 100644 index 0000000..f3460f3 Binary files /dev/null and b/img/cover.gif differ diff --git a/img/d_cull.gif b/img/d_cull.gif new file mode 100644 index 0000000..0d0da2a Binary files /dev/null and b/img/d_cull.gif differ diff --git a/img/f_cull.gif b/img/f_cull.gif new file mode 100644 index 0000000..55ca83e Binary files /dev/null and b/img/f_cull.gif differ diff --git a/img/grass_tile.gif b/img/grass_tile.gif new file mode 100644 index 0000000..a600198 Binary files /dev/null and b/img/grass_tile.gif differ diff --git a/img/grass_up.png b/img/grass_up.png new file mode 100644 index 0000000..7a4ae61 Binary files /dev/null and b/img/grass_up.png differ diff --git a/img/gravity.png b/img/gravity.png new file mode 100644 index 0000000..5175a99 Binary files /dev/null and b/img/gravity.png differ diff --git a/img/num_blades.png b/img/num_blades.png new file mode 100644 index 0000000..dd41504 Binary files /dev/null and b/img/num_blades.png differ diff --git a/img/o_cull.gif b/img/o_cull.gif new file mode 100644 index 0000000..0689656 Binary files /dev/null and b/img/o_cull.gif differ diff --git a/img/recovery.png b/img/recovery.png new file mode 100644 index 0000000..16be506 Binary files /dev/null and b/img/recovery.png differ diff --git a/img/wind.gif b/img/wind.gif new file mode 100644 index 0000000..aff073e Binary files /dev/null and b/img/wind.gif differ diff --git a/src/Blades.cpp b/src/Blades.cpp index 80e3d76..230d65c 100644 --- a/src/Blades.cpp +++ b/src/Blades.cpp @@ -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_VERTEX_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); } diff --git a/src/Blades.h b/src/Blades.h index 9bd1eed..7376de8 100644 --- a/src/Blades.h +++ b/src/Blades.h @@ -4,12 +4,12 @@ #include #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 unsigned int NUM_BLADES = 1 << 8; +constexpr static float MIN_HEIGHT = 1.0f; +constexpr static float MAX_HEIGHT = 2.2f; +constexpr static float MIN_WIDTH = 0.06f; +constexpr static float MAX_WIDTH = 0.16f; +constexpr static float MIN_BEND = 8.0f; constexpr static float MAX_BEND = 13.0f; struct Blade { diff --git a/src/Renderer.cpp b/src/Renderer.cpp index b445d04..f15975d 100644 --- a/src/Renderer.cpp +++ b/src/Renderer.cpp @@ -198,6 +198,39 @@ 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 + // Describe the binding of the descriptor set layout + VkDescriptorSetLayoutBinding storageAllLayoutBinding = {}; + storageAllLayoutBinding.binding = 0; + storageAllLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + storageAllLayoutBinding.descriptorCount = 1; + storageAllLayoutBinding.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + storageAllLayoutBinding.pImmutableSamplers = nullptr; + + VkDescriptorSetLayoutBinding storageCulledLayoutBinding = {}; + storageCulledLayoutBinding.binding = 1; + storageCulledLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + storageCulledLayoutBinding.descriptorCount = 1; + storageCulledLayoutBinding.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + storageCulledLayoutBinding.pImmutableSamplers = nullptr; + + VkDescriptorSetLayoutBinding storageMetaLayoutBinding = {}; + storageMetaLayoutBinding.binding = 2; + storageMetaLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + storageMetaLayoutBinding.descriptorCount = 1; + storageMetaLayoutBinding.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + storageMetaLayoutBinding.pImmutableSamplers = nullptr; + + std::vector bindings = { storageAllLayoutBinding, storageCulledLayoutBinding, storageMetaLayoutBinding }; + + // Create the descriptor set layout + VkDescriptorSetLayoutCreateInfo layoutInfo = {}; + layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + layoutInfo.bindingCount = static_cast(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() { @@ -216,6 +249,8 @@ void Renderer::CreateDescriptorPool() { { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER , 1 }, // TODO: Add any additional types and counts of descriptors you will need to allocate + //Three Storage Buffers + { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER , static_cast(3 * scene->GetBlades().size()) }, }; VkDescriptorPoolCreateInfo poolInfo = {}; @@ -320,6 +355,50 @@ void Renderer::CreateModelDescriptorSets() { void Renderer::CreateGrassDescriptorSets() { // TODO: Create Descriptor sets for the grass. // This should involve creating descriptor sets which point to the model matrix of each group of grass blades + + //Changing model function + grassDescriptorSets.resize(scene->GetBlades().size()); + + // Describe the desciptor set + //Can use model because its allocated for model + grass + VkDescriptorSetLayout layouts[] = { modelDescriptorSetLayout }; + VkDescriptorSetAllocateInfo allocInfo = {}; + allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + allocInfo.descriptorPool = descriptorPool; //Update descip pool? + allocInfo.descriptorSetCount = static_cast(grassDescriptorSets.size()); + allocInfo.pSetLayouts = layouts; + + // Allocate descriptor sets + if (vkAllocateDescriptorSets(logicalDevice, &allocInfo, grassDescriptorSets.data()) != VK_SUCCESS) { + throw std::runtime_error("Failed to allocate descriptor set"); + } + + // Why 2*? + std::vector descriptorWrites(grassDescriptorSets.size()); + + for (uint32_t i = 0; i < scene->GetBlades().size(); ++i) { + VkDescriptorBufferInfo grassBufferInfo = {}; + grassBufferInfo.buffer = scene->GetBlades()[i]->GetModelBuffer(); + grassBufferInfo.offset = 0; + grassBufferInfo.range = sizeof(ModelBufferObject); + + //Do something with models but not images + descriptorWrites[i].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrites[i].dstSet = grassDescriptorSets[i]; + descriptorWrites[i].dstBinding = 0; + descriptorWrites[i].dstArrayElement = 0; + descriptorWrites[i].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + descriptorWrites[i].descriptorCount = 1; + descriptorWrites[i].pBufferInfo = &grassBufferInfo; + descriptorWrites[i].pImageInfo = nullptr; + descriptorWrites[i].pTexelBufferView = nullptr; + } + + // Update descriptor sets + vkUpdateDescriptorSets(logicalDevice, static_cast(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr); + + + } void Renderer::CreateTimeDescriptorSet() { @@ -358,8 +437,74 @@ 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 + + computeDescriptorSets.resize(scene->GetBlades().size()); + + // Describe the desciptor set + VkDescriptorSetLayout layouts[] = { computeDescriptorSetLayout }; + VkDescriptorSetAllocateInfo allocInfo = {}; + allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + allocInfo.descriptorPool = descriptorPool; + allocInfo.descriptorSetCount = static_cast(computeDescriptorSets.size()); + allocInfo.pSetLayouts = layouts; + + // Allocate descriptor sets + if (vkAllocateDescriptorSets(logicalDevice, &allocInfo, computeDescriptorSets.data()) != VK_SUCCESS) { + throw std::runtime_error("Failed to allocate descriptor set"); + } + + std::vector descriptorWrites(3 * computeDescriptorSets.size()); + + for (uint32_t i = 0; i < scene->GetBlades().size(); ++i) { + VkDescriptorBufferInfo allBufferInfo = {}; + allBufferInfo.buffer = scene->GetBlades()[i]->GetBladesBuffer(); + allBufferInfo.offset = 0; + allBufferInfo.range = sizeof(Blade) * NUM_BLADES; + + VkDescriptorBufferInfo culledBufferInfo = {}; + culledBufferInfo.buffer = scene->GetBlades()[i]->GetCulledBladesBuffer(); + culledBufferInfo.offset = 0; + culledBufferInfo.range = sizeof(Blade) * NUM_BLADES; + + VkDescriptorBufferInfo metaBufferInfo = {}; + metaBufferInfo.buffer = scene->GetBlades()[i]->GetNumBladesBuffer(); + metaBufferInfo.offset = 0; + metaBufferInfo.range = sizeof(BladeDrawIndirect); + + // Bind storage resources to the descriptor + descriptorWrites[3 * i + 0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrites[3 * i + 0].dstSet = computeDescriptorSets[i]; + descriptorWrites[3 * i + 0].dstBinding = 0; + descriptorWrites[3 * i + 0].dstArrayElement = 0; + descriptorWrites[3 * i + 0].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + descriptorWrites[3 * i + 0].descriptorCount = 1; + descriptorWrites[3 * i + 0].pBufferInfo = &allBufferInfo; + descriptorWrites[3 * i + 0].pImageInfo = nullptr; + descriptorWrites[3 * i + 0].pTexelBufferView = nullptr; + + descriptorWrites[3 * i + 1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrites[3 * i + 1].dstSet = computeDescriptorSets[i]; + descriptorWrites[3 * i + 1].dstBinding = 1; + descriptorWrites[3 * i + 1].dstArrayElement = 0; + descriptorWrites[3 * i + 1].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + descriptorWrites[3 * i + 1].descriptorCount = 1; + descriptorWrites[3 * i + 1].pBufferInfo = &culledBufferInfo; + descriptorWrites[3 * i + 1].pImageInfo = nullptr; + descriptorWrites[3 * i + 1].pTexelBufferView = nullptr; + + descriptorWrites[3 * i + 2].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrites[3 * i + 2].dstSet = computeDescriptorSets[i]; + descriptorWrites[3 * i + 2].dstBinding = 2; + descriptorWrites[3 * i + 2].dstArrayElement = 0; + descriptorWrites[3 * i + 2].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + descriptorWrites[3 * i + 2].descriptorCount = 1; + descriptorWrites[3 * i + 2].pBufferInfo = &metaBufferInfo; + descriptorWrites[3 * i + 2].pImageInfo = nullptr; + descriptorWrites[3 * i + 2].pTexelBufferView = nullptr; + } + + // Update descriptor sets + vkUpdateDescriptorSets(logicalDevice, static_cast(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr); } void Renderer::CreateGraphicsPipeline() { @@ -717,7 +862,7 @@ void Renderer::CreateComputePipeline() { computeShaderStageInfo.pName = "main"; // TODO: Add the compute dsecriptor set layout you create to this list - std::vector descriptorSetLayouts = { cameraDescriptorSetLayout, timeDescriptorSetLayout }; + std::vector descriptorSetLayouts = { cameraDescriptorSetLayout, timeDescriptorSetLayout, computeDescriptorSetLayout }; // Create pipeline layout VkPipelineLayoutCreateInfo pipelineLayoutInfo = {}; @@ -884,6 +1029,12 @@ 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 + for (uint32_t j = 0; j < scene->GetBlades().size(); ++j) + { + vkCmdBindDescriptorSets(computeCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, computePipelineLayout, 2, 1, &computeDescriptorSets[j], 0, nullptr); + vkCmdDispatch(computeCommandBuffer, ((NUM_BLADES - 1) / 32)+1, 1, 1); + } + // ~ End recording ~ if (vkEndCommandBuffer(computeCommandBuffer) != VK_SUCCESS) { @@ -973,16 +1124,19 @@ void Renderer::RecordCommandBuffers() { vkCmdBindPipeline(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, grassPipeline); for (uint32_t j = 0; j < scene->GetBlades().size(); ++j) { - VkBuffer vertexBuffers[] = { scene->GetBlades()[j]->GetCulledBladesBuffer() }; + //TODO Use Culled blades + 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 + //Copy from above + vkCmdBindDescriptorSets(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, grassPipelineLayout, 1, 1, &grassDescriptorSets[j], 0, nullptr); // 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 @@ -1057,6 +1211,8 @@ Renderer::~Renderer() { vkDestroyDescriptorSetLayout(logicalDevice, cameraDescriptorSetLayout, nullptr); vkDestroyDescriptorSetLayout(logicalDevice, modelDescriptorSetLayout, nullptr); vkDestroyDescriptorSetLayout(logicalDevice, timeDescriptorSetLayout, nullptr); + vkDestroyDescriptorSetLayout(logicalDevice, grassDescriptorSetLayout, nullptr); + vkDestroyDescriptorSetLayout(logicalDevice, computeDescriptorSetLayout, nullptr); vkDestroyDescriptorPool(logicalDevice, descriptorPool, nullptr); diff --git a/src/Renderer.h b/src/Renderer.h index 95e025f..4032cd9 100644 --- a/src/Renderer.h +++ b/src/Renderer.h @@ -56,12 +56,17 @@ class Renderer { VkDescriptorSetLayout cameraDescriptorSetLayout; VkDescriptorSetLayout modelDescriptorSetLayout; VkDescriptorSetLayout timeDescriptorSetLayout; + VkDescriptorSetLayout grassDescriptorSetLayout; + VkDescriptorSetLayout computeDescriptorSetLayout; + VkDescriptorPool descriptorPool; VkDescriptorSet cameraDescriptorSet; std::vector modelDescriptorSets; VkDescriptorSet timeDescriptorSet; + std::vector grassDescriptorSets; + std::vector computeDescriptorSets; VkPipelineLayout graphicsPipelineLayout; VkPipelineLayout grassPipelineLayout; diff --git a/src/main.cpp b/src/main.cpp index 8bf822b..71380ff 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,6 +5,7 @@ #include "Camera.h" #include "Scene.h" #include "Image.h" +#include "stdio.h" Device* device; SwapChain* swapChain; @@ -154,6 +155,7 @@ int main() { vkDestroyImage(device->GetVkDevice(), grassImage, nullptr); vkFreeMemory(device->GetVkDevice(), grassImageMemory, nullptr); + delete scene; delete plane; delete blades; @@ -161,6 +163,7 @@ int main() { delete renderer; delete swapChain; delete device; + vkDestroySurfaceKHR(instance->GetVkInstance(), surface, nullptr); delete instance; DestroyWindow(); return 0; diff --git a/src/shaders/compute.comp b/src/shaders/compute.comp index 0fd0224..306bef6 100644 --- a/src/shaders/compute.comp +++ b/src/shaders/compute.comp @@ -2,6 +2,18 @@ #extension GL_ARB_separate_shader_objects : enable #define WORKGROUP_SIZE 32 +#define TOLERANCE 1 +#define N 10 +#define D_MAX 21.0 + +#define O_CULL 1 +#define F_CULL 1 +#define D_CULL 1 + + + + + layout(local_size_x = WORKGROUP_SIZE, local_size_y = 1, local_size_z = 1) in; layout(set = 0, binding = 0) uniform CameraBufferObject { @@ -21,6 +33,27 @@ struct Blade { vec4 up; }; +layout (set = 2, binding = 0) buffer AllBlades +{ + Blade allBlades[]; +}; + +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; + + //Gravity Vector +vec4 D = vec4(0, -1, 0, 7.84); + + // TODO: Add bindings to: // 1. Store the input blades // 2. Write out the culled blades @@ -34,21 +67,178 @@ struct Blade { // uint instanceCount; // = 1 // uint firstVertex; // = 0 // uint firstInstance; // = 0 -// } numBlades; +// } numBlades; + bool inBounds(float value, float bounds) { return (value >= -bounds) && (value <= bounds); } +bool notInBounds(float value, float bounds) { + return !((value >= -bounds) && (value <= bounds)); +} + +vec2 random2(vec2 p ) +{ + return fract(sin(vec2(dot(p,vec2(127.1, 311.7)), + dot(p, vec2(269.5,183.3)))) + * 43758.5453); +} + +float surflet(vec2 p, vec2 gridPoint) +{ + // Compute the distance between p and the grid point along each axis, and warp it with a + // quintic function so we can smooth our cells + vec2 t2 = abs(p - gridPoint); + vec2 t = vec2(1.f) - 6.f * pow(t2, vec2(5.f)) + 15.f * pow(t2, vec2(4.f)) - 10.f * pow(t2, vec2(3.f)); + // Get the random vector for the grid point + // that returns a vec2 in the range [0, 1]) + vec2 gradient = random2(gridPoint) * 2.0f -vec2(1,1); + // Get the vector from the grid point to P + vec2 diff = p - gridPoint; + // Get the value of our height field by dotting grid->P with our gradient + float height = dot(diff, gradient); + // Scale our height field (i.e. reduce it) by our polynomial falloff function + return height * t.x * t.y; +} + + +float perlinNoise(vec2 uv) +{ + float surfletSum = 0.f; + // Iterate over the four integer corners surrounding uv + for(int dx = 0; dx <= 1; ++dx) { + for(int dy = 0; dy <= 1; ++dy) { + surfletSum += surflet(uv, floor(uv) + vec2(dx, dy)); + } + } + return surfletSum; +} + + +vec3 wind_influence(vec3 control_point, float t) +{ + vec3 result = sin(cos(control_point * 0.1 + 6 + t ) + 6)* 8; + + return vec3(result.x, 0, result.z); +} + + 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 + Blade cur_blade = allBlades[gl_GlobalInvocationID.x]; + //Unpack this + vec3 v0 = cur_blade.v0.xyz; + vec3 v1 = cur_blade.v1.xyz; + vec3 v2 = cur_blade.v2.xyz; + vec3 up = cur_blade.up.xyz; + float theta = cur_blade.v0.w; + float height = cur_blade.v1.w; + float width = cur_blade.v2.w; + float stiffness = cur_blade.up.w; // TODO: Apply forces on every blade and update the vertices in the buffer + //WIND + vec3 wi = wind_influence(cur_blade.v0.xyz, totalTime); + float fd = 1 - abs(dot(wi/length(wi),(cur_blade.v2.xyz - cur_blade.v0.xyz)/length(cur_blade.v2.xyz - cur_blade.v0.xyz))); + float fr = dot(cur_blade.v2.xyz - cur_blade.v0.xyz, cur_blade.up.xyz)/cur_blade.v1.w; + float alignment_factor = fd*fr; + vec3 wind = wi * alignment_factor; + + //GRAVITY + vec3 normal = vec3(cos(cur_blade.v0.w), 0.0f , sin(cur_blade.v0.w)); + vec3 ge = normalize(D.xyz)*D.w; + vec3 f = normalize(cross(normal, cur_blade.up.xyz)); + vec3 gf = 0.25 * length(ge) * f; + vec3 g = ge + gf; + + //RECOVERY + //Iv2 given by bladePosition + bladeUp * height + vec3 i_v2 = v0 + up * height; + vec3 r_force = (i_v2 - v2) * stiffness; + + vec3 tf = (g + wind + r_force) * deltaTime; + v2 += tf; + //Corrections + + v2 = v2 - up * min( dot(up, v2 - v0), 0.0f); + float l_proj = length(v2 - v0 - up * dot((v2 - v0), up)); + v1 = v0 + height * up * max(1 - l_proj/height, 0.05 * max(l_proj/height, 1.0f)); + float l_0 = distance(v0, v2); + float l_1 = distance(v0, v1) + distance(v1, v2); + float l =( 2 * l_0 + (2 - 1) * l_1) / ( 2 + 1); + float r_cor = height / l; + vec3 v1_corr = v0 + r_cor * (v1 - v0); + v2 = v1_corr + r_cor * (v2 - v1); + v1 = v1_corr; + cur_blade.v2.xyz = v2; + cur_blade.v1.xyz = v1; + + + + + allBlades[gl_GlobalInvocationID.x] = cur_blade; + + vec3 c = inverse(camera.view)[3].xyz; + //Camera to blade of grass view vector + vec3 view_vector = v0 - c - up * (dot((v0-c), up)); + //no #IF in GLSL? + if (O_CULL == 1) + { + //This doesn't look great and I'm not sure why + + if ( 0.9 > abs(dot(view_vector, normal))) + { + return; + } + } + //Frustrum Culling + if (F_CULL == 1) + { + vec3 m = 0.25 * v0 + 0.5 * v1 + 0.25 * v2; + //v0 + vec4 p = camera.proj * camera.view * vec4(v0,1); + float h = p.w * TOLERANCE; + bool v = inBounds(p.x, h) && inBounds(p.y, h) && inBounds(p.z, h); + //v2 + if (!v) + { + return; + } + p = camera.proj * camera.view * vec4(v2,1); + h = p.w * TOLERANCE; + v = inBounds(p.x, h) && inBounds(p.y, h) && inBounds(p.z, h); + if (!v) + { + return; + } + //Midpoint + p = camera.proj * camera.view * vec4(m,1); + h = p.w * TOLERANCE; + v = inBounds(p.x, h) && inBounds(p.y, h) && inBounds(p.z, h); + if (!v) + { + return; + } + } + //Distance + if (D_CULL == 1) + { + float d_proj = length(view_vector); + float d = distance(c, v0); + if ((gl_GlobalInvocationID.x % N) < int(floor(N * (1 - d_proj/D_MAX)))) + { + return; + } + } + uint vcount = atomicAdd(numBlades.vertexCount, 1); + culledBlades[vcount] = cur_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 diff --git a/src/shaders/grass.frag b/src/shaders/grass.frag index c7df157..648654b 100644 --- a/src/shaders/grass.frag +++ b/src/shaders/grass.frag @@ -7,11 +7,15 @@ layout(set = 0, binding = 0) uniform CameraBufferObject { } camera; // TODO: Declare fragment shader inputs - +layout (location = 0) in vec2 uv; layout(location = 0) out vec4 outColor; +vec3 bottom = normalize(vec3(179, 255, 174)); +vec3 top = normalize(vec3(255, 100, 100)); +//vec3 bottom = vec3(13./255.,10./255.,75./255.); +//vec3 top = normalize(vec3(102, 154, 27)); +void main(){ + // DONE: Compute fragment color -void main() { - // TODO: Compute fragment color - - outColor = vec4(1.0); + outColor.w = 1; + outColor.xyz = mix(bottom, top, uv.y); } diff --git a/src/shaders/grass.tesc b/src/shaders/grass.tesc index f9ffd07..fdf9729 100644 --- a/src/shaders/grass.tesc +++ b/src/shaders/grass.tesc @@ -8,19 +8,38 @@ layout(set = 0, binding = 0) uniform CameraBufferObject { mat4 proj; } camera; + +layout(location = 0) in vec4 in_v0[]; +layout(location = 1) in vec4 in_v1[]; +layout(location = 2) in vec4 in_v2[]; +layout(location = 3) in vec4 in_up[]; + +layout(location = 0) out vec4 out_v0[]; +layout(location = 1) out vec4 out_v1[]; +layout(location = 2) out vec4 out_v2[]; +layout(location = 3) out vec4 out_up[]; // TODO: Declare tessellation control shader inputs and outputs 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 + //gl_InvocationID should always be 0?? + out_v0[gl_InvocationID]= in_v0[gl_InvocationID]; + out_v1[gl_InvocationID]= in_v1[gl_InvocationID]; + out_v2[gl_InvocationID]= in_v2[gl_InvocationID]; + out_up[gl_InvocationID]= in_up[gl_InvocationID]; + + //TODO: Set level of tesselation + //TODO: update Tess level bassed on view + int tess_level = 7; - // 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] = tess_level; + gl_TessLevelInner[1] = tess_level; + gl_TessLevelOuter[0] = tess_level; + gl_TessLevelOuter[1] = tess_level; + gl_TessLevelOuter[2] = tess_level; + gl_TessLevelOuter[3] = tess_level; } diff --git a/src/shaders/grass.tese b/src/shaders/grass.tese index 751fff6..a7312e2 100644 --- a/src/shaders/grass.tese +++ b/src/shaders/grass.tese @@ -9,10 +9,44 @@ layout(set = 0, binding = 0) uniform CameraBufferObject { } camera; // TODO: Declare tessellation evaluation shader inputs and outputs +layout(location = 0) in vec4 in_v0[]; +layout(location = 1) in vec4 in_v1[]; +layout(location = 2) in vec4 in_v2[]; +layout(location = 3) in vec4 in_up[]; + +layout(location = 0) out vec2 uv; void main() { float u = gl_TessCoord.x; float v = gl_TessCoord.y; - + uv = vec2(u, v); + float theta = in_v0[0].w; + float width = in_v2[0].w; + //vec3 normal = vec3(sin(theta), 0 , -cos(theta)); + //vec3 tangent = vec3(cos(theta), 0, sin(theta)); + vec3 normal = vec3(sin(theta), 1, -cos(theta)); + vec3 tangent = vec3(cos(theta), 0, sin(theta)); + // vec3 t1 = normalize(cross(normal, tangent)); + //Looks better? + vec3 t1 = normalize(vec3(-cos(theta), 0.0f, -sin(theta))); // TODO: Use u and v to parameterize along the grass blade and output positions for each vertex of the grass blade + //0 for 1 vertex hopefully + vec3 a = in_v0[0].xyz + v*(in_v1[0].xyz - in_v0[0].xyz); + vec3 b = in_v1[0].xyz + v*(in_v2[0].xyz - in_v1[0].xyz); + vec3 c = a + v*(b-a); + vec3 c0 = c - width*t1; + vec3 c1 = c + width*t1; + //Maybe distance + vec3 t0 = (b-a)/distance(b,a); + vec3 n = cross(t0, t1)/length(cross(t0,t1)); + //Tri Tip + float tau = 0.75f; + float t = 0.5f + (u - 0.5f)*(1.0f-max((v-tau), 0)/(1.0f-tau)); + vec3 p = (1-t)*c0 + t *c1; + vec3 d = width * n * (0.5 - abs(u-0.5) * (1-v)); + p += d; + + + gl_Position = camera.proj * camera.view * vec4(p, 1); + } diff --git a/src/shaders/grass.vert b/src/shaders/grass.vert index db9dfe9..e7c815d 100644 --- a/src/shaders/grass.vert +++ b/src/shaders/grass.vert @@ -7,6 +7,15 @@ layout(set = 1, binding = 0) uniform ModelBufferObject { }; // TODO: Declare vertex shader inputs and outputs +layout(location = 0) in vec4 in_v0; +layout(location = 1) in vec4 in_v1; +layout(location = 2) in vec4 in_v2; +layout(location = 3) in vec4 in_up; + +layout(location = 0) out vec4 out_v0; +layout(location = 1) out vec4 out_v1; +layout(location = 2) out vec4 out_v2; +layout(location = 3) out vec4 out_up; out gl_PerVertex { vec4 gl_Position; @@ -14,4 +23,9 @@ out gl_PerVertex { void main() { // TODO: Write gl_Position and any other shader outputs + gl_Position = model * in_v0; + out_v0 = model * in_v0; + out_v1 = model * in_v1; + out_v2 = model * in_v2; + out_up = model * in_up; }