diff --git a/src/devicelibrary.cpp b/src/devicelibrary.cpp index 076e396..d0ec953 100644 --- a/src/devicelibrary.cpp +++ b/src/devicelibrary.cpp @@ -285,7 +285,7 @@ namespace device_libs { vkDestroySwapchainKHR(Global::device, Global::swapChain, nullptr); if(Global::enableValidationLayers) std::cout << "Destroyed Swap Chain safely\n" << std::endl; } - VkImageView DeviceControl::createImageView(VkImage image, VkFormat format, VkImageAspectFlags flags) { + VkImageView DeviceControl::createImageView(VkImage image, VkFormat format, VkImageAspectFlags flags, uint32_t mipLevels) { // This defines the parameters of a newly created image object! VkImageViewCreateInfo viewInfo{}; viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; @@ -297,6 +297,7 @@ namespace device_libs { viewInfo.subresourceRange.levelCount = 1; viewInfo.subresourceRange.baseArrayLayer = 0; viewInfo.subresourceRange.layerCount = 1; + viewInfo.subresourceRange.levelCount = mipLevels; VkImageView imageView; if (vkCreateImageView(Global::device, &viewInfo, nullptr, &imageView) != VK_SUCCESS) { @@ -309,7 +310,7 @@ namespace device_libs { swapChainImageViews.resize(swapChainImages.size()); for (uint32_t i = 0; i < swapChainImages.size(); i++) { - swapChainImageViews[i] = createImageView(swapChainImages[i], swapChainImageFormat, VK_IMAGE_ASPECT_COLOR_BIT); + swapChainImageViews[i] = createImageView(swapChainImages[i], swapChainImageFormat, VK_IMAGE_ASPECT_COLOR_BIT, 1); } } void DeviceControl::destroyImageViews() { diff --git a/src/devicelibrary.h b/src/devicelibrary.h index 37e2144..a60f318 100644 --- a/src/devicelibrary.h +++ b/src/devicelibrary.h @@ -14,7 +14,7 @@ class DeviceControl { static void destroySurface(VkInstance& instance); static void createSwapChain(GLFWwindow* window); static void destroySwapChain(); - static VkImageView createImageView(VkImage image, VkFormat format, VkImageAspectFlags flags); + static VkImageView createImageView(VkImage image, VkFormat format, VkImageAspectFlags flags, uint32_t mipLevels); static void createImageViews(); static void destroyImageViews(); static void createCommandPool(); diff --git a/src/graphics/buffers.cpp b/src/graphics/buffers.cpp index d0b1316..1257433 100644 --- a/src/graphics/buffers.cpp +++ b/src/graphics/buffers.cpp @@ -203,9 +203,9 @@ namespace buffers_libs { ubo.model = glm::rotate(glm::mat4(1.0f), time * glm::radians(30.0f), glm::vec3(0.0f, 0.0f, 1.0f)); // Modify the view transformation to look at the object from above at a 45 degree angle. // This takes the eye position, center position, and the up direction. - ubo.view = glm::lookAt(glm::vec3(2.0f, 2.0f, 3.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f)); + ubo.view = glm::lookAt(glm::vec3(2.0f, 2.0f*time, 2.0f), glm::vec3(0.0f, 0.0f, 0.5f), glm::vec3(0.0f, 0.0f, 1.0f)); // 45 degree field of view, set aspect ratio, and near and far clipping range. - ubo.proj = glm::perspective(glm::radians(45.0f), device_libs::DeviceControl::getSwapChainExtent().width / (float) device_libs::DeviceControl::getSwapChainExtent().height, 0.1f, 10.0f); + ubo.proj = glm::perspective(glm::radians(45.0f), device_libs::DeviceControl::getSwapChainExtent().width / (float) device_libs::DeviceControl::getSwapChainExtent().height, 0.1f, 100.0f); // GLM was created for OpenGL, where the Y coordinate was inverted. This simply flips the sign. ubo.proj[1][1] *= -1; @@ -285,3 +285,4 @@ namespace buffers_libs { vkDestroyDescriptorPool(Global::device, descriptorPool, nullptr); } } + diff --git a/src/graphics/texture.cpp b/src/graphics/texture.cpp index 92c5d50..9d63d6c 100644 --- a/src/graphics/texture.cpp +++ b/src/graphics/texture.cpp @@ -1,7 +1,10 @@ +#include +#include #define STB_IMAGE_IMPLEMENTATION #include #include "texture.h" +uint32_t mipLevels; VkImage textureImage; VkDeviceMemory textureImageMemory; @@ -9,7 +12,7 @@ VkPipelineStageFlags sourceStage; VkPipelineStageFlags destinationStage; namespace texture_libs { - void createImage(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VkImage& image, VkDeviceMemory& imageMemory) { + void createImage(uint32_t width, uint32_t height, uint32_t mipLevels, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VkImage& image, VkDeviceMemory& imageMemory) { // This function specifies all the data in an image object, this is called directly after the creation of an image object. VkImageCreateInfo imageInfo{}; imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; @@ -25,6 +28,7 @@ namespace texture_libs { imageInfo.usage = usage; imageInfo.samples = VK_SAMPLE_COUNT_1_BIT; imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + imageInfo.mipLevels = mipLevels; if (vkCreateImage(Global::device, &imageInfo, nullptr, &image) != VK_SUCCESS) { throw std::runtime_error("failed to create image!"); @@ -90,7 +94,7 @@ namespace texture_libs { endSingleTimeCommands(commandBuffer); } - void transitionImageLayout(VkImage image, VkFormat format, VkImageLayout oldLayout, VkImageLayout newLayout) { + void transitionImageLayout(VkImage image, VkFormat format, VkImageLayout oldLayout, VkImageLayout newLayout, uint32_t mipLevels) { // This function handles transitioning image layout data from one layout to another. VkCommandBuffer commandBuffer = beginSingleTimeCommands(); @@ -104,7 +108,7 @@ namespace texture_libs { barrier.image = image; barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; barrier.subresourceRange.baseMipLevel = 0; - barrier.subresourceRange.levelCount = 1; + barrier.subresourceRange.levelCount = mipLevels; barrier.subresourceRange.baseArrayLayer = 0; barrier.subresourceRange.layerCount = 1; @@ -175,17 +179,103 @@ void copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t } throw std::runtime_error("failed to find supported depth buffering format!"); } - bool hasStencilComponent(VkFormat format) { return format == VK_FORMAT_D32_SFLOAT_S8_UINT || format == VK_FORMAT_D24_UNORM_S8_UINT; } + void generateMipmaps(VkImage image, VkFormat imageFormat, int32_t textureWidth, int32_t textureHeight, uint32_t mipLevels) { + // Check if image format supports linear blitting + VkFormatProperties formatProperties; + vkGetPhysicalDeviceFormatProperties(Global::physicalDevice, imageFormat, &formatProperties); + + if (!(formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT)) { + throw std::runtime_error("texture image format does not support linear blitting!"); + } + + VkCommandBuffer commandBuffer = beginSingleTimeCommands(); + + // Specify the parameters of an image memory barrier + VkImageMemoryBarrier barrier{}; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.image = image; + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + barrier.subresourceRange.baseArrayLayer = 0; + barrier.subresourceRange.layerCount = 1; + barrier.subresourceRange.levelCount = 1; + + int32_t mipWidth = textureWidth; + int32_t mipHeight = textureHeight; + + for(uint32_t mip = 1; mip < mipLevels; mip++) { + barrier.subresourceRange.baseMipLevel = mip - 1; + barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + + vkCmdPipelineBarrier(commandBuffer, + VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, + 0, nullptr, + 0, nullptr, + 1, &barrier); + + VkImageBlit blit{}; + blit.srcOffsets[0] = { 0, 0, 0 }; + blit.srcOffsets[1] = { mipWidth, mipHeight, 1 }; + blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + blit.srcSubresource.mipLevel = mip - 1; + blit.srcSubresource.baseArrayLayer = 0; + blit.srcSubresource.layerCount = 1; + blit.dstOffsets[0] = { 0, 0, 0 }; + blit.dstOffsets[1] = { mipWidth > 1 ? mipWidth / 2 : 1, mipHeight > 1 ? mipHeight / 2 : 1, 1 }; + blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + blit.dstSubresource.mipLevel = mip; + blit.dstSubresource.baseArrayLayer = 0; + blit.dstSubresource.layerCount = 1; + + vkCmdBlitImage(commandBuffer, + image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, &blit, VK_FILTER_LINEAR); + + barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + + vkCmdPipelineBarrier(commandBuffer, + VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, + 0, nullptr, + 0, nullptr, + 1, &barrier); + + if (mipWidth > 1) mipWidth /= 2; + if (mipHeight > 1) mipHeight /= 2; + } + barrier.subresourceRange.baseMipLevel = mipLevels - 1; + barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + + vkCmdPipelineBarrier(commandBuffer, + VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, + 0, nullptr, + 0, nullptr, + 1, &barrier); + + endSingleTimeCommands(commandBuffer); + + } // -------------------------------- Image Libraries ------------------------------- // void Texture::createTextureImage() { // Import pixels from image with data on color channels, width and height, and colorspace! // Its a lot of kind of complicated memory calls to bring it from a file -> to a buffer -> to a image object. int textureWidth, textureHeight, textureChannels; stbi_uc* pixels = stbi_load(Global::TEXTURE_PATH.c_str(), &textureWidth, &textureHeight, &textureChannels, STBI_rgb_alpha); - + mipLevels = static_cast(std::floor(std::log2(std::max(textureWidth, textureHeight)))) + 1; + VkDeviceSize imageSize = textureWidth * textureHeight * 4; if(!pixels) { @@ -202,18 +292,19 @@ void copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t stbi_image_free(pixels); - createImage(textureWidth, textureHeight, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, textureImage, textureImageMemory); + createImage(textureWidth, textureHeight, mipLevels, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, textureImage, textureImageMemory); - transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, mipLevels); copyBufferToImage(stagingBuffer, textureImage, static_cast(textureWidth), static_cast(textureHeight)); - transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); vkDestroyBuffer(Global::device, stagingBuffer, nullptr); vkFreeMemory(Global::device, stagingBufferMemory, nullptr); + + generateMipmaps(textureImage, VK_FORMAT_R8G8B8A8_SRGB, textureWidth, textureHeight, mipLevels); } void Texture::createTextureImageView() { // Create a texture image view, which is a struct of information about the image. - Global::textureImageView = device_libs::DeviceControl::createImageView(textureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_ASPECT_COLOR_BIT); + Global::textureImageView = device_libs::DeviceControl::createImageView(textureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_ASPECT_COLOR_BIT, mipLevels); } void Texture::createTextureSampler() { // Create a sampler to access and parse the texture object. @@ -251,7 +342,7 @@ void copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; samplerInfo.mipLodBias = 0.0f; samplerInfo.minLod = 0.0f; - samplerInfo.maxLod = 0.0f; + samplerInfo.maxLod = VK_LOD_CLAMP_NONE; if (vkCreateSampler(Global::device, &samplerInfo, nullptr, &Global::textureSampler) != VK_SUCCESS) { throw std::runtime_error("failed to create texture sampler!"); @@ -275,11 +366,15 @@ void copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t void Texture::createDepthResources() { VkFormat depthFormat = findDepthFormat(); VkExtent2D swapChainExtent = device_libs::DeviceControl::getSwapChainExtent(); - createImage(swapChainExtent.width, swapChainExtent.height, depthFormat, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, Global::depthImage, Global::depthImageMemory); - Global::depthImageView = device_libs::DeviceControl::createImageView(Global::depthImage, depthFormat, VK_IMAGE_ASPECT_DEPTH_BIT); + createImage(swapChainExtent.width, swapChainExtent.height, 1, depthFormat, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, Global::depthImage, Global::depthImageMemory); + Global::depthImageView = device_libs::DeviceControl::createImageView(Global::depthImage, depthFormat, VK_IMAGE_ASPECT_DEPTH_BIT, 1); // Explicit transition from the layout of the image to the depth attachment is unnecessary here, since that will be handled in the render pass! + } +// ---------------------------- Getters & Setters ---------------------------------// + uint32_t Texture::getMipLevels() { + return mipLevels; } } diff --git a/src/graphics/texture.h b/src/graphics/texture.h index 944f8ed..3bfa48a 100644 --- a/src/graphics/texture.h +++ b/src/graphics/texture.h @@ -13,5 +13,8 @@ namespace texture_libs { static void destroyTextureSampler(); static VkFormat findDepthFormat(); static void createDepthResources(); + + // ------------ Getters & Setters ------------ // + static uint32_t getMipLevels(); }; } diff --git a/src/shaders/fragment.frag b/src/shaders/fragment.frag index 6a1933b..ea2e60b 100644 --- a/src/shaders/fragment.frag +++ b/src/shaders/fragment.frag @@ -7,5 +7,6 @@ layout(location = 1) in vec2 fragTexCoord; layout(location = 0) out vec4 outColor; void main() { + outColor = vec4(texture(texSampler, fragTexCoord).rgb, 1.0); } diff --git a/src/shaders/vertex.vert b/src/shaders/vertex.vert index 36087b1..03db775 100644 --- a/src/shaders/vertex.vert +++ b/src/shaders/vertex.vert @@ -18,6 +18,7 @@ layout(location = 0) out vec3 fragColor; layout(location = 1) out vec2 fragTexCoord; void main() { + gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 1.0); fragColor = inColor; fragTexCoord = inTexCoord;