diff --git a/README.md b/README.md index 20ee451..cf0bde4 100644 --- a/README.md +++ b/README.md @@ -3,10 +3,59 @@ 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) +* Xiaoxiao Zou + * [LinkedIn](https://www.linkedin.com/in/xiaoxiao-zou-23482a1b9/) +* Tested on: Windows 11, AMD Ryzen 9 7940HS @ 4.00 GHz, Nvidia RTX 4060 Laptop -### (TODO: Your README) -*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. +### Result & Features +![](./img/result.gif) + +Features: +- culling test + - orientation culling test + + ![](./img/result1.gif) + Orietation culling is based on if the view direction is not in the same direction as orientation, since the grass has no width, it will cause aliasing effect. + + - view frustum culling test + + ![](./img/result2.gif) + + view frustum culling is based on if the grass is inside camera view, if it is not, the grass will not be rendered. + + - distance culling test + + ![](./img/result3.gif) + + distance culling is based on if grass is too far away from the camera, if it is too far, it will not be rendered. + +- physics simulation (no force shown below) + + ![](./img/result4.gif) + - gravity force + + ![](./img/result5.gif) + + gravity force will be applied to the grass. since there is no other force be applied to grass, all of grass is laid down. + + - recovery force (with gravity) + + ![](./img/result6.gif) + + with recovery force, the grass will be back to form a relatively steady position for the grass. + + - wind force (with gravity and wind) + + ![](./img/result.gif) + + with wind force, it will give grass a wind effect. I am using a wind force as sine function to get a effect to grass floating back and force. + +### Performance & Analysis + +![](./img/analysis1.png) +The FPS descreases as the blades counts become higher, especially when it reaches around 2^15. However, it roughly stays the same for low blades count (since threads count might be larger than blades count). + + +![](./img/analysis2.png) +The FPS get increased for all culling method individually. (culling test on same view with same blade count). Distance culling gives better performance improvement for this certain view compared to the other twos. All together, it gives best performance improvement. diff --git a/img/analysis1.png b/img/analysis1.png new file mode 100644 index 0000000..e8cdca2 Binary files /dev/null and b/img/analysis1.png differ diff --git a/img/analysis2.png b/img/analysis2.png new file mode 100644 index 0000000..03a9d86 Binary files /dev/null and b/img/analysis2.png differ diff --git a/img/result.gif b/img/result.gif new file mode 100644 index 0000000..cdd7964 Binary files /dev/null and b/img/result.gif differ diff --git a/img/result1.gif b/img/result1.gif new file mode 100644 index 0000000..ef06d3e Binary files /dev/null and b/img/result1.gif differ diff --git a/img/result2.gif b/img/result2.gif new file mode 100644 index 0000000..34840a0 Binary files /dev/null and b/img/result2.gif differ diff --git a/img/result3.gif b/img/result3.gif new file mode 100644 index 0000000..3441141 Binary files /dev/null and b/img/result3.gif differ diff --git a/img/result4.gif b/img/result4.gif new file mode 100644 index 0000000..8c972bd Binary files /dev/null and b/img/result4.gif differ diff --git a/img/result5.gif b/img/result5.gif new file mode 100644 index 0000000..ae7c893 Binary files /dev/null and b/img/result5.gif differ diff --git a/img/result6.gif b/img/result6.gif new file mode 100644 index 0000000..27753a7 Binary files /dev/null and b/img/result6.gif differ diff --git a/src/Renderer.cpp b/src/Renderer.cpp index b445d04..ff2ee8e 100644 --- a/src/Renderer.cpp +++ b/src/Renderer.cpp @@ -198,6 +198,38 @@ 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 + VkDescriptorSetLayoutBinding bladeLayoutBinding = {}; + bladeLayoutBinding.binding = 0; + bladeLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + bladeLayoutBinding.descriptorCount = 1; + bladeLayoutBinding.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + bladeLayoutBinding.pImmutableSamplers = nullptr; + + VkDescriptorSetLayoutBinding culledLayoutBinding = {}; + culledLayoutBinding.binding = 1; + culledLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + culledLayoutBinding.descriptorCount = 1; + culledLayoutBinding.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + culledLayoutBinding.pImmutableSamplers = nullptr; + + VkDescriptorSetLayoutBinding numLayoutBinding = {}; + numLayoutBinding.binding = 2; + numLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + numLayoutBinding.descriptorCount = 1; + numLayoutBinding.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + numLayoutBinding.pImmutableSamplers = nullptr; + + std::vector bindings = {bladeLayoutBinding,culledLayoutBinding,numLayoutBinding}; + + // 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 +248,7 @@ 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(3*scene->GetBlades().size()) } }; VkDescriptorPoolCreateInfo poolInfo = {}; @@ -320,6 +353,42 @@ 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()); + + // Describe the desciptor set + 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; + + // Allocate descriptor sets + 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; + } + + // Update descriptor sets + vkUpdateDescriptorSets(logicalDevice, static_cast(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr); } void Renderer::CreateTimeDescriptorSet() { @@ -360,6 +429,72 @@ 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 bladeBufferInfo = {}; + bladeBufferInfo.buffer = scene->GetBlades()[i]->GetBladesBuffer(); + bladeBufferInfo.offset = 0; + bladeBufferInfo.range =NUM_BLADES *sizeof(Blade); + + VkDescriptorBufferInfo culledBufferInfo = {}; + culledBufferInfo.buffer = scene->GetBlades()[i]->GetCulledBladesBuffer(); + culledBufferInfo.offset = 0; + culledBufferInfo.range = NUM_BLADES *sizeof(Blade); + + VkDescriptorBufferInfo numbladeBufferInfo = {}; + numbladeBufferInfo.buffer = scene->GetBlades()[i]->GetNumBladesBuffer(); + numbladeBufferInfo.offset = 0; + numbladeBufferInfo.range = sizeof(BladeDrawIndirect); + + descriptorWrites[3*i].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrites[3*i].dstSet = computeDescriptorSets[i]; + descriptorWrites[3*i].dstBinding = 0; + descriptorWrites[3*i].dstArrayElement = 0; + descriptorWrites[3*i].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + descriptorWrites[3*i].descriptorCount = 1; + descriptorWrites[3*i].pBufferInfo = &bladeBufferInfo; + descriptorWrites[3*i].pImageInfo = nullptr; + descriptorWrites[3*i].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 = &numbladeBufferInfo; + 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 +852,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 +1019,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 + 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/32, 1, 1); + } // ~ End recording ~ if (vkEndCommandBuffer(computeCommandBuffer) != VK_SUCCESS) { @@ -976,13 +1116,14 @@ 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, 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 @@ -1036,6 +1177,11 @@ void Renderer::Frame() { if (!swapChain->Present()) { RecreateFrameResources(); } + framecount++; + if(framecount%1000==0){ + printf("FPS: %f \n", 1.0f/scene->getDeltatime()); + } + } Renderer::~Renderer() { @@ -1057,6 +1203,7 @@ Renderer::~Renderer() { vkDestroyDescriptorSetLayout(logicalDevice, cameraDescriptorSetLayout, nullptr); vkDestroyDescriptorSetLayout(logicalDevice, modelDescriptorSetLayout, nullptr); vkDestroyDescriptorSetLayout(logicalDevice, timeDescriptorSetLayout, nullptr); + vkDestroyDescriptorSetLayout(logicalDevice, computeDescriptorSetLayout, nullptr); vkDestroyDescriptorPool(logicalDevice, descriptorPool, nullptr); diff --git a/src/Renderer.h b/src/Renderer.h index 95e025f..1cccb71 100644 --- a/src/Renderer.h +++ b/src/Renderer.h @@ -47,6 +47,7 @@ class Renderer { SwapChain* swapChain; Scene* scene; Camera* camera; + long int framecount; VkCommandPool graphicsCommandPool; VkCommandPool computeCommandPool; @@ -55,12 +56,15 @@ class Renderer { VkDescriptorSetLayout cameraDescriptorSetLayout; VkDescriptorSetLayout modelDescriptorSetLayout; + VkDescriptorSetLayout computeDescriptorSetLayout; VkDescriptorSetLayout timeDescriptorSetLayout; VkDescriptorPool descriptorPool; VkDescriptorSet cameraDescriptorSet; std::vector modelDescriptorSets; + std::vector grassDescriptorSets; + std::vector computeDescriptorSets; VkDescriptorSet timeDescriptorSet; VkPipelineLayout graphicsPipelineLayout; diff --git a/src/Scene.h b/src/Scene.h index 7699d78..7eae234 100644 --- a/src/Scene.h +++ b/src/Scene.h @@ -33,6 +33,9 @@ high_resolution_clock::time_point startTime = high_resolution_clock::now(); Scene(Device* device); ~Scene(); + float gettime(){return time.totalTime;}; + float getDeltatime(){return time.deltaTime;}; + const std::vector& GetModels() const; const std::vector& GetBlades() const; diff --git a/src/shaders/compute.comp b/src/shaders/compute.comp index 0fd0224..84b7979 100644 --- a/src/shaders/compute.comp +++ b/src/shaders/compute.comp @@ -2,6 +2,23 @@ #extension GL_ARB_separate_shader_objects : enable #define WORKGROUP_SIZE 32 + +#define WORKGROUP_SIZE 32 +#define TOLERANCE 0.01f; + +vec4 D=vec4(0.0f,-1.0f,0.0f,9.8f); +vec3 WIND_DIRECTION=vec3(-0.9f,0.0f,0.0f); +float WIND_WAVE_FREQUENCY=1.0f; +float WIND_MAGNIFIER=5.0f; +float DISTANCE_MAX_CULL=15.0f; +float DISTANCE_NUM_CULL=15; +float GRASS_MASS=0.9f; +float COLLISIONSTRENGTH=0.9f; + +bool orientationcull=true; +bool frustumcull=true; +bool distancecull=true; + layout(local_size_x = WORKGROUP_SIZE, local_size_y = 1, local_size_z = 1) in; layout(set = 0, binding = 0) uniform CameraBufferObject { @@ -21,6 +38,14 @@ struct Blade { vec4 up; }; +layout(set = 2, binding = 0) buffer Blades { + Blade in_blades[]; +} blades; + +layout(set = 2, binding = 1) buffer culledBlades { + Blade in_blades[]; +} cblades; + // TODO: Add bindings to: // 1. Store the input blades // 2. Write out the culled blades @@ -29,28 +54,118 @@ struct Blade { // 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 = 2) buffer NumBlades { + uint vertexCount; + uint instanceCount; + uint firstVertex; + uint firstInstance; +} numBlades; bool inBounds(float value, float bounds) { return (value >= -bounds) && (value <= bounds); } +vec3 wind(vec3 invec,float time){ + float mag=sin(mod(time*WIND_WAVE_FREQUENCY,6.28f))+1.0f; + return mag*WIND_MAGNIFIER*WIND_DIRECTION; + //return vec3(0.0f); +} + +bool inFrustum(vec3 in_p) { + vec4 p= camera.proj * camera.view * vec4(in_p, 1.0f); + float h = p.w + TOLERANCE; + return inBounds(p.x, h)&&inBounds(p.y, h)&&inBounds(p.z, h); +} + 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 blade = blades.in_blades[gl_GlobalInvocationID.x]; + + vec3 v0=blade.v0.xyz; + vec3 v1=blade.v1.xyz; + vec3 v2=blade.v2.xyz; + vec3 up=blade.up.xyz; + float orit = blade.v0.w; + float height = blade.v1.w; + float width = blade.v2.w; + float stiffness = blade.up.w; + vec3 f=normalize(cross(vec3(cos(orit), 0, sin(orit)), up)); + // TODO: Apply forces on every blade and update the vertices in the buffer + // gravity + vec3 gE = GRASS_MASS*normalize(D.xyz) * D.w; + vec3 gF = 0.25f * length(gE) * f; + vec3 gravity = gE + gF; + + // recovery + vec3 iv2 = v0 + up * height; + vec3 recovery = (iv2 - v2) * stiffness*COLLISIONSTRENGTH; + + // wind + vec3 winddir=wind(v0,totalTime); + float f_d=1.0f-abs(dot(normalize(winddir),normalize(v2-v0))); + float f_r=dot(v2-v0,up)/height; + float theta=f_d*f_r; + vec3 windforce=winddir*theta; + + // total force + vec3 tv2 = (gravity+recovery+windforce) * deltaTime; + //vec3 tv2=vec3(0.0f); + v2 += tv2; + v2 = v2-up*min(dot(up,v2-v0),0.0f); + //blades.in_blades[gl_GlobalInvocationID.x].v2.xyz=v2.xyz; + + // state validation + float l_proj = length(v2 - v0 - up * dot((v2 - v0), up)); + v1= v0+height*up*max(1.0f-l_proj/height,0.05f*max(l_proj/height,1.0f)); + + float L1 = length(v2-v0); + float L0 = length(v2-v1)+length(v1-v0); + float n = 2; + float L = (2*L0+(n-1)*L1)/(n+1); + float r =height/L; + + v1 = v0 + r * (v1 - v0); + v2 = v1 + r * (v2 - v1); + blades.in_blades[gl_GlobalInvocationID.x].v1.xyz=v1.xyz; + blades.in_blades[gl_GlobalInvocationID.x].v2.xyz=v2.xyz; + // TODO: Cull blades that are too far away or not in the camera frustum and write them // to the culled blades buffer + + mat4 inverseview=inverse(camera.view); + vec3 cpos=vec3(inverseview[3][0],inverseview[3][1],inverseview[3][2]); + + if(orientationcull){ + vec3 dir_c=cpos-v0; + vec3 dir_b = vec3(cos(orit), 0.f, sin(orit)); + if(abs(dot(normalize(dir_c), normalize(dir_b))) >=0.9f) + return; + } + + + if(frustumcull){ + if(!inFrustum(v0)||!inFrustum(v1)||!inFrustum(v2)) + return; + } + + if(distancecull){ + float d_proj = length(v0 - cpos - up * dot((v0-cpos), up)); + if(mod(gl_GlobalInvocationID.x,DISTANCE_NUM_CULL)<=floor(DISTANCE_NUM_CULL*(1.0f,d_proj/DISTANCE_MAX_CULL))) + return; + } + + + // 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 + + cblades.in_blades[atomicAdd(numBlades.vertexCount, 1)] = blades.in_blades[gl_GlobalInvocationID.x]; } diff --git a/src/shaders/grass.frag b/src/shaders/grass.frag index c7df157..4afcb8b 100644 --- a/src/shaders/grass.frag +++ b/src/shaders/grass.frag @@ -7,11 +7,16 @@ layout(set = 0, binding = 0) uniform CameraBufferObject { } camera; // TODO: Declare fragment shader inputs +layout(location = 0) in vec3 in_normal; +layout(location = 1) in vec3 in_pos; layout(location = 0) out vec4 outColor; +vec3 lightpos=vec3(10.0f,10.0f,10.0f); + void main() { // TODO: Compute fragment color - - outColor = vec4(1.0); + float mag=abs(dot(normalize(lightpos-in_pos),in_normal)); + mag=clamp(mag+0.3f,0.0f,1.0f); + outColor = mag*vec4(0.1f,0.54f,0.0f,1.0f); } diff --git a/src/shaders/grass.tesc b/src/shaders/grass.tesc index f9ffd07..7b1cb58 100644 --- a/src/shaders/grass.tesc +++ b/src/shaders/grass.tesc @@ -1,5 +1,6 @@ #version 450 #extension GL_ARB_separate_shader_objects : enable +#define level 15 layout(vertices = 1) out; @@ -9,18 +10,31 @@ layout(set = 0, binding = 0) uniform CameraBufferObject { } camera; // 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 v3[]; + +layout(location=0) out vec4 outv0[]; +layout(location=1) out vec4 outv1[]; +layout(location=2) out vec4 outv2[]; +layout(location=3) out vec4 outv3[]; 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 + outv0[gl_InvocationID]=v0[gl_InvocationID]; + outv1[gl_InvocationID]=v1[gl_InvocationID]; + outv2[gl_InvocationID]=v2[gl_InvocationID]; + outv3[gl_InvocationID]=v3[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] = ??? + gl_TessLevelInner[0] = level; + gl_TessLevelInner[1] = level; + gl_TessLevelOuter[0] = level; + gl_TessLevelOuter[1] = level; + gl_TessLevelOuter[2] = level; + gl_TessLevelOuter[3] = level; } diff --git a/src/shaders/grass.tese b/src/shaders/grass.tese index 751fff6..41caa34 100644 --- a/src/shaders/grass.tese +++ b/src/shaders/grass.tese @@ -9,10 +9,37 @@ layout(set = 0, binding = 0) uniform CameraBufferObject { } camera; // 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 v3[]; + +layout(location = 0) out vec3 out_normal; +layout(location = 1) out vec3 out_pos; void main() { float u = gl_TessCoord.x; float v = gl_TessCoord.y; + + vec3 ver0=v0[0].xyz; + vec3 ver1=v1[0].xyz; + vec3 ver2=v2[0].xyz; + vec3 up=v3[0].xyz; + float orit = v0[0].w; + float w = v2[0].w; + + vec3 t1=vec3(cos(orit), 0, sin(orit)); + vec3 a = mix(ver0, ver1, v); + vec3 b = mix(ver1, ver2, v); + vec3 c = mix(a, b, v); + vec3 c0=c-w*t1; + vec3 c1=c+w*t1; + vec3 t0=normalize(b-a); + + out_pos=t0; + out_normal=normalize(cross(t0,t1)); - // TODO: Use u and v to parameterize along the grass blade and output positions for each vertex of the grass blade + float t = u + 0.5 * v - u * v; + vec3 pos=mix(c0,c1,t); + gl_Position = camera.proj * camera.view * vec4(pos,1.0f); } diff --git a/src/shaders/grass.vert b/src/shaders/grass.vert index db9dfe9..202ab85 100644 --- a/src/shaders/grass.vert +++ b/src/shaders/grass.vert @@ -7,11 +7,25 @@ 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 v3; + +layout(location=0) out vec4 outv0; +layout(location=1) out vec4 outv1; +layout(location=2) out vec4 outv2; +layout(location=3) out vec4 outv3; out gl_PerVertex { - vec4 gl_Position; + vec4 gl_Position; }; void main() { // TODO: Write gl_Position and any other shader outputs + gl_Position = model * vec4(v0.xyz, 1.0); + outv0=vec4(vec3(model*vec4(vec3(v0),1.0f)),v0.w); + outv1=vec4(vec3(model*vec4(vec3(v1),1.0f)),v1.w); + outv2=vec4(vec3(model*vec4(vec3(v2),1.0f)),v2.w); + outv3=vec4(vec3(model*vec4(vec3(v3),1.0f)),v3.w); }