procedural-3d-engine/examples/bloom/bloom.cpp

792 lines
34 KiB
C++
Raw Normal View History

2016-02-16 15:07:25 +01:00
/*
* Vulkan Example - Implements a separable two-pass fullscreen blur (also known as bloom)
2016-02-16 15:07:25 +01:00
*
* Copyright (C) Sascha Willems - www.saschawillems.de
2016-02-16 15:07:25 +01:00
*
* 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
2016-02-16 15:07:25 +01:00
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <vulkan/vulkan.h>
#include "vulkanexamplebase.h"
#include "VulkanTexture.hpp"
#include "VulkanModel.hpp"
#include "VulkanBuffer.hpp"
2016-02-16 15:07:25 +01:00
#define ENABLE_VALIDATION true
2016-02-16 15:07:25 +01:00
// Offscreen frame buffer properties
#define FB_DIM 256
2016-02-16 15:07:25 +01:00
#define FB_COLOR_FORMAT VK_FORMAT_R8G8B8A8_UNORM
class VulkanExample : public VulkanExampleBase
{
public:
bool bloom = true;
struct {
vks::TextureCubeMap cubemap;
2016-02-16 15:07:25 +01:00
} textures;
// Vertex layout for the models
vks::VertexLayout vertexLayout = vks::VertexLayout({
vks::VERTEX_COMPONENT_POSITION,
vks::VERTEX_COMPONENT_UV,
vks::VERTEX_COMPONENT_COLOR,
vks::VERTEX_COMPONENT_NORMAL,
});
2016-02-16 15:07:25 +01:00
struct {
vks::Model ufo;
vks::Model ufoGlow;
vks::Model skyBox;
} models;
2016-02-16 15:07:25 +01:00
struct {
vks::Buffer scene;
vks::Buffer skyBox;
vks::Buffer blurParams;
} uniformBuffers;
2016-02-16 15:07:25 +01:00
struct UBO {
glm::mat4 projection;
glm::mat4 view;
2016-02-16 15:07:25 +01:00
glm::mat4 model;
};
struct UBOBlurParams {
2016-02-16 15:07:25 +01:00
float blurScale = 1.0f;
float blurStrength = 1.5f;
2016-02-16 15:07:25 +01:00
};
struct {
UBO scene, skyBox;
UBOBlurParams blurParams;
2016-02-16 15:07:25 +01:00
} ubos;
struct {
VkPipeline blurVert;
VkPipeline blurHorz;
VkPipeline glowPass;
2016-02-16 15:07:25 +01:00
VkPipeline phongPass;
VkPipeline skyBox;
} pipelines;
struct {
VkPipelineLayout blur;
VkPipelineLayout scene;
} pipelineLayouts;
2016-02-16 15:07:25 +01:00
struct {
VkDescriptorSet blurVert;
VkDescriptorSet blurHorz;
2016-02-16 15:07:25 +01:00
VkDescriptorSet scene;
VkDescriptorSet skyBox;
} descriptorSets;
struct {
VkDescriptorSetLayout blur;
VkDescriptorSetLayout scene;
} descriptorSetLayouts;
2016-02-16 15:07:25 +01:00
// Framebuffer for offscreen rendering
struct FrameBufferAttachment {
VkImage image;
VkDeviceMemory mem;
VkImageView view;
};
struct FrameBuffer {
VkFramebuffer framebuffer;
2016-02-16 15:07:25 +01:00
FrameBufferAttachment color, depth;
VkDescriptorImageInfo descriptor;
};
struct OffscreenPass {
int32_t width, height;
VkRenderPass renderPass;
VkSampler sampler;
std::array<FrameBuffer, 2> framebuffers;
} offscreenPass;
2016-02-16 15:07:25 +01:00
VulkanExample() : VulkanExampleBase(ENABLE_VALIDATION)
{
title = "Bloom (offscreen rendering)";
2016-02-16 15:07:25 +01:00
timerSpeed *= 0.5f;
settings.overlay = true;
camera.type = Camera::CameraType::lookat;
camera.setPosition(glm::vec3(0.0f, 0.0f, -10.25f));
camera.setRotation(glm::vec3(7.5f, -343.0f, 0.0f));
camera.setPerspective(45.0f, (float)width / (float)height, 0.1f, 256.0f);
2016-02-16 15:07:25 +01:00
}
~VulkanExample()
{
// Clean up used Vulkan resources
// Note : Inherited destructor cleans up resources stored in base class
vkDestroySampler(device, offscreenPass.sampler, nullptr);
2016-02-16 15:07:25 +01:00
// Frame buffer
for (auto& framebuffer : offscreenPass.framebuffers)
{
// Attachments
vkDestroyImageView(device, framebuffer.color.view, nullptr);
vkDestroyImage(device, framebuffer.color.image, nullptr);
vkFreeMemory(device, framebuffer.color.mem, nullptr);
vkDestroyImageView(device, framebuffer.depth.view, nullptr);
vkDestroyImage(device, framebuffer.depth.image, nullptr);
vkFreeMemory(device, framebuffer.depth.mem, nullptr);
vkDestroyFramebuffer(device, framebuffer.framebuffer, nullptr);
}
vkDestroyRenderPass(device, offscreenPass.renderPass, nullptr);
2016-02-16 15:07:25 +01:00
vkDestroyPipeline(device, pipelines.blurHorz, nullptr);
2016-02-16 15:07:25 +01:00
vkDestroyPipeline(device, pipelines.blurVert, nullptr);
vkDestroyPipeline(device, pipelines.phongPass, nullptr);
vkDestroyPipeline(device, pipelines.glowPass, nullptr);
2016-02-16 15:07:25 +01:00
vkDestroyPipeline(device, pipelines.skyBox, nullptr);
vkDestroyPipelineLayout(device, pipelineLayouts.blur , nullptr);
vkDestroyPipelineLayout(device, pipelineLayouts.scene, nullptr);
2016-02-16 15:07:25 +01:00
vkDestroyDescriptorSetLayout(device, descriptorSetLayouts.blur, nullptr);
vkDestroyDescriptorSetLayout(device, descriptorSetLayouts.scene, nullptr);
2016-02-16 15:07:25 +01:00
// Models
models.ufo.destroy();
models.ufoGlow.destroy();
models.skyBox.destroy();
2016-02-16 15:07:25 +01:00
// Uniform buffers
uniformBuffers.scene.destroy();
uniformBuffers.skyBox.destroy();
uniformBuffers.blurParams.destroy();
2016-02-16 15:07:25 +01:00
textures.cubemap.destroy();
2016-02-16 15:07:25 +01:00
}
// Setup the offscreen framebuffer for rendering the mirrored scene
// The color attachment of this framebuffer will then be sampled from
void prepareOffscreenFramebuffer(FrameBuffer *frameBuf, VkFormat colorFormat, VkFormat depthFormat)
2016-02-16 15:07:25 +01:00
{
// Color attachment
VkImageCreateInfo image = vks::initializers::imageCreateInfo();
2016-02-16 15:07:25 +01:00
image.imageType = VK_IMAGE_TYPE_2D;
image.format = colorFormat;
image.extent.width = FB_DIM;
image.extent.height = FB_DIM;
image.extent.depth = 1;
2016-02-16 15:07:25 +01:00
image.mipLevels = 1;
image.arrayLayers = 1;
image.samples = VK_SAMPLE_COUNT_1_BIT;
image.tiling = VK_IMAGE_TILING_OPTIMAL;
// We will sample directly from the color attachment
image.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
2016-02-16 15:07:25 +01:00
VkMemoryAllocateInfo memAlloc = vks::initializers::memoryAllocateInfo();
2016-02-16 15:07:25 +01:00
VkMemoryRequirements memReqs;
VkImageViewCreateInfo colorImageView = vks::initializers::imageViewCreateInfo();
2016-02-16 15:07:25 +01:00
colorImageView.viewType = VK_IMAGE_VIEW_TYPE_2D;
colorImageView.format = colorFormat;
2016-02-16 15:07:25 +01:00
colorImageView.flags = 0;
colorImageView.subresourceRange = {};
colorImageView.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
colorImageView.subresourceRange.baseMipLevel = 0;
colorImageView.subresourceRange.levelCount = 1;
colorImageView.subresourceRange.baseArrayLayer = 0;
colorImageView.subresourceRange.layerCount = 1;
VK_CHECK_RESULT(vkCreateImage(device, &image, nullptr, &frameBuf->color.image));
2016-02-16 15:07:25 +01:00
vkGetImageMemoryRequirements(device, frameBuf->color.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, &frameBuf->color.mem));
VK_CHECK_RESULT(vkBindImageMemory(device, frameBuf->color.image, frameBuf->color.mem, 0));
2016-02-16 15:07:25 +01:00
colorImageView.image = frameBuf->color.image;
VK_CHECK_RESULT(vkCreateImageView(device, &colorImageView, nullptr, &frameBuf->color.view));
2016-02-16 15:07:25 +01:00
// Depth stencil attachment
image.format = depthFormat;
2016-02-16 15:07:25 +01:00
image.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
VkImageViewCreateInfo depthStencilView = vks::initializers::imageViewCreateInfo();
2016-02-16 15:07:25 +01:00
depthStencilView.viewType = VK_IMAGE_VIEW_TYPE_2D;
depthStencilView.format = depthFormat;
2016-02-16 15:07:25 +01:00
depthStencilView.flags = 0;
depthStencilView.subresourceRange = {};
depthStencilView.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
depthStencilView.subresourceRange.baseMipLevel = 0;
depthStencilView.subresourceRange.levelCount = 1;
depthStencilView.subresourceRange.baseArrayLayer = 0;
depthStencilView.subresourceRange.layerCount = 1;
VK_CHECK_RESULT(vkCreateImage(device, &image, nullptr, &frameBuf->depth.image));
2016-02-16 15:07:25 +01:00
vkGetImageMemoryRequirements(device, frameBuf->depth.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, &frameBuf->depth.mem));
VK_CHECK_RESULT(vkBindImageMemory(device, frameBuf->depth.image, frameBuf->depth.mem, 0));
2016-02-16 15:07:25 +01:00
depthStencilView.image = frameBuf->depth.image;
VK_CHECK_RESULT(vkCreateImageView(device, &depthStencilView, nullptr, &frameBuf->depth.view));
2016-02-16 15:07:25 +01:00
VkImageView attachments[2];
attachments[0] = frameBuf->color.view;
attachments[1] = frameBuf->depth.view;
VkFramebufferCreateInfo fbufCreateInfo = vks::initializers::framebufferCreateInfo();
fbufCreateInfo.renderPass = offscreenPass.renderPass;
2016-02-16 15:07:25 +01:00
fbufCreateInfo.attachmentCount = 2;
fbufCreateInfo.pAttachments = attachments;
fbufCreateInfo.width = FB_DIM;
fbufCreateInfo.height = FB_DIM;
2016-02-16 15:07:25 +01:00
fbufCreateInfo.layers = 1;
VK_CHECK_RESULT(vkCreateFramebuffer(device, &fbufCreateInfo, nullptr, &frameBuf->framebuffer));
// Fill a descriptor for later use in a descriptor set
frameBuf->descriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
frameBuf->descriptor.imageView = frameBuf->color.view;
frameBuf->descriptor.sampler = offscreenPass.sampler;
}
// Prepare the offscreen framebuffers used for the vertical- and horizontal blur
void prepareOffscreen()
{
offscreenPass.width = FB_DIM;
offscreenPass.height = FB_DIM;
// Find a suitable depth format
VkFormat fbDepthFormat;
VkBool32 validDepthFormat = vks::tools::getSupportedDepthFormat(physicalDevice, &fbDepthFormat);
assert(validDepthFormat);
// Create a separate render pass for the offscreen rendering as it may differ from the one used for scene rendering
std::array<VkAttachmentDescription, 2> attchmentDescriptions = {};
// Color attachment
attchmentDescriptions[0].format = FB_COLOR_FORMAT;
attchmentDescriptions[0].samples = VK_SAMPLE_COUNT_1_BIT;
attchmentDescriptions[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
attchmentDescriptions[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
attchmentDescriptions[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attchmentDescriptions[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attchmentDescriptions[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
attchmentDescriptions[0].finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
// Depth attachment
attchmentDescriptions[1].format = fbDepthFormat;
attchmentDescriptions[1].samples = VK_SAMPLE_COUNT_1_BIT;
attchmentDescriptions[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
attchmentDescriptions[1].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attchmentDescriptions[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attchmentDescriptions[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attchmentDescriptions[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
attchmentDescriptions[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
VkAttachmentReference colorReference = { 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
VkAttachmentReference depthReference = { 1, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL };
VkSubpassDescription subpassDescription = {};
subpassDescription.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpassDescription.colorAttachmentCount = 1;
subpassDescription.pColorAttachments = &colorReference;
subpassDescription.pDepthStencilAttachment = &depthReference;
// Use subpass dependencies for layout transitions
std::array<VkSubpassDependency, 2> dependencies;
dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL;
dependencies[0].dstSubpass = 0;
dependencies[0].srcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependencies[0].srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
dependencies[0].dstAccessMask = 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_FRAGMENT_SHADER_BIT;
dependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
dependencies[1].dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
dependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
// Create the actual renderpass
VkRenderPassCreateInfo renderPassInfo = {};
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
renderPassInfo.attachmentCount = static_cast<uint32_t>(attchmentDescriptions.size());
renderPassInfo.pAttachments = attchmentDescriptions.data();
renderPassInfo.subpassCount = 1;
renderPassInfo.pSubpasses = &subpassDescription;
renderPassInfo.dependencyCount = static_cast<uint32_t>(dependencies.size());
renderPassInfo.pDependencies = dependencies.data();
VK_CHECK_RESULT(vkCreateRenderPass(device, &renderPassInfo, nullptr, &offscreenPass.renderPass));
// Create sampler to sample from the color attachments
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 = sampler.addressModeU;
sampler.addressModeW = sampler.addressModeU;
sampler.mipLodBias = 0.0f;
sampler.maxAnisotropy = 1.0f;
sampler.minLod = 0.0f;
sampler.maxLod = 1.0f;
sampler.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;
VK_CHECK_RESULT(vkCreateSampler(device, &sampler, nullptr, &offscreenPass.sampler));
// Create two frame buffers
prepareOffscreenFramebuffer(&offscreenPass.framebuffers[0], FB_COLOR_FORMAT, fbDepthFormat);
prepareOffscreenFramebuffer(&offscreenPass.framebuffers[1], FB_COLOR_FORMAT, fbDepthFormat);
2016-02-16 15:07:25 +01:00
}
void buildCommandBuffers()
2016-02-16 15:07:25 +01:00
{
VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo();
VkClearValue clearValues[2];
VkViewport viewport;
VkRect2D scissor;
VkDeviceSize offsets[1] = { 0 };
/*
The blur method used in this example is multi pass and renders the vertical blur first and then the horizontal one
While it's possible to blur in one pass, this method is widely used as it requires far less samples to generate the blur
*/
2016-02-16 15:07:25 +01:00
for (int32_t i = 0; i < drawCmdBuffers.size(); ++i)
{
VK_CHECK_RESULT(vkBeginCommandBuffer(drawCmdBuffers[i], &cmdBufInfo));
if (bloom) {
clearValues[0].color = { { 0.0f, 0.0f, 0.0f, 1.0f } };
clearValues[1].depthStencil = { 1.0f, 0 };
2016-02-16 15:07:25 +01:00
VkRenderPassBeginInfo renderPassBeginInfo = vks::initializers::renderPassBeginInfo();
renderPassBeginInfo.renderPass = offscreenPass.renderPass;
renderPassBeginInfo.framebuffer = offscreenPass.framebuffers[0].framebuffer;
renderPassBeginInfo.renderArea.extent.width = offscreenPass.width;
renderPassBeginInfo.renderArea.extent.height = offscreenPass.height;
renderPassBeginInfo.clearValueCount = 2;
renderPassBeginInfo.pClearValues = clearValues;
2016-02-16 15:07:25 +01:00
viewport = vks::initializers::viewport((float)offscreenPass.width, (float)offscreenPass.height, 0.0f, 1.0f);
vkCmdSetViewport(drawCmdBuffers[i], 0, 1, &viewport);
scissor = vks::initializers::rect2D(offscreenPass.width, offscreenPass.height, 0, 0);
vkCmdSetScissor(drawCmdBuffers[i], 0, 1, &scissor);
2016-02-16 15:07:25 +01:00
/*
First render pass: Render glow parts of the model (separate mesh) to an offscreen frame buffer
*/
2016-02-16 15:07:25 +01:00
vkCmdBeginRenderPass(drawCmdBuffers[i], &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
2016-02-16 15:07:25 +01:00
vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayouts.scene, 0, 1, &descriptorSets.scene, 0, NULL);
vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.glowPass);
2016-02-16 15:07:25 +01:00
VkDeviceSize offsets[1] = { 0 };
vkCmdBindVertexBuffers(drawCmdBuffers[i], 0, 1, &models.ufoGlow.vertices.buffer, offsets);
vkCmdBindIndexBuffer(drawCmdBuffers[i], models.ufoGlow.indices.buffer, 0, VK_INDEX_TYPE_UINT32);
vkCmdDrawIndexed(drawCmdBuffers[i], models.ufoGlow.indexCount, 1, 0, 0, 0);
2016-02-16 15:07:25 +01:00
vkCmdEndRenderPass(drawCmdBuffers[i]);
2016-02-16 15:07:25 +01:00
/*
Second render pass: Vertical blur
2016-02-16 15:07:25 +01:00
Render contents of the first pass into a second framebuffer and apply a vertical blur
This is the first blur pass, the horizontal blur is applied when rendering on top of the scene
*/
2016-02-16 15:07:25 +01:00
renderPassBeginInfo.framebuffer = offscreenPass.framebuffers[1].framebuffer;
2016-02-16 15:07:25 +01:00
vkCmdBeginRenderPass(drawCmdBuffers[i], &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
2016-02-16 15:07:25 +01:00
vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayouts.blur, 0, 1, &descriptorSets.blurVert, 0, NULL);
vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.blurVert);
vkCmdDraw(drawCmdBuffers[i], 3, 1, 0, 0);
vkCmdEndRenderPass(drawCmdBuffers[i]);
}
2016-02-16 15:07:25 +01:00
/*
Note: Explicit synchronization is not required between the render pass, as this is done implicit via sub pass dependencies
*/
2016-02-16 15:07:25 +01:00
/*
Third render pass: Scene rendering with applied vertical blur
2016-02-16 15:07:25 +01:00
Renders the scene and the (vertically blurred) contents of the second framebuffer and apply a horizontal blur
2016-02-16 15:07:25 +01:00
*/
{
clearValues[0].color = defaultClearColor;
clearValues[1].depthStencil = { 1.0f, 0 };
2016-02-16 15:07:25 +01:00
VkRenderPassBeginInfo renderPassBeginInfo = vks::initializers::renderPassBeginInfo();
renderPassBeginInfo.renderPass = renderPass;
renderPassBeginInfo.framebuffer = frameBuffers[i];
renderPassBeginInfo.renderArea.extent.width = width;
renderPassBeginInfo.renderArea.extent.height = height;
renderPassBeginInfo.clearValueCount = 2;
renderPassBeginInfo.pClearValues = clearValues;
2016-02-16 15:07:25 +01:00
vkCmdBeginRenderPass(drawCmdBuffers[i], &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
2016-02-16 15:07:25 +01:00
VkViewport viewport = vks::initializers::viewport((float)width, (float)height, 0.0f, 1.0f);
vkCmdSetViewport(drawCmdBuffers[i], 0, 1, &viewport);
2016-02-16 15:07:25 +01:00
VkRect2D scissor = vks::initializers::rect2D(width, height, 0, 0);
vkCmdSetScissor(drawCmdBuffers[i], 0, 1, &scissor);
2016-02-16 15:07:25 +01:00
VkDeviceSize offsets[1] = { 0 };
2016-02-16 15:07:25 +01:00
// Skybox
vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayouts.scene, 0, 1, &descriptorSets.skyBox, 0, NULL);
vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.skyBox);
2016-02-16 15:07:25 +01:00
vkCmdBindVertexBuffers(drawCmdBuffers[i], 0, 1, &models.skyBox.vertices.buffer, offsets);
vkCmdBindIndexBuffer(drawCmdBuffers[i], models.skyBox.indices.buffer, 0, VK_INDEX_TYPE_UINT32);
vkCmdDrawIndexed(drawCmdBuffers[i], models.skyBox.indexCount, 1, 0, 0, 0);
// 3D scene
vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayouts.scene, 0, 1, &descriptorSets.scene, 0, NULL);
vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.phongPass);
2016-02-16 15:07:25 +01:00
vkCmdBindVertexBuffers(drawCmdBuffers[i], 0, 1, &models.ufo.vertices.buffer, offsets);
vkCmdBindIndexBuffer(drawCmdBuffers[i], models.ufo.indices.buffer, 0, VK_INDEX_TYPE_UINT32);
vkCmdDrawIndexed(drawCmdBuffers[i], models.ufo.indexCount, 1, 0, 0, 0);
2016-02-16 15:07:25 +01:00
if (bloom)
{
vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayouts.blur, 0, 1, &descriptorSets.blurHorz, 0, NULL);
vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.blurHorz);
vkCmdDraw(drawCmdBuffers[i], 3, 1, 0, 0);
}
2016-02-16 15:07:25 +01:00
drawUI(drawCmdBuffers[i]);
vkCmdEndRenderPass(drawCmdBuffers[i]);
2016-02-16 15:07:25 +01:00
}
2016-02-16 15:07:25 +01:00
VK_CHECK_RESULT(vkEndCommandBuffer(drawCmdBuffers[i]));
2016-02-16 15:07:25 +01:00
}
}
void loadAssets()
2016-02-16 15:07:25 +01:00
{
models.ufo.loadFromFile(getAssetPath() + "models/retroufo.dae", vertexLayout, 0.05f, vulkanDevice, queue);
models.ufoGlow.loadFromFile(getAssetPath() + "models/retroufo_glow.dae", vertexLayout, 0.05f, vulkanDevice, queue);
models.skyBox.loadFromFile(getAssetPath() + "models/cube.obj", vertexLayout, 1.0f, vulkanDevice, queue);
textures.cubemap.loadFromFile(getAssetPath() + "textures/cubemap_space.ktx", VK_FORMAT_R8G8B8A8_UNORM, vulkanDevice, queue);
2016-02-16 15:07:25 +01:00
}
void setupDescriptorPool()
{
std::vector<VkDescriptorPoolSize> poolSizes =
{
vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 8),
vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 6)
2016-02-16 15:07:25 +01:00
};
VkDescriptorPoolCreateInfo descriptorPoolInfo =
vks::initializers::descriptorPoolCreateInfo(
2016-02-16 15:07:25 +01:00
poolSizes.size(),
poolSizes.data(),
5);
VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolInfo, nullptr, &descriptorPool));
2016-02-16 15:07:25 +01:00
}
void setupDescriptorSetLayout()
{
std::vector<VkDescriptorSetLayoutBinding> setLayoutBindings;
VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo;
VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo;
// Fullscreen blur
setLayoutBindings = {
vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_FRAGMENT_BIT, 0), // Binding 0: Fragment shader uniform buffer
vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 1) // Binding 1: Fragment shader image sampler
2016-02-16 15:07:25 +01:00
};
descriptorSetLayoutCreateInfo = vks::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings.data(), static_cast<uint32_t>(setLayoutBindings.size()));
VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorSetLayoutCreateInfo, nullptr, &descriptorSetLayouts.blur));
pipelineLayoutCreateInfo = vks::initializers::pipelineLayoutCreateInfo(&descriptorSetLayouts.blur, 1);
VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, nullptr, &pipelineLayouts.blur));
2016-02-16 15:07:25 +01:00
// Scene rendering
setLayoutBindings = {
vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_VERTEX_BIT, 0), // Binding 0 : Vertex shader uniform buffer
vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 1), // Binding 1 : Fragment shader image sampler
vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_FRAGMENT_BIT, 2), // Binding 2 : Framgnet shader image sampler
};
2016-02-16 15:07:25 +01:00
descriptorSetLayoutCreateInfo = vks::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings.data(), setLayoutBindings.size());
VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorSetLayoutCreateInfo, nullptr, &descriptorSetLayouts.scene));
pipelineLayoutCreateInfo = vks::initializers::pipelineLayoutCreateInfo(&descriptorSetLayouts.scene, 1);
VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, nullptr, &pipelineLayouts.scene));
2016-02-16 15:07:25 +01:00
}
void setupDescriptorSet()
{
VkDescriptorSetAllocateInfo descriptorSetAllocInfo;
std::vector<VkWriteDescriptorSet> writeDescriptorSets;
// Full screen blur
// Vertical
descriptorSetAllocInfo = vks::initializers::descriptorSetAllocateInfo(descriptorPool, &descriptorSetLayouts.blur, 1);
VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &descriptorSetAllocInfo, &descriptorSets.blurVert));
writeDescriptorSets = {
vks::initializers::writeDescriptorSet(descriptorSets.blurVert, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &uniformBuffers.blurParams.descriptor), // Binding 0: Fragment shader uniform buffer
vks::initializers::writeDescriptorSet(descriptorSets.blurVert, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, &offscreenPass.framebuffers[0].descriptor), // Binding 1: Fragment shader texture sampler
2016-02-16 15:07:25 +01:00
};
vkUpdateDescriptorSets(device, writeDescriptorSets.size(), writeDescriptorSets.data(), 0, NULL);
// Horizontal
descriptorSetAllocInfo = vks::initializers::descriptorSetAllocateInfo(descriptorPool, &descriptorSetLayouts.blur, 1);
VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &descriptorSetAllocInfo, &descriptorSets.blurHorz));
writeDescriptorSets = {
vks::initializers::writeDescriptorSet(descriptorSets.blurHorz, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &uniformBuffers.blurParams.descriptor), // Binding 0: Fragment shader uniform buffer
vks::initializers::writeDescriptorSet(descriptorSets.blurHorz, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, &offscreenPass.framebuffers[1].descriptor), // Binding 1: Fragment shader texture sampler
2016-02-16 15:07:25 +01:00
};
vkUpdateDescriptorSets(device, writeDescriptorSets.size(), writeDescriptorSets.data(), 0, NULL);
// Scene rendering
descriptorSetAllocInfo = vks::initializers::descriptorSetAllocateInfo(descriptorPool, &descriptorSetLayouts.scene, 1);
VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &descriptorSetAllocInfo, &descriptorSets.scene));
writeDescriptorSets = {
vks::initializers::writeDescriptorSet(descriptorSets.scene, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &uniformBuffers.scene.descriptor) // Binding 0: Vertex shader uniform buffer
2016-02-16 15:07:25 +01:00
};
vkUpdateDescriptorSets(device, writeDescriptorSets.size(), writeDescriptorSets.data(), 0, NULL);
// Skybox
VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &descriptorSetAllocInfo, &descriptorSets.skyBox));
writeDescriptorSets = {
vks::initializers::writeDescriptorSet(descriptorSets.skyBox, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &uniformBuffers.skyBox.descriptor), // Binding 0: Vertex shader uniform buffer
vks::initializers::writeDescriptorSet(descriptorSets.skyBox, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, &textures.cubemap.descriptor), // Binding 1: Fragment shader texture sampler
2016-02-16 15:07:25 +01:00
};
vkUpdateDescriptorSets(device, writeDescriptorSets.size(), writeDescriptorSets.data(), 0, NULL);
}
void preparePipelines()
{
VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCI = vks::initializers::pipelineInputAssemblyStateCreateInfo(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0, VK_FALSE);
VkPipelineRasterizationStateCreateInfo rasterizationStateCI = vks::initializers::pipelineRasterizationStateCreateInfo(VK_POLYGON_MODE_FILL, VK_CULL_MODE_NONE, VK_FRONT_FACE_CLOCKWISE, 0);
VkPipelineColorBlendAttachmentState blendAttachmentState = vks::initializers::pipelineColorBlendAttachmentState(0xf, VK_FALSE);
VkPipelineColorBlendStateCreateInfo colorBlendStateCI = vks::initializers::pipelineColorBlendStateCreateInfo(1, &blendAttachmentState);
VkPipelineDepthStencilStateCreateInfo depthStencilStateCI = vks::initializers::pipelineDepthStencilStateCreateInfo(VK_TRUE, VK_TRUE, VK_COMPARE_OP_LESS_OR_EQUAL);
VkPipelineViewportStateCreateInfo viewportStateCI = vks::initializers::pipelineViewportStateCreateInfo(1, 1, 0);
VkPipelineMultisampleStateCreateInfo multisampleStateCI = vks::initializers::pipelineMultisampleStateCreateInfo(VK_SAMPLE_COUNT_1_BIT, 0);
std::vector<VkDynamicState> dynamicStateEnables = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR };
VkPipelineDynamicStateCreateInfo dynamicStateCI = vks::initializers::pipelineDynamicStateCreateInfo(dynamicStateEnables.data(), dynamicStateEnables.size(), 0);
2016-02-16 15:07:25 +01:00
std::array<VkPipelineShaderStageCreateInfo, 2> shaderStages;
VkGraphicsPipelineCreateInfo pipelineCI = vks::initializers::pipelineCreateInfo(pipelineLayouts.blur, renderPass, 0);
pipelineCI.pInputAssemblyState = &inputAssemblyStateCI;
pipelineCI.pRasterizationState = &rasterizationStateCI;
pipelineCI.pColorBlendState = &colorBlendStateCI;
pipelineCI.pMultisampleState = &multisampleStateCI;
pipelineCI.pViewportState = &viewportStateCI;
pipelineCI.pDepthStencilState = &depthStencilStateCI;
pipelineCI.pDynamicState = &dynamicStateCI;
pipelineCI.stageCount = shaderStages.size();
pipelineCI.pStages = shaderStages.data();
2016-02-16 15:07:25 +01:00
// Blur pipelines
shaderStages[0] = loadShader(getAssetPath() + "shaders/bloom/gaussblur.vert.spv", VK_SHADER_STAGE_VERTEX_BIT);
shaderStages[1] = loadShader(getAssetPath() + "shaders/bloom/gaussblur.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT);
// Empty vertex input state
VkPipelineVertexInputStateCreateInfo emptyInputState = vks::initializers::pipelineVertexInputStateCreateInfo();
pipelineCI.pVertexInputState = &emptyInputState;
pipelineCI.layout = pipelineLayouts.blur;
2016-02-16 15:07:25 +01:00
// Additive blending
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;
// Use specialization constants to select between horizontal and vertical blur
uint32_t blurdirection = 0;
VkSpecializationMapEntry specializationMapEntry = vks::initializers::specializationMapEntry(0, 0, sizeof(uint32_t));
VkSpecializationInfo specializationInfo = vks::initializers::specializationInfo(1, &specializationMapEntry, sizeof(uint32_t), &blurdirection);
shaderStages[1].pSpecializationInfo = &specializationInfo;
// Vertical blur pipeline
pipelineCI.renderPass = offscreenPass.renderPass;
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipelines.blurVert));
// Horizontal blur pipeline
blurdirection = 1;
pipelineCI.renderPass = renderPass;
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipelines.blurHorz));
2016-02-16 15:07:25 +01:00
// Phong pass (3D model)
// Vertex bindings and attributes
std::vector<VkVertexInputBindingDescription> vertexInputBindings = {
vks::initializers::vertexInputBindingDescription(0, vertexLayout.stride(), VK_VERTEX_INPUT_RATE_VERTEX),
};
std::vector<VkVertexInputAttributeDescription> vertexInputAttributes = {
vks::initializers::vertexInputAttributeDescription(0, 0, VK_FORMAT_R32G32B32_SFLOAT, 0), // Position
vks::initializers::vertexInputAttributeDescription(0, 1, VK_FORMAT_R32G32_SFLOAT, sizeof(float) * 3), // Texture coordinates
vks::initializers::vertexInputAttributeDescription(0, 2, VK_FORMAT_R32G32B32_SFLOAT, sizeof(float) * 5), // Color
vks::initializers::vertexInputAttributeDescription(0, 3, VK_FORMAT_R32G32B32_SFLOAT, sizeof(float) * 8), // Normal
};
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();
pipelineCI.pVertexInputState = &vertexInputState;
pipelineCI.layout = pipelineLayouts.scene;
shaderStages[0] = loadShader(getAssetPath() + "shaders/bloom/phongpass.vert.spv", VK_SHADER_STAGE_VERTEX_BIT);
shaderStages[1] = loadShader(getAssetPath() + "shaders/bloom/phongpass.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT);
2016-02-16 15:07:25 +01:00
blendAttachmentState.blendEnable = VK_FALSE;
depthStencilStateCI.depthWriteEnable = VK_TRUE;
rasterizationStateCI.cullMode = VK_CULL_MODE_BACK_BIT;
pipelineCI.renderPass = renderPass;
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipelines.phongPass));
2016-02-16 15:07:25 +01:00
// Color only pass (offscreen blur base)
shaderStages[0] = loadShader(getAssetPath() + "shaders/bloom/colorpass.vert.spv", VK_SHADER_STAGE_VERTEX_BIT);
shaderStages[1] = loadShader(getAssetPath() + "shaders/bloom/colorpass.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT);
pipelineCI.renderPass = offscreenPass.renderPass;
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipelines.glowPass));
2016-02-16 15:07:25 +01:00
// Skybox (cubemap)
shaderStages[0] = loadShader(getAssetPath() + "shaders/bloom/skybox.vert.spv", VK_SHADER_STAGE_VERTEX_BIT);
shaderStages[1] = loadShader(getAssetPath() + "shaders/bloom/skybox.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT);
depthStencilStateCI.depthWriteEnable = VK_FALSE;
rasterizationStateCI.cullMode = VK_CULL_MODE_FRONT_BIT;
pipelineCI.renderPass = renderPass;
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipelines.skyBox));
2016-02-16 15:07:25 +01:00
}
// Prepare and initialize uniform buffer containing shader uniforms
void prepareUniformBuffers()
{
// Phong and color pass vertex shader uniform buffer
VK_CHECK_RESULT(vulkanDevice->createBuffer(
2016-02-16 15:07:25 +01:00
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
&uniformBuffers.scene,
sizeof(ubos.scene)));
2016-02-16 15:07:25 +01:00
// Blur parameters uniform buffers
VK_CHECK_RESULT(vulkanDevice->createBuffer(
2016-02-16 15:07:25 +01:00
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
&uniformBuffers.blurParams,
sizeof(ubos.blurParams)));
2016-02-16 15:07:25 +01:00
// Skybox
VK_CHECK_RESULT(vulkanDevice->createBuffer(
2016-02-16 15:07:25 +01:00
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
&uniformBuffers.skyBox,
sizeof(ubos.skyBox)));
// Map persistent
VK_CHECK_RESULT(uniformBuffers.scene.map());
VK_CHECK_RESULT(uniformBuffers.blurParams.map());
VK_CHECK_RESULT(uniformBuffers.skyBox.map());
2016-02-16 15:07:25 +01:00
// Intialize uniform buffers
updateUniformBuffersScene();
updateUniformBuffersBlur();
2016-02-16 15:07:25 +01:00
}
// Update uniform buffers for rendering the 3D scene
void updateUniformBuffersScene()
{
// UFO
ubos.scene.projection = camera.matrices.perspective;
ubos.scene.view = camera.matrices.view;
2016-02-16 15:07:25 +01:00
2017-09-24 18:17:07 +02:00
ubos.scene.model = glm::translate(glm::mat4(1.0f), glm::vec3(sin(glm::radians(timer * 360.0f)) * 0.25f, -1.0f, cos(glm::radians(timer * 360.0f)) * 0.25f) + cameraPos);
ubos.scene.model = glm::rotate(ubos.scene.model, -sinf(glm::radians(timer * 360.0f)) * 0.15f, glm::vec3(1.0f, 0.0f, 0.0f));
ubos.scene.model = glm::rotate(ubos.scene.model, glm::radians(timer * 360.0f), glm::vec3(0.0f, 1.0f, 0.0f));
2016-02-16 15:07:25 +01:00
memcpy(uniformBuffers.scene.mapped, &ubos.scene, sizeof(ubos.scene));
2016-02-16 15:07:25 +01:00
// Skybox
ubos.skyBox.projection = glm::perspective(glm::radians(45.0f), (float)width / (float)height, 0.1f, 256.0f);
ubos.skyBox.view = glm::mat4(glm::mat3(camera.matrices.view));
2017-09-24 18:17:07 +02:00
ubos.skyBox.model = glm::mat4(1.0f);
2016-02-16 15:07:25 +01:00
memcpy(uniformBuffers.skyBox.mapped, &ubos.skyBox, sizeof(ubos.skyBox));
2016-02-16 15:07:25 +01:00
}
// Update blur pass parameter uniform buffer
void updateUniformBuffersBlur()
2016-02-16 15:07:25 +01:00
{
memcpy(uniformBuffers.blurParams.mapped, &ubos.blurParams, sizeof(ubos.blurParams));
2016-02-16 15:07:25 +01:00
}
void draw()
{
VulkanExampleBase::prepareFrame();
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer];
VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE));
VulkanExampleBase::submitFrame();
}
2016-02-16 15:07:25 +01:00
void prepare()
{
VulkanExampleBase::prepare();
loadAssets();
2016-02-16 15:07:25 +01:00
prepareUniformBuffers();
prepareOffscreen();
2016-02-16 15:07:25 +01:00
setupDescriptorSetLayout();
preparePipelines();
setupDescriptorPool();
setupDescriptorSet();
buildCommandBuffers();
prepared = true;
}
virtual void render()
{
if (!prepared)
return;
draw();
if (!paused || camera.updated)
2016-02-16 15:07:25 +01:00
{
updateUniformBuffersScene();
}
}
virtual void OnUpdateUIOverlay(vks::UIOverlay *overlay)
{
if (overlay->header("Settings")) {
if (overlay->checkBox("Bloom", &bloom)) {
buildCommandBuffers();
}
if (overlay->inputFloat("Scale", &ubos.blurParams.blurScale, 0.1f, 2)) {
updateUniformBuffersBlur();
}
}
}
2016-02-16 15:07:25 +01:00
};
VULKAN_EXAMPLE_MAIN()