diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..378eac2 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +build diff --git a/README.md b/README.md index 20ee451..e911f99 100644 --- a/README.md +++ b/README.md @@ -3,10 +3,36 @@ 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) +* Han Wang -### (TODO: Your README) +* Tested on: Windows 11, 11th Gen Intel(R) Core(TM) i9-11900H @ 2.50GHz 22GB, GTX 3070 Laptop GPU -*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. +![Unlock FPS](img/hw5.gif) + + +## Summery + + +This project involves the creation of a grass rendering system built on the foundation of Vulkan. The primary tasks encompass three essential components: The rendering pipeline, compute shader and grass simulation. The rendering pipeline follows the standard Vulkan structure, progressing through the vertex shader, tessellation control shader, primitive generator, tessellation evaluation shader, geometry shader, and finally the fragment shader. In our pursuit of enhancing rendering efficiency, particularly when dealing with the complex demands of rendering grass, I strategically prioritized the implementation of the compute shader ahead of the fragment shader. Furthermore, I incorporated a sophisticated culling system designed to eliminate unnecessary computations in areas not visible, thereby significantly improving both efficiency and performance. This culling system comprises distance culling, view-frustum culling, and orientation culling. + + + + +![Unlock FPS](img/hw5_2.gif) + +## Analysis + +Based on the output I got, I tested the different numbers of blades and their run time on the rendering part and got the following graph: + +![Unlock FPS](img/runtime.png) + +Analyzing the above graph, it becomes readily apparent that a direct correlation exists between the number of blades to be drawn and the computational time required for these calculations. As the quantity of blades in the scene increases, the overall runtime is expected to exhibit an uptick. It's worth noting, though, that this increment is not particularly substantial, primarily due to the inherent parallel nature of the processes involved. In other words, the scaling of runtime with blade count, while observable, is moderated by the parallelism in the system, resulting in a relatively minor impact on overall performance. + +Also, since I've implemented all three different culling methods, I tried to implement them separately and get the following graph: + +![Unlock FPS](img/culling.png)] + + +The graph presented above offers a clear visual representation of discernible variations in frames per second (FPS) performance across three distinct culling methodologies. In my analysis, I believe that the primary factor contributing to potential FPS improvements with culling lies in the optimization of the rendering process, specifically in reducing the number of grass blades that necessitate rendering. + +These three culling methods diverge in their efficacy based on their respective capabilities to cull out portions of the grass scene. Essentially, the essence of their distinction is rooted in how effectively each method can eliminate or exclude certain grass blades from the rendering pipeline. The underlying principle is straightforward: the fewer grass blades that must be processed and rendered, the greater the potential increase in FPS, resulting in a more responsive and smoother visual experience for the user. diff --git a/bin/Debug/vulkan_grass_rendering.exe b/bin/Debug/vulkan_grass_rendering.exe new file mode 100644 index 0000000..6622e7d Binary files /dev/null and b/bin/Debug/vulkan_grass_rendering.exe differ diff --git a/bin/Debug/vulkan_grass_rendering.pdb b/bin/Debug/vulkan_grass_rendering.pdb new file mode 100644 index 0000000..ccca605 Binary files /dev/null and b/bin/Debug/vulkan_grass_rendering.pdb differ diff --git a/img/culling.png b/img/culling.png new file mode 100644 index 0000000..81a5c72 Binary files /dev/null and b/img/culling.png differ diff --git a/img/hw5.gif b/img/hw5.gif new file mode 100644 index 0000000..02f795c Binary files /dev/null and b/img/hw5.gif differ diff --git a/img/hw5_2.gif b/img/hw5_2.gif new file mode 100644 index 0000000..e178deb Binary files /dev/null and b/img/hw5_2.gif differ diff --git a/img/runtime.png b/img/runtime.png new file mode 100644 index 0000000..7922c95 Binary files /dev/null and b/img/runtime.png differ diff --git a/src/Blades.cpp b/src/Blades.cpp index 80e3d76..fe6961f 100644 --- a/src/Blades.cpp +++ b/src/Blades.cpp @@ -51,7 +51,7 @@ Blades::Blades(Device* device, VkCommandPool commandPool, float planeDim) : Mode VkBuffer Blades::GetBladesBuffer() const { return bladesBuffer; -} + } VkBuffer Blades::GetCulledBladesBuffer() const { return culledBladesBuffer; diff --git a/src/Renderer.cpp b/src/Renderer.cpp index b445d04..f7fa030 100644 --- a/src/Renderer.cpp +++ b/src/Renderer.cpp @@ -6,6 +6,10 @@ #include "Camera.h" #include "Image.h" +#include +#include +#include + static constexpr unsigned int WORKGROUP_SIZE = 32; Renderer::Renderer(Device* device, SwapChain* swapChain, Scene* scene, Camera* camera) @@ -14,6 +18,9 @@ Renderer::Renderer(Device* device, SwapChain* swapChain, Scene* scene, Camera* c swapChain(swapChain), scene(scene), camera(camera) { + //reference https://stackoverflow.com/questions/997946/how-to-get-current-time-and-date-in-c + auto start = std::chrono::system_clock::now(); + CreateCommandPools(); CreateRenderPass(); @@ -33,6 +40,15 @@ Renderer::Renderer(Device* device, SwapChain* swapChain, Scene* scene, Camera* c CreateComputePipeline(); RecordCommandBuffers(); RecordComputeCommandBuffer(); + + auto end = std::chrono::system_clock::now(); + + std::chrono::duration elapsed_seconds = end - start; + std::time_t end_time = std::chrono::system_clock::to_time_t(end); + + std::cout << "finished computation at " << std::ctime(&end_time) + << "elapsed time: " << elapsed_seconds.count()*1000 << "ms" + << std::endl; } void Renderer::CreateCommandPools() { @@ -198,6 +214,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 + // reference https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkDescriptorType.html#:~:text=VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER%20specifies%20a%20uniform%20buffer,a%20dynamic%20uniform%20buffer%20descriptor. + VkDescriptorSetLayoutBinding bladesBuffer = {}; + bladesBuffer.binding = 0; + bladesBuffer.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + bladesBuffer.descriptorCount = 1; + bladesBuffer.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + bladesBuffer.pImmutableSamplers = nullptr; + + VkDescriptorSetLayoutBinding culledBladesBuffer = {}; + culledBladesBuffer.binding = 1; + culledBladesBuffer.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + culledBladesBuffer.descriptorCount = 1; + culledBladesBuffer.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + culledBladesBuffer.pImmutableSamplers = nullptr; + + VkDescriptorSetLayoutBinding numBladesBuffer = {}; + numBladesBuffer.binding = 2; + numBladesBuffer.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + numBladesBuffer.descriptorCount = 1; + numBladesBuffer.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + numBladesBuffer.pImmutableSamplers = nullptr; + + std::vector bindings = { bladesBuffer, culledBladesBuffer, numBladesBuffer}; + + 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 +265,8 @@ void Renderer::CreateDescriptorPool() { { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER , 1 }, // TODO: Add any additional types and counts of descriptors you will need to allocate + + { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,static_cast(scene->GetBlades().size()*3) } }; VkDescriptorPoolCreateInfo poolInfo = {}; @@ -320,6 +371,46 @@ 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 + grassDescriptorSets.resize(scene->GetBlades().size()); + + VkDescriptorSetLayout layouts[] = { modelDescriptorSetLayout }; + VkDescriptorSetAllocateInfo allocInfo = {}; + allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + allocInfo.descriptorPool = descriptorPool; + allocInfo.descriptorSetCount = static_cast(grassDescriptorSets.size()); + allocInfo.pSetLayouts = layouts; + + if (vkAllocateDescriptorSets(logicalDevice, &allocInfo, grassDescriptorSets.data()) != VK_SUCCESS) { + throw std::runtime_error("Failed to allocate descriptor set"); + } + + std::vector descriptorWrites(grassDescriptorSets.size()); + + + for (uint32_t i = 0; i < scene->GetBlades().size(); ++i) { + VkDescriptorBufferInfo modelBufferInfo = {}; + modelBufferInfo.buffer = scene->GetBlades()[i]->GetModelBuffer(); + modelBufferInfo.offset = 0; + modelBufferInfo.range = sizeof(ModelBufferObject); + + 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 = &modelBufferInfo; + descriptorWrites[i].pImageInfo = nullptr; + descriptorWrites[i].pTexelBufferView = nullptr; + + + + } + + vkUpdateDescriptorSets(logicalDevice, static_cast(grassDescriptorSets.size()), descriptorWrites.data(), 0, nullptr); + + + } void Renderer::CreateTimeDescriptorSet() { @@ -360,6 +451,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()); + + 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; + + 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 BladeBuffer_info = {}; + BladeBuffer_info.buffer = scene->GetBlades()[i]->GetBladesBuffer(); + BladeBuffer_info.offset = 0; + BladeBuffer_info.range = sizeof(Blade)*NUM_BLADES; + + 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 = &BladeBuffer_info; + descriptorWrites[3 * i + 0].pImageInfo = nullptr; + descriptorWrites[3 * i + 0].pTexelBufferView = nullptr; + + VkDescriptorBufferInfo culledBladesBuffer_info = {}; + culledBladesBuffer_info.buffer = scene->GetBlades()[i]->GetCulledBladesBuffer(); + culledBladesBuffer_info.offset = 0; + culledBladesBuffer_info.range = sizeof(Blade) * NUM_BLADES; + + 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 = &culledBladesBuffer_info; + descriptorWrites[3 * i + 1].pImageInfo = nullptr; + descriptorWrites[3 * i + 1].pTexelBufferView = nullptr; + + VkDescriptorBufferInfo numBladesBuffer_info = {}; + numBladesBuffer_info.buffer = scene->GetBlades()[i]->GetNumBladesBuffer(); + numBladesBuffer_info.offset = 0; + numBladesBuffer_info.range = sizeof(BladeDrawIndirect); + + 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 = &numBladesBuffer_info; + descriptorWrites[3 * i + 2].pImageInfo = nullptr; + descriptorWrites[3 * i + 2].pTexelBufferView = nullptr; + + } + + vkUpdateDescriptorSets(logicalDevice, static_cast(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr); + + } void Renderer::CreateGraphicsPipeline() { @@ -717,7 +876,11 @@ 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 = {}; @@ -885,6 +1048,14 @@ void Renderer::RecordComputeCommandBuffer() { // TODO: For each group of blades bind its descriptor set and dispatch + for (uint32_t i = 0; i < scene->GetBlades().size(); ++i) { + + vkCmdBindDescriptorSets(computeCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, computePipelineLayout, 2, 1, &computeDescriptorSets[i], 0, nullptr); + vkCmdDispatch(computeCommandBuffer, NUM_BLADES / WORKGROUP_SIZE, 1, 1); + } + + + // ~ End recording ~ if (vkEndCommandBuffer(computeCommandBuffer) != VK_SUCCESS) { throw std::runtime_error("Failed to record compute command buffer"); @@ -976,13 +1147,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 - + vkCmdBindDescriptorSets(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipelineLayout, 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 diff --git a/src/Renderer.h b/src/Renderer.h index 95e025f..a725d82 100644 --- a/src/Renderer.h +++ b/src/Renderer.h @@ -56,13 +56,18 @@ class Renderer { VkDescriptorSetLayout cameraDescriptorSetLayout; VkDescriptorSetLayout modelDescriptorSetLayout; VkDescriptorSetLayout timeDescriptorSetLayout; + VkDescriptorSetLayout computeDescriptorSetLayout; VkDescriptorPool descriptorPool; VkDescriptorSet cameraDescriptorSet; std::vector modelDescriptorSets; + VkDescriptorSet timeDescriptorSet; + std::vector grassDescriptorSets; + std::vector computeDescriptorSets; + VkPipelineLayout graphicsPipelineLayout; VkPipelineLayout grassPipelineLayout; VkPipelineLayout computePipelineLayout; diff --git a/src/Scene.cpp b/src/Scene.cpp index 86894f2..b4d53ac 100644 --- a/src/Scene.cpp +++ b/src/Scene.cpp @@ -1,5 +1,6 @@ #include "Scene.h" #include "BufferUtils.h" +#include Scene::Scene(Device* device) : device(device) { BufferUtils::CreateBuffer(device, sizeof(Time), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, timeBuffer, timeBufferMemory); diff --git a/src/shaders/compute.comp b/src/shaders/compute.comp index 0fd0224..9c72bbb 100644 --- a/src/shaders/compute.comp +++ b/src/shaders/compute.comp @@ -21,36 +21,144 @@ 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; - bool inBounds(float value, float bounds) { return (value >= -bounds) && (value <= bounds); } +bool inBounds_vec4(vec4 value) { + + float h = value.w -0.05; + return inBounds(value.x,h) && inBounds(value.y,h) && inBounds(value.z,h) ; +} + + +layout(set=2, binding = 0) buffer inputBlades{ + Blade input_blade[]; +}; + +layout(set=2, binding = 1) buffer culledBlades{ + Blade culled_blade[]; +}; + + +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; + + + + + 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 + barrier(); + + uint m_index = gl_GlobalInvocationID.x; + + + bool leave = true; + + + + vec3 c = inverse(camera.view)[3].xyz; + vec3 v0 = input_blade[m_index].v0.xyz; + vec3 v1 = input_blade[m_index].v1.xyz; + vec3 v2 = input_blade[m_index].v2.xyz; + vec3 up = input_blade[m_index].up.xyz; + float dmax = 50; + float level = 20; + + //Distance test + //reference https://community.khronos.org/t/extracting-camera-position-from-a-modelview-matrix/68031 + float dproj = length(v0 - c - up * dot(v0 - c,up)); + float idModn =mod(m_index,level); + if(idModn >(level * (1 - dproj/dmax))){ + leave = false; + } + //orientation test £» + + float angle = input_blade[m_index].v0.w; + vec3 dirc = normalize(c-v0); + vec3 dirb = normalize(vec3(cos(angle),0.0,sin(angle))); + + if(abs(dot(dirc,dirb))>0.9){ + leave = false; + } + //View-frustum test; + + vec3 midPoint = 0.25 * v0 + 0.5*v1 + 0.25*v2; + vec4 homo_v0 = vec4(v0,1); + vec4 homo_v1 = vec4(v1,1); + vec4 homo_v2 = vec4(midPoint,1); + + mat4 vp = camera.proj * camera.view; + vec4 p1 = vp* homo_v0; + vec4 p2 = vp* homo_v1; + vec4 p3 = vp* homo_v2; + float h = input_blade[m_index].v1.w; + + if ((!inBounds_vec4(p1)) && (!inBounds_vec4(p2)) && (!inBounds_vec4(p3)) ){ + leave = false; + } + + + if(leave){ + + //gravity------------------------------------- + vec4 D = vec4(0,-1,0,9.8); + vec3 gE = normalize(D.xyz) * D.w; + vec3 f = normalize(cross(normalize(vec3(cos(angle),0.0,sin(angle))),up)); + vec3 gF = 0.25 * length(gE) * f; + vec3 g = gE+gF; + //wind----------------------------------------- + + vec3 wi = 6 * normalize(vec3(-1,0,0))* (cos(totalTime)+1); + float fdWiV0 = 1 - abs(dot(normalize(wi),normalize(v2-v0))); + float frh = dot((v2-v0),up)/h; + float theta = fdWiV0*frh; + + vec3 w = wi*theta; + + //recovery------------------------------------- + + vec3 re = (v0 + up * h - v2) * input_blade[m_index].up.w; + + + //overall + vec3 overallForce = (g + re + w) * deltaTime; + //vec3 overallForce = vec3(0); + + + v2 = v2 + overallForce; + v2 = v2 - up* min(dot(up, v2 - v0), 0.0); + + float L_proj = length(v2 - v0 - up * dot(v2-v0 , up) ); + + v1 = v0 + h * up * max(1.0-L_proj/h, 0.05*max(L_proj/h,1.0)); + + float n = 3; + float L0 = length(v2-v0); + float L1 = length(v2-v1) + length(v1-v0); + float L = (2*L0 + (n-1)*L1)/(n+1); + + float r = h/L; + vec3 v1_correct = v0 + r*(v1-v0); + vec3 v2_correct = v1_correct + r*(v2-v1); + + input_blade[m_index].v1.xyz = v1_correct; + input_blade[m_index].v2.xyz = v2_correct; + + - // TODO: Apply forces on every blade and update the vertices in the buffer - // 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 + culled_blade[atomicAdd(numBlades.vertexCount, 1)] = input_blade[m_index]; + + } + } diff --git a/src/shaders/grass.frag b/src/shaders/grass.frag index c7df157..b8e0937 100644 --- a/src/shaders/grass.frag +++ b/src/shaders/grass.frag @@ -7,11 +7,18 @@ layout(set = 0, binding = 0) uniform CameraBufferObject { } camera; // TODO: Declare fragment shader inputs +layout(location = 0) in vec3 normal; layout(location = 0) out vec4 outColor; void main() { // TODO: Compute fragment color + vec3 lightDirection = normalize(vec3(1,-1,0)); + vec3 grassColor = vec3(0.29, 0.929, 0.173); + float lambert = max(0,dot(normal,lightDirection)); + vec3 ambient = normalize(vec3(69, 111, 124)); - outColor = vec4(1.0); + vec3 new_vert = ambient + lambert * grassColor; + + outColor = vec4(new_vert,1); } diff --git a/src/shaders/grass.tesc b/src/shaders/grass.tesc index f9ffd07..87de2c2 100644 --- a/src/shaders/grass.tesc +++ b/src/shaders/grass.tesc @@ -10,6 +10,20 @@ layout(set = 0, binding = 0) uniform CameraBufferObject { // TODO: Declare tessellation control 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[] outV0; +layout(location = 1) out vec4[] outV1; +layout(location = 2) out vec4[] outV2; +layout(location = 3) out vec4[] outUp; + + + + void main() { // Don't move the origin location of the patch gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position; @@ -17,10 +31,15 @@ void main() { // TODO: Write any shader outputs // 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; + gl_TessLevelInner[1] = 1; + gl_TessLevelOuter[0] = 6; + gl_TessLevelOuter[1] = 1; + gl_TessLevelOuter[2] = 6; + gl_TessLevelOuter[3] = 1; + + outV0[gl_InvocationID] = v0[gl_InvocationID]; + outV1[gl_InvocationID] = v1[gl_InvocationID]; + outV2[gl_InvocationID] = v2[gl_InvocationID]; + outUp[gl_InvocationID] = up[gl_InvocationID]; } diff --git a/src/shaders/grass.tese b/src/shaders/grass.tese index 751fff6..8a7ac8f 100644 --- a/src/shaders/grass.tese +++ b/src/shaders/grass.tese @@ -10,9 +10,50 @@ layout(set = 0, binding = 0) uniform CameraBufferObject { // TODO: Declare tessellation evaluation 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 vec3 normal; + + + void main() { float u = gl_TessCoord.x; float v = gl_TessCoord.y; + // From geom part of the papger + + vec3 new_v0 = vec3(v0[0]); + vec3 new_v1 = vec3(v1[0]); + vec3 new_v2 = vec3(v2[0]); + vec3 new_up = vec3(up[0]); + float w = v2[0].w; + float angle = v0[0].w; + + //vec3 forward = cross(up,vec3(sin(angle),0.0,cos(angle))); + + vec3 a = new_v0 + v * (new_v1 - new_v0); + vec3 b = new_v1 + v * (new_v2 - new_v1); + vec3 c = a + v * (b - a); + + + + vec3 t1 = normalize(vec3(cos(angle),0.0,sin(angle))); + vec3 c0 = c - w * t1; + vec3 c1 = c + w * t1; + + vec3 t0 = normalize(b - a); + normal = normalize(cross(t0, t1)); + + float t = u + 0.5 * v - u * v; + vec3 position = (1 - t) * c0 + t * c1; + vec4 new_position = vec4(position, 1); + + gl_Position = camera.proj * camera.view * new_position; + + // TODO: Use u and v to parameterize along the grass blade and output positions for each vertex of the grass blade } diff --git a/src/shaders/grass.vert b/src/shaders/grass.vert index db9dfe9..696f8bc 100644 --- a/src/shaders/grass.vert +++ b/src/shaders/grass.vert @@ -8,10 +8,29 @@ 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 outV0; +layout(location = 1) out vec4 outV1; +layout(location = 2) out vec4 outV2; +layout(location = 3) out vec4 outUp; + out gl_PerVertex { vec4 gl_Position; }; void main() { - // TODO: Write gl_Position and any other shader outputs + + gl_Position = v0; + + outV0 = v0; + outV1 = v1; + outV2 = v2; + outUp = up; + + // TODO: Write gl_Position and any other shader outputs }