diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..00bd05aa --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,7 @@ +{ + "files.associations": { + "*.embeddedhtml": "html", + "iosfwd": "cpp", + "xlocbuf": "cpp" + } +} \ No newline at end of file diff --git a/android/examples/base/liblibktx.a b/android/examples/base/liblibktx.a index df4b7190..19ac186f 100644 Binary files a/android/examples/base/liblibktx.a and b/android/examples/base/liblibktx.a differ diff --git a/android/examples/clean.bat b/android/examples/clean.bat new file mode 100644 index 00000000..650b1ed9 --- /dev/null +++ b/android/examples/clean.bat @@ -0,0 +1,2 @@ +FOR /d /r . %%d IN (assets) DO @IF EXIST "%%d" rd /s /q "%%d" +FOR /d /r . %%d IN (build) DO @IF EXIST "%%d" rd /s /q "%%d" \ No newline at end of file diff --git a/base/libbase.a b/base/libbase.a new file mode 100644 index 00000000..ff4f184f Binary files /dev/null and b/base/libbase.a differ diff --git a/examples/.vscode/settings.json b/examples/.vscode/settings.json new file mode 100644 index 00000000..845f1187 --- /dev/null +++ b/examples/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "files.associations": { + "*.embeddedhtml": "html", + "array": "cpp" + } +} \ No newline at end of file diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 0644da9e..8438574a 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -103,6 +103,7 @@ set(EXAMPLES descriptorbuffer descriptorindexing descriptorsets + devicegeneratedcommands displacement distancefieldfonts dynamicrendering diff --git a/examples/devicegeneratedcommands/devicegeneratedcommands.cpp b/examples/devicegeneratedcommands/devicegeneratedcommands.cpp new file mode 100644 index 00000000..a6e33a0c --- /dev/null +++ b/examples/devicegeneratedcommands/devicegeneratedcommands.cpp @@ -0,0 +1,493 @@ +/* + * Vulkan Example - Device generated commands + * + * Copyright (C) 2024 by Sascha Willems - www.saschawillems.de + * + * This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + */ + +#include "vulkanexamplebase.h" +#include "VulkanglTFModel.h" + +// Number of instances per object +#if defined(__ANDROID__) +#define OBJECT_INSTANCE_COUNT 1024 +// Circular range of plant distribution +#define PLANT_RADIUS 20.0f +#else +#define OBJECT_INSTANCE_COUNT 2048 +// Circular range of plant distribution +#define PLANT_RADIUS 25.0f +#endif + +class VulkanExample : public VulkanExampleBase +{ +public: + struct { + vks::Texture2DArray plants; + vks::Texture2D ground; + } textures; + + struct { + vkglTF::Model plants; + vkglTF::Model ground; + vkglTF::Model skysphere; + } models; + + // Per-instance data block + struct InstanceData { + glm::vec3 pos; + glm::vec3 rot; + float scale; + uint32_t texIndex; + }; + + // Contains the instanced data + vks::Buffer instanceBuffer; + // Contains the indirect drawing commands + vks::Buffer indirectCommandsBuffer; + uint32_t indirectDrawCount{ 0 }; + + struct UniformData { + glm::mat4 projection; + glm::mat4 view; + } uniformData; + vks::Buffer uniformBuffer; + + struct { + VkPipeline plants{ VK_NULL_HANDLE }; + VkPipeline ground{ VK_NULL_HANDLE }; + VkPipeline skysphere{ VK_NULL_HANDLE }; + } pipelines; + + VkPipelineLayout pipelineLayout{ VK_NULL_HANDLE }; + VkDescriptorSet descriptorSet{ VK_NULL_HANDLE }; + VkDescriptorSetLayout descriptorSetLayout{ VK_NULL_HANDLE }; + + VkSampler samplerRepeat{ VK_NULL_HANDLE }; + + uint32_t objectCount = 0; + + // Store the indirect draw commands containing index offsets and instance count per object + std::vector indirectCommands; + + VkPhysicalDeviceBufferDeviceAddressFeatures enabledBufferDeviceAddresFeatures{}; + + // @todo: base on pipeline library sample? + + VulkanExample() : VulkanExampleBase() + { + title = "Device generated commands"; + camera.type = Camera::CameraType::firstperson; + camera.setPerspective(60.0f, (float)width / (float)height, 0.1f, 512.0f); + camera.setRotation(glm::vec3(-12.0f, 159.0f, 0.0f)); + camera.setTranslation(glm::vec3(0.4f, 1.25f, 0.0f)); + camera.movementSpeed = 5.0f; + + // VK_EXT_device_generated_commands requires api version 1.1, buffer device address and maintenance5 + + apiVersion = VK_API_VERSION_1_1; + + // Required by buffer device address + enabledInstanceExtensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); + enabledInstanceExtensions.push_back(VK_KHR_DEVICE_GROUP_CREATION_EXTENSION_NAME); + enabledDeviceExtensions.push_back(VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME); + enabledDeviceExtensions.push_back(VK_KHR_DEVICE_GROUP_EXTENSION_NAME); + enabledDeviceExtensions.push_back(VK_KHR_MAINTENANCE_5_EXTENSION_NAME); + // Required by maintenance5 + enabledDeviceExtensions.push_back(VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME); + // Required by dynamic rendering + enabledDeviceExtensions.push_back(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME); + enabledDeviceExtensions.push_back(VK_KHR_DEPTH_STENCIL_RESOLVE_EXTENSION_NAME); + + enabledBufferDeviceAddresFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES; + enabledBufferDeviceAddresFeatures.bufferDeviceAddress = VK_TRUE; + } + + ~VulkanExample() + { + if (device) { + vkDestroyPipeline(device, pipelines.plants, nullptr); + vkDestroyPipeline(device, pipelines.ground, nullptr); + vkDestroyPipeline(device, pipelines.skysphere, nullptr); + vkDestroyPipelineLayout(device, pipelineLayout, nullptr); + vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); + textures.plants.destroy(); + textures.ground.destroy(); + instanceBuffer.destroy(); + indirectCommandsBuffer.destroy(); + uniformBuffer.destroy(); + } + } + + // Enable physical device features required for this example + virtual void getEnabledFeatures() + { + // Example uses multi draw indirect if available + if (deviceFeatures.multiDrawIndirect) { + enabledFeatures.multiDrawIndirect = VK_TRUE; + } + // Enable anisotropic filtering if supported + if (deviceFeatures.samplerAnisotropy) { + enabledFeatures.samplerAnisotropy = VK_TRUE; + } + }; + + void buildCommandBuffers() + { + VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo(); + + VkClearValue clearValues[2]; + clearValues[0].color = { { 0.18f, 0.27f, 0.5f, 0.0f } }; + clearValues[1].depthStencil = { 1.0f, 0 }; + + VkRenderPassBeginInfo renderPassBeginInfo = vks::initializers::renderPassBeginInfo(); + renderPassBeginInfo.renderPass = renderPass; + 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)); + + 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 }; + vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, NULL); + + // Skysphere + vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.skysphere); + models.skysphere.draw(drawCmdBuffers[i]); + // Ground + vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.ground); + models.ground.draw(drawCmdBuffers[i]); + + // [POI] Instanced multi draw rendering of the plants + vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.plants); + // Binding point 0 : Mesh vertex buffer + vkCmdBindVertexBuffers(drawCmdBuffers[i], 0, 1, &models.plants.vertices.buffer, offsets); + // Binding point 1 : Instance data buffer + vkCmdBindVertexBuffers(drawCmdBuffers[i], 1, 1, &instanceBuffer.buffer, offsets); + + vkCmdBindIndexBuffer(drawCmdBuffers[i], models.plants.indices.buffer, 0, VK_INDEX_TYPE_UINT32); + + // If the multi draw feature is supported: + // One draw call for an arbitrary number of objects + // Index offsets and instance count are taken from the indirect buffer + if (vulkanDevice->features.multiDrawIndirect) + { + vkCmdDrawIndexedIndirect(drawCmdBuffers[i], indirectCommandsBuffer.buffer, 0, indirectDrawCount, sizeof(VkDrawIndexedIndirectCommand)); + } + else + { + // If multi draw is not available, we must issue separate draw commands + for (auto j = 0; j < indirectCommands.size(); j++) + { + vkCmdDrawIndexedIndirect(drawCmdBuffers[i], indirectCommandsBuffer.buffer, j * sizeof(VkDrawIndexedIndirectCommand), 1, sizeof(VkDrawIndexedIndirectCommand)); + } + } + + drawUI(drawCmdBuffers[i]); + + vkCmdEndRenderPass(drawCmdBuffers[i]); + + VK_CHECK_RESULT(vkEndCommandBuffer(drawCmdBuffers[i])); + } + } + + void loadAssets() + { + const uint32_t glTFLoadingFlags = vkglTF::FileLoadingFlags::PreTransformVertices | vkglTF::FileLoadingFlags::PreMultiplyVertexColors | vkglTF::FileLoadingFlags::FlipY; + models.plants.loadFromFile(getAssetPath() + "models/plants.gltf", vulkanDevice, queue, glTFLoadingFlags); + models.ground.loadFromFile(getAssetPath() + "models/plane_circle.gltf", vulkanDevice, queue, glTFLoadingFlags); + models.skysphere.loadFromFile(getAssetPath() + "models/sphere.gltf", vulkanDevice, queue, glTFLoadingFlags); + textures.plants.loadFromFile(getAssetPath() + "textures/texturearray_plants_rgba.ktx", VK_FORMAT_R8G8B8A8_UNORM, vulkanDevice, queue); + textures.ground.loadFromFile(getAssetPath() + "textures/ground_dry_rgba.ktx", VK_FORMAT_R8G8B8A8_UNORM, vulkanDevice, queue); + } + + void setupDescriptors() + { + // Pool + std::vector poolSizes = { + vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1), + vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 2), + }; + VkDescriptorPoolCreateInfo descriptorPoolInfo = vks::initializers::descriptorPoolCreateInfo(poolSizes, 2); + VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolInfo, nullptr, &descriptorPool)); + + // Layout + std::vector setLayoutBindings = { + // Binding 0: Vertex shader uniform buffer + vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_VERTEX_BIT, 0), + // Binding 1: Fragment shader combined sampler (plants texture array) + vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 1), + // Binding 1: Fragment shader combined sampler (ground texture) + vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 2), + }; + VkDescriptorSetLayoutCreateInfo descriptorLayout = vks::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings); + VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayout, nullptr, &descriptorSetLayout)); + + // Set + VkDescriptorSetAllocateInfo allocInfo = vks::initializers::descriptorSetAllocateInfo(descriptorPool, &descriptorSetLayout, 1); + VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSet)); + std::vector writeDescriptorSets = { + // Binding 0: Vertex shader uniform buffer + vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &uniformBuffer.descriptor), + // Binding 1: Plants texture array combined + vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, &textures.plants.descriptor), + // Binding 2: Ground texture combined + vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 2, &textures.ground.descriptor) + }; + vkUpdateDescriptorSets(device, static_cast(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, nullptr); + } + + void preparePipelines() + { + // Layout + VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = vks::initializers::pipelineLayoutCreateInfo(&descriptorSetLayout, 1); + VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, nullptr, &pipelineLayout)); + + // Pipelines + VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = vks::initializers::pipelineInputAssemblyStateCreateInfo(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0, VK_FALSE); + 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); + std::array shaderStages; + + VkGraphicsPipelineCreateInfo pipelineCreateInfo = vks::initializers::pipelineCreateInfo(pipelineLayout, renderPass); + 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(); + + // This example uses two different input states, one for the instanced part and one for non-instanced rendering + VkPipelineVertexInputStateCreateInfo inputState = vks::initializers::pipelineVertexInputStateCreateInfo(); + std::vector bindingDescriptions; + std::vector attributeDescriptions; + + // Vertex input bindings + // The instancing pipeline uses a vertex input state with two bindings + bindingDescriptions = { + // Binding point 0: Mesh vertex layout description at per-vertex rate + vks::initializers::vertexInputBindingDescription(0, sizeof(vkglTF::Vertex), VK_VERTEX_INPUT_RATE_VERTEX), + // Binding point 1: Instanced data at per-instance rate + vks::initializers::vertexInputBindingDescription(1, sizeof(InstanceData), VK_VERTEX_INPUT_RATE_INSTANCE) + }; + + // Vertex attribute bindings + // Note that the shader declaration for per-vertex and per-instance attributes is the same, the different input rates are only stored in the bindings: + // instanced.vert: + // layout (location = 0) in vec3 inPos; Per-Vertex + // ... + // layout (location = 4) in vec3 instancePos; Per-Instance + attributeDescriptions = { + // Per-vertex attributes + // These are advanced for each vertex fetched by the vertex shader + vks::initializers::vertexInputAttributeDescription(0, 0, VK_FORMAT_R32G32B32_SFLOAT, 0), // Location 0: Position + vks::initializers::vertexInputAttributeDescription(0, 1, VK_FORMAT_R32G32B32_SFLOAT, sizeof(float) * 3), // Location 1: Normal + vks::initializers::vertexInputAttributeDescription(0, 2, VK_FORMAT_R32G32_SFLOAT, sizeof(float) * 6), // Location 2: Texture coordinates + vks::initializers::vertexInputAttributeDescription(0, 3, VK_FORMAT_R32G32B32_SFLOAT, sizeof(float) * 8), // Location 3: Color + // Per-Instance attributes + // These are fetched for each instance rendered + vks::initializers::vertexInputAttributeDescription(1, 4, VK_FORMAT_R32G32B32_SFLOAT, offsetof(InstanceData, pos)), // Location 4: Position + vks::initializers::vertexInputAttributeDescription(1, 5, VK_FORMAT_R32G32B32_SFLOAT, offsetof(InstanceData, rot)), // Location 5: Rotation + vks::initializers::vertexInputAttributeDescription(1, 6, VK_FORMAT_R32_SFLOAT, offsetof(InstanceData, scale)), // Location 6: Scale + vks::initializers::vertexInputAttributeDescription(1, 7, VK_FORMAT_R32_SINT, offsetof(InstanceData, texIndex)), // Location 7: Texture array layer index + }; + inputState.pVertexBindingDescriptions = bindingDescriptions.data(); + inputState.pVertexAttributeDescriptions = attributeDescriptions.data(); + inputState.vertexBindingDescriptionCount = static_cast(bindingDescriptions.size()); + inputState.vertexAttributeDescriptionCount = static_cast(attributeDescriptions.size()); + + pipelineCreateInfo.pVertexInputState = &inputState; + + // Indirect (and instanced) pipeline for the plants + shaderStages[0] = loadShader(getShadersPath() + "indirectdraw/indirectdraw.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); + shaderStages[1] = loadShader(getShadersPath() + "indirectdraw/indirectdraw.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT); + VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipelines.plants)); + + // Only use non-instanced vertex attributes for models rendered without instancing + inputState.vertexBindingDescriptionCount = 1; + inputState.vertexAttributeDescriptionCount = 4; + + // Ground + shaderStages[0] = loadShader(getShadersPath() + "indirectdraw/ground.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); + shaderStages[1] = loadShader(getShadersPath() + "indirectdraw/ground.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT); + rasterizationState.cullMode = VK_CULL_MODE_BACK_BIT; + VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipelines.ground)); + + // Skysphere + shaderStages[0] = loadShader(getShadersPath() + "indirectdraw/skysphere.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); + shaderStages[1] = loadShader(getShadersPath() + "indirectdraw/skysphere.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT); + depthStencilState.depthWriteEnable = VK_FALSE; + rasterizationState.cullMode = VK_CULL_MODE_FRONT_BIT; + VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipelines.skysphere)); + } + + // Prepare (and stage) a buffer containing the indirect draw commands + void prepareIndirectData() + { + indirectCommands.clear(); + + // Create on indirect command for node in the scene with a mesh attached to it + uint32_t m = 0; + for (auto &node : models.plants.nodes) + { + if (node->mesh) + { + VkDrawIndexedIndirectCommand indirectCmd{}; + indirectCmd.instanceCount = OBJECT_INSTANCE_COUNT; + indirectCmd.firstInstance = m * OBJECT_INSTANCE_COUNT; + // A glTF node may consist of multiple primitives, but for this saample we only care for the first primitive + indirectCmd.firstIndex = node->mesh->primitives[0]->firstIndex; + indirectCmd.indexCount = node->mesh->primitives[0]->indexCount; + + indirectCommands.push_back(indirectCmd); + + m++; + } + } + + indirectDrawCount = static_cast(indirectCommands.size()); + + objectCount = 0; + for (auto indirectCmd : indirectCommands) + { + objectCount += indirectCmd.instanceCount; + } + + vks::Buffer stagingBuffer; + VK_CHECK_RESULT(vulkanDevice->createBuffer( + VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + &stagingBuffer, + indirectCommands.size() * sizeof(VkDrawIndexedIndirectCommand), + indirectCommands.data())); + + VK_CHECK_RESULT(vulkanDevice->createBuffer( + VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + &indirectCommandsBuffer, + stagingBuffer.size)); + + vulkanDevice->copyBuffer(&stagingBuffer, &indirectCommandsBuffer, queue); + + stagingBuffer.destroy(); + } + + // Prepare (and stage) a buffer containing instanced data for the mesh draws + void prepareInstanceData() + { + std::vector instanceData; + instanceData.resize(objectCount); + + std::default_random_engine rndEngine(benchmark.active ? 0 : (unsigned)time(nullptr)); + std::uniform_real_distribution uniformDist(0.0f, 1.0f); + + for (uint32_t i = 0; i < objectCount; i++) { + float theta = 2 * float(M_PI) * uniformDist(rndEngine); + float phi = acos(1 - 2 * uniformDist(rndEngine)); + instanceData[i].rot = glm::vec3(0.0f, float(M_PI) * uniformDist(rndEngine), 0.0f); + instanceData[i].pos = glm::vec3(sin(phi) * cos(theta), 0.0f, cos(phi)) * PLANT_RADIUS; + instanceData[i].scale = 1.0f + uniformDist(rndEngine) * 2.0f; + instanceData[i].texIndex = i / OBJECT_INSTANCE_COUNT; + } + + vks::Buffer stagingBuffer; + VK_CHECK_RESULT(vulkanDevice->createBuffer( + VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + &stagingBuffer, + instanceData.size() * sizeof(InstanceData), + instanceData.data())); + + VK_CHECK_RESULT(vulkanDevice->createBuffer( + VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + &instanceBuffer, + stagingBuffer.size)); + + vulkanDevice->copyBuffer(&stagingBuffer, &instanceBuffer, queue); + + stagingBuffer.destroy(); + } + + void prepareUniformBuffers() + { + VK_CHECK_RESULT(vulkanDevice->createBuffer(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &uniformBuffer, sizeof(uniformData))); + VK_CHECK_RESULT(uniformBuffer.map()); + } + + void updateUniformBuffer() + { + uniformData.projection = camera.matrices.perspective; + uniformData.view = camera.matrices.view; + memcpy(uniformBuffer.mapped, &uniformData, sizeof(uniformData)); + } + + void prepare() + { + VulkanExampleBase::prepare(); + loadAssets(); + prepareIndirectData(); + prepareInstanceData(); + prepareUniformBuffers(); + setupDescriptors(); + preparePipelines(); + buildCommandBuffers(); + prepared = true; + } + + void draw() + { + VulkanExampleBase::prepareFrame(); + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer]; + VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE)); + VulkanExampleBase::submitFrame(); + } + + virtual void render() + { + if (!prepared) { + return; + } + updateUniformBuffer(); + draw(); + } + + virtual void OnUpdateUIOverlay(vks::UIOverlay *overlay) + { + if (!vulkanDevice->features.multiDrawIndirect) { + if (overlay->header("Info")) { + overlay->text("multiDrawIndirect not supported"); + } + } + if (overlay->header("Statistics")) { + overlay->text("Objects: %d", objectCount); + } + } +}; + +VULKAN_EXAMPLE_MAIN() diff --git a/examples/linerendering/linerendering.cpp b/examples/linerendering/linerendering.cpp new file mode 100644 index 00000000..47229a0b --- /dev/null +++ b/examples/linerendering/linerendering.cpp @@ -0,0 +1,335 @@ +/* +* Vulkan Example - Line rendering +* +* Copyright (C) 2024 by Sascha Willems - www.saschawillems.de +* +* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) +*/ + +#include "vulkanexamplebase.h" +#include "VulkanglTFModel.h" + +class VulkanExample : public VulkanExampleBase +{ +public: + int32_t gridSize{ 3 }; + + vkglTF::Model model; + + struct UniformData { + glm::mat4 projection; + glm::mat4 modelview; + glm::vec4 lightPos{ -10.0f, -10.0f, 10.0f, 1.0f }; + } uniformData; + vks::Buffer uniformBuffer; + + struct Box { + vks::Buffer vertices; + vks::Buffer indices; + uint32_t indexCount{ 0 }; + } box; + + PFN_vkCmdSetLineRasterizationModeEXT vkCmdSetLineRasterizationModeEXT{ VK_NULL_HANDLE }; + PFN_vkCmdSetLineStippleEnableEXT vkCmdSetLineStippleEnableEXT{ VK_NULL_HANDLE }; + PFN_vkCmdSetLineStippleEXT vkCmdSetLineStippleEXT{ VK_NULL_HANDLE }; + + VkPipeline pipeline{ VK_NULL_HANDLE }; + VkPipelineLayout pipelineLayout{ VK_NULL_HANDLE }; + VkDescriptorSet descriptorSet{ VK_NULL_HANDLE }; + VkDescriptorSetLayout descriptorSetLayout{ VK_NULL_HANDLE }; + + VkPipeline pipelineLines{ VK_NULL_HANDLE }; + + VulkanExample() : VulkanExampleBase() + { + title = "Line rendering"; + camera.type = Camera::CameraType::firstperson; + camera.setPosition(glm::vec3(-3.0f, 1.0f, -2.75f)); + camera.setRotation(glm::vec3(-15.25f, -46.5f, 0.0f)); + camera.setPerspective(60.0f, (float)width / (float)height, 0.1f, 256.0f); + camera.movementSpeed = 4.0f; + camera.rotationSpeed = 0.25f; + // @todo + enabledInstanceExtensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); + enabledDeviceExtensions.push_back(VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME); + } + + ~VulkanExample() + { + if (device) { + vkDestroyPipeline(device, pipeline, nullptr); + vkDestroyPipelineLayout(device, pipelineLayout, nullptr); + vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); + uniformBuffer.destroy(); + } + } + + // Creates vertex and index buffers for rendering a box using line segments + void generateBox(glm::vec3 scale) + { + std::vector vertices = { + // Front + { -1.0f, -1.0f, 1.0f }, + { 1.0f, -1.0f, 1.0f }, + { 1.0f, 1.0f, 1.0f }, + { -1.0f, 1.0f, 1.0f }, + // Back + { -1.0f, -1.0f, -1.0f }, + { 1.0f, -1.0f, -1.0f }, + { 1.0f, 1.0f, -1.0f }, + { -1.0f, 1.0f, -1.0f }, + }; + for (glm::vec3& pos : vertices) { + pos *= scale; + } + + // Each pair defines a line segment + std::vector indices = { + 0,1, 1,2, 2,3, 3,0, 4,5, 5,6, 6,7, 7,4, 0,4, 1,5, 2,6, 3,7 + }; + + box.indexCount = static_cast(indices.size()); + + // Create buffers and upload data to the GPU + struct StagingBuffers { + vks::Buffer vertices; + vks::Buffer indices; + } stagingBuffers; + + // Host visible source buffers (staging) + VK_CHECK_RESULT(vulkanDevice->createBuffer(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &stagingBuffers.vertices, vertices.size() * sizeof(glm::vec3), vertices.data())); + VK_CHECK_RESULT(vulkanDevice->createBuffer(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &stagingBuffers.indices, indices.size() * sizeof(uint32_t), indices.data())); + + // Device local destination buffers + VK_CHECK_RESULT(vulkanDevice->createBuffer(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &box.vertices, vertices.size() * sizeof(glm::vec3))); + VK_CHECK_RESULT(vulkanDevice->createBuffer(VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &box.indices, indices.size() * sizeof(uint32_t))); + + // Copy from host do device + vulkanDevice->copyBuffer(&stagingBuffers.vertices, &box.vertices, queue); + vulkanDevice->copyBuffer(&stagingBuffers.indices, &box.indices, queue); + + // Clean up + stagingBuffers.vertices.destroy(); + stagingBuffers.indices.destroy(); + } + + + void buildCommandBuffers() + { + VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo(); + + VkClearValue clearValues[2]; + clearValues[0].color = defaultClearColor; + 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) { + renderPassBeginInfo.framebuffer = frameBuffers[i]; + + VK_CHECK_RESULT(vkBeginCommandBuffer(drawCmdBuffers[i], &cmdBufInfo)); + + 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 }; + + //vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + //vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, NULL); + //vkCmdBindVertexBuffers(drawCmdBuffers[i], 0, 1, &model.vertices.buffer, offsets); + //vkCmdBindIndexBuffer(drawCmdBuffers[i], model.indices.buffer, 0, VK_INDEX_TYPE_UINT32); + + for (int32_t y = 0; y < gridSize; y++) { + for (int32_t x = 0; x < gridSize; x++) { + + vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, NULL); + model.bindBuffers(drawCmdBuffers[i]); + glm::vec3 pos = glm::vec3(float(x - (gridSize / 2.0f)) * 2.5f, 0.0f, float(y - (gridSize / 2.0f)) * 2.5f); + vkCmdPushConstants(drawCmdBuffers[i], pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(glm::vec3), &pos); + model.draw(drawCmdBuffers[i]); + + vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLines); + vkCmdBindVertexBuffers(drawCmdBuffers[i], 0, 1, &box.vertices.buffer, offsets); + vkCmdBindIndexBuffer(drawCmdBuffers[i], box.indices.buffer, 0, VK_INDEX_TYPE_UINT32); + vkCmdDrawIndexed(drawCmdBuffers[i], box.indexCount, 1, 0, 0, 0); + } + } + + drawUI(drawCmdBuffers[i]); + + vkCmdEndRenderPass(drawCmdBuffers[i]); + + VK_CHECK_RESULT(vkEndCommandBuffer(drawCmdBuffers[i])); + } + } + + void loadAssets() + { + model.loadFromFile(getAssetPath() + "models/retroufo_red_lowpoly.gltf", vulkanDevice, queue, vkglTF::FileLoadingFlags::PreTransformVertices | vkglTF::FileLoadingFlags::FlipY | vkglTF::FileLoadingFlags::PreMultiplyVertexColors); + // @todo + generateBox(glm::vec3(1.0)); +// generateBox(model.dimensions.size); + } + + void setupDescriptors() + { + // Pool + std::vector poolSizes = { + vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 3) + }; + VkDescriptorPoolCreateInfo descriptorPoolInfo = vks::initializers::descriptorPoolCreateInfo(poolSizes, 3); + VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolInfo, nullptr, &descriptorPool)); + + // Layout + std::vector setLayoutBindings = { + vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_VERTEX_BIT, 0) + }; + VkDescriptorSetLayoutCreateInfo descriptorLayout = vks::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings); + VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayout, nullptr, &descriptorSetLayout)); + + // Set + VkDescriptorSetAllocateInfo allocInfo = vks::initializers::descriptorSetAllocateInfo(descriptorPool, &descriptorSetLayout, 1); + VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSet)); + std::vector writeDescriptorSets = { + vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &uniformBuffer.descriptor) + }; + vkUpdateDescriptorSets(device, static_cast(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, NULL); + } + + void preparePipelines() + { + // Layout + VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = vks::initializers::pipelineLayoutCreateInfo(&descriptorSetLayout, 1); + VkPushConstantRange pushConstantRange = vks::initializers::pushConstantRange(VK_SHADER_STAGE_VERTEX_BIT, sizeof(glm::vec3), 0); + pipelineLayoutCreateInfo.pushConstantRangeCount = 1; + pipelineLayoutCreateInfo.pPushConstantRanges = &pushConstantRange; + VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, nullptr, &pipelineLayout)); + + VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = vks::initializers::pipelineInputAssemblyStateCreateInfo(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0, VK_FALSE); + VkPipelineRasterizationStateCreateInfo rasterizationState = vks::initializers::pipelineRasterizationStateCreateInfo(VK_POLYGON_MODE_FILL, VK_CULL_MODE_BACK_BIT, 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.data(), static_cast(dynamicStateEnables.size()), 0); + VkPipelineTessellationStateCreateInfo tessellationState = vks::initializers::pipelineTessellationStateCreateInfo(3); + std::vector shaderStages(2); + + VkGraphicsPipelineCreateInfo pipelineCI = vks::initializers::pipelineCreateInfo(pipelineLayout, renderPass, 0); + pipelineCI.pInputAssemblyState = &inputAssemblyState; + pipelineCI.pRasterizationState = &rasterizationState; + pipelineCI.pColorBlendState = &colorBlendState; + pipelineCI.pMultisampleState = &multisampleState; + pipelineCI.pViewportState = &viewportState; + pipelineCI.pDepthStencilState = &depthStencilState; + pipelineCI.pDynamicState = &dynamicState; + pipelineCI.pVertexInputState = vkglTF::Vertex::getPipelineVertexInputState({ vkglTF::VertexComponent::Position, vkglTF::VertexComponent::Normal, vkglTF::VertexComponent::Color }); + pipelineCI.stageCount = static_cast(shaderStages.size()); + pipelineCI.pStages = shaderStages.data(); + + shaderStages[0] = loadShader(getShadersPath() + "linerendering/scene.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); + shaderStages[1] = loadShader(getShadersPath() + "linerendering/scene.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT); + VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipeline)); + + // Line rendering + inputAssemblyState.topology = VK_PRIMITIVE_TOPOLOGY_LINE_LIST; + rasterizationState.cullMode = VK_CULL_MODE_NONE; + + // Vertex bindings and attributes + VkVertexInputBindingDescription vertexInputBinding = vks::initializers::vertexInputBindingDescription(0, sizeof(glm::vec3), VK_VERTEX_INPUT_RATE_VERTEX); + VkVertexInputAttributeDescription vertexInputAttribute = vks::initializers::vertexInputAttributeDescription(0, 0, VK_FORMAT_R32G32B32_SFLOAT, 0); + VkPipelineVertexInputStateCreateInfo vertexInputStateCI = vks::initializers::pipelineVertexInputStateCreateInfo(); + vertexInputStateCI.vertexBindingDescriptionCount = 1; + vertexInputStateCI.pVertexBindingDescriptions = &vertexInputBinding; + vertexInputStateCI.vertexAttributeDescriptionCount = 1; + vertexInputStateCI.pVertexAttributeDescriptions = &vertexInputAttribute; + + pipelineCI.pVertexInputState = &vertexInputStateCI; + + VkPipelineRasterizationLineStateCreateInfoEXT lineRasterizationStateCI{}; + lineRasterizationStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_LINE_STATE_CREATE_INFO_KHR; + lineRasterizationStateCI.lineRasterizationMode = VK_LINE_RASTERIZATION_MODE_RECTANGULAR_SMOOTH_KHR; + lineRasterizationStateCI.stippledLineEnable = VK_TRUE; + lineRasterizationStateCI.lineStipplePattern = 0b01010101; + lineRasterizationStateCI.lineStippleFactor = 32; + + rasterizationState.pNext = &lineRasterizationStateCI; + + //pipelineCI.pNext = &lineRasterizationStateCI; + + shaderStages[0] = loadShader(getShadersPath() + "linerendering/line.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); + shaderStages[1] = loadShader(getShadersPath() + "linerendering/line.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT); + VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipelineLines)); + + } + + // Prepare and initialize uniform buffer containing shader uniforms + void prepareUniformBuffers() + { + VK_CHECK_RESULT(vulkanDevice->createBuffer(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &uniformBuffer, sizeof(UniformData))); + VK_CHECK_RESULT(uniformBuffer.map()); + } + + void updateUniformBuffers() + { + uniformData.projection = camera.matrices.perspective; + uniformData.modelview = camera.matrices.view; + memcpy(uniformBuffer.mapped, &uniformData, sizeof(UniformData)); + } + + void prepare() + { + VulkanExampleBase::prepare(); + + //vkCmdSetLineRasterizationModeEXT = reinterpret_cast(vkGetDeviceProcAddr(device, "vkCmdSetLineRasterizationModeEXT")); + //vkCmdSetLineStippleEnableEXT = reinterpret_cast(vkGetDeviceProcAddr(device, "vkCmdSetLineStippleEnableEXT")); + //vkCmdSetLineStippleEXT = reinterpret_cast(vkGetDeviceProcAddr(device, "vkCmdSetLineStippleEXT")); + + loadAssets(); + prepareUniformBuffers(); + setupDescriptors(); + preparePipelines(); + buildCommandBuffers(); + prepared = true; + } + + void draw() + { + VulkanExampleBase::prepareFrame(); + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer]; + VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE)); + VulkanExampleBase::submitFrame(); + } + + virtual void render() + { + if (!prepared) + return; + updateUniformBuffers(); + draw(); + } + + virtual void OnUpdateUIOverlay(vks::UIOverlay *overlay) + { + // @todo + } + +}; + +VULKAN_EXAMPLE_MAIN() \ No newline at end of file diff --git a/examples/oit/oit.cpp b/examples/oit/oit.cpp index 7c428220..5448c19a 100644 --- a/examples/oit/oit.cpp +++ b/examples/oit/oit.cpp @@ -303,7 +303,12 @@ public: descriptorLayoutCI = vks::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings); VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayoutCI, nullptr, &descriptorSetLayouts.color)); - // Sets + updateDescriptors(); + } + + void updateDescriptors() + { + // Images and linked buffers are recreated on resize and part of the descriptors, so we need to update those at runtime VkDescriptorSetAllocateInfo allocInfo = vks::initializers::descriptorSetAllocateInfo(descriptorPool, &descriptorSetLayouts.geometry, 1); // Update a geometry descriptor set @@ -569,7 +574,7 @@ public: destroyGeometryPass(); prepareGeometryPass(); vkResetDescriptorPool(device, descriptorPool, 0); - setupDescriptors(); + updateDescriptors(); resized = false; buildCommandBuffers(); } diff --git a/shaders/glsl/linerendering/line.frag b/shaders/glsl/linerendering/line.frag new file mode 100644 index 00000000..56abd630 --- /dev/null +++ b/shaders/glsl/linerendering/line.frag @@ -0,0 +1,8 @@ +#version 450 + +layout (location = 0) out vec4 outFragColor; + +void main() +{ + outFragColor = vec4(vec3(1.0), 1.0); +} \ No newline at end of file diff --git a/shaders/glsl/linerendering/line.frag.spv b/shaders/glsl/linerendering/line.frag.spv new file mode 100644 index 00000000..c899f0b7 Binary files /dev/null and b/shaders/glsl/linerendering/line.frag.spv differ diff --git a/shaders/glsl/linerendering/line.vert b/shaders/glsl/linerendering/line.vert new file mode 100644 index 00000000..f0caf5c3 --- /dev/null +++ b/shaders/glsl/linerendering/line.vert @@ -0,0 +1,21 @@ +#version 450 + +layout (location = 0) in vec3 inPos; + +layout (binding = 0) uniform UBO +{ + mat4 projection; + mat4 modelview; + vec4 lightPos; +} ubo; + +layout(push_constant) uniform PushConsts { + vec3 objPos; +} pushConsts; + +void main() +{ + vec3 locPos = vec3(ubo.modelview * vec4(inPos, 1.0)); + vec3 worldPos = vec3(ubo.modelview * vec4(inPos + pushConsts.objPos, 1.0)); + gl_Position = ubo.projection /* ubo.modelview */ * vec4(worldPos, 1.0); +} \ No newline at end of file diff --git a/shaders/glsl/linerendering/line.vert.spv b/shaders/glsl/linerendering/line.vert.spv new file mode 100644 index 00000000..3b0ff5c1 Binary files /dev/null and b/shaders/glsl/linerendering/line.vert.spv differ diff --git a/shaders/glsl/linerendering/scene.frag b/shaders/glsl/linerendering/scene.frag new file mode 100644 index 00000000..43f18f5f --- /dev/null +++ b/shaders/glsl/linerendering/scene.frag @@ -0,0 +1,19 @@ +#version 450 + +layout (location = 0) in vec3 inNormal; +layout (location = 1) in vec3 inColor; +layout (location = 2) in vec3 inViewVec; +layout (location = 3) in vec3 inLightVec; + +layout (location = 0) out vec4 outFragColor; + +void main() +{ + vec3 N = normalize(inNormal); + vec3 L = normalize(inLightVec); + vec3 V = normalize(inViewVec); + vec3 R = reflect(-L, N); + vec3 diffuse = max(dot(N, L), 0.0) * inColor; + vec3 specular = pow(max(dot(R, V), 0.0), 8.0) * vec3(0.75); + outFragColor = vec4(diffuse + specular, 0.5); +} \ No newline at end of file diff --git a/shaders/glsl/linerendering/scene.frag.spv b/shaders/glsl/linerendering/scene.frag.spv new file mode 100644 index 00000000..e1458224 Binary files /dev/null and b/shaders/glsl/linerendering/scene.frag.spv differ diff --git a/shaders/glsl/linerendering/scene.vert b/shaders/glsl/linerendering/scene.vert new file mode 100644 index 00000000..fc54284e --- /dev/null +++ b/shaders/glsl/linerendering/scene.vert @@ -0,0 +1,36 @@ +#version 450 + +layout (location = 0) in vec3 inPos; +layout (location = 1) in vec3 inNormal; +layout (location = 2) in vec3 inColor; + +layout (binding = 0) uniform UBO +{ + mat4 projection; + mat4 modelview; + vec4 lightPos; +} ubo; + +layout (location = 0) out vec3 outNormal; +layout (location = 1) out vec3 outColor; +layout (location = 2) out vec3 outViewVec; +layout (location = 3) out vec3 outLightVec; + +layout(push_constant) uniform PushConsts { + vec3 objPos; +} pushConsts; + +void main() +{ + outNormal = inNormal; + outColor = inColor; + + vec3 locPos = vec3(ubo.modelview * vec4(inPos, 1.0)); + vec3 worldPos = vec3(ubo.modelview * vec4(inPos + pushConsts.objPos, 1.0)); + gl_Position = ubo.projection /* ubo.modelview */ * vec4(worldPos, 1.0); + + vec4 pos = ubo.modelview * vec4(worldPos, 1.0); + outNormal = mat3(ubo.modelview) * inNormal; + outLightVec = ubo.lightPos.xyz - pos.xyz; + outViewVec = -pos.xyz; +} \ No newline at end of file diff --git a/shaders/glsl/linerendering/scene.vert.spv b/shaders/glsl/linerendering/scene.vert.spv new file mode 100644 index 00000000..65da5eff Binary files /dev/null and b/shaders/glsl/linerendering/scene.vert.spv differ