/* * Vulkan Example - 3D texture loading (and generation using perlin noise) example * * Copyright (C) 2016 by Sascha Willems - www.saschawillems.de * * This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) */ #include "vulkanexamplebase.h" #define VERTEX_BUFFER_BIND_ID 0 #define ENABLE_VALIDATION false // Vertex layout for this example struct Vertex { float pos[3]; float uv[2]; float normal[3]; }; // Translation of Ken Perlin's JAVA implementation (http://mrl.nyu.edu/~perlin/noise/) template class PerlinNoise { private: uint32_t permutations[512]; T fade(T t) { return t * t * t * (t * (t * (T)6 - (T)15) + (T)10); } T lerp(T t, T a, T b) { return a + t * (b - a); } T grad(int hash, T x, T y, T z) { // Convert LO 4 bits of hash code into 12 gradient directions int h = hash & 15; T u = h < 8 ? x : y; T v = h < 4 ? y : h == 12 || h == 14 ? x : z; return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v); } public: PerlinNoise() { // Generate random lookup for permutations containing all numbers from 0..255 std::vector plookup; plookup.resize(256); std::iota(plookup.begin(), plookup.end(), 0); std::default_random_engine rndEngine(std::random_device{}()); std::shuffle(plookup.begin(), plookup.end(), rndEngine); for (uint32_t i = 0; i < 256; i++) { permutations[i] = permutations[256 + i] = plookup[i]; } } T noise(T x, T y, T z) { // Find unit cube that contains point int32_t X = (int32_t)floor(x) & 255; int32_t Y = (int32_t)floor(y) & 255; int32_t Z = (int32_t)floor(z) & 255; // Find relative x,y,z of point in cube x -= floor(x); y -= floor(y); z -= floor(z); // Compute fade curves for each of x,y,z T u = fade(x); T v = fade(y); T w = fade(z); // Hash coordinates of the 8 cube corners uint32_t A = permutations[X] + Y; uint32_t AA = permutations[A] + Z; uint32_t AB = permutations[A + 1] + Z; uint32_t B = permutations[X + 1] + Y; uint32_t BA = permutations[B] + Z; uint32_t BB = permutations[B + 1] + Z; // And add blended results for 8 corners of the cube; T res = lerp(w, lerp(v, lerp(u, grad(permutations[AA], x, y, z), grad(permutations[BA], x - 1, y, z)), lerp(u, grad(permutations[AB], x, y - 1, z), grad(permutations[BB], x - 1, y - 1, z))), lerp(v, lerp(u, grad(permutations[AA + 1], x, y, z - 1), grad(permutations[BA + 1], x - 1, y, z - 1)), lerp(u, grad(permutations[AB + 1], x, y - 1, z - 1), grad(permutations[BB + 1], x - 1, y - 1, z - 1)))); return res; } }; // Fractal noise generator based on perlin noise above template class FractalNoise { private: PerlinNoise perlinNoise; uint32_t octaves; T frequency; T amplitude; T persistence; public: FractalNoise(const PerlinNoise &perlinNoise) { this->perlinNoise = perlinNoise; octaves = 6; persistence = (T)0.5; } T noise(T x, T y, T z) { T sum = 0; T frequency = (T)1; T amplitude = (T)1; T max = (T)0; for (uint32_t i = 0; i < octaves; i++) { sum += perlinNoise.noise(x * frequency, y * frequency, z * frequency) * amplitude; max += amplitude; amplitude *= persistence; frequency *= (T)2; } sum = sum / max; return (sum + (T)1.0) / (T)2.0; } }; class VulkanExample : public VulkanExampleBase { public: // Contains all Vulkan objects that are required to store and use a 3D texture struct Texture { VkSampler sampler = VK_NULL_HANDLE; VkImage image = VK_NULL_HANDLE; VkImageLayout imageLayout; VkDeviceMemory deviceMemory = VK_NULL_HANDLE; VkImageView view = VK_NULL_HANDLE; VkDescriptorImageInfo descriptor; VkFormat format; uint32_t width, height, depth; uint32_t mipLevels; } texture; struct { VkPipelineVertexInputStateCreateInfo inputState; std::vector inputBinding; std::vector inputAttributes; } vertices; vks::Buffer vertexBuffer; vks::Buffer indexBuffer; uint32_t indexCount; vks::Buffer uniformBufferVS; struct UboVS { glm::mat4 projection; glm::mat4 modelView; glm::vec4 viewPos; float depth = 0.0f; } uboVS; struct { VkPipeline solid; } pipelines; VkPipelineLayout pipelineLayout; VkDescriptorSet descriptorSet; VkDescriptorSetLayout descriptorSetLayout; VulkanExample() : VulkanExampleBase(ENABLE_VALIDATION) { title = "3D textures"; camera.type = Camera::CameraType::lookat; camera.setPosition(glm::vec3(0.0f, 0.0f, -2.5f)); camera.setRotation(glm::vec3(0.0f, 15.0f, 0.0f)); camera.setPerspective(60.0f, (float)width / (float)height, 0.1f, 256.0f); settings.overlay = true; srand((unsigned int)time(NULL)); } ~VulkanExample() { // Clean up used Vulkan resources // Note : Inherited destructor cleans up resources stored in base class destroyTextureImage(texture); vkDestroyPipeline(device, pipelines.solid, nullptr); vkDestroyPipelineLayout(device, pipelineLayout, nullptr); vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); vertexBuffer.destroy(); indexBuffer.destroy(); uniformBufferVS.destroy(); } // Prepare all Vulkan resources for the 3D texture (including descriptors) // Does not fill the texture with data void prepareNoiseTexture(uint32_t width, uint32_t height, uint32_t depth) { // A 3D texture is described as width x height x depth texture.width = width; texture.height = height; texture.depth = depth; texture.mipLevels = 1; texture.format = VK_FORMAT_R8_UNORM; // Format support check // 3D texture support in Vulkan is mandatory (in contrast to OpenGL) so no need to check if it's supported VkFormatProperties formatProperties; vkGetPhysicalDeviceFormatProperties(physicalDevice, texture.format, &formatProperties); // Check if format supports transfer if (!(formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_TRANSFER_DST_BIT)) { std::cout << "Error: Device does not support flag TRANSFER_DST for selected texture format!" << std::endl; return; } // Check if GPU supports requested 3D texture dimensions uint32_t maxImageDimension3D(vulkanDevice->properties.limits.maxImageDimension3D); if (width > maxImageDimension3D || height > maxImageDimension3D || depth > maxImageDimension3D) { std::cout << "Error: Requested texture dimensions is greater than supported 3D texture dimension!" << std::endl; return; } // Create optimal tiled target image VkImageCreateInfo imageCreateInfo = vks::initializers::imageCreateInfo(); imageCreateInfo.imageType = VK_IMAGE_TYPE_3D; imageCreateInfo.format = texture.format; imageCreateInfo.mipLevels = texture.mipLevels; imageCreateInfo.arrayLayers = 1; imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; imageCreateInfo.extent.width = texture.width; imageCreateInfo.extent.height = texture.height; imageCreateInfo.extent.depth = texture.depth; // Set initial layout of the image to undefined imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; imageCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; VK_CHECK_RESULT(vkCreateImage(device, &imageCreateInfo, nullptr, &texture.image)); // Device local memory to back up image VkMemoryAllocateInfo memAllocInfo = vks::initializers::memoryAllocateInfo(); VkMemoryRequirements memReqs = {}; vkGetImageMemoryRequirements(device, texture.image, &memReqs); memAllocInfo.allocationSize = memReqs.size; memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); VK_CHECK_RESULT(vkAllocateMemory(device, &memAllocInfo, nullptr, &texture.deviceMemory)); VK_CHECK_RESULT(vkBindImageMemory(device, texture.image, texture.deviceMemory, 0)); // Create sampler VkSamplerCreateInfo sampler = vks::initializers::samplerCreateInfo(); sampler.magFilter = VK_FILTER_LINEAR; sampler.minFilter = VK_FILTER_LINEAR; sampler.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; sampler.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; sampler.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; sampler.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; sampler.mipLodBias = 0.0f; sampler.compareOp = VK_COMPARE_OP_NEVER; sampler.minLod = 0.0f; sampler.maxLod = 0.0f; sampler.maxAnisotropy = 1.0; sampler.anisotropyEnable = VK_FALSE; sampler.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; VK_CHECK_RESULT(vkCreateSampler(device, &sampler, nullptr, &texture.sampler)); // Create image view VkImageViewCreateInfo view = vks::initializers::imageViewCreateInfo(); view.image = texture.image; view.viewType = VK_IMAGE_VIEW_TYPE_3D; view.format = texture.format; view.components = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A }; view.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; view.subresourceRange.baseMipLevel = 0; view.subresourceRange.baseArrayLayer = 0; view.subresourceRange.layerCount = 1; view.subresourceRange.levelCount = 1; VK_CHECK_RESULT(vkCreateImageView(device, &view, nullptr, &texture.view)); // Fill image descriptor image info to be used descriptor set setup texture.descriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; texture.descriptor.imageView = texture.view; texture.descriptor.sampler = texture.sampler; updateNoiseTexture(); } // Generate randomized noise and upload it to the 3D texture using staging void updateNoiseTexture() { const uint32_t texMemSize = texture.width * texture.height * texture.depth; uint8_t *data = new uint8_t[texMemSize]; memset(data, 0, texMemSize); // Generate perlin based noise std::cout << "Generating " << texture.width << " x " << texture.height << " x " << texture.depth << " noise texture..." << std::endl; auto tStart = std::chrono::high_resolution_clock::now(); PerlinNoise perlinNoise; FractalNoise fractalNoise(perlinNoise); const float noiseScale = static_cast(rand() % 10) + 4.0f; #pragma omp parallel for for (int32_t z = 0; z < texture.depth; z++) { for (int32_t y = 0; y < texture.height; y++) { for (int32_t x = 0; x < texture.width; x++) { float nx = (float)x / (float)texture.width; float ny = (float)y / (float)texture.height; float nz = (float)z / (float)texture.depth; #define FRACTAL #ifdef FRACTAL float n = fractalNoise.noise(nx * noiseScale, ny * noiseScale, nz * noiseScale); #else float n = 20.0 * perlinNoise.noise(nx, ny, nz); #endif n = n - floor(n); data[x + y * texture.width + z * texture.width * texture.height] = static_cast(floor(n * 255)); } } } auto tEnd = std::chrono::high_resolution_clock::now(); auto tDiff = std::chrono::duration(tEnd - tStart).count(); std::cout << "Done in " << tDiff << "ms" << std::endl; // Create a host-visible staging buffer that contains the raw image data VkBuffer stagingBuffer; VkDeviceMemory stagingMemory; // Buffer object VkBufferCreateInfo bufferCreateInfo = vks::initializers::bufferCreateInfo(); bufferCreateInfo.size = texMemSize; bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; VK_CHECK_RESULT(vkCreateBuffer(device, &bufferCreateInfo, nullptr, &stagingBuffer)); // Allocate host visible memory for data upload VkMemoryAllocateInfo memAllocInfo = vks::initializers::memoryAllocateInfo(); VkMemoryRequirements memReqs = {}; vkGetBufferMemoryRequirements(device, stagingBuffer, &memReqs); memAllocInfo.allocationSize = memReqs.size; memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); VK_CHECK_RESULT(vkAllocateMemory(device, &memAllocInfo, nullptr, &stagingMemory)); VK_CHECK_RESULT(vkBindBufferMemory(device, stagingBuffer, stagingMemory, 0)); // Copy texture data into staging buffer uint8_t *mapped; VK_CHECK_RESULT(vkMapMemory(device, stagingMemory, 0, memReqs.size, 0, (void **)&mapped)); memcpy(mapped, data, texMemSize); vkUnmapMemory(device, stagingMemory); VkCommandBuffer copyCmd = vulkanDevice->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); // The sub resource range describes the regions of the image we will be transitioned VkImageSubresourceRange subresourceRange = {}; subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; subresourceRange.baseMipLevel = 0; subresourceRange.levelCount = 1; subresourceRange.layerCount = 1; // Optimal image will be used as destination for the copy, so we must transfer from our // initial undefined image layout to the transfer destination layout vks::tools::setImageLayout( copyCmd, texture.image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, subresourceRange); // Copy 3D noise data to texture // Setup buffer copy regions VkBufferImageCopy bufferCopyRegion{}; bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; bufferCopyRegion.imageSubresource.mipLevel = 0; bufferCopyRegion.imageSubresource.baseArrayLayer = 0; bufferCopyRegion.imageSubresource.layerCount = 1; bufferCopyRegion.imageExtent.width = texture.width; bufferCopyRegion.imageExtent.height = texture.height; bufferCopyRegion.imageExtent.depth = texture.depth; vkCmdCopyBufferToImage( copyCmd, stagingBuffer, texture.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &bufferCopyRegion); // Change texture image layout to shader read after all mip levels have been copied texture.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; vks::tools::setImageLayout( copyCmd, texture.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, texture.imageLayout, subresourceRange); vulkanDevice->flushCommandBuffer(copyCmd, queue, true); // Clean up staging resources delete[] data; vkFreeMemory(device, stagingMemory, nullptr); vkDestroyBuffer(device, stagingBuffer, nullptr); } // Free all Vulkan resources used a texture object void destroyTextureImage(Texture texture) { if (texture.view != VK_NULL_HANDLE) vkDestroyImageView(device, texture.view, nullptr); if (texture.image != VK_NULL_HANDLE) vkDestroyImage(device, texture.image, nullptr); if (texture.sampler != VK_NULL_HANDLE) vkDestroySampler(device, texture.sampler, nullptr); if (texture.deviceMemory != VK_NULL_HANDLE) vkFreeMemory(device, texture.deviceMemory, nullptr); } 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) { // 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); vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, NULL); vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.solid); VkDeviceSize offsets[1] = { 0 }; vkCmdBindVertexBuffers(drawCmdBuffers[i], VERTEX_BUFFER_BIND_ID, 1, &vertexBuffer.buffer, offsets); vkCmdBindIndexBuffer(drawCmdBuffers[i], indexBuffer.buffer, 0, VK_INDEX_TYPE_UINT32); vkCmdDrawIndexed(drawCmdBuffers[i], indexCount, 1, 0, 0, 0); drawUI(drawCmdBuffers[i]); vkCmdEndRenderPass(drawCmdBuffers[i]); VK_CHECK_RESULT(vkEndCommandBuffer(drawCmdBuffers[i])); } } void draw() { VulkanExampleBase::prepareFrame(); // Command buffer to be submitted to the queue submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer]; // Submit to queue VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE)); VulkanExampleBase::submitFrame(); } void generateQuad() { // Setup vertices for a single uv-mapped quad made from two triangles std::vector vertices = { { { 1.0f, 1.0f, 0.0f }, { 1.0f, 1.0f },{ 0.0f, 0.0f, 1.0f } }, { { -1.0f, 1.0f, 0.0f }, { 0.0f, 1.0f },{ 0.0f, 0.0f, 1.0f } }, { { -1.0f, -1.0f, 0.0f }, { 0.0f, 0.0f },{ 0.0f, 0.0f, 1.0f } }, { { 1.0f, -1.0f, 0.0f }, { 1.0f, 0.0f },{ 0.0f, 0.0f, 1.0f } } }; // Setup indices std::vector indices = { 0,1,2, 2,3,0 }; indexCount = static_cast(indices.size()); // Create buffers // For the sake of simplicity we won't stage the vertex data to the gpu memory // Vertex buffer VK_CHECK_RESULT(vulkanDevice->createBuffer( VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &vertexBuffer, vertices.size() * sizeof(Vertex), vertices.data())); // Index buffer VK_CHECK_RESULT(vulkanDevice->createBuffer( VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &indexBuffer, indices.size() * sizeof(uint32_t), indices.data())); } void setupVertexDescriptions() { // Binding description vertices.inputBinding.resize(1); vertices.inputBinding[0] = vks::initializers::vertexInputBindingDescription( VERTEX_BUFFER_BIND_ID, sizeof(Vertex), VK_VERTEX_INPUT_RATE_VERTEX); // Attribute descriptions // Describes memory layout and shader positions vertices.inputAttributes.resize(3); // Location 0 : Position vertices.inputAttributes[0] = vks::initializers::vertexInputAttributeDescription( VERTEX_BUFFER_BIND_ID, 0, VK_FORMAT_R32G32B32_SFLOAT, offsetof(Vertex, pos)); // Location 1 : Texture coordinates vertices.inputAttributes[1] = vks::initializers::vertexInputAttributeDescription( VERTEX_BUFFER_BIND_ID, 1, VK_FORMAT_R32G32_SFLOAT, offsetof(Vertex, uv)); // Location 1 : Vertex normal vertices.inputAttributes[2] = vks::initializers::vertexInputAttributeDescription( VERTEX_BUFFER_BIND_ID, 2, VK_FORMAT_R32G32B32_SFLOAT, offsetof(Vertex, normal)); vertices.inputState = vks::initializers::pipelineVertexInputStateCreateInfo(); vertices.inputState.vertexBindingDescriptionCount = static_cast(vertices.inputBinding.size()); vertices.inputState.pVertexBindingDescriptions = vertices.inputBinding.data(); vertices.inputState.vertexAttributeDescriptionCount = static_cast(vertices.inputAttributes.size()); vertices.inputState.pVertexAttributeDescriptions = vertices.inputAttributes.data(); } void setupDescriptorPool() { // Example uses one ubo and one image sampler std::vector poolSizes = { vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1), vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1) }; VkDescriptorPoolCreateInfo descriptorPoolInfo = vks::initializers::descriptorPoolCreateInfo( static_cast(poolSizes.size()), poolSizes.data(), 2); VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolInfo, nullptr, &descriptorPool)); } void setupDescriptorSetLayout() { 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 image sampler vks::initializers::descriptorSetLayoutBinding( VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 1) }; VkDescriptorSetLayoutCreateInfo descriptorLayout = vks::initializers::descriptorSetLayoutCreateInfo( setLayoutBindings.data(), static_cast(setLayoutBindings.size())); VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayout, nullptr, &descriptorSetLayout)); VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo = vks::initializers::pipelineLayoutCreateInfo( &descriptorSetLayout, 1); VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pPipelineLayoutCreateInfo, nullptr, &pipelineLayout)); } void setupDescriptorSet() { 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, &uniformBufferVS.descriptor), // Binding 1 : Fragment shader texture sampler vks::initializers::writeDescriptorSet( descriptorSet, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, &texture.descriptor) }; vkUpdateDescriptorSets(device, static_cast(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, NULL); } void preparePipelines() { 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.data(), static_cast(dynamicStateEnables.size()), 0); // Load shaders std::array shaderStages; shaderStages[0] = loadShader(getShadersPath() + "texture3d/texture3d.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); shaderStages[1] = loadShader(getShadersPath() + "texture3d/texture3d.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT); VkGraphicsPipelineCreateInfo pipelineCreateInfo = vks::initializers::pipelineCreateInfo( pipelineLayout, renderPass, 0); pipelineCreateInfo.pVertexInputState = &vertices.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(); VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipelines.solid)); } // Prepare and initialize uniform buffer containing shader uniforms void prepareUniformBuffers() { // Vertex shader uniform buffer block VK_CHECK_RESULT(vulkanDevice->createBuffer( VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &uniformBufferVS, sizeof(uboVS), &uboVS)); VK_CHECK_RESULT(uniformBufferVS.map()); updateUniformBuffers(); } void updateUniformBuffers(bool viewchanged = true) { if (viewchanged) { uboVS.projection = camera.matrices.perspective; uboVS.modelView = camera.matrices.view; uboVS.viewPos = camera.viewPos; } else { uboVS.depth += frameTimer * 0.15f; if (uboVS.depth > 1.0f) uboVS.depth = uboVS.depth - 1.0f; } memcpy(uniformBufferVS.mapped, &uboVS, sizeof(uboVS)); } void prepare() { VulkanExampleBase::prepare(); generateQuad(); setupVertexDescriptions(); prepareUniformBuffers(); prepareNoiseTexture(128, 128, 128); setupDescriptorSetLayout(); preparePipelines(); setupDescriptorPool(); setupDescriptorSet(); buildCommandBuffers(); prepared = true; } virtual void render() { if (!prepared) return; draw(); if (!paused || camera.updated) updateUniformBuffers(camera.updated); } virtual void OnUpdateUIOverlay(vks::UIOverlay *overlay) { if (overlay->header("Settings")) { if (overlay->button("Generate new texture")) { updateNoiseTexture(); } } } }; VULKAN_EXAMPLE_MAIN()