procedural-3d-engine/hdr/hdr.cpp
Bill Hollings 96601494d5 Updates to iOS and macOS functionality using MoltenVK.
Use getAssetPath() instead of ASSET_PATH to support broader range of platforms.
Multisampling example determines sample rate from device at runtime.
Move example wrapper code from DemoViewController.mm to dedicated MVKExample.cpp file.
Remove AssImp libraries for iOS and macOS from repo, and add instructions for
generating them from AssImp source files.
Update general README.md file to mention support for iOS and macOS platforms.
Add Apple logo for README.md.
Update Vulkan logo to current registered TM logo.
Update copyright notice of MoltenVK example files to MIT license.
Examples use +/- on main keyboard, instead of numpad.
2017-06-22 14:53:49 -04:00

1057 lines
41 KiB
C++

/*
* Vulkan Example - HDR
*
* Note: Requires the separate asset pack (see data/README.md)
*
* 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <vector>
#define GLM_FORCE_RADIANS
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <gli/gli.hpp>
#include <vulkan/vulkan.h>
#include "vulkanexamplebase.h"
#include "VulkanBuffer.hpp"
#include "VulkanModel.hpp"
#include "VulkanTexture.hpp"
#define ENABLE_VALIDATION false
class VulkanExample : public VulkanExampleBase
{
public:
bool bloom = true;
bool displaySkybox = true;
// Vertex layout for the models
vks::VertexLayout vertexLayout = vks::VertexLayout({
vks::VERTEX_COMPONENT_POSITION,
vks::VERTEX_COMPONENT_NORMAL,
vks::VERTEX_COMPONENT_UV,
});
struct {
vks::TextureCubeMap envmap;
} textures;
struct Models {
vks::Model skybox;
std::vector<vks::Model> objects;
uint32_t objectIndex = 1;
} models;
struct {
vks::Buffer matrices;
vks::Buffer params;
} uniformBuffers;
struct UBOVS {
glm::mat4 projection;
glm::mat4 modelview;
} uboVS;
struct UBOParams {
float exposure = 1.0f;
} uboParams;
struct {
VkPipeline skybox;
VkPipeline reflect;
VkPipeline composition;
VkPipeline bloom[2];
} pipelines;
struct {
VkPipelineLayout models;
VkPipelineLayout composition;
VkPipelineLayout bloomFilter;
} pipelineLayouts;
struct {
VkDescriptorSet object;
VkDescriptorSet skybox;
VkDescriptorSet composition;
VkDescriptorSet bloomFilter;
} descriptorSets;
struct {
VkDescriptorSetLayout models;
VkDescriptorSetLayout composition;
VkDescriptorSetLayout bloomFilter;
} descriptorSetLayouts;
// Framebuffer for offscreen rendering
struct FrameBufferAttachment {
VkImage image;
VkDeviceMemory mem;
VkImageView view;
VkFormat format;
void destroy(VkDevice device)
{
vkDestroyImageView(device, view, nullptr);
vkDestroyImage(device, image, nullptr);
vkFreeMemory(device, mem, nullptr);
}
};
struct FrameBuffer {
int32_t width, height;
VkFramebuffer frameBuffer;
FrameBufferAttachment color[2];
FrameBufferAttachment depth;
VkRenderPass renderPass;
VkSampler sampler;
VkCommandBuffer cmdBuffer = VK_NULL_HANDLE;
VkSemaphore semaphore = VK_NULL_HANDLE;
} offscreen;
struct {
int32_t width, height;
VkFramebuffer frameBuffer;
FrameBufferAttachment color[1];
VkRenderPass renderPass;
VkSampler sampler;
} filterPass;
VulkanExample() : VulkanExampleBase(ENABLE_VALIDATION)
{
title = "Vulkan Example - HDR rendering";
enableTextOverlay = true;
camera.type = Camera::CameraType::lookat;
camera.setPosition(glm::vec3(0.0f, 0.0f, -4.0f));
camera.setRotation(glm::vec3(0.0f, 180.0f, 0.0f));
camera.setPerspective(60.0f, (float)width / (float)height, 0.1f, 256.0f);
}
~VulkanExample()
{
vkDestroyPipeline(device, pipelines.skybox, nullptr);
vkDestroyPipeline(device, pipelines.reflect, nullptr);
vkDestroyPipeline(device, pipelines.composition, nullptr);
vkDestroyPipeline(device, pipelines.bloom[0], nullptr);
vkDestroyPipeline(device, pipelines.bloom[1], nullptr);
vkDestroyPipelineLayout(device, pipelineLayouts.models, nullptr);
vkDestroyPipelineLayout(device, pipelineLayouts.composition, nullptr);
vkDestroyPipelineLayout(device, pipelineLayouts.bloomFilter, nullptr);
vkDestroyDescriptorSetLayout(device, descriptorSetLayouts.models, nullptr);
vkDestroyDescriptorSetLayout(device, descriptorSetLayouts.composition, nullptr);
vkDestroyDescriptorSetLayout(device, descriptorSetLayouts.bloomFilter, nullptr);
vkDestroySemaphore(device, offscreen.semaphore, nullptr);
vkDestroyRenderPass(device, offscreen.renderPass, nullptr);
vkDestroyRenderPass(device, filterPass.renderPass, nullptr);
vkDestroyFramebuffer(device, offscreen.frameBuffer, nullptr);
vkDestroyFramebuffer(device, filterPass.frameBuffer, nullptr);
vkDestroySampler(device, offscreen.sampler, nullptr);
vkDestroySampler(device, filterPass.sampler, nullptr);
offscreen.depth.destroy(device);
offscreen.color[0].destroy(device);
offscreen.color[1].destroy(device);
filterPass.color[0].destroy(device);
for (auto& model : models.objects) {
model.destroy();
}
models.skybox.destroy();
uniformBuffers.matrices.destroy();
uniformBuffers.params.destroy();
textures.envmap.destroy();
}
void reBuildCommandBuffers()
{
if (!checkCommandBuffers())
{
destroyCommandBuffers();
createCommandBuffers();
}
buildCommandBuffers();
buildDeferredCommandBuffer();
}
void buildCommandBuffers()
{
VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo();
VkClearValue clearValues[2];
clearValues[0].color = defaultClearColor;
clearValues[0].color = { { 0.0f, 0.0f, 0.0f, 0.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.clearValueCount = 2;
renderPassBeginInfo.pClearValues = clearValues;
VkViewport viewport;
VkRect2D scissor;
VkDeviceSize offsets[1] = { 0 };
for (int32_t i = 0; i < drawCmdBuffers.size(); ++i)
{
VK_CHECK_RESULT(vkBeginCommandBuffer(drawCmdBuffers[i], &cmdBufInfo));
// Bloom filter
renderPassBeginInfo.framebuffer = filterPass.frameBuffer;
renderPassBeginInfo.renderPass = filterPass.renderPass;
renderPassBeginInfo.clearValueCount = 1;
renderPassBeginInfo.renderArea.extent.width = filterPass.width;
renderPassBeginInfo.renderArea.extent.height = filterPass.height;
viewport = vks::initializers::viewport((float)filterPass.width, (float)filterPass.height, 0.0f, 1.0f);
scissor = vks::initializers::rect2D(filterPass.width, filterPass.height, 0, 0);
vkCmdBeginRenderPass(drawCmdBuffers[i], &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
vkCmdSetViewport(drawCmdBuffers[i], 0, 1, &viewport);
vkCmdSetScissor(drawCmdBuffers[i], 0, 1, &scissor);
vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayouts.bloomFilter, 0, 1, &descriptorSets.bloomFilter, 0, NULL);
vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.bloom[1]);
vkCmdDraw(drawCmdBuffers[i], 3, 1, 0, 0);
vkCmdEndRenderPass(drawCmdBuffers[i]);
viewport = vks::initializers::viewport((float)width, (float)height, 0.0f, 1.0f);
scissor = vks::initializers::rect2D(width, height, 0, 0);
// Final composition
renderPassBeginInfo.framebuffer = frameBuffers[i];
renderPassBeginInfo.renderPass = renderPass;
renderPassBeginInfo.clearValueCount = 2;
renderPassBeginInfo.renderArea.extent.width = width;
renderPassBeginInfo.renderArea.extent.height = height;
vkCmdBeginRenderPass(drawCmdBuffers[i], &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
vkCmdSetViewport(drawCmdBuffers[i], 0, 1, &viewport);
vkCmdSetScissor(drawCmdBuffers[i], 0, 1, &scissor);
vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayouts.composition, 0, 1, &descriptorSets.composition, 0, NULL);
// Scene
vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.composition);
vkCmdDraw(drawCmdBuffers[i], 3, 1, 0, 0);
// Bloom
if (bloom)
{
vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.bloom[0]);
vkCmdDraw(drawCmdBuffers[i], 3, 1, 0, 0);
}
vkCmdEndRenderPass(drawCmdBuffers[i]);
VK_CHECK_RESULT(vkEndCommandBuffer(drawCmdBuffers[i]));
}
}
void createAttachment(VkFormat format, VkImageUsageFlagBits usage, FrameBufferAttachment *attachment)
{
VkImageAspectFlags aspectMask = 0;
VkImageLayout imageLayout;
attachment->format = format;
if (usage & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)
{
aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
}
if (usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)
{
aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
imageLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
}
assert(aspectMask > 0);
VkImageCreateInfo image = vks::initializers::imageCreateInfo();
image.imageType = VK_IMAGE_TYPE_2D;
image.format = format;
image.extent.width = offscreen.width;
image.extent.height = offscreen.height;
image.extent.depth = 1;
image.mipLevels = 1;
image.arrayLayers = 1;
image.samples = VK_SAMPLE_COUNT_1_BIT;
image.tiling = VK_IMAGE_TILING_OPTIMAL;
image.usage = usage | VK_IMAGE_USAGE_SAMPLED_BIT;
VkMemoryAllocateInfo memAlloc = vks::initializers::memoryAllocateInfo();
VkMemoryRequirements memReqs;
VK_CHECK_RESULT(vkCreateImage(device, &image, nullptr, &attachment->image));
vkGetImageMemoryRequirements(device, attachment->image, &memReqs);
memAlloc.allocationSize = memReqs.size;
memAlloc.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
VK_CHECK_RESULT(vkAllocateMemory(device, &memAlloc, nullptr, &attachment->mem));
VK_CHECK_RESULT(vkBindImageMemory(device, attachment->image, attachment->mem, 0));
VkImageViewCreateInfo imageView = vks::initializers::imageViewCreateInfo();
imageView.viewType = VK_IMAGE_VIEW_TYPE_2D;
imageView.format = format;
imageView.subresourceRange = {};
imageView.subresourceRange.aspectMask = aspectMask;
imageView.subresourceRange.baseMipLevel = 0;
imageView.subresourceRange.levelCount = 1;
imageView.subresourceRange.baseArrayLayer = 0;
imageView.subresourceRange.layerCount = 1;
imageView.image = attachment->image;
VK_CHECK_RESULT(vkCreateImageView(device, &imageView, nullptr, &attachment->view));
}
// Prepare a new framebuffer and attachments for offscreen rendering (G-Buffer)
void prepareoffscreenfer()
{
{
offscreen.width = width;
offscreen.height = height;
// Color attachments
// Two floating point color buffers
createAttachment(VK_FORMAT_R32G32B32A32_SFLOAT, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, &offscreen.color[0]);
createAttachment(VK_FORMAT_R32G32B32A32_SFLOAT, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, &offscreen.color[1]);
// Depth attachment
createAttachment(depthFormat, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, &offscreen.depth);
// Set up separate renderpass with references to the colorand depth attachments
std::array<VkAttachmentDescription, 3> attachmentDescs = {};
// Init attachment properties
for (uint32_t i = 0; i < 3; ++i)
{
attachmentDescs[i].samples = VK_SAMPLE_COUNT_1_BIT;
attachmentDescs[i].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
attachmentDescs[i].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
attachmentDescs[i].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attachmentDescs[i].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
if (i == 2)
{
attachmentDescs[i].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
attachmentDescs[i].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
}
else
{
attachmentDescs[i].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
attachmentDescs[i].finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
}
}
// Formats
attachmentDescs[0].format = offscreen.color[0].format;
attachmentDescs[1].format = offscreen.color[1].format;
attachmentDescs[2].format = offscreen.depth.format;
std::vector<VkAttachmentReference> colorReferences;
colorReferences.push_back({ 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL });
colorReferences.push_back({ 1, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL });
VkAttachmentReference depthReference = {};
depthReference.attachment = 2;
depthReference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
VkSubpassDescription subpass = {};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass.pColorAttachments = colorReferences.data();
subpass.colorAttachmentCount = 2;
subpass.pDepthStencilAttachment = &depthReference;
// Use subpass dependencies for attachment layput transitions
std::array<VkSubpassDependency, 2> dependencies;
dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL;
dependencies[0].dstSubpass = 0;
dependencies[0].srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependencies[0].srcAccessMask = VK_ACCESS_MEMORY_READ_BIT;
dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
dependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
dependencies[1].srcSubpass = 0;
dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL;
dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependencies[1].dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
dependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
dependencies[1].dstAccessMask = VK_ACCESS_MEMORY_READ_BIT;
dependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
VkRenderPassCreateInfo renderPassInfo = {};
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
renderPassInfo.pAttachments = attachmentDescs.data();
renderPassInfo.attachmentCount = static_cast<uint32_t>(attachmentDescs.size());
renderPassInfo.subpassCount = 1;
renderPassInfo.pSubpasses = &subpass;
renderPassInfo.dependencyCount = 2;
renderPassInfo.pDependencies = dependencies.data();
VK_CHECK_RESULT(vkCreateRenderPass(device, &renderPassInfo, nullptr, &offscreen.renderPass));
std::array<VkImageView, 3> attachments;
attachments[0] = offscreen.color[0].view;
attachments[1] = offscreen.color[1].view;
attachments[2] = offscreen.depth.view;
VkFramebufferCreateInfo fbufCreateInfo = {};
fbufCreateInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
fbufCreateInfo.pNext = NULL;
fbufCreateInfo.renderPass = offscreen.renderPass;
fbufCreateInfo.pAttachments = attachments.data();
fbufCreateInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
fbufCreateInfo.width = offscreen.width;
fbufCreateInfo.height = offscreen.height;
fbufCreateInfo.layers = 1;
VK_CHECK_RESULT(vkCreateFramebuffer(device, &fbufCreateInfo, nullptr, &offscreen.frameBuffer));
// Create sampler to sample from the color attachments
VkSamplerCreateInfo sampler = vks::initializers::samplerCreateInfo();
sampler.magFilter = VK_FILTER_NEAREST;
sampler.minFilter = VK_FILTER_NEAREST;
sampler.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
sampler.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
sampler.addressModeV = sampler.addressModeU;
sampler.addressModeW = sampler.addressModeU;
sampler.mipLodBias = 0.0f;
sampler.maxAnisotropy = 0;
sampler.minLod = 0.0f;
sampler.maxLod = 1.0f;
sampler.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;
VK_CHECK_RESULT(vkCreateSampler(device, &sampler, nullptr, &offscreen.sampler));
}
// Bloom separable filter pass
{
filterPass.width = width;
filterPass.height = height;
// Color attachments
// Two floating point color buffers
createAttachment(VK_FORMAT_R32G32B32A32_SFLOAT, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, &filterPass.color[0]);
// Set up separate renderpass with references to the colorand depth attachments
std::array<VkAttachmentDescription, 1> attachmentDescs = {};
// Init attachment properties
attachmentDescs[0].samples = VK_SAMPLE_COUNT_1_BIT;
attachmentDescs[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
attachmentDescs[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
attachmentDescs[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attachmentDescs[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attachmentDescs[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
attachmentDescs[0].finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
attachmentDescs[0].format = filterPass.color[0].format;
std::vector<VkAttachmentReference> colorReferences;
colorReferences.push_back({ 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL });
VkSubpassDescription subpass = {};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass.pColorAttachments = colorReferences.data();
subpass.colorAttachmentCount = 1;
// Use subpass dependencies for attachment layput transitions
std::array<VkSubpassDependency, 2> dependencies;
dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL;
dependencies[0].dstSubpass = 0;
dependencies[0].srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependencies[0].srcAccessMask = VK_ACCESS_MEMORY_READ_BIT;
dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
dependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
dependencies[1].srcSubpass = 0;
dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL;
dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependencies[1].dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
dependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
dependencies[1].dstAccessMask = VK_ACCESS_MEMORY_READ_BIT;
dependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
VkRenderPassCreateInfo renderPassInfo = {};
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
renderPassInfo.pAttachments = attachmentDescs.data();
renderPassInfo.attachmentCount = static_cast<uint32_t>(attachmentDescs.size());
renderPassInfo.subpassCount = 1;
renderPassInfo.pSubpasses = &subpass;
renderPassInfo.dependencyCount = 2;
renderPassInfo.pDependencies = dependencies.data();
VK_CHECK_RESULT(vkCreateRenderPass(device, &renderPassInfo, nullptr, &filterPass.renderPass));
std::array<VkImageView, 1> attachments;
attachments[0] = filterPass.color[0].view;
VkFramebufferCreateInfo fbufCreateInfo = {};
fbufCreateInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
fbufCreateInfo.pNext = NULL;
fbufCreateInfo.renderPass = filterPass.renderPass;
fbufCreateInfo.pAttachments = attachments.data();
fbufCreateInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
fbufCreateInfo.width = filterPass.width;
fbufCreateInfo.height = filterPass.height;
fbufCreateInfo.layers = 1;
VK_CHECK_RESULT(vkCreateFramebuffer(device, &fbufCreateInfo, nullptr, &filterPass.frameBuffer));
// Create sampler to sample from the color attachments
VkSamplerCreateInfo sampler = vks::initializers::samplerCreateInfo();
sampler.magFilter = VK_FILTER_NEAREST;
sampler.minFilter = VK_FILTER_NEAREST;
sampler.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
sampler.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
sampler.addressModeV = sampler.addressModeU;
sampler.addressModeW = sampler.addressModeU;
sampler.mipLodBias = 0.0f;
sampler.maxAnisotropy = 0;
sampler.minLod = 0.0f;
sampler.maxLod = 1.0f;
sampler.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;
VK_CHECK_RESULT(vkCreateSampler(device, &sampler, nullptr, &filterPass.sampler));
}
}
// Build command buffer for rendering the scene to the offscreen frame buffer attachments
void buildDeferredCommandBuffer()
{
if (offscreen.cmdBuffer == VK_NULL_HANDLE)
{
offscreen.cmdBuffer = VulkanExampleBase::createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, false);
}
// Create a semaphore used to synchronize offscreen rendering and usage
if (offscreen.semaphore == VK_NULL_HANDLE)
{
VkSemaphoreCreateInfo semaphoreCreateInfo = vks::initializers::semaphoreCreateInfo();
VK_CHECK_RESULT(vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &offscreen.semaphore));
}
VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo();
// Clear values for all attachments written in the fragment sahder
std::array<VkClearValue, 3> clearValues;
clearValues[0].color = { { 0.0f, 0.0f, 0.0f, 0.0f } };
clearValues[1].color = { { 0.0f, 0.0f, 0.0f, 0.0f } };
clearValues[2].depthStencil = { 1.0f, 0 };
VkRenderPassBeginInfo renderPassBeginInfo = vks::initializers::renderPassBeginInfo();
renderPassBeginInfo.renderPass = offscreen.renderPass;
renderPassBeginInfo.framebuffer = offscreen.frameBuffer;
renderPassBeginInfo.renderArea.extent.width = offscreen.width;
renderPassBeginInfo.renderArea.extent.height = offscreen.height;
renderPassBeginInfo.clearValueCount = 3;
renderPassBeginInfo.pClearValues = clearValues.data();
VK_CHECK_RESULT(vkBeginCommandBuffer(offscreen.cmdBuffer, &cmdBufInfo));
vkCmdBeginRenderPass(offscreen.cmdBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
VkViewport viewport = vks::initializers::viewport((float)offscreen.width, (float)offscreen.height, 0.0f, 1.0f);
vkCmdSetViewport(offscreen.cmdBuffer, 0, 1, &viewport);
VkRect2D scissor = vks::initializers::rect2D(offscreen.width, offscreen.height, 0, 0);
vkCmdSetScissor(offscreen.cmdBuffer, 0, 1, &scissor);
VkDeviceSize offsets[1] = { 0 };
// Skybox
if (displaySkybox)
{
vkCmdBindDescriptorSets(offscreen.cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayouts.models, 0, 1, &descriptorSets.skybox, 0, NULL);
vkCmdBindVertexBuffers(offscreen.cmdBuffer, 0, 1, &models.skybox.vertices.buffer, offsets);
vkCmdBindIndexBuffer(offscreen.cmdBuffer, models.skybox.indices.buffer, 0, VK_INDEX_TYPE_UINT32);
vkCmdBindPipeline(offscreen.cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.skybox);
vkCmdDrawIndexed(offscreen.cmdBuffer, models.skybox.indexCount, 1, 0, 0, 0);
}
// 3D object
vkCmdBindDescriptorSets(offscreen.cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayouts.models, 0, 1, &descriptorSets.object, 0, NULL);
vkCmdBindVertexBuffers(offscreen.cmdBuffer, 0, 1, &models.objects[models.objectIndex].vertices.buffer, offsets);
vkCmdBindIndexBuffer(offscreen.cmdBuffer, models.objects[models.objectIndex].indices.buffer, 0, VK_INDEX_TYPE_UINT32);
vkCmdBindPipeline(offscreen.cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.reflect);
vkCmdDrawIndexed(offscreen.cmdBuffer, models.objects[models.objectIndex].indexCount, 1, 0, 0, 0);
vkCmdEndRenderPass(offscreen.cmdBuffer);
VK_CHECK_RESULT(vkEndCommandBuffer(offscreen.cmdBuffer));
}
void loadAssets()
{
// Models
models.skybox.loadFromFile(getAssetPath() + "models/cube.obj", vertexLayout, 0.05f, vulkanDevice, queue);
std::vector<std::string> filenames = { "geosphere.obj", "teapot.dae", "torusknot.obj", "venus.fbx" };
for (auto file : filenames) {
vks::Model model;
model.loadFromFile(getAssetPath() + "models/" + file, vertexLayout, 0.05f * (file == "venus.fbx" ? 3.0f : 1.0f), vulkanDevice, queue);
models.objects.push_back(model);
}
// Load HDR cube map
textures.envmap.loadFromFile(getAssetPath() + "textures/hdr/uffizi_cube.ktx", VK_FORMAT_R16G16B16A16_SFLOAT, vulkanDevice, queue);
}
void setupDescriptorPool()
{
std::vector<VkDescriptorPoolSize> poolSizes = {
vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 4),
vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 6)
};
uint32_t numDescriptorSets = 4;
VkDescriptorPoolCreateInfo descriptorPoolInfo =
vks::initializers::descriptorPoolCreateInfo(static_cast<uint32_t>(poolSizes.size()), poolSizes.data(), numDescriptorSets);
VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolInfo, nullptr, &descriptorPool));
}
void setupDescriptorSetLayout()
{
std::vector<VkDescriptorSetLayoutBinding> 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),
vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_FRAGMENT_BIT, 2),
};
VkDescriptorSetLayoutCreateInfo descriptorLayoutInfo =
vks::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings.data(), static_cast<uint32_t>(setLayoutBindings.size()));
VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayoutInfo, nullptr, &descriptorSetLayouts.models));
VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo =
vks::initializers::pipelineLayoutCreateInfo(
&descriptorSetLayouts.models,
1);
VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, nullptr, &pipelineLayouts.models));
// Bloom filter
setLayoutBindings = {
vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 0),
vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 1),
};
descriptorLayoutInfo = vks::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings.data(), static_cast<uint32_t>(setLayoutBindings.size()));
VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayoutInfo, nullptr, &descriptorSetLayouts.bloomFilter));
pipelineLayoutCreateInfo = vks::initializers::pipelineLayoutCreateInfo(&descriptorSetLayouts.bloomFilter, 1);
VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, nullptr, &pipelineLayouts.bloomFilter));
// G-Buffer composition
setLayoutBindings = {
vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 0),
vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 1),
};
descriptorLayoutInfo = vks::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings.data(), static_cast<uint32_t>(setLayoutBindings.size()));
VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayoutInfo, nullptr, &descriptorSetLayouts.composition));
pipelineLayoutCreateInfo = vks::initializers::pipelineLayoutCreateInfo(&descriptorSetLayouts.composition, 1);
VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, nullptr, &pipelineLayouts.composition));
}
void setupDescriptorSets()
{
VkDescriptorSetAllocateInfo allocInfo =
vks::initializers::descriptorSetAllocateInfo(
descriptorPool,
&descriptorSetLayouts.models,
1);
// 3D object descriptor set
VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSets.object));
std::vector<VkWriteDescriptorSet> writeDescriptorSets = {
vks::initializers::writeDescriptorSet(descriptorSets.object, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &uniformBuffers.matrices.descriptor),
vks::initializers::writeDescriptorSet(descriptorSets.object, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, &textures.envmap.descriptor),
vks::initializers::writeDescriptorSet(descriptorSets.object, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 2, &uniformBuffers.params.descriptor),
};
vkUpdateDescriptorSets(device, static_cast<uint32_t>(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, NULL);
// Sky box descriptor set
VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSets.skybox));
writeDescriptorSets = {
vks::initializers::writeDescriptorSet(descriptorSets.skybox, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0,&uniformBuffers.matrices.descriptor),
vks::initializers::writeDescriptorSet(descriptorSets.skybox, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, &textures.envmap.descriptor),
vks::initializers::writeDescriptorSet(descriptorSets.skybox, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 2, &uniformBuffers.params.descriptor),
};
vkUpdateDescriptorSets(device, static_cast<uint32_t>(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, NULL);
// Bloom filter
allocInfo = vks::initializers::descriptorSetAllocateInfo(descriptorPool, &descriptorSetLayouts.bloomFilter, 1);
VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSets.bloomFilter));
std::vector<VkDescriptorImageInfo> colorDescriptors = {
vks::initializers::descriptorImageInfo(offscreen.sampler, offscreen.color[0].view, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL),
vks::initializers::descriptorImageInfo(offscreen.sampler, offscreen.color[1].view, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL),
};
writeDescriptorSets = {
vks::initializers::writeDescriptorSet(descriptorSets.bloomFilter, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 0, &colorDescriptors[0]),
vks::initializers::writeDescriptorSet(descriptorSets.bloomFilter, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, &colorDescriptors[1]),
};
vkUpdateDescriptorSets(device, static_cast<uint32_t>(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, NULL);
// Composition descriptor set
allocInfo = vks::initializers::descriptorSetAllocateInfo(descriptorPool, &descriptorSetLayouts.composition, 1);
VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSets.composition));
colorDescriptors = {
vks::initializers::descriptorImageInfo(offscreen.sampler, offscreen.color[0].view, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL),
vks::initializers::descriptorImageInfo(offscreen.sampler, filterPass.color[0].view, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL),
};
writeDescriptorSets = {
vks::initializers::writeDescriptorSet(descriptorSets.composition, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 0, &colorDescriptors[0]),
vks::initializers::writeDescriptorSet(descriptorSets.composition, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, &colorDescriptors[1]),
};
vkUpdateDescriptorSets(device, static_cast<uint32_t>(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_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_FALSE,
VK_FALSE,
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<VkDynamicState> dynamicStateEnables = {
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR
};
VkPipelineDynamicStateCreateInfo dynamicState =
vks::initializers::pipelineDynamicStateCreateInfo(
dynamicStateEnables.data(),
static_cast<uint32_t>(dynamicStateEnables.size()),
0);
VkGraphicsPipelineCreateInfo pipelineCreateInfo =
vks::initializers::pipelineCreateInfo(
pipelineLayouts.models,
renderPass,
0);
std::vector<VkPipelineColorBlendAttachmentState> blendAttachmentStates = {
vks::initializers::pipelineColorBlendAttachmentState(0xf, VK_FALSE),
vks::initializers::pipelineColorBlendAttachmentState(0xf, VK_FALSE),
};
pipelineCreateInfo.pInputAssemblyState = &inputAssemblyState;
pipelineCreateInfo.pRasterizationState = &rasterizationState;
pipelineCreateInfo.pColorBlendState = &colorBlendState;
pipelineCreateInfo.pMultisampleState = &multisampleState;
pipelineCreateInfo.pViewportState = &viewportState;
pipelineCreateInfo.pDepthStencilState = &depthStencilState;
pipelineCreateInfo.pDynamicState = &dynamicState;
std::array<VkPipelineShaderStageCreateInfo, 2> shaderStages;
pipelineCreateInfo.stageCount = static_cast<uint32_t>(shaderStages.size());
pipelineCreateInfo.pStages = shaderStages.data();
VkSpecializationInfo specializationInfo;
std::array<VkSpecializationMapEntry, 1> specializationMapEntries;
// Full screen pipelines
// Empty vertex input state, full screen triangles are generated by the vertex shader
VkPipelineVertexInputStateCreateInfo emptyInputState = vks::initializers::pipelineVertexInputStateCreateInfo();
pipelineCreateInfo.pVertexInputState = &emptyInputState;
// Final fullscreen composition pass pipeline
shaderStages[0] = loadShader(getAssetPath() + "shaders/hdr/composition.vert.spv", VK_SHADER_STAGE_VERTEX_BIT);
shaderStages[1] = loadShader(getAssetPath() + "shaders/hdr/composition.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT);
pipelineCreateInfo.layout = pipelineLayouts.composition;
pipelineCreateInfo.renderPass = renderPass;
rasterizationState.cullMode = VK_CULL_MODE_BACK_BIT;
rasterizationState.cullMode = VK_CULL_MODE_NONE;
colorBlendState.attachmentCount = 1;
colorBlendState.pAttachments = blendAttachmentStates.data();
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipelines.composition));
// Bloom pass
shaderStages[0] = loadShader(getAssetPath() + "shaders/hdr/bloom.vert.spv", VK_SHADER_STAGE_VERTEX_BIT);
shaderStages[1] = loadShader(getAssetPath() + "shaders/hdr/bloom.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT);
colorBlendState.pAttachments = &blendAttachmentState;
blendAttachmentState.colorWriteMask = 0xF;
blendAttachmentState.blendEnable = VK_TRUE;
blendAttachmentState.colorBlendOp = VK_BLEND_OP_ADD;
blendAttachmentState.srcColorBlendFactor = VK_BLEND_FACTOR_ONE;
blendAttachmentState.dstColorBlendFactor = VK_BLEND_FACTOR_ONE;
blendAttachmentState.alphaBlendOp = VK_BLEND_OP_ADD;
blendAttachmentState.srcAlphaBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
blendAttachmentState.dstAlphaBlendFactor = VK_BLEND_FACTOR_DST_ALPHA;
// Set constant parameters via specialization constants
specializationMapEntries[0] = vks::initializers::specializationMapEntry(0, 0, sizeof(uint32_t));
uint32_t dir = 1;
specializationInfo = vks::initializers::specializationInfo(1, specializationMapEntries.data(), sizeof(dir), &dir);
shaderStages[1].pSpecializationInfo = &specializationInfo;
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipelines.bloom[0]));
// Second blur pass (into separate framebuffer)
pipelineCreateInfo.renderPass = filterPass.renderPass;
dir = 0;
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipelines.bloom[1]));
// Object rendering pipelines
// Vertex bindings an attributes for model rendering
// Binding description
std::vector<VkVertexInputBindingDescription> vertexInputBindings = {
vks::initializers::vertexInputBindingDescription(0, vertexLayout.stride(), VK_VERTEX_INPUT_RATE_VERTEX),
};
// Attribute descriptions
std::vector<VkVertexInputAttributeDescription> vertexInputAttributes = {
vks::initializers::vertexInputAttributeDescription(0, 0, VK_FORMAT_R32G32B32_SFLOAT, 0), // Position
vks::initializers::vertexInputAttributeDescription(0, 1, VK_FORMAT_R32G32B32_SFLOAT, sizeof(float) * 3), // Normal
vks::initializers::vertexInputAttributeDescription(0, 2, VK_FORMAT_R32G32_SFLOAT, sizeof(float) * 5), // UV
};
VkPipelineVertexInputStateCreateInfo vertexInputState = vks::initializers::pipelineVertexInputStateCreateInfo();
vertexInputState.vertexBindingDescriptionCount = static_cast<uint32_t>(vertexInputBindings.size());
vertexInputState.pVertexBindingDescriptions = vertexInputBindings.data();
vertexInputState.vertexAttributeDescriptionCount = static_cast<uint32_t>(vertexInputAttributes.size());
vertexInputState.pVertexAttributeDescriptions = vertexInputAttributes.data();
pipelineCreateInfo.renderPass = renderPass;
pipelineCreateInfo.pVertexInputState = &vertexInputState;
// Skybox pipeline (background cube)
blendAttachmentState.blendEnable = VK_FALSE;
pipelineCreateInfo.layout = pipelineLayouts.models;
pipelineCreateInfo.renderPass = offscreen.renderPass;
colorBlendState.attachmentCount = 2;
colorBlendState.pAttachments = blendAttachmentStates.data();
shaderStages[0] = loadShader(getAssetPath() + "shaders/hdr/gbuffer.vert.spv", VK_SHADER_STAGE_VERTEX_BIT);
shaderStages[1] = loadShader(getAssetPath() + "shaders/hdr/gbuffer.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT);
// Set constant parameters via specialization constants
specializationMapEntries[0] = vks::initializers::specializationMapEntry(0, 0, sizeof(uint32_t));
uint32_t shadertype = 0;
specializationInfo = vks::initializers::specializationInfo(1, specializationMapEntries.data(), sizeof(shadertype), &shadertype);
shaderStages[0].pSpecializationInfo = &specializationInfo;
shaderStages[1].pSpecializationInfo = &specializationInfo;
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipelines.skybox));
// Object rendering pipeline
shadertype = 1;
// Enable depth test and write
depthStencilState.depthWriteEnable = VK_TRUE;
depthStencilState.depthTestEnable = VK_TRUE;
// Flip cull mode
rasterizationState.cullMode = VK_CULL_MODE_NONE;
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipelines.reflect));
}
// Prepare and initialize uniform buffer containing shader uniforms
void prepareUniformBuffers()
{
// Matrices vertex shader uniform buffer
VK_CHECK_RESULT(vulkanDevice->createBuffer(
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
&uniformBuffers.matrices,
sizeof(uboVS)));
// Params
VK_CHECK_RESULT(vulkanDevice->createBuffer(
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
&uniformBuffers.params,
sizeof(uboParams)));
// Map persistent
VK_CHECK_RESULT(uniformBuffers.matrices.map());
VK_CHECK_RESULT(uniformBuffers.params.map());
updateUniformBuffers();
updateParams();
}
void updateUniformBuffers()
{
uboVS.projection = camera.matrices.perspective;
uboVS.modelview = camera.matrices.view;
memcpy(uniformBuffers.matrices.mapped, &uboVS, sizeof(uboVS));
}
void updateParams()
{
memcpy(uniformBuffers.params.mapped, &uboParams, sizeof(uboParams));
}
void draw()
{
VulkanExampleBase::prepareFrame();
submitInfo.pWaitSemaphores = &semaphores.presentComplete;
submitInfo.pSignalSemaphores = &offscreen.semaphore;
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &offscreen.cmdBuffer;
VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE));
submitInfo.pWaitSemaphores = &offscreen.semaphore;
submitInfo.pSignalSemaphores = &semaphores.renderComplete;
submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer];
VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE));
VulkanExampleBase::submitFrame();
}
void prepare()
{
VulkanExampleBase::prepare();
loadAssets();
prepareUniformBuffers();
prepareoffscreenfer();
setupDescriptorSetLayout();
preparePipelines();
setupDescriptorPool();
setupDescriptorSets();
buildCommandBuffers();
buildDeferredCommandBuffer();
prepared = true;
}
virtual void render()
{
if (!prepared)
return;
draw();
}
virtual void viewChanged()
{
updateUniformBuffers();
}
void toggleSkyBox()
{
displaySkybox = !displaySkybox;
reBuildCommandBuffers();
}
void toggleBloom()
{
bloom = !bloom;
reBuildCommandBuffers();
}
void toggleObject()
{
models.objectIndex++;
if (models.objectIndex >= static_cast<uint32_t>(models.objects.size()))
{
models.objectIndex = 0;
}
reBuildCommandBuffers();
}
void changeExposure(float delta)
{
uboParams.exposure += delta;
if (uboParams.exposure < 0.0f) {
uboParams.exposure = 0.0f;
}
updateParams();
updateTextOverlay();
}
virtual void keyPressed(uint32_t keyCode)
{
switch (keyCode)
{
case KEY_B:
case GAMEPAD_BUTTON_Y:
toggleBloom();
break;
case KEY_S:
case GAMEPAD_BUTTON_A:
toggleSkyBox();
break;
case KEY_SPACE:
case GAMEPAD_BUTTON_X:
toggleObject();
break;
case KEY_KPADD:
case GAMEPAD_BUTTON_R1:
changeExposure(0.05f);
break;
case KEY_KPSUB:
case GAMEPAD_BUTTON_L1:
changeExposure(-0.05f);
break;
}
}
virtual void getOverlayText(VulkanTextOverlay *textOverlay)
{
std::stringstream ss;
ss << std::setprecision(2) << std::fixed << uboParams.exposure;
#if defined(__ANDROID__)
textOverlay->addText("Exposure: " + ss.str() + " (L1/R1)", 5.0f, 85.0f, VulkanTextOverlay::alignLeft);
#else
textOverlay->addText("Exposure: " + ss.str() + " (+/-)", 5.0f, 85.0f, VulkanTextOverlay::alignLeft);
#endif
}
};
VULKAN_EXAMPLE_MAIN()