From c37b53882918aff37ee494c68d3fa567b1bf54e7 Mon Sep 17 00:00:00 2001 From: Lindsay Smith Date: Fri, 29 Oct 2021 03:10:52 -0400 Subject: [PATCH 1/4] shaders with bug --- src/Renderer.cpp | 182 ++++++++++++++++++++++++++++++++++++--- src/Renderer.h | 5 ++ src/shaders/compute.comp | 78 ++++++++++++++--- src/shaders/grass.frag | 14 ++- src/shaders/grass.tesc | 29 +++++-- src/shaders/grass.tese | 30 ++++++- src/shaders/grass.vert | 18 +++- 7 files changed, 314 insertions(+), 42 deletions(-) diff --git a/src/Renderer.cpp b/src/Renderer.cpp index b445d04..ba1051d 100644 --- a/src/Renderer.cpp +++ b/src/Renderer.cpp @@ -21,6 +21,7 @@ Renderer::Renderer(Device* device, SwapChain* swapChain, Scene* scene, Camera* c CreateModelDescriptorSetLayout(); CreateTimeDescriptorSetLayout(); CreateComputeDescriptorSetLayout(); + CreateGrassDescriptorSetLayout(); CreateDescriptorPool(); CreateCameraDescriptorSet(); CreateModelDescriptorSets(); @@ -195,9 +196,60 @@ void Renderer::CreateTimeDescriptorSetLayout() { } void Renderer::CreateComputeDescriptorSetLayout() { - // TODO: Create the descriptor set layout for the compute pipeline + // Done: 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 + VkDescriptorSetLayoutBinding inBladesLayoutBinding = {}; + inBladesLayoutBinding.binding = 0; + inBladesLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + inBladesLayoutBinding.descriptorCount = 1; + inBladesLayoutBinding.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + inBladesLayoutBinding.pImmutableSamplers = nullptr; + + VkDescriptorSetLayoutBinding culledBladesLayoutBinding = {}; + culledBladesLayoutBinding.binding = 1; + culledBladesLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + culledBladesLayoutBinding.descriptorCount = 1; + culledBladesLayoutBinding.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + culledBladesLayoutBinding.pImmutableSamplers = nullptr; + + VkDescriptorSetLayoutBinding numBladesLayoutBinding = {}; + numBladesLayoutBinding.binding = 2; + numBladesLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + numBladesLayoutBinding.descriptorCount = 1; + numBladesLayoutBinding.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + numBladesLayoutBinding.pImmutableSamplers = nullptr; + + std::vector bindings = { inBladesLayoutBinding, culledBladesLayoutBinding, numBladesLayoutBinding }; + + 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::CreateGrassDescriptorSetLayout() { + VkDescriptorSetLayoutBinding uboLayoutBinding = {}; + uboLayoutBinding.binding = 0; + uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + uboLayoutBinding.descriptorCount = 1; + uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; + uboLayoutBinding.pImmutableSamplers = nullptr; + + std::vector bindings = { uboLayoutBinding }; + + 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, &grassDescriptorSetLayout) != VK_SUCCESS) { + throw std::runtime_error("Failed to create descriptor set layout"); + } } void Renderer::CreateDescriptorPool() { @@ -215,7 +267,8 @@ void Renderer::CreateDescriptorPool() { // Time (compute) { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER , 1 }, - // TODO: Add any additional types and counts of descriptors you will need to allocate + // Done: Add any additional types and counts of descriptors you will need to allocate + { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER , static_cast(3 * scene->GetBlades().size()) }, }; VkDescriptorPoolCreateInfo poolInfo = {}; @@ -318,8 +371,39 @@ void Renderer::CreateModelDescriptorSets() { } void Renderer::CreateGrassDescriptorSets() { - // TODO: Create Descriptor sets for the grass. + // Done: 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[] = { grassDescriptorSetLayout }; + 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 grass descriptor set"); + } + + std::vector descriptorWrites(2 * 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); + + 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; + } } void Renderer::CreateTimeDescriptorSet() { @@ -358,8 +442,71 @@ void Renderer::CreateTimeDescriptorSet() { } void Renderer::CreateComputeDescriptorSets() { - // TODO: Create Descriptor sets for the compute pipeline + // Done: 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 compute descriptor set"); + } + + std::vector descriptorWrites(3 * computeDescriptorSets.size()); + + for (uint32_t i = 0; i < scene->GetBlades().size(); ++i) { + VkDescriptorBufferInfo inBladesBufferInfo = {}; + inBladesBufferInfo.buffer = scene->GetBlades()[i]->GetBladesBuffer(); + inBladesBufferInfo.offset = 0; + inBladesBufferInfo.range = NUM_BLADES * sizeof(Blade); + + VkDescriptorBufferInfo culledBladesBufferInfo = {}; + culledBladesBufferInfo.buffer = scene->GetBlades()[i]->GetCulledBladesBuffer(); + culledBladesBufferInfo.offset = 0; + culledBladesBufferInfo.range = NUM_BLADES * sizeof(Blade); + + VkDescriptorBufferInfo numBladesBufferInfo = {}; + numBladesBufferInfo.buffer = scene->GetBlades()[i]->GetNumBladesBuffer(); + numBladesBufferInfo.offset = 0; + numBladesBufferInfo.range = sizeof(BladeDrawIndirect); + + 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 = &inBladesBufferInfo; + 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 = &culledBladesBufferInfo; + 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 = &numBladesBufferInfo; + 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() { @@ -654,7 +801,7 @@ void Renderer::CreateGrassPipeline() { colorBlending.blendConstants[2] = 0.0f; colorBlending.blendConstants[3] = 0.0f; - std::vector descriptorSetLayouts = { cameraDescriptorSetLayout, modelDescriptorSetLayout }; + std::vector descriptorSetLayouts = { cameraDescriptorSetLayout, grassDescriptorSetLayout }; // Pipeline layout: used to specify uniform values VkPipelineLayoutCreateInfo pipelineLayoutInfo = {}; @@ -716,8 +863,8 @@ void Renderer::CreateComputePipeline() { computeShaderStageInfo.module = computeShaderModule; computeShaderStageInfo.pName = "main"; - // TODO: Add the compute dsecriptor set layout you create to this list - std::vector descriptorSetLayouts = { cameraDescriptorSetLayout, timeDescriptorSetLayout }; + // Done: Add the compute dsecriptor set layout you create to this list + std::vector descriptorSetLayouts = { cameraDescriptorSetLayout, timeDescriptorSetLayout, computeDescriptorSetLayout }; // Create pipeline layout VkPipelineLayoutCreateInfo pipelineLayoutInfo = {}; @@ -883,7 +1030,11 @@ void Renderer::RecordComputeCommandBuffer() { // Bind descriptor set for time uniforms 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 + // Done: For each group of blades bind its descriptor set and dispatch + for (size_t i = 0; i < scene->GetBlades().size(); ++i) { + vkCmdBindDescriptorSets(computeCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, computePipelineLayout, 2 + i, 1, &computeDescriptorSets[i], 0, nullptr); + vkCmdDispatch(computeCommandBuffer, (int)ceil((NUM_BLADES + WORKGROUP_SIZE - 1) / WORKGROUP_SIZE), 1, 1); + } // ~ End recording ~ if (vkEndCommandBuffer(computeCommandBuffer) != VK_SUCCESS) { @@ -975,14 +1126,15 @@ void Renderer::RecordCommandBuffers() { for (uint32_t j = 0; j < scene->GetBlades().size(); ++j) { VkBuffer vertexBuffers[] = { scene->GetBlades()[j]->GetCulledBladesBuffer() }; VkDeviceSize offsets[] = { 0 }; - // TODO: Uncomment this when the buffers are populated - // vkCmdBindVertexBuffers(commandBuffers[i], 0, 1, vertexBuffers, offsets); + // Done: Uncomment this when the buffers are populated + vkCmdBindVertexBuffers(commandBuffers[i], 0, 1, vertexBuffers, offsets); - // TODO: Bind the descriptor set for each grass blades model + // Done: Bind the descriptor set for each grass blades model + 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)); + // Done: Uncomment this when the buffers are populated + vkCmdDrawIndirect(commandBuffers[i], scene->GetBlades()[j]->GetNumBladesBuffer(), 0, 1, sizeof(BladeDrawIndirect)); } // End render pass @@ -1041,7 +1193,7 @@ void Renderer::Frame() { Renderer::~Renderer() { vkDeviceWaitIdle(logicalDevice); - // TODO: destroy any resources you created + // Done: destroy any resources you created vkFreeCommandBuffers(logicalDevice, graphicsCommandPool, static_cast(commandBuffers.size()), commandBuffers.data()); vkFreeCommandBuffers(logicalDevice, computeCommandPool, 1, &computeCommandBuffer); @@ -1057,6 +1209,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..230d824 100644 --- a/src/Renderer.h +++ b/src/Renderer.h @@ -19,6 +19,7 @@ class Renderer { void CreateModelDescriptorSetLayout(); void CreateTimeDescriptorSetLayout(); void CreateComputeDescriptorSetLayout(); + void CreateGrassDescriptorSetLayout(); void CreateDescriptorPool(); @@ -56,12 +57,16 @@ class Renderer { VkDescriptorSetLayout cameraDescriptorSetLayout; VkDescriptorSetLayout modelDescriptorSetLayout; VkDescriptorSetLayout timeDescriptorSetLayout; + VkDescriptorSetLayout computeDescriptorSetLayout; + VkDescriptorSetLayout grassDescriptorSetLayout; VkDescriptorPool descriptorPool; VkDescriptorSet cameraDescriptorSet; std::vector modelDescriptorSets; VkDescriptorSet timeDescriptorSet; + std::vector computeDescriptorSets; + std::vector grassDescriptorSets; VkPipelineLayout graphicsPipelineLayout; VkPipelineLayout grassPipelineLayout; diff --git a/src/shaders/compute.comp b/src/shaders/compute.comp index 0fd0224..8da9d8c 100644 --- a/src/shaders/compute.comp +++ b/src/shaders/compute.comp @@ -1,6 +1,10 @@ #version 450 #extension GL_ARB_separate_shader_objects : enable +#define ORIENTATION_CULL 0 +#define FRUSTUM_CULL 0 +#define DISTANCE_CULL 0 + #define WORKGROUP_SIZE 32 layout(local_size_x = WORKGROUP_SIZE, local_size_y = 1, local_size_z = 1) in; @@ -21,20 +25,26 @@ 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 -// 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 InBlades { + Blade inBlades[]; +}; + +layout(set = 2, binding = 1) buffer RemainingBlades { + Blade remainingBlades[]; +}; + + 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); @@ -43,14 +53,58 @@ 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 + // Done: Apply forces on every blade and update the vertices in the buffer + vec3 v0 = inBlades[gl_GlobalInvocationID.x].v0.xyz; + vec3 v1 = inBlades[gl_GlobalInvocationID.x].v1.xyz; + vec3 v2 = inBlades[gl_GlobalInvocationID.x].v2.xyz; + vec3 up = inBlades[gl_GlobalInvocationID.x].up.xyz; + float orientation = inBlades[gl_GlobalInvocationID.x].v0.w; + float h = inBlades[gl_GlobalInvocationID.x].v1.w; //height + float w = inBlades[gl_GlobalInvocationID.x].v2.w; //width + float s = inBlades[gl_GlobalInvocationID.x].up.w; //stiffness + vec3 oDir = normalize(vec3(cos(orientation), 0.0, sin(orientation))); //orientation direction + vec3 fDir = normalize(cross(up, oDir)); // face direction + + // recovery force + vec3 iv2 = v0 + h * up; + vec3 recovery = (iv2 - v2) * s; + + // gravity force + vec3 gDir = vec3(0.f, -1.f, 0.f); + float acc = -9.81f; //normal acceleration of gravity + vec3 gE = gDir * acc; + vec3 gF = 0.25 * length(gE) * fDir; + vec3 gravity = gE + gF; + + // TODO: wind force + //vec3 wind = vec3(0.f); + + vec3 tv2 = (gravity + recovery) * 0.01; + v2 += tv2; + + // state validation + v2 = v2 - up * min(dot(up, v2 - v0), 0.0); + float lProj = length(v2 - v0 - up * dot(v2 - v0, up)); + v1 = v0 + h * up * max(1.0 - (lProj / h), 0.05 * max(lProj / h, 1.0)); + float n = 2.0; //degrees + float L0 = distance(v0, v2); + float L1 = distance(v0, v1) + distance(v1, v2); + float L = (2.f * L0 + (n - 1.f) * L1) / (n + 1.f); + float r = h / L; + vec3 v1Corr = v0 + r * (v1 - v0); + vec3 v2Corr = v1Corr + r * (v2 - v1); + + inBlades[gl_GlobalInvocationID.x].v1 = vec4(v1Corr, h); + inBlades[gl_GlobalInvocationID.x].v2 = vec4(v2Corr, w); // 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 + + remainingBlades[atomicAdd(numBlades.vertexCount, 1)] = inBlades[gl_GlobalInvocationID.x]; } diff --git a/src/shaders/grass.frag b/src/shaders/grass.frag index c7df157..e970513 100644 --- a/src/shaders/grass.frag +++ b/src/shaders/grass.frag @@ -6,12 +6,20 @@ 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 inUV; +layout(location = 1) in float inHeight; layout(location = 0) out vec4 outColor; void main() { - // TODO: Compute fragment color + // Done: Compute fragment color + vec3 lightGreen = vec3(199.0, 232.0, 146.0) * (1.0 / 255.0); + vec3 medGreen = vec3(119.0, 150.0, 69.0) * (1.0 / 255.0); + vec3 darkGreen = vec3(59.0, 79.0, 26.0) * (1.0 / 255.0); + float t = inUV.y * inHeight / 2.5f; // max height of 2.5 - outColor = vec4(1.0); + vec3 color = mix(mix(lightGreen, medGreen, t), mix(medGreen, darkGreen, t), t); + + outColor = vec4(color, 1.0); } diff --git a/src/shaders/grass.tesc b/src/shaders/grass.tesc index f9ffd07..801d1ce 100644 --- a/src/shaders/grass.tesc +++ b/src/shaders/grass.tesc @@ -8,19 +8,30 @@ layout(set = 0, binding = 0) uniform CameraBufferObject { mat4 proj; } camera; -// TODO: Declare tessellation control shader inputs and outputs +// Done: Declare tessellation control shader inputs and outputs +layout(location = 0) in vec4[] inV0; +layout(location = 1) in vec4[] inV1; +layout(location = 2) in vec4[] inV2; +layout(location = 3) in vec4[] inUp; + +layout(location = 0) out vec4[] outV0; +layout(location = 1) out vec4[] outV1; +layout(location = 2) out vec4[] outV2; 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 + // Done: Write any shader outputs + outV0[gl_InvocationID] = inV0[gl_InvocationID]; + outV1[gl_InvocationID] = inV1[gl_InvocationID]; + outV2[gl_InvocationID] = inV2[gl_InvocationID]; - // TODO: Set level of tesselation - // gl_TessLevelInner[0] = ??? - // gl_TessLevelInner[1] = ??? - // gl_TessLevelOuter[0] = ??? - // gl_TessLevelOuter[1] = ??? - // gl_TessLevelOuter[2] = ??? - // gl_TessLevelOuter[3] = ??? + // Done (?): Set level of tesselation + gl_TessLevelInner[0] = 2; + gl_TessLevelInner[1] = 8; + gl_TessLevelOuter[0] = 8; + gl_TessLevelOuter[1] = 2; + gl_TessLevelOuter[2] = 8; + gl_TessLevelOuter[3] = 8; } diff --git a/src/shaders/grass.tese b/src/shaders/grass.tese index 751fff6..398e502 100644 --- a/src/shaders/grass.tese +++ b/src/shaders/grass.tese @@ -8,11 +8,37 @@ layout(set = 0, binding = 0) uniform CameraBufferObject { mat4 proj; } camera; -// TODO: Declare tessellation evaluation shader inputs and outputs +// Done: Declare tessellation evaluation shader inputs and outputs +layout(location = 0) in vec4[] inV0; +layout(location = 1) in vec4[] inV1; +layout(location = 2) in vec4[] inV2; + +layout(location = 0) out vec2 outUV; +layout(location = 1) out float outHeight; 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 + // Done: Use u and v to parameterize along the grass blade and output positions for each vertex of the grass blade + vec3 v0 = inV0[0].xyz; + vec3 v1 = inV1[0].xyz; + vec3 v2 = inV2[0].xyz; + float orientation = inV0[0].w; + float w = inV2[0].w; + + vec3 a = mix(v0, v1, v); + vec3 b = mix(v1, v2, v); + vec3 c = mix(a, b, v); + vec3 t1 = normalize(vec3(cos(orientation), 0.0, sin(orientation))); + vec3 c0 = c - w * t1; + vec3 c1 = c + w * t1; + + //triangle shape + float t = u + 0.5 * v - u * v; + vec4 pos = vec4(mix(c0, c1, t), 1.0); + + outUV = vec2(u, v); + outHeight = inV1[0].w; + gl_Position = camera.proj * camera.view * pos; } diff --git a/src/shaders/grass.vert b/src/shaders/grass.vert index db9dfe9..bbac645 100644 --- a/src/shaders/grass.vert +++ b/src/shaders/grass.vert @@ -6,12 +6,26 @@ layout(set = 1, binding = 0) uniform ModelBufferObject { mat4 model; }; -// TODO: Declare vertex shader inputs and outputs +// Done: Declare vertex shader inputs and outputs +layout(location = 0) in vec4 inV0; +layout(location = 1) in vec4 inV1; +layout(location = 2) in vec4 inV2; +layout(location = 3) in vec4 inUp; + +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 + // Done: Write gl_Position and any other shader outputs + outV0 = model * inV0; + outV1 = model * inV1; + outV2 = model * inV2; + outUp = inUp; + gl_Position = model * vec4(inV0.xyz, 1.0); } From e239120697dbbd83b22780580f10ce9ed8e24af1 Mon Sep 17 00:00:00 2001 From: Lindsay Smith <56971812+lsmith24@users.noreply.github.com> Date: Mon, 1 Nov 2021 00:14:27 -0400 Subject: [PATCH 2/4] Update README.md --- README.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 20ee451..101f0c2 100644 --- a/README.md +++ b/README.md @@ -3,10 +3,12 @@ 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) -### (TODO: Your README) +* Lindsay Smith +* [LinkedIn](https://www.linkedin.com/in/lindsay-j-smith/), [personal website](https://lindsays-portfolio-d6aa5d.webflow.io/). +* Tested on: Windows 10, i7-11800H 144Hz 16GB RAM, GeForce RTX 3060 512GB SSD (Personal Laptop) -*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. +## Notes +This project draws healivy from [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). It simulates the movement of grass blades, taking into account recovery, gravity, and wind forces. It also implements culling based on three different tests: distance, orientation, and view-frustum. + +This project currently has bugs/errors that I do not know how to solve, but contains a large amount of code intended to implement the various components mentioned. From eeae57d09bb515d78d5e4126e8ab8483fae62353 Mon Sep 17 00:00:00 2001 From: Lindsay Smith Date: Mon, 1 Nov 2021 01:12:56 -0400 Subject: [PATCH 3/4] culling and worley noise --- src/shaders/compute.comp | 69 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 65 insertions(+), 4 deletions(-) diff --git a/src/shaders/compute.comp b/src/shaders/compute.comp index 8da9d8c..f60c302 100644 --- a/src/shaders/compute.comp +++ b/src/shaders/compute.comp @@ -4,6 +4,8 @@ #define ORIENTATION_CULL 0 #define FRUSTUM_CULL 0 #define DISTANCE_CULL 0 +#define NOISE_FREQUENCY 7.0 +#define FBM_OCTAVES 6 #define WORKGROUP_SIZE 32 layout(local_size_x = WORKGROUP_SIZE, local_size_y = 1, local_size_z = 1) in; @@ -50,6 +52,37 @@ bool inBounds(float value, float bounds) { return (value >= -bounds) && (value <= bounds); } +// worley noise adapted from cis 460 + https://www.shadertoy.com/view/tlKXzh + +vec2 random( vec2 p ) { + return fract(sin(vec2(dot(p,vec2(127.1,311.7)),dot(p,vec2(269.5,183.3))))*43758.5453); +} + +float worleyNoise(vec2 uv) { + // Tile the space + vec2 uvInt = floor(uv); + vec2 uvFract = fract(uv); + float minDist = 1.0; // Minimum distance initialized to max. + + // Search all neighboring cells and this cell for their point + for(int y = -1; y <= 1; y++) { + for(int x = -1; x <= 1; x++) { + vec2 neighbor = vec2(float(x), float(y)); + + // Random point inside current neighboring cell + vec2 point = random(uvInt + neighbor); + + // Compute the distance b/t the point and the fragment + // Store the min dist thus far + vec2 diff = neighbor + point - uvFract; + float dist = length(diff); + minDist = min(minDist, dist); + } + } + return minDist; +} + + void main() { // Reset the number of blades to 0 if (gl_GlobalInvocationID.x == 0) { @@ -57,6 +90,8 @@ void main() { } barrier(); // Wait till all threads reach this point + mat4 invView = inverse(camera.view); //for culling + // Done: Apply forces on every blade and update the vertices in the buffer vec3 v0 = inBlades[gl_GlobalInvocationID.x].v0.xyz; vec3 v1 = inBlades[gl_GlobalInvocationID.x].v1.xyz; @@ -80,10 +115,10 @@ void main() { vec3 gF = 0.25 * length(gE) * fDir; vec3 gravity = gE + gF; - // TODO: wind force - //vec3 wind = vec3(0.f); + // Done: wind force + vec3 wind = normalize(vec3(sin(totalTime * worleyNoise(0.5 * v0.xz)), 0.5, cos(totalTime * worleyNoise(0.5 * v0.xz))));; - vec3 tv2 = (gravity + recovery) * 0.01; + vec3 tv2 = (gravity + recovery + wind) * 0.01; v2 += tv2; // state validation @@ -106,5 +141,31 @@ void main() { // 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 - remainingBlades[atomicAdd(numBlades.vertexCount, 1)] = inBlades[gl_GlobalInvocationID.x]; + bool cull = false; + + // orientation cull + vec3 viewDir = normalize(vec3(camera.view[0][2], camera.view[1][2], camera.view[2][2])); + cull = cull || abs(dot(oDir, viewDir)) > 0.9; + + // view frustum cull + vec3 midPt = 0.25 * v0 + 0.5 * v1 + 0.25 * v2; + vec4 v0NDC = camera.proj * camera.view * vec4(v0, 1.0); + vec4 v2NDC = camera.proj * camera.view * vec4(v2, 1.0); + vec4 midNDC = camera.proj * camera.view * vec4(midPt, 1.0); + float v0Bound = v0NDC.w + 0.1; + float v2Bound = v2NDC.w + 0.1; + float midBound = midNDC.w + 0.1; + bool v0Bool = inBounds(v0NDC.x, v0Bound) && inBounds(v0NDC.y, v0Bound) && inBounds(v0NDC.z, v0Bound); + bool v2Bool = inBounds(v2NDC.x, v2Bound) && inBounds(v2NDC.y, v2Bound) && inBounds(v2NDC.z, v2Bound); + bool midBool = inBounds(midNDC.x, midBound) && inBounds(midNDC.y, midBound) && inBounds(midNDC.z, midBound); + cull = cull || (!v0Bool && !v2Bool && !midBool); + + // distance cull + vec3 cameraPos = vec3(invView[3][0], invView[3][1], invView[3][2]); + float dProj = length(v0 - cameraPos - up * dot(v0 - cameraPos, up)); + cull = cull || (mod(gl_GlobalInvocationID.x, 3) > floor(3 * (1.0 - dProj / 50.f))); + + if (!cull) { + remainingBlades[atomicAdd(numBlades.vertexCount, 1)] = inBlades[gl_GlobalInvocationID.x]; + } } From 928fb91b3f0b53c5f82bbeaafc75a0daa719275d Mon Sep 17 00:00:00 2001 From: Lindsay Smith Date: Tue, 2 Nov 2021 10:26:00 -0400 Subject: [PATCH 4/4] one error fix --- src/Blades.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Blades.cpp b/src/Blades.cpp index 80e3d76..624c4fe 100644 --- a/src/Blades.cpp +++ b/src/Blades.cpp @@ -45,7 +45,8 @@ Blades::Blades(Device* device, VkCommandPool commandPool, float planeDim) : Mode 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::CreateBuffer(device, NUM_BLADES * sizeof(Blade), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, culledBladesBuffer, culledBladesBufferMemory); + 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); }