diff --git a/computecloth/computecloth.cpp b/computecloth/computecloth.cpp new file mode 100644 index 00000000..b3aeb489 --- /dev/null +++ b/computecloth/computecloth.cpp @@ -0,0 +1,731 @@ +/* +* Vulkan Example - Compute shader sloth simulation +* +* Updated compute shader by Lukas Bergdoll (https://github.com/Voultapher) +* +* Copyright (C) 2016-2017 by Sascha Willems - www.saschawillems.de +* +* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) +*/ + +#include +#include +#include +#include +#include +#include + +#define GLM_FORCE_RADIANS +#define GLM_FORCE_DEPTH_ZERO_TO_ONE +#include +#include + +#include +#include "vulkanexamplebase.h" +#include "VulkanTexture.hpp" +#include "VulkanModel.hpp" + +#define ENABLE_VALIDATION false + +class VulkanExample : public VulkanExampleBase +{ +public: + uint32_t sceneSetup = 1; + uint32_t readSet = 0; + uint32_t indexCount; + + vks::Texture2D textureCloth; + + vks::VertexLayout vertexLayout = vks::VertexLayout({ + vks::VERTEX_COMPONENT_POSITION, + vks::VERTEX_COMPONENT_UV, + vks::VERTEX_COMPONENT_NORMAL, + }); + vks::Model modelSphere; + + // Resources for the graphics part of the example + struct { + VkDescriptorSetLayout descriptorSetLayout; + VkDescriptorSet descriptorSet; + VkPipelineLayout pipelineLayout; + struct Pipelines { + VkPipeline cloth; + VkPipeline sphere; + } pipelines; + vks::Buffer indices; + vks::Buffer uniformBuffer; + struct graphicsUBO { + glm::mat4 projection; + glm::mat4 view; + glm::vec4 lightPos = glm::vec4(-1.0f, 2.0f, -1.0f, 1.0f); + } ubo; + } graphics; + + // Resources for the compute part of the example + struct { + struct StorageBuffers { + vks::Buffer input; + vks::Buffer output; + } storageBuffers; + vks::Buffer uniformBuffer; + VkQueue queue; + VkCommandPool commandPool; + std::array commandBuffers; + VkFence fence; + VkDescriptorSetLayout descriptorSetLayout; + std::array descriptorSets; + VkPipelineLayout pipelineLayout; + VkPipeline pipeline; + struct computeUBO { + float deltaT = 0.0f; + float particleMass = 0.1f; + float springStiffness = 2000.0f; + float damping = 0.25f; + float restDistH; + float restDistV; + float restDistD; + float sphereRadius = 0.5f; + glm::vec4 spherePos = glm::vec4(0.0f, 0.0f, 0.0f, 0.0f); + glm::vec4 gravity = glm::vec4(0.0f, 9.8f, 0.0f, 0.0f); + glm::ivec2 particleCount; + } ubo; + } compute; + + // SSBO cloth grid particle declaration + struct Particle { + glm::vec4 pos; + glm::vec4 vel; + glm::vec4 uv; + glm::vec4 normal; + float pinned; + glm::vec3 _pad0; + }; + + struct Cloth { + glm::uvec2 gridsize = glm::uvec2(60, 60); + glm::vec2 size = glm::vec2(2.5f, 2.5f); + } cloth; + + VulkanExample() : VulkanExampleBase(ENABLE_VALIDATION) + { + enableTextOverlay = true; + title = "Vulkan Example - Compute shader cloth simulation"; + camera.type = Camera::CameraType::lookat; + camera.setPerspective(60.0f, (float)width / (float)height, 0.1f, 512.0f); + camera.setRotation(glm::vec3(-30.0f, -45.0f, 0.0f)); + camera.setTranslation(glm::vec3(0.0f, 0.0f, -3.5f)); + paused = true; + } + + ~VulkanExample() + { + // Graphics + graphics.uniformBuffer.destroy(); + vkDestroyPipeline(device, graphics.pipelines.cloth, nullptr); + vkDestroyPipeline(device, graphics.pipelines.sphere, nullptr); + vkDestroyPipelineLayout(device, graphics.pipelineLayout, nullptr); + vkDestroyDescriptorSetLayout(device, graphics.descriptorSetLayout, nullptr); + textureCloth.destroy(); + modelSphere.destroy(); + + // Compute + compute.storageBuffers.input.destroy(); + compute.storageBuffers.output.destroy(); + compute.uniformBuffer.destroy(); + vkDestroyPipelineLayout(device, compute.pipelineLayout, nullptr); + vkDestroyDescriptorSetLayout(device, compute.descriptorSetLayout, nullptr); + vkDestroyPipeline(device, compute.pipeline, nullptr); + vkDestroyFence(device, compute.fence, nullptr); + vkDestroyCommandPool(device, compute.commandPool, nullptr); + } + + // Enable physical device features required for this example + virtual void getEnabledFeatures() + { + if (deviceFeatures.samplerAnisotropy) { + enabledFeatures.samplerAnisotropy = VK_TRUE; + } + }; + + void loadAssets() + { + textureCloth.loadFromFile(getAssetPath() + "textures/vulkan_cloth_rgba.ktx", VK_FORMAT_R8G8B8A8_UNORM, vulkanDevice, queue); + modelSphere.loadFromFile(ASSET_PATH "models/geosphere.obj", vertexLayout, compute.ubo.sphereRadius * 0.05f, vulkanDevice, queue); + } + + void buildCommandBuffers() + { + // Destroy command buffers if already present + if (!checkCommandBuffers()) + { + destroyCommandBuffers(); + createCommandBuffers(); + } + + VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo(); + + VkClearValue clearValues[2]; + clearValues[0].color = { { 0.0f, 0.0f, 0.0f, 1.0f } };; + clearValues[1].depthStencil = { 1.0f, 0 }; + + VkRenderPassBeginInfo renderPassBeginInfo = vks::initializers::renderPassBeginInfo(); + renderPassBeginInfo.renderPass = renderPass; + renderPassBeginInfo.renderArea.offset.x = 0; + renderPassBeginInfo.renderArea.offset.y = 0; + renderPassBeginInfo.renderArea.extent.width = width; + renderPassBeginInfo.renderArea.extent.height = height; + renderPassBeginInfo.clearValueCount = 2; + renderPassBeginInfo.pClearValues = clearValues; + + for (int32_t i = 0; i < drawCmdBuffers.size(); ++i) + { + // Set target frame buffer + renderPassBeginInfo.framebuffer = frameBuffers[i]; + + VK_CHECK_RESULT(vkBeginCommandBuffer(drawCmdBuffers[i], &cmdBufInfo)); + + // Draw the particle system using the update vertex buffer + + vkCmdBeginRenderPass(drawCmdBuffers[i], &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); + + VkViewport viewport = vks::initializers::viewport((float)width, (float)height, 0.0f, 1.0f); + vkCmdSetViewport(drawCmdBuffers[i], 0, 1, &viewport); + + VkRect2D scissor = vks::initializers::rect2D(width, height, 0, 0); + vkCmdSetScissor(drawCmdBuffers[i], 0, 1, &scissor); + + VkDeviceSize offsets[1] = { 0 }; + + // Render sphere + if (sceneSetup == 0) { + vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, graphics.pipelines.sphere); + vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, graphics.pipelineLayout, 0, 1, &graphics.descriptorSet, 0, NULL); + vkCmdBindIndexBuffer(drawCmdBuffers[i], modelSphere.indices.buffer, 0, VK_INDEX_TYPE_UINT32); + vkCmdBindVertexBuffers(drawCmdBuffers[i], 0, 1, &modelSphere.vertices.buffer, offsets); + vkCmdDrawIndexed(drawCmdBuffers[i], modelSphere.indexCount, 1, 0, 0, 0); + } + + // Render cloth + vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, graphics.pipelines.cloth); + vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, graphics.pipelineLayout, 0, 1, &graphics.descriptorSet, 0, NULL); + vkCmdBindIndexBuffer(drawCmdBuffers[i], graphics.indices.buffer, 0, VK_INDEX_TYPE_UINT32); + vkCmdBindVertexBuffers(drawCmdBuffers[i], 0, 1, &compute.storageBuffers.output.buffer, offsets); + vkCmdDrawIndexed(drawCmdBuffers[i], indexCount, 1, 0, 0, 0); + + vkCmdEndRenderPass(drawCmdBuffers[i]); + + VK_CHECK_RESULT(vkEndCommandBuffer(drawCmdBuffers[i])); + } + + } + + // todo: check barriers (validation, separate compute queue) + void buildComputeCommandBuffer() + { + VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo(); + + for (uint32_t i = 0; i < 2; i++) { + + VK_CHECK_RESULT(vkBeginCommandBuffer(compute.commandBuffers[i], &cmdBufInfo)); + + VkBufferMemoryBarrier bufferBarrier = vks::initializers::bufferMemoryBarrier(); + bufferBarrier.srcAccessMask = VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT; + bufferBarrier.dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT; + bufferBarrier.srcQueueFamilyIndex = vulkanDevice->queueFamilyIndices.graphics; + bufferBarrier.dstQueueFamilyIndex = vulkanDevice->queueFamilyIndices.compute; + bufferBarrier.size = VK_WHOLE_SIZE; + + std::vector bufferBarriers; + bufferBarrier.buffer = compute.storageBuffers.input.buffer; + bufferBarriers.push_back(bufferBarrier); + bufferBarrier.buffer = compute.storageBuffers.output.buffer; + bufferBarriers.push_back(bufferBarrier); + + vkCmdPipelineBarrier(compute.commandBuffers[i], + VK_PIPELINE_STAGE_VERTEX_SHADER_BIT, + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + VK_FLAGS_NONE, + 0, nullptr, + static_cast(bufferBarriers.size()), bufferBarriers.data(), + 0, nullptr); + + vkCmdBindPipeline(compute.commandBuffers[i], VK_PIPELINE_BIND_POINT_COMPUTE, compute.pipeline); + + // Dispatch the compute job + for (uint32_t j = 0; j < 64; j++) { + readSet = 1 - readSet; + vkCmdBindDescriptorSets(compute.commandBuffers[i], VK_PIPELINE_BIND_POINT_COMPUTE, compute.pipelineLayout, 0, 1, &compute.descriptorSets[readSet], 0, 0); + + vkCmdDispatch(compute.commandBuffers[i], cloth.gridsize.x / 10, cloth.gridsize.y / 10, 1); + + for (auto &barrier : bufferBarriers) { + barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + barrier.srcQueueFamilyIndex = vulkanDevice->queueFamilyIndices.compute; + barrier.dstQueueFamilyIndex = vulkanDevice->queueFamilyIndices.graphics; + } + + vkCmdPipelineBarrier( + compute.commandBuffers[i], + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + VK_FLAGS_NONE, + 0, nullptr, + static_cast(bufferBarriers.size()), bufferBarriers.data(), + 0, nullptr); + + } + + for (auto &barrier : bufferBarriers) { + barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT; + barrier.srcQueueFamilyIndex = vulkanDevice->queueFamilyIndices.compute; + barrier.dstQueueFamilyIndex = vulkanDevice->queueFamilyIndices.graphics; + } + + vkCmdPipelineBarrier( + compute.commandBuffers[i], + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + VK_PIPELINE_STAGE_VERTEX_SHADER_BIT, + VK_FLAGS_NONE, + 0, nullptr, + static_cast(bufferBarriers.size()), bufferBarriers.data(), + 0, nullptr); + + vkEndCommandBuffer(compute.commandBuffers[i]); + } + } + + // Setup and fill the compute shader storage buffers containing the particles + void prepareStorageBuffers() + { + std::vector particleBuffer(cloth.gridsize.x * cloth.gridsize.y); + + float dx = cloth.size.x / (cloth.gridsize.x - 1); + float dy = cloth.size.y / (cloth.gridsize.y - 1); + float du = 1.0f / (cloth.gridsize.x - 1); + float dv = 1.0f / (cloth.gridsize.y - 1); + + switch (sceneSetup) { + case 0 : + { + // Horz. cloth falls onto sphere + glm::mat4 transM = glm::translate(glm::mat4(), glm::vec3(- cloth.size.x / 2.0f, -2.0f, - cloth.size.y / 2.0f)); + for (uint32_t i = 0; i < cloth.gridsize.y; i++) { + for (uint32_t j = 0; j < cloth.gridsize.x; j++) { + particleBuffer[i + j * cloth.gridsize.y].pos = transM * glm::vec4(dx * j, 0.0f, dy * i, 1.0f); + particleBuffer[i + j * cloth.gridsize.y].vel = glm::vec4(0.0f); + particleBuffer[i + j * cloth.gridsize.y].uv = glm::vec4(1.0f - du * i, dv * j, 0.0f, 0.0f); + } + } + break; + } + case 1: + { + // Vert. Pinned cloth + glm::mat4 transM = glm::translate(glm::mat4(), glm::vec3(- cloth.size.x / 2.0f, - cloth.size.y / 2.0f, 0.0f)); + for (uint32_t i = 0; i < cloth.gridsize.y; i++) { + for (uint32_t j = 0; j < cloth.gridsize.x; j++) { + particleBuffer[i + j * cloth.gridsize.y].pos = transM * glm::vec4(dx * j, dy * i, 0.0f, 1.0f); + particleBuffer[i + j * cloth.gridsize.y].vel = glm::vec4(0.0f); + particleBuffer[i + j * cloth.gridsize.y].uv = glm::vec4(du * j, dv * i, 0.0f, 0.0f); + // Pin some particles + particleBuffer[i + j * cloth.gridsize.y].pinned = (i == 0) && ((j == 0) || (j == cloth.gridsize.x / 3) || (j == cloth.gridsize.x - cloth.gridsize.x / 3) || (j == cloth.gridsize.x - 1)); + // Remove sphere + compute.ubo.spherePos.z = -10.0f; + } + } + break; + } + } + + VkDeviceSize storageBufferSize = particleBuffer.size() * sizeof(Particle); + + // Staging + // SSBO won't be changed on the host after upload so copy to device local memory + + vks::Buffer stagingBuffer; + + vulkanDevice->createBuffer( + VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + &stagingBuffer, + storageBufferSize, + particleBuffer.data()); + + vulkanDevice->createBuffer( + VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + &compute.storageBuffers.input, + storageBufferSize); + + vulkanDevice->createBuffer( + VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + &compute.storageBuffers.output, + storageBufferSize); + + // Copy from staging buffer + VkCommandBuffer copyCmd = VulkanExampleBase::createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); + VkBufferCopy copyRegion = {}; + copyRegion.size = storageBufferSize; + vkCmdCopyBuffer(copyCmd, stagingBuffer.buffer, compute.storageBuffers.input.buffer, 1, ©Region); + vkCmdCopyBuffer(copyCmd, stagingBuffer.buffer, compute.storageBuffers.output.buffer, 1, ©Region); + VulkanExampleBase::flushCommandBuffer(copyCmd, queue, true); + + stagingBuffer.destroy(); + + // Indices + std::vector indices; + for (uint32_t y = 0; y < cloth.gridsize.y - 1; y++) { + for (uint32_t x = 0; x < cloth.gridsize.x; x++) { + indices.push_back((y + 1) * cloth.gridsize.x + x); + indices.push_back((y)* cloth.gridsize.x + x); + } + // Primitive restart (signlaed by special value 0xFFFFFFFF) + indices.push_back(0xFFFFFFFF); + } + uint32_t indexBufferSize = static_cast(indices.size()) * sizeof(uint32_t); + indexCount = static_cast(indices.size()); + + vulkanDevice->createBuffer( + VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + &stagingBuffer, + indexBufferSize, + indices.data()); + + vulkanDevice->createBuffer( + VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + &graphics.indices, + indexBufferSize); + + // Copy from staging buffer + copyCmd = VulkanExampleBase::createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); + copyRegion = {}; + copyRegion.size = indexBufferSize; + vkCmdCopyBuffer(copyCmd, stagingBuffer.buffer, graphics.indices.buffer, 1, ©Region); + VulkanExampleBase::flushCommandBuffer(copyCmd, queue, true); + + stagingBuffer.destroy(); + } + + void setupDescriptorPool() + { + std::vector poolSizes = { + vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 3), + vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 4), + vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 2) + }; + + VkDescriptorPoolCreateInfo descriptorPoolInfo = + vks::initializers::descriptorPoolCreateInfo(poolSizes, 3); + + VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolInfo, nullptr, &descriptorPool)); + } + + void setupLayoutsAndDescriptors() + { + // Set layout + std::vector setLayoutBindings = { + vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_VERTEX_BIT, 0), + vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 1) + }; + VkDescriptorSetLayoutCreateInfo descriptorLayout = + vks::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings); + VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayout, nullptr, &graphics.descriptorSetLayout)); + + VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = + vks::initializers::pipelineLayoutCreateInfo(&graphics.descriptorSetLayout, 1); + VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, nullptr, &graphics.pipelineLayout)); + + // Set + VkDescriptorSetAllocateInfo allocInfo = + vks::initializers::descriptorSetAllocateInfo(descriptorPool, &graphics.descriptorSetLayout, 1); + VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &graphics.descriptorSet)); + + std::vector writeDescriptorSets = { + vks::initializers::writeDescriptorSet(graphics.descriptorSet, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &graphics.uniformBuffer.descriptor), + vks::initializers::writeDescriptorSet(graphics.descriptorSet, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, &textureCloth.descriptor) + }; + vkUpdateDescriptorSets(device, static_cast(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, NULL); + } + + void preparePipelines() + { + VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = + vks::initializers::pipelineInputAssemblyStateCreateInfo(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, 0, VK_TRUE); + + VkPipelineRasterizationStateCreateInfo rasterizationState = + vks::initializers::pipelineRasterizationStateCreateInfo(VK_POLYGON_MODE_FILL, VK_CULL_MODE_NONE, VK_FRONT_FACE_COUNTER_CLOCKWISE, 0); + + VkPipelineColorBlendAttachmentState blendAttachmentState = + vks::initializers::pipelineColorBlendAttachmentState(0xf, VK_FALSE); + + VkPipelineColorBlendStateCreateInfo colorBlendState = + vks::initializers::pipelineColorBlendStateCreateInfo(1, &blendAttachmentState); + + VkPipelineDepthStencilStateCreateInfo depthStencilState = + vks::initializers::pipelineDepthStencilStateCreateInfo(VK_TRUE, VK_TRUE, VK_COMPARE_OP_LESS_OR_EQUAL); + + VkPipelineViewportStateCreateInfo viewportState = + vks::initializers::pipelineViewportStateCreateInfo(1, 1, 0); + + VkPipelineMultisampleStateCreateInfo multisampleState = + vks::initializers::pipelineMultisampleStateCreateInfo(VK_SAMPLE_COUNT_1_BIT, 0); + + std::vector dynamicStateEnables = { + VK_DYNAMIC_STATE_VIEWPORT, + VK_DYNAMIC_STATE_SCISSOR + }; + VkPipelineDynamicStateCreateInfo dynamicState = + vks::initializers::pipelineDynamicStateCreateInfo(dynamicStateEnables, 0); + + // Rendering pipeline + std::array shaderStages; + + shaderStages[0] = loadShader(getAssetPath() + "shaders/computecloth/cloth.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); + shaderStages[1] = loadShader(getAssetPath() + "shaders/computecloth/cloth.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT); + + VkGraphicsPipelineCreateInfo pipelineCreateInfo = + vks::initializers::pipelineCreateInfo( + graphics.pipelineLayout, + renderPass, + 0); + + // Input attributes + + // Binding description + std::vector inputBindings = { + vks::initializers::vertexInputBindingDescription(0, sizeof(Particle), VK_VERTEX_INPUT_RATE_VERTEX) + }; + + // Attribute descriptions + std::vector inputAttributes = { + vks::initializers::vertexInputAttributeDescription(0, 0, VK_FORMAT_R32G32B32_SFLOAT, offsetof(Particle, pos)), + vks::initializers::vertexInputAttributeDescription(0, 1, VK_FORMAT_R32G32_SFLOAT, offsetof(Particle, uv)), + vks::initializers::vertexInputAttributeDescription(0, 2, VK_FORMAT_R32G32B32_SFLOAT, offsetof(Particle, normal)) + }; + + // Assign to vertex buffer + VkPipelineVertexInputStateCreateInfo inputState = vks::initializers::pipelineVertexInputStateCreateInfo(); + inputState.vertexBindingDescriptionCount = static_cast(inputBindings.size()); + inputState.pVertexBindingDescriptions = inputBindings.data(); + inputState.vertexAttributeDescriptionCount = static_cast(inputAttributes.size()); + inputState.pVertexAttributeDescriptions = inputAttributes.data(); + + pipelineCreateInfo.pVertexInputState = &inputState; + pipelineCreateInfo.pInputAssemblyState = &inputAssemblyState; + pipelineCreateInfo.pRasterizationState = &rasterizationState; + pipelineCreateInfo.pColorBlendState = &colorBlendState; + pipelineCreateInfo.pMultisampleState = &multisampleState; + pipelineCreateInfo.pViewportState = &viewportState; + pipelineCreateInfo.pDepthStencilState = &depthStencilState; + pipelineCreateInfo.pDynamicState = &dynamicState; + pipelineCreateInfo.stageCount = static_cast(shaderStages.size()); + pipelineCreateInfo.pStages = shaderStages.data(); + pipelineCreateInfo.renderPass = renderPass; + + VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &graphics.pipelines.cloth)); + + // Sphere rendering pipeline + inputBindings = { + vks::initializers::vertexInputBindingDescription(0, vertexLayout.stride(), VK_VERTEX_INPUT_RATE_VERTEX) + }; + inputAttributes = { + vks::initializers::vertexInputAttributeDescription(0, 0, VK_FORMAT_R32G32B32_SFLOAT, 0), + vks::initializers::vertexInputAttributeDescription(0, 1, VK_FORMAT_R32G32_SFLOAT, sizeof(float) * 3), + vks::initializers::vertexInputAttributeDescription(0, 2, VK_FORMAT_R32G32B32_SFLOAT, sizeof(float) * 5) + }; + inputState.vertexAttributeDescriptionCount = static_cast(inputAttributes.size()); + inputAssemblyState.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + rasterizationState.polygonMode = VK_POLYGON_MODE_FILL; + shaderStages[0] = loadShader(getAssetPath() + "shaders/computecloth/sphere.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); + shaderStages[1] = loadShader(getAssetPath() + "shaders/computecloth/sphere.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT); + VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &graphics.pipelines.sphere)); + } + + void prepareCompute() + { + // Create a compute capable device queue + vkGetDeviceQueue(device, vulkanDevice->queueFamilyIndices.compute, 0, &compute.queue); + + // Create compute pipeline + std::vector setLayoutBindings = { + vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_COMPUTE_BIT, 0), + vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_COMPUTE_BIT, 1), + vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_COMPUTE_BIT, 2), + }; + + VkDescriptorSetLayoutCreateInfo descriptorLayout = + vks::initializers::descriptorSetLayoutCreateInfo( + setLayoutBindings.data(), + static_cast(setLayoutBindings.size())); + + VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayout, nullptr, &compute.descriptorSetLayout)); + + VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo = + vks::initializers::pipelineLayoutCreateInfo( + &compute.descriptorSetLayout, + 1); + + VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pPipelineLayoutCreateInfo, nullptr, &compute.pipelineLayout)); + + VkDescriptorSetAllocateInfo allocInfo = + vks::initializers::descriptorSetAllocateInfo( + descriptorPool, + &compute.descriptorSetLayout, + 1); + + // Create two descriptor sets with input and output buffers switched + VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &compute.descriptorSets[0])); + VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &compute.descriptorSets[1])); + + std::vector computeWriteDescriptorSets = { + vks::initializers::writeDescriptorSet(compute.descriptorSets[0], VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 0, &compute.storageBuffers.input.descriptor), + vks::initializers::writeDescriptorSet(compute.descriptorSets[0], VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, &compute.storageBuffers.output.descriptor), + vks::initializers::writeDescriptorSet(compute.descriptorSets[0], VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 2, &compute.uniformBuffer.descriptor), + + vks::initializers::writeDescriptorSet(compute.descriptorSets[1], VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 0, &compute.storageBuffers.output.descriptor), + vks::initializers::writeDescriptorSet(compute.descriptorSets[1], VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, &compute.storageBuffers.input.descriptor), + vks::initializers::writeDescriptorSet(compute.descriptorSets[1], VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 2, &compute.uniformBuffer.descriptor) + }; + + vkUpdateDescriptorSets(device, static_cast(computeWriteDescriptorSets.size()), computeWriteDescriptorSets.data(), 0, NULL); + + // Create pipeline + VkComputePipelineCreateInfo computePipelineCreateInfo = vks::initializers::computePipelineCreateInfo(compute.pipelineLayout, 0); + computePipelineCreateInfo.stage = loadShader(getAssetPath() + "shaders/computecloth/cloth.comp.spv", VK_SHADER_STAGE_COMPUTE_BIT); + VK_CHECK_RESULT(vkCreateComputePipelines(device, pipelineCache, 1, &computePipelineCreateInfo, nullptr, &compute.pipeline)); + + // Separate command pool as queue family for compute may be different than graphics + VkCommandPoolCreateInfo cmdPoolInfo = {}; + cmdPoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + cmdPoolInfo.queueFamilyIndex = vulkanDevice->queueFamilyIndices.compute; + cmdPoolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + VK_CHECK_RESULT(vkCreateCommandPool(device, &cmdPoolInfo, nullptr, &compute.commandPool)); + + // Create a command buffer for compute operations + VkCommandBufferAllocateInfo cmdBufAllocateInfo = + vks::initializers::commandBufferAllocateInfo(compute.commandPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, 2); + + VK_CHECK_RESULT(vkAllocateCommandBuffers(device, &cmdBufAllocateInfo, &compute.commandBuffers[0])); + + // Fence for compute CB sync + VkFenceCreateInfo fenceCreateInfo = vks::initializers::fenceCreateInfo(VK_FENCE_CREATE_SIGNALED_BIT); + VK_CHECK_RESULT(vkCreateFence(device, &fenceCreateInfo, nullptr, &compute.fence)); + + // Build a single command buffer containing the compute dispatch commands + buildComputeCommandBuffer(); + } + + // Prepare and initialize uniform buffer containing shader uniforms + void prepareUniformBuffers() + { + // Compute shader uniform buffer block + vulkanDevice->createBuffer( + VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + &compute.uniformBuffer, + sizeof(compute.ubo)); + VK_CHECK_RESULT(compute.uniformBuffer.map()); + + // Initial values + float dx = cloth.size.x / (cloth.gridsize.x - 1); + float dy = cloth.size.y / (cloth.gridsize.y - 1); + + compute.ubo.restDistH = dx; + compute.ubo.restDistV = dy; + compute.ubo.restDistD = sqrtf(dx * dx + dy * dy); + compute.ubo.particleCount = cloth.gridsize; + + updateComputeUBO(); + + // Vertex shader uniform buffer block + vulkanDevice->createBuffer( + VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + &graphics.uniformBuffer, + sizeof(graphics.ubo)); + VK_CHECK_RESULT(graphics.uniformBuffer.map()); + + updateGraphicsUBO(); + } + + void updateComputeUBO() + { + if (!paused) { + compute.ubo.deltaT = 0.000005f; + // todo: base on frametime + //compute.ubo.deltaT = frameTimer * 0.0075f; + } + else { + compute.ubo.deltaT = 0.0f; + } + memcpy(compute.uniformBuffer.mapped, &compute.ubo, sizeof(compute.ubo)); + } + + void updateGraphicsUBO() + { + graphics.ubo.projection = camera.matrices.perspective; + graphics.ubo.view = camera.matrices.view; + memcpy(graphics.uniformBuffer.mapped, &graphics.ubo, sizeof(graphics.ubo)); + } + + void draw() + { + // Submit graphics commands + VulkanExampleBase::prepareFrame(); + + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer]; + VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE)); + + VulkanExampleBase::submitFrame(); + + vkWaitForFences(device, 1, &compute.fence, VK_TRUE, UINT64_MAX); + vkResetFences(device, 1, &compute.fence); + + VkSubmitInfo computeSubmitInfo = vks::initializers::submitInfo(); + computeSubmitInfo.commandBufferCount = 1; + computeSubmitInfo.pCommandBuffers = &compute.commandBuffers[readSet]; + + VK_CHECK_RESULT(vkQueueSubmit(compute.queue, 1, &computeSubmitInfo, compute.fence)); + } + + void prepare() + { + VulkanExampleBase::prepare(); + loadAssets(); + prepareStorageBuffers(); + prepareUniformBuffers(); + setupDescriptorPool(); + setupLayoutsAndDescriptors(); + preparePipelines(); + prepareCompute(); + buildCommandBuffers(); + prepared = true; + } + + virtual void render() + { + if (!prepared) + return; + draw(); + + updateComputeUBO(); + } + + virtual void viewChanged() + { + updateGraphicsUBO(); + } + + virtual void getOverlayText(VulkanTextOverlay *textOverlay) + { + textOverlay->addText(std::to_string(frameTimer * 0.0075f), 5.0f, 85.0f, VulkanTextOverlay::alignLeft); + } +}; + +VULKAN_EXAMPLE_MAIN() \ No newline at end of file diff --git a/computecloth/computecloth.vcxproj b/computecloth/computecloth.vcxproj new file mode 100644 index 00000000..26e510f8 --- /dev/null +++ b/computecloth/computecloth.vcxproj @@ -0,0 +1,102 @@ + + + + + Debug + x64 + + + Release + x64 + + + + {12ACF921-90B1-44D9-AF06-B5C0F0B8191A} + Win32Proj + 8.1 + + + + Application + true + v140 + + + Application + false + v140 + + + + + + + + + + + + + true + $(SolutionDir)\bin\ + $(SolutionDir)\bin\intermediate\$(ProjectName)\$(ConfigurationName) + + + true + $(SolutionDir)\bin\ + $(SolutionDir)\bin\intermediate\$(ProjectName)\$(ConfigurationName) + + + + WIN32;_DEBUG;_WINDOWS;VK_USE_PLATFORM_WIN32_KHR;_USE_MATH_DEFINES;NOMINMAX;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + MultiThreadedDebugDLL + Level3 + ProgramDatabase + Disabled + ..\base;..\external\glm;..\external\gli;..\external\assimp;..\external;%(AdditionalIncludeDirectories) + /FS %(AdditionalOptions) + + + true + Windows + ..\libs\vulkan\vulkan-1.lib;..\libs\assimp\assimp.lib;%(AdditionalDependencies) + + + + + WIN32;NDEBUG;_WINDOWS;VK_USE_PLATFORM_WIN32_KHR;_USE_MATH_DEFINES;NOMINMAX;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + MultiThreadedDLL + Level3 + ProgramDatabase + ..\base;..\external\glm;..\external\gli;..\external\assimp;..\external;%(AdditionalIncludeDirectories) + + + true + Windows + true + true + ..\libs\vulkan\vulkan-1.lib;..\libs\assimp\assimp.lib;%(AdditionalDependencies) + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/computecloth/computecloth.vcxproj.filters b/computecloth/computecloth.vcxproj.filters new file mode 100644 index 00000000..68a12b56 --- /dev/null +++ b/computecloth/computecloth.vcxproj.filters @@ -0,0 +1,62 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav + + + {e28680da-cc95-413d-b6f0-0e1f9967ee88} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + + + Shaders + + + Shaders + + + Shaders + + + Shaders + + + Shaders + + + \ No newline at end of file diff --git a/data/shaders/computecloth/cloth.comp b/data/shaders/computecloth/cloth.comp new file mode 100644 index 00000000..edd97488 --- /dev/null +++ b/data/shaders/computecloth/cloth.comp @@ -0,0 +1,148 @@ +#version 450 + +struct Particle { + vec4 pos; + vec4 vel; + vec4 uv; + vec4 normal; + float pinned; +}; + +layout(std430, binding = 0) buffer ParticleIn { + Particle particleIn[ ]; +}; + +layout(std430, binding = 1) buffer ParticleOut { + Particle particleOut[ ]; +}; + +// todo: use shared memory to speed up calculation + +layout (local_size_x = 10, local_size_y = 10) in; + +layout (binding = 2) uniform UBO +{ + float deltaT; + float particleMass; + float springStiffness; + float damping; + float restDistH; + float restDistV; + float restDistD; + float sphereRadius; + vec4 spherePos; + vec4 gravity; + ivec2 particleCount; +} params; + +vec3 springForce(vec3 p0, vec3 p1, float restDist) +{ + vec3 dist = p0 - p1; + return normalize(dist) * params.springStiffness * (length(dist) - restDist); +} + +void main() +{ + uvec3 id = gl_GlobalInvocationID; + + uint index = id.y * params.particleCount.x + id.x; + if (index > params.particleCount.x * params.particleCount.y) + return; + + // Pinned? + if (particleIn[index].pinned == 1.0) { + particleOut[index].pos = particleOut[index].pos; + particleOut[index].vel = vec4(0.0); + return; + } + + // Initial force from gravity + vec3 force = params.gravity.xyz * params.particleMass; + + vec3 pos = particleIn[index].pos.xyz; + vec3 vel = particleIn[index].vel.xyz; + + // Spring forces from neighboring particles + // left + if (id.x > 0) { + force += springForce(particleIn[index-1].pos.xyz, pos, params.restDistH); + } + // right + if (id.x < params.particleCount.x - 1) { + force += springForce(particleIn[index + 1].pos.xyz, pos, params.restDistH); + } + // upper + if (id.y < params.particleCount.y - 1) { + force += springForce(particleIn[index + params.particleCount.x].pos.xyz, pos, params.restDistV); + } + // lower + if (id.y > 0) { + force += springForce(particleIn[index - params.particleCount.x].pos.xyz, pos, params.restDistV); + } + // upper-left + if ((id.x > 0) && (id.y < params.particleCount.y - 1)) { + force += springForce(particleIn[index + params.particleCount.x - 1].pos.xyz, pos, params.restDistD); + } + // lower-left + if ((id.x > 0) && (id.y > 0)) { + force += springForce(particleIn[index - params.particleCount.x - 1].pos.xyz, pos, params.restDistD); + } + // upper-right + if ((id.x < params.particleCount.x - 1) && (id.y < params.particleCount.y - 1)) { + force += springForce(particleIn[index + params.particleCount.x + 1].pos.xyz, pos, params.restDistD); + } + // lower-right + if ((id.x < params.particleCount.x - 1) && (id.y > 0)) { + force += springForce(particleIn[index - params.particleCount.x + 1].pos.xyz, pos, params.restDistD); + } + + force += (-params.damping * vel); + + // Integrate + vec3 f = force * (1.0 / params.particleMass); + particleOut[index].pos = vec4(pos + vel * params.deltaT + 0.5 * f * params.deltaT * params.deltaT, 1.0); + particleOut[index].vel = vec4(vel + f * params.deltaT, 0.0); + + // Sphere collision + vec3 sphereDist = particleOut[index].pos.xyz - params.spherePos.xyz; + if (length(sphereDist) < params.sphereRadius + 0.01) { + // If the particle is inside the sphere, push it to the outer radius + particleOut[index].pos.xyz = params.spherePos.xyz + normalize(sphereDist) * (params.sphereRadius + 0.01); + // Cancel out velocity + particleOut[index].vel = vec4(0.0); + } + + // Normals + // todo: Only once (use push const to check) + vec3 normal = vec3(0.0); + vec3 a, b, c; + if (id.y > 0) { + if (id.x > 0) { + a = particleIn[index - 1].pos.xyz - pos; + b = particleIn[index - params.particleCount.x - 1].pos.xyz - pos; + c = particleIn[index - params.particleCount.x].pos.xyz - pos; + normal += cross(a,b) + cross(b,c); + } + if (id.x < params.particleCount.x - 1) { + a = particleIn[index - params.particleCount.x].pos.xyz - pos; + b = particleIn[index - params.particleCount.x + 1].pos.xyz - pos; + c = particleIn[index + 1].pos.xyz - pos; + normal += cross(a,b) + cross(b,c); + } + } + if (id.y < params.particleCount.y - 1) { + if (id.x > 0) { + a = particleIn[index + params.particleCount.x].pos.xyz - pos; + b = particleIn[index + params.particleCount.x - 1].pos.xyz - pos; + c = particleIn[index - 1].pos.xyz - pos; + normal += cross(a,b) + cross(b,c); + } + if (id.x < params.particleCount.x - 1) { + a = particleIn[index + 1].pos.xyz - pos; + b = particleIn[index + params.particleCount.x + 1].pos.xyz - pos; + c = particleIn[index + params.particleCount.x].pos.xyz - pos; + normal += cross(a,b) + cross(b,c); + } + } + particleOut[index].normal = vec4(normalize(normal), 0.0f); +} \ No newline at end of file diff --git a/data/shaders/computecloth/cloth.comp.spv b/data/shaders/computecloth/cloth.comp.spv new file mode 100644 index 00000000..98bba249 Binary files /dev/null and b/data/shaders/computecloth/cloth.comp.spv differ diff --git a/data/shaders/computecloth/cloth.frag b/data/shaders/computecloth/cloth.frag new file mode 100644 index 00000000..e5f30db5 --- /dev/null +++ b/data/shaders/computecloth/cloth.frag @@ -0,0 +1,22 @@ +#version 450 + +layout (binding = 1) uniform sampler2D samplerColor; + +layout (location = 0) in vec2 inUV; +layout (location = 1) in vec3 inNormal; +layout (location = 2) in vec3 inViewVec; +layout (location = 3) in vec3 inLightVec; + +layout (location = 0) out vec4 outFragColor; + +void main () +{ + vec3 color = texture(samplerColor, inUV).rgb; + vec3 N = normalize(inNormal); + vec3 L = normalize(inLightVec); + vec3 V = normalize(inViewVec); + vec3 R = reflect(-L, N); + vec3 diffuse = max(dot(N, L), 0.15) * vec3(1.0); + vec3 specular = pow(max(dot(R, V), 0.0), 8.0) * vec3(0.2); + outFragColor = vec4(diffuse * color.rgb + specular, 1.0); +} diff --git a/data/shaders/computecloth/cloth.frag.spv b/data/shaders/computecloth/cloth.frag.spv new file mode 100644 index 00000000..03f2ff30 Binary files /dev/null and b/data/shaders/computecloth/cloth.frag.spv differ diff --git a/data/shaders/computecloth/cloth.vert b/data/shaders/computecloth/cloth.vert new file mode 100644 index 00000000..c2cf9b57 --- /dev/null +++ b/data/shaders/computecloth/cloth.vert @@ -0,0 +1,35 @@ +#version 450 + +layout (location = 0) in vec3 inPos; +layout (location = 1) in vec2 inUV; +layout (location = 2) in vec3 inNormal; + +layout (location = 0) out vec2 outUV; +layout (location = 1) out vec3 outNormal; +layout (location = 2) out vec3 outViewVec; +layout (location = 3) out vec3 outLightVec; + + +layout (binding = 0) uniform UBO +{ + mat4 projection; + mat4 modelview; + vec4 lightPos; +} ubo; + +out gl_PerVertex +{ + vec4 gl_Position; +}; + +void main () +{ + outUV = inUV; + outNormal = inNormal.xyz; + vec4 eyePos = ubo.modelview * vec4(inPos.x, inPos.y, inPos.z, 1.0); + gl_Position = ubo.projection * eyePos; + vec4 pos = vec4(inPos, 1.0); + vec3 lPos = ubo.lightPos.xyz; + outLightVec = lPos - pos.xyz; + outViewVec = -pos.xyz; +} \ No newline at end of file diff --git a/data/shaders/computecloth/cloth.vert.spv b/data/shaders/computecloth/cloth.vert.spv new file mode 100644 index 00000000..8791e31d Binary files /dev/null and b/data/shaders/computecloth/cloth.vert.spv differ diff --git a/data/shaders/computecloth/sphere.frag b/data/shaders/computecloth/sphere.frag new file mode 100644 index 00000000..45be5319 --- /dev/null +++ b/data/shaders/computecloth/sphere.frag @@ -0,0 +1,19 @@ +#version 450 + +layout (location = 0) in vec3 inNormal; +layout (location = 1) in vec3 inViewVec; +layout (location = 2) in vec3 inLightVec; + +layout (location = 0) out vec4 outFragColor; + +void main () +{ + vec3 color = vec3(0.5); + vec3 N = normalize(inNormal); + vec3 L = normalize(inLightVec); + vec3 V = normalize(inViewVec); + vec3 R = reflect(-L, N); + vec3 diffuse = max(dot(N, L), 0.15) * vec3(1.0); + vec3 specular = pow(max(dot(R, V), 0.0), 32.0) * vec3(1.0); + outFragColor = vec4(diffuse * color.rgb + specular, 1.0); +} diff --git a/data/shaders/computecloth/sphere.frag.spv b/data/shaders/computecloth/sphere.frag.spv new file mode 100644 index 00000000..aa279158 Binary files /dev/null and b/data/shaders/computecloth/sphere.frag.spv differ diff --git a/data/shaders/computecloth/sphere.vert b/data/shaders/computecloth/sphere.vert new file mode 100644 index 00000000..de6b5a6d --- /dev/null +++ b/data/shaders/computecloth/sphere.vert @@ -0,0 +1,31 @@ +#version 450 + +layout (location = 0) in vec3 inPos; +layout (location = 2) in vec3 inNormal; + +layout (location = 0) out vec3 outNormal; +layout (location = 1) out vec3 outViewVec; +layout (location = 2) out vec3 outLightVec; + +layout (binding = 0) uniform UBO +{ + mat4 projection; + mat4 modelview; + vec4 lightPos; +} ubo; + +out gl_PerVertex +{ + vec4 gl_Position; +}; + +void main () +{ + vec4 eyePos = ubo.modelview * vec4(inPos.x, inPos.y, inPos.z, 1.0); + gl_Position = ubo.projection * eyePos; + vec4 pos = vec4(inPos, 1.0); + vec3 lPos = ubo.lightPos.xyz; + outLightVec = lPos - pos.xyz; + outViewVec = -pos.xyz; + outNormal = inNormal; +} \ No newline at end of file diff --git a/data/shaders/computecloth/sphere.vert.spv b/data/shaders/computecloth/sphere.vert.spv new file mode 100644 index 00000000..b670f69c Binary files /dev/null and b/data/shaders/computecloth/sphere.vert.spv differ diff --git a/vulkanExamples.sln b/vulkanExamples.sln index dc42a5ff..97156091 100644 --- a/vulkanExamples.sln +++ b/vulkanExamples.sln @@ -151,6 +151,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "imgui", "imgui\imgui.vcxpro EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pbrtexture", "pbrtexture\pbrtexture.vcxproj", "{60316D67-E879-4671-B9BA-ED6B26F13AC7}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "computecloth", "computecloth\computecloth.vcxproj", "{12ACF921-90B1-44D9-AF06-B5C0F0B8191A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -369,6 +371,10 @@ Global {60316D67-E879-4671-B9BA-ED6B26F13AC7}.Debug|x64.Build.0 = Debug|x64 {60316D67-E879-4671-B9BA-ED6B26F13AC7}.Release|x64.ActiveCfg = Release|x64 {60316D67-E879-4671-B9BA-ED6B26F13AC7}.Release|x64.Build.0 = Release|x64 + {12ACF921-90B1-44D9-AF06-B5C0F0B8191A}.Debug|x64.ActiveCfg = Debug|x64 + {12ACF921-90B1-44D9-AF06-B5C0F0B8191A}.Debug|x64.Build.0 = Debug|x64 + {12ACF921-90B1-44D9-AF06-B5C0F0B8191A}.Release|x64.ActiveCfg = Release|x64 + {12ACF921-90B1-44D9-AF06-B5C0F0B8191A}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -394,5 +400,6 @@ Global {659987E9-863C-4B9B-A3D4-CBA7D67A9516} = {BE290A75-7E65-4D0A-B419-774A309B6A60} {92B2640A-0CC5-48EA-B34C-520BA13938D1} = {BE290A75-7E65-4D0A-B419-774A309B6A60} {60316D67-E879-4671-B9BA-ED6B26F13AC7} = {BE290A75-7E65-4D0A-B419-774A309B6A60} + {12ACF921-90B1-44D9-AF06-B5C0F0B8191A} = {6B47BC47-0394-429E-9441-867EC23DFCD4} EndGlobalSection EndGlobal