plumageRender/src/render/render.cpp

1796 lines
77 KiB
C++
Raw Normal View History

2023-05-26 16:19:12 +08:00
2023-05-17 14:49:05 +08:00
/*
* Vulkan Example - glTF scene loading and rendering
*
* Copyright (C) 2020-2022 by Sascha Willems - www.saschawillems.de
*
* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
*/
/*
* Shows how to load and display a simple scene from a glTF file
* Note that this isn't a complete glTF loader and only basic functions are shown here
* This means no complex materials, no animations, no skins, etc.
* For details on how glTF 2.0 works, see the official spec at https://github.com/KhronosGroup/glTF/tree/master/specification/2.0
*
* Other samples will load models using a dedicated model loader with more features (see base/VulkanglTFModel.hpp)
*
* If you are looking for a complete glTF implementation, check out https://github.com/SaschaWillems/Vulkan-glTF-PBR/
*/
2023-05-26 16:19:12 +08:00
#ifndef TINYGLTF_IMPLEMENTATION
#define TINYGLTF_IMPLEMENTATION
#endif
#ifndef STB_IMAGE_IMPLEMENTATION
#define STB_IMAGE_IMPLEMENTATION
#endif
2023-05-26 16:19:12 +08:00
#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
#define TINYGLTF_NO_STB_IMAGE_WRITE
#endif
2023-05-26 16:19:12 +08:00
#include "render.h"
2023-06-06 17:18:45 +08:00
#include "GUIFunc.h"
2023-06-06 11:40:59 +08:00
#include "assetLoader.h"
2023-06-06 17:18:45 +08:00
#include <VulkanTools.h>
#include <VulkanTexture.h>
2023-05-17 14:53:31 +08:00
2023-06-06 17:18:45 +08:00
PlumageRender::PlumageRender():PlumageRender()
2023-05-17 14:49:05 +08:00
{
2023-06-05 16:40:39 +08:00
title = "plumage render";
2023-05-17 14:49:05 +08:00
}
void PlumageRender::renderNode(glTFModel::Node* node, uint32_t cbIndex, glTFModel::Material::AlphaMode alphaMode) {
if (node->mesh) {
// Render mesh primitives
for (glTFModel::Primitive* primitive : node->mesh->primitives) {
if (primitive->material.alphaMode == alphaMode) {
VkPipeline pipeline = VK_NULL_HANDLE;
switch (alphaMode) {
case glTFModel::Material::ALPHAMODE_OPAQUE:
case glTFModel::Material::ALPHAMODE_MASK:
pipeline = primitive->material.doubleSided ? pipelines.pbrDoubleSided : pipelines.pbr;
break;
case glTFModel::Material::ALPHAMODE_BLEND:
pipeline = pipelines.pbrAlphaBlend;
break;
}
if (pipeline != boundPipeline) {
vkCmdBindPipeline(commandBuffers[cbIndex], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
boundPipeline = pipeline;
}
const std::vector<VkDescriptorSet> descriptorsets = {
descriptorSets[cbIndex].scene,
primitive->material.descriptorSet,
node->mesh->uniformBuffer.descriptorSet,
};
vkCmdBindDescriptorSets(commandBuffers[cbIndex], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, static_cast<uint32_t>(descriptorsets.size()), descriptorsets.data(), 0, NULL);
// Pass material parameters as push constants
PushConstBlockMaterial pushConstBlockMaterial{};
pushConstBlockMaterial.emissiveFactor = primitive->material.emissiveFactor;
// To save push constant space, availabilty and texture coordiante set are combined
// -1 = texture not used for this material, >= 0 texture used and index of texture coordinate set
pushConstBlockMaterial.colorTextureSet = primitive->material.baseColorTexture != nullptr ? primitive->material.texCoordSets.baseColor : -1;
pushConstBlockMaterial.normalTextureSet = primitive->material.normalTexture != nullptr ? primitive->material.texCoordSets.normal : -1;
pushConstBlockMaterial.occlusionTextureSet = primitive->material.occlusionTexture != nullptr ? primitive->material.texCoordSets.occlusion : -1;
pushConstBlockMaterial.emissiveTextureSet = primitive->material.emissiveTexture != nullptr ? primitive->material.texCoordSets.emissive : -1;
2023-06-05 22:37:31 +08:00
pushConstBlockMaterial.alphaMask = static_cast<float>(primitive->material.alphaMode == glTFModel::Material::ALPHAMODE_MASK);
pushConstBlockMaterial.alphaMaskCutoff = primitive->material.alphaCutoff;
// TODO: glTF specs states that metallic roughness should be preferred, even if specular glosiness is present
if (primitive->material.pbrWorkflows.metallicRoughness) {
// Metallic roughness workflow
pushConstBlockMaterial.workflow = static_cast<float>(PBR_WORKFLOW_METALLIC_ROUGHNESS);
pushConstBlockMaterial.baseColorFactor = primitive->material.baseColorFactor;
pushConstBlockMaterial.metallicFactor = primitive->material.metallicFactor;
pushConstBlockMaterial.roughnessFactor = primitive->material.roughnessFactor;
pushConstBlockMaterial.PhysicalDescriptorTextureSet = primitive->material.metallicRoughnessTexture != nullptr ? primitive->material.texCoordSets.metallicRoughness : -1;
pushConstBlockMaterial.colorTextureSet = primitive->material.baseColorTexture != nullptr ? primitive->material.texCoordSets.baseColor : -1;
}
if (primitive->material.pbrWorkflows.specularGlossiness) {
// Specular glossiness workflow
pushConstBlockMaterial.workflow = static_cast<float>(PBR_WORKFLOW_SPECULAR_GLOSINESS);
pushConstBlockMaterial.PhysicalDescriptorTextureSet = primitive->material.extension.specularGlossinessTexture != nullptr ? primitive->material.texCoordSets.specularGlossiness : -1;
pushConstBlockMaterial.colorTextureSet = primitive->material.extension.diffuseTexture != nullptr ? primitive->material.texCoordSets.baseColor : -1;
pushConstBlockMaterial.diffuseFactor = primitive->material.extension.diffuseFactor;
pushConstBlockMaterial.specularFactor = glm::vec4(primitive->material.extension.specularFactor, 1.0f);
}
vkCmdPushConstants(commandBuffers[cbIndex], pipelineLayout, VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(PushConstBlockMaterial), &pushConstBlockMaterial);
if (primitive->hasIndices) {
vkCmdDrawIndexed(commandBuffers[cbIndex], primitive->indexCount, 1, primitive->firstIndex, 0, 0);
}
else {
vkCmdDraw(commandBuffers[cbIndex], primitive->vertexCount, 1, 0, 0);
}
}
}
};
for (auto child : node->children) {
renderNode(child, cbIndex, alphaMode);
}
2023-06-05 22:37:31 +08:00
}
2023-05-17 14:49:05 +08:00
2023-06-02 09:56:49 +08:00
void PlumageRender::buildCommandBuffers()
2023-05-17 14:49:05 +08:00
{
2023-06-05 22:37:31 +08:00
VkCommandBufferBeginInfo cmdBufferBeginInfo{};
cmdBufferBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
2023-05-17 14:49:05 +08:00
2023-06-05 22:37:31 +08:00
VkClearValue clearValues[3];
if (settings.multiSampling) {
clearValues[0].color = { { 0.0f, 0.0f, 0.0f, 1.0f } };
clearValues[1].color = { { 0.0f, 0.0f, 0.0f, 1.0f } };
clearValues[2].depthStencil = { 1.0f, 0 };
}
else {
clearValues[0].color = { { 0.0f, 0.0f, 0.0f, 1.0f } };
clearValues[1].depthStencil = { 1.0f, 0 };
}
2023-05-17 14:49:05 +08:00
2023-06-05 22:37:31 +08:00
VkRenderPassBeginInfo renderPassBeginInfo{};
renderPassBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
renderPassBeginInfo.renderPass = renderPass;
2023-05-17 14:49:05 +08:00
renderPassBeginInfo.renderArea.offset.x = 0;
renderPassBeginInfo.renderArea.offset.y = 0;
renderPassBeginInfo.renderArea.extent.width = width;
renderPassBeginInfo.renderArea.extent.height = height;
2023-06-05 22:37:31 +08:00
renderPassBeginInfo.clearValueCount = settings.multiSampling ? 3 : 2;
2023-05-17 14:49:05 +08:00
renderPassBeginInfo.pClearValues = clearValues;
2023-06-05 22:37:31 +08:00
for (uint32_t i = 0; i < commandBuffers.size(); ++i) {
renderPassBeginInfo.framebuffer = frameBuffers[i];
2023-05-17 14:49:05 +08:00
2023-06-05 22:37:31 +08:00
VkCommandBuffer currentCB = commandBuffers[i];
VK_CHECK_RESULT(vkBeginCommandBuffer(currentCB, &cmdBufferBeginInfo));
vkCmdBeginRenderPass(currentCB, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
VkViewport viewport{};
viewport.width = (float)width;
viewport.height = (float)height;
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
vkCmdSetViewport(currentCB, 0, 1, &viewport);
VkRect2D scissor{};
scissor.extent = { width, height };
vkCmdSetScissor(currentCB, 0, 1, &scissor);
VkDeviceSize offsets[1] = { 0 };
if (displayBackground) {
vkCmdBindDescriptorSets(currentCB, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSets[i].skybox, 0, nullptr);
vkCmdBindPipeline(currentCB, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.skybox);
models.skybox.draw(currentCB);
}
2023-06-05 22:37:31 +08:00
glTFModel::Model& model = models.scene;
vkCmdBindVertexBuffers(currentCB, 0, 1, &model.vertices.buffer, offsets);
if (model.indices.buffer != VK_NULL_HANDLE) {
vkCmdBindIndexBuffer(currentCB, model.indices.buffer, 0, VK_INDEX_TYPE_UINT32);
}
boundPipeline = VK_NULL_HANDLE;
// Opaque primitives first
for (auto node : model.nodes) {
renderNode(node, i, glTFModel::Material::ALPHAMODE_OPAQUE);
}
// Alpha masked primitives
for (auto node : model.nodes) {
renderNode(node, i, glTFModel::Material::ALPHAMODE_MASK);
}
// Transparent primitives
// TODO: Correct depth sorting
for (auto node : model.nodes) {
renderNode(node, i, glTFModel::Material::ALPHAMODE_BLEND);
}
// User interface
//ui->draw(currentCB);
vkCmdEndRenderPass(currentCB);
VK_CHECK_RESULT(vkEndCommandBuffer(currentCB));
2023-05-17 14:49:05 +08:00
}
}
2023-06-05 22:37:31 +08:00
void PlumageRender::loadScene(std::string filename)
{
std::cout << "Loading scene from " << filename << std::endl;
models.scene.destroy(device);
animationIndex = 0;
animationTimer = 0.0f;
auto tStart = std::chrono::high_resolution_clock::now();
models.scene.loadFromFile(filename, vulkanDevice, queue);
auto tFileLoad = std::chrono::duration<double, std::milli>(std::chrono::high_resolution_clock::now() - tStart).count();
std::cout << "Loading took " << tFileLoad << " ms" << std::endl;
camera.setPosition({ 0.0f, 0.0f, 1.0f });
camera.setRotation({ 0.0f, 0.0f, 0.0f });
}
void PlumageRender::loadEnvironment(std::string filename)
{
std::cout << "Loading environment from " << filename << std::endl;
if (textures.environmentCube.image) {
textures.environmentCube.destroy();
textures.irradianceCube.destroy();
textures.prefilteredCube.destroy();
}
textures.environmentCube.loadFromFile(filename, VK_FORMAT_R16G16B16A16_SFLOAT, vulkanDevice, queue);
generateCubemaps();
}
2023-06-05 10:38:28 +08:00
2023-06-06 11:40:59 +08:00
2023-06-02 09:56:49 +08:00
void PlumageRender::loadAssets()
2023-05-17 14:49:05 +08:00
{
2023-06-06 11:40:59 +08:00
const std::string assetpath = getAssetPath();
if (_access(assetpath.c_str(),0) != 0) {
2023-06-05 22:37:31 +08:00
std::string msg = "Could not locate asset path in \"" + assetpath + "\".\nMake sure binary is run from correct relative directory!";
std::cerr << msg << std::endl;
exit(-1);
}
2023-06-06 11:40:59 +08:00
assetLoader::readDirectory(assetpath + "environments", "*.ktx", environments, false);
2023-06-05 22:37:31 +08:00
textures.empty.loadFromFile(assetpath + "textures/empty.ktx", VK_FORMAT_R8G8B8A8_UNORM, vulkanDevice, queue);
2023-06-06 11:40:59 +08:00
std::string sceneFile = filePath.glTFModelFilePath;
std::string envMapFile = filePath.envMapFilePath;
2023-06-05 22:37:31 +08:00
for (size_t i = 0; i < args.size(); i++) {
if ((std::string(args[i]).find(".gltf") != std::string::npos) || (std::string(args[i]).find(".glb") != std::string::npos)) {
std::ifstream file(args[i]);
if (file.good()) {
sceneFile = args[i];
}
else {
std::cout << "could not load \"" << args[i] << "\"" << std::endl;
}
}
if (std::string(args[i]).find(".ktx") != std::string::npos) {
std::ifstream file(args[i]);
if (file.good()) {
envMapFile = args[i];
}
else {
std::cout << "could not load \"" << args[i] << "\"" << std::endl;
}
}
}
loadScene(sceneFile.c_str());
models.skybox.loadFromFile(assetpath + "models/Box/glTF-Embedded/Box.gltf", vulkanDevice, queue);
loadEnvironment(envMapFile.c_str());
2023-05-17 14:49:05 +08:00
}
2023-06-06 11:40:59 +08:00
void PlumageRender::setupNodeDescriptorSet(glTFModel::Node* node)
2023-05-17 14:49:05 +08:00
{
/*
This sample uses separate descriptor sets (and layouts) for the matrices and materials (textures)
*/
2023-06-06 11:40:59 +08:00
if (node->mesh) {
VkDescriptorSetAllocateInfo descriptorSetAllocInfo{};
descriptorSetAllocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
descriptorSetAllocInfo.descriptorPool = descriptorPool;
descriptorSetAllocInfo.pSetLayouts = &descriptorSetLayouts.node;
descriptorSetAllocInfo.descriptorSetCount = 1;
VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &descriptorSetAllocInfo, &node->mesh->uniformBuffer.descriptorSet));
VkWriteDescriptorSet writeDescriptorSet{};
writeDescriptorSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writeDescriptorSet.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
writeDescriptorSet.descriptorCount = 1;
writeDescriptorSet.dstSet = node->mesh->uniformBuffer.descriptorSet;
writeDescriptorSet.dstBinding = 0;
writeDescriptorSet.pBufferInfo = &node->mesh->uniformBuffer.descriptor;
vkUpdateDescriptorSets(device, 1, &writeDescriptorSet, 0, nullptr);
2023-05-25 16:13:37 +08:00
}
2023-06-06 11:40:59 +08:00
for (auto& child : node->children) {
setupNodeDescriptorSet(child);
}
}
2023-05-17 14:49:05 +08:00
2023-06-06 11:40:59 +08:00
void PlumageRender::setupDescriptors()
{
/*
Descriptor Pool
*/
uint32_t imageSamplerCount = 0;
uint32_t materialCount = 0;
uint32_t meshCount = 0;
// Environment samplers (radiance, irradiance, brdf lut)
imageSamplerCount += 3;
std::vector<glTFModel::Model*> modellist = { &models.skybox, &models.scene };
for (auto& model : modellist) {
for (auto& material : model->materials) {
imageSamplerCount += 5;
materialCount++;
}
for (auto node : model->linearNodes) {
if (node->mesh) {
meshCount++;
}
}
}
2023-05-17 14:49:05 +08:00
2023-06-06 11:40:59 +08:00
std::vector<VkDescriptorPoolSize> poolSizes = {
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, (4 + meshCount) * swapChain.imageCount },
{ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, imageSamplerCount * swapChain.imageCount }
2023-05-17 14:49:05 +08:00
};
2023-06-06 11:40:59 +08:00
VkDescriptorPoolCreateInfo descriptorPoolCI{};
descriptorPoolCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
descriptorPoolCI.poolSizeCount = 2;
descriptorPoolCI.pPoolSizes = poolSizes.data();
descriptorPoolCI.maxSets = (2 + materialCount + meshCount) * swapChain.imageCount;
VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolCI, nullptr, &descriptorPool));
2023-06-06 11:40:59 +08:00
/*
Descriptor sets
*/
2023-05-17 14:49:05 +08:00
2023-06-06 11:40:59 +08:00
// Scene (matrices and environment maps)
{
2023-06-06 11:40:59 +08:00
std::vector<VkDescriptorSetLayoutBinding> setLayoutBindings = {
{ 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, nullptr },
{ 1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr },
{ 2, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr },
{ 3, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr },
{ 4, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr },
2023-05-25 16:13:37 +08:00
};
2023-06-06 11:40:59 +08:00
VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCI{};
descriptorSetLayoutCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
descriptorSetLayoutCI.pBindings = setLayoutBindings.data();
descriptorSetLayoutCI.bindingCount = static_cast<uint32_t>(setLayoutBindings.size());
VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorSetLayoutCI, nullptr, &descriptorSetLayouts.scene));
for (auto i = 0; i < descriptorSets.size(); i++) {
VkDescriptorSetAllocateInfo descriptorSetAllocInfo{};
descriptorSetAllocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
descriptorSetAllocInfo.descriptorPool = descriptorPool;
descriptorSetAllocInfo.pSetLayouts = &descriptorSetLayouts.scene;
descriptorSetAllocInfo.descriptorSetCount = 1;
VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &descriptorSetAllocInfo, &descriptorSets[i].scene));
std::array<VkWriteDescriptorSet, 5> writeDescriptorSets{};
writeDescriptorSets[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writeDescriptorSets[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
writeDescriptorSets[0].descriptorCount = 1;
writeDescriptorSets[0].dstSet = descriptorSets[i].scene;
writeDescriptorSets[0].dstBinding = 0;
writeDescriptorSets[0].pBufferInfo = &uniformBuffers[i].scene.descriptor;
writeDescriptorSets[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writeDescriptorSets[1].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
writeDescriptorSets[1].descriptorCount = 1;
writeDescriptorSets[1].dstSet = descriptorSets[i].scene;
writeDescriptorSets[1].dstBinding = 1;
writeDescriptorSets[1].pBufferInfo = &uniformBuffers[i].params.descriptor;
writeDescriptorSets[2].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writeDescriptorSets[2].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
writeDescriptorSets[2].descriptorCount = 1;
writeDescriptorSets[2].dstSet = descriptorSets[i].scene;
writeDescriptorSets[2].dstBinding = 2;
writeDescriptorSets[2].pImageInfo = &textures.irradianceCube.descriptor;
writeDescriptorSets[3].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writeDescriptorSets[3].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
writeDescriptorSets[3].descriptorCount = 1;
writeDescriptorSets[3].dstSet = descriptorSets[i].scene;
writeDescriptorSets[3].dstBinding = 3;
writeDescriptorSets[3].pImageInfo = &textures.prefilteredCube.descriptor;
writeDescriptorSets[4].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writeDescriptorSets[4].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
writeDescriptorSets[4].descriptorCount = 1;
writeDescriptorSets[4].dstSet = descriptorSets[i].scene;
writeDescriptorSets[4].dstBinding = 4;
writeDescriptorSets[4].pImageInfo = &textures.lutBrdf.descriptor;
vkUpdateDescriptorSets(device, static_cast<uint32_t>(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, NULL);
}
}
2023-05-25 16:13:37 +08:00
2023-06-06 11:40:59 +08:00
// Material (samplers)
{
std::vector<VkDescriptorSetLayoutBinding> setLayoutBindings = {
{ 0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr },
{ 1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr },
{ 2, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr },
{ 3, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr },
{ 4, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr },
2023-05-25 16:13:37 +08:00
};
2023-06-06 11:40:59 +08:00
VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCI{};
descriptorSetLayoutCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
descriptorSetLayoutCI.pBindings = setLayoutBindings.data();
descriptorSetLayoutCI.bindingCount = static_cast<uint32_t>(setLayoutBindings.size());
VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorSetLayoutCI, nullptr, &descriptorSetLayouts.material));
// Per-Material descriptor sets
for (auto& material : models.scene.materials) {
VkDescriptorSetAllocateInfo descriptorSetAllocInfo{};
descriptorSetAllocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
descriptorSetAllocInfo.descriptorPool = descriptorPool;
descriptorSetAllocInfo.pSetLayouts = &descriptorSetLayouts.material;
descriptorSetAllocInfo.descriptorSetCount = 1;
VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &descriptorSetAllocInfo, &material.descriptorSet));
std::vector<VkDescriptorImageInfo> imageDescriptors = {
textures.empty.descriptor,
textures.empty.descriptor,
material.normalTexture ? material.normalTexture->descriptor : textures.empty.descriptor,
material.occlusionTexture ? material.occlusionTexture->descriptor : textures.empty.descriptor,
material.emissiveTexture ? material.emissiveTexture->descriptor : textures.empty.descriptor
};
if (material.pbrWorkflows.metallicRoughness) {
if (material.baseColorTexture) {
imageDescriptors[0] = material.baseColorTexture->descriptor;
}
if (material.metallicRoughnessTexture) {
imageDescriptors[1] = material.metallicRoughnessTexture->descriptor;
}
}
2023-05-25 16:13:37 +08:00
2023-06-06 11:40:59 +08:00
if (material.pbrWorkflows.specularGlossiness) {
if (material.extension.diffuseTexture) {
imageDescriptors[0] = material.extension.diffuseTexture->descriptor;
}
if (material.extension.specularGlossinessTexture) {
imageDescriptors[1] = material.extension.specularGlossinessTexture->descriptor;
}
}
2023-06-06 11:40:59 +08:00
std::array<VkWriteDescriptorSet, 5> writeDescriptorSets{};
for (size_t i = 0; i < imageDescriptors.size(); i++) {
writeDescriptorSets[i].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writeDescriptorSets[i].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
writeDescriptorSets[i].descriptorCount = 1;
writeDescriptorSets[i].dstSet = material.descriptorSet;
writeDescriptorSets[i].dstBinding = static_cast<uint32_t>(i);
writeDescriptorSets[i].pImageInfo = &imageDescriptors[i];
}
vkUpdateDescriptorSets(device, static_cast<uint32_t>(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, NULL);
2023-05-25 16:13:37 +08:00
}
2023-06-06 11:40:59 +08:00
// Model node (matrices)
2023-05-25 16:13:37 +08:00
{
2023-06-06 11:40:59 +08:00
std::vector<VkDescriptorSetLayoutBinding> setLayoutBindings = {
{ 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT, nullptr },
};
VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCI{};
descriptorSetLayoutCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
descriptorSetLayoutCI.pBindings = setLayoutBindings.data();
descriptorSetLayoutCI.bindingCount = static_cast<uint32_t>(setLayoutBindings.size());
VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorSetLayoutCI, nullptr, &descriptorSetLayouts.node));
// Per-Node descriptor set
for (auto& node : models.scene.nodes) {
setupNodeDescriptorSet(node);
}
2023-05-25 16:13:37 +08:00
}
2023-05-17 14:49:05 +08:00
2023-06-06 11:40:59 +08:00
}
2023-05-25 16:13:37 +08:00
2023-06-06 11:40:59 +08:00
// Skybox (fixed set)
for (auto i = 0; i < uniformBuffers.size(); i++) {
VkDescriptorSetAllocateInfo descriptorSetAllocInfo{};
descriptorSetAllocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
descriptorSetAllocInfo.descriptorPool = descriptorPool;
descriptorSetAllocInfo.pSetLayouts = &descriptorSetLayouts.scene;
descriptorSetAllocInfo.descriptorSetCount = 1;
VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &descriptorSetAllocInfo, &descriptorSets[i].skybox));
std::array<VkWriteDescriptorSet, 3> writeDescriptorSets{};
writeDescriptorSets[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writeDescriptorSets[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
writeDescriptorSets[0].descriptorCount = 1;
writeDescriptorSets[0].dstSet = descriptorSets[i].skybox;
writeDescriptorSets[0].dstBinding = 0;
writeDescriptorSets[0].pBufferInfo = &uniformBuffers[i].skybox.descriptor;
writeDescriptorSets[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writeDescriptorSets[1].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
writeDescriptorSets[1].descriptorCount = 1;
writeDescriptorSets[1].dstSet = descriptorSets[i].skybox;
writeDescriptorSets[1].dstBinding = 1;
writeDescriptorSets[1].pBufferInfo = &uniformBuffers[i].params.descriptor;
writeDescriptorSets[2].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writeDescriptorSets[2].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
writeDescriptorSets[2].descriptorCount = 1;
writeDescriptorSets[2].dstSet = descriptorSets[i].skybox;
writeDescriptorSets[2].dstBinding = 2;
writeDescriptorSets[2].pImageInfo = &textures.prefilteredCube.descriptor;
vkUpdateDescriptorSets(device, static_cast<uint32_t>(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, nullptr);
2023-05-17 14:49:05 +08:00
}
}
2023-06-02 09:56:49 +08:00
void PlumageRender::preparePipelines()
2023-05-17 14:49:05 +08:00
{
2023-06-06 11:40:59 +08:00
VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCI{};
inputAssemblyStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
inputAssemblyStateCI.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
VkPipelineRasterizationStateCreateInfo rasterizationStateCI{};
rasterizationStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rasterizationStateCI.polygonMode = VK_POLYGON_MODE_FILL;
rasterizationStateCI.cullMode = VK_CULL_MODE_BACK_BIT;
rasterizationStateCI.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
rasterizationStateCI.lineWidth = 1.0f;
VkPipelineColorBlendAttachmentState blendAttachmentState{};
blendAttachmentState.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
blendAttachmentState.blendEnable = VK_FALSE;
VkPipelineColorBlendStateCreateInfo colorBlendStateCI{};
colorBlendStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
colorBlendStateCI.attachmentCount = 1;
colorBlendStateCI.pAttachments = &blendAttachmentState;
VkPipelineDepthStencilStateCreateInfo depthStencilStateCI{};
depthStencilStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
depthStencilStateCI.depthTestEnable = VK_FALSE;
depthStencilStateCI.depthWriteEnable = VK_FALSE;
depthStencilStateCI.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL;
depthStencilStateCI.front = depthStencilStateCI.back;
depthStencilStateCI.back.compareOp = VK_COMPARE_OP_ALWAYS;
VkPipelineViewportStateCreateInfo viewportStateCI{};
viewportStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewportStateCI.viewportCount = 1;
viewportStateCI.scissorCount = 1;
VkPipelineMultisampleStateCreateInfo multisampleStateCI{};
multisampleStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
if (settings.multiSampling) {
multisampleStateCI.rasterizationSamples = settings.sampleCount;
}
std::vector<VkDynamicState> dynamicStateEnables = {
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR
2023-05-17 14:49:05 +08:00
};
2023-06-06 11:40:59 +08:00
VkPipelineDynamicStateCreateInfo dynamicStateCI{};
dynamicStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
dynamicStateCI.pDynamicStates = dynamicStateEnables.data();
dynamicStateCI.dynamicStateCount = static_cast<uint32_t>(dynamicStateEnables.size());
// Pipeline layout
const std::vector<VkDescriptorSetLayout> setLayouts = {
descriptorSetLayouts.scene, descriptorSetLayouts.material, descriptorSetLayouts.node
2023-05-17 14:49:05 +08:00
};
2023-06-06 11:40:59 +08:00
VkPipelineLayoutCreateInfo pipelineLayoutCI{};
pipelineLayoutCI.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipelineLayoutCI.setLayoutCount = static_cast<uint32_t>(setLayouts.size());
pipelineLayoutCI.pSetLayouts = setLayouts.data();
VkPushConstantRange pushConstantRange{};
pushConstantRange.size = sizeof(PushConstBlockMaterial);
pushConstantRange.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
pipelineLayoutCI.pushConstantRangeCount = 1;
pipelineLayoutCI.pPushConstantRanges = &pushConstantRange;
VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCI, nullptr, &pipelineLayout));
// Vertex bindings an attributes
VkVertexInputBindingDescription vertexInputBinding = { 0, sizeof(glTFModel::Model::Vertex), VK_VERTEX_INPUT_RATE_VERTEX };
std::vector<VkVertexInputAttributeDescription> vertexInputAttributes = {
{ 0, 0, VK_FORMAT_R32G32B32_SFLOAT, 0 },
{ 1, 0, VK_FORMAT_R32G32B32_SFLOAT, sizeof(float) * 3 },
{ 2, 0, VK_FORMAT_R32G32_SFLOAT, sizeof(float) * 6 },
{ 3, 0, VK_FORMAT_R32G32_SFLOAT, sizeof(float) * 8 },
{ 4, 0, VK_FORMAT_R32G32B32A32_SFLOAT, sizeof(float) * 10 },
{ 5, 0, VK_FORMAT_R32G32B32A32_SFLOAT, sizeof(float) * 14 },
{ 6, 0, VK_FORMAT_R32G32B32A32_SFLOAT, sizeof(float) * 18 }
};
VkPipelineVertexInputStateCreateInfo vertexInputStateCI{};
vertexInputStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vertexInputStateCI.vertexBindingDescriptionCount = 1;
vertexInputStateCI.pVertexBindingDescriptions = &vertexInputBinding;
2023-05-17 14:49:05 +08:00
vertexInputStateCI.vertexAttributeDescriptionCount = static_cast<uint32_t>(vertexInputAttributes.size());
vertexInputStateCI.pVertexAttributeDescriptions = vertexInputAttributes.data();
2023-06-06 11:40:59 +08:00
// Pipelines
std::array<VkPipelineShaderStageCreateInfo, 2> shaderStages;
2023-05-17 14:49:05 +08:00
2023-06-06 11:40:59 +08:00
VkGraphicsPipelineCreateInfo pipelineCI{};
pipelineCI.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipelineCI.layout = pipelineLayout;
pipelineCI.renderPass = renderPass;
2023-05-17 14:49:05 +08:00
pipelineCI.pInputAssemblyState = &inputAssemblyStateCI;
2023-06-06 11:40:59 +08:00
pipelineCI.pVertexInputState = &vertexInputStateCI;
2023-05-17 14:49:05 +08:00
pipelineCI.pRasterizationState = &rasterizationStateCI;
pipelineCI.pColorBlendState = &colorBlendStateCI;
pipelineCI.pMultisampleState = &multisampleStateCI;
pipelineCI.pViewportState = &viewportStateCI;
pipelineCI.pDepthStencilState = &depthStencilStateCI;
pipelineCI.pDynamicState = &dynamicStateCI;
pipelineCI.stageCount = static_cast<uint32_t>(shaderStages.size());
pipelineCI.pStages = shaderStages.data();
2023-06-06 11:40:59 +08:00
if (settings.multiSampling) {
multisampleStateCI.rasterizationSamples = settings.sampleCount;
}
2023-05-17 14:49:05 +08:00
2023-06-06 11:40:59 +08:00
// Skybox pipeline (background cube)
shaderStages = {
2023-06-06 17:18:45 +08:00
loadShader(device,filePath.skyboxVertShaderPath, VK_SHADER_STAGE_VERTEX_BIT),
loadShader(device,filePath.skyboxFragShaderPath, VK_SHADER_STAGE_FRAGMENT_BIT)
2023-06-06 11:40:59 +08:00
};
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipelines.skybox));
for (auto shaderStage : shaderStages) {
vkDestroyShaderModule(device, shaderStage.module, nullptr);
}
// PBR pipeline
shaderStages = {
2023-06-06 17:18:45 +08:00
loadShader(device,filePath.pbrVertShaderPath, VK_SHADER_STAGE_VERTEX_BIT),
loadShader(device,filePath.pbrFragShaderPath, VK_SHADER_STAGE_FRAGMENT_BIT)
2023-06-06 11:40:59 +08:00
};
depthStencilStateCI.depthWriteEnable = VK_TRUE;
depthStencilStateCI.depthTestEnable = VK_TRUE;
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipelines.pbr));
rasterizationStateCI.cullMode = VK_CULL_MODE_NONE;
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipelines.pbrDoubleSided));
rasterizationStateCI.cullMode = VK_CULL_MODE_NONE;
blendAttachmentState.blendEnable = VK_TRUE;
blendAttachmentState.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
blendAttachmentState.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
blendAttachmentState.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
blendAttachmentState.colorBlendOp = VK_BLEND_OP_ADD;
blendAttachmentState.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
blendAttachmentState.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
blendAttachmentState.alphaBlendOp = VK_BLEND_OP_ADD;
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipelines.pbrAlphaBlend));
for (auto shaderStage : shaderStages) {
vkDestroyShaderModule(device, shaderStage.module, nullptr);
2023-05-17 14:49:05 +08:00
}
2023-05-25 11:38:10 +08:00
//Create Tone Mapping render pipeline
2023-06-06 11:40:59 +08:00
//CreateToneMappingPipeline();
2023-05-17 14:49:05 +08:00
}
2023-05-24 17:07:38 +08:00
2023-06-02 09:56:49 +08:00
void PlumageRender::CreateToneMappingPipeline()
{
if (pipelines.toneMapping != VK_NULL_HANDLE)
{
vkDestroyPipeline(device, pipelines.toneMapping, nullptr);
pipelines.toneMapping = VK_NULL_HANDLE;
}
VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCI = vks::initializers::pipelineInputAssemblyStateCreateInfo(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0, VK_FALSE);
2023-05-25 16:13:37 +08:00
VkPipelineRasterizationStateCreateInfo rasterizationStateCI = vks::initializers::pipelineRasterizationStateCreateInfo(VK_POLYGON_MODE_FILL, VK_CULL_MODE_NONE, VK_FRONT_FACE_COUNTER_CLOCKWISE, 0);
VkPipelineColorBlendAttachmentState blendAttachmentStateCI = vks::initializers::pipelineColorBlendAttachmentState(0xf, VK_FALSE);
VkPipelineColorBlendStateCreateInfo colorBlendStateCI = vks::initializers::pipelineColorBlendStateCreateInfo(1, &blendAttachmentStateCI);
2023-05-25 16:13:37 +08:00
VkPipelineDepthStencilStateCreateInfo depthStencilStateCI = vks::initializers::pipelineDepthStencilStateCreateInfo(VK_FALSE, VK_FALSE, 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);
const std::vector<VkDynamicState> dynamicStateEnables = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR };
VkPipelineDynamicStateCreateInfo dynamicStateCI = vks::initializers::pipelineDynamicStateCreateInfo(dynamicStateEnables.data(), static_cast<uint32_t>(dynamicStateEnables.size()), 0);
VkPipelineVertexInputStateCreateInfo emptyInputState = vks::initializers::pipelineVertexInputStateCreateInfo();
2023-05-25 16:13:37 +08:00
2023-06-01 15:32:08 +08:00
const std::string fragPath = ToneMapping ? filePath.tonemappingEnableFragShaderPath : filePath.tonemappingDisableFragShaderPath;
std::array<VkPipelineShaderStageCreateInfo, 2> shaderStages = {
2023-06-06 17:18:45 +08:00
loadShader(device,filePath.tonemappingVertShaderPath, VK_SHADER_STAGE_VERTEX_BIT),
loadShader(device,fragPath, VK_SHADER_STAGE_FRAGMENT_BIT)
};
VkGraphicsPipelineCreateInfo pipelineCI = vks::initializers::pipelineCreateInfo(pipelineLayouts.tonemappingLayout, renderPass, 0);
pipelineCI.pVertexInputState = &emptyInputState;
pipelineCI.pInputAssemblyState = &inputAssemblyStateCI;
pipelineCI.pRasterizationState = &rasterizationStateCI;
pipelineCI.pColorBlendState = &colorBlendStateCI;
pipelineCI.pMultisampleState = &multisampleStateCI;
pipelineCI.pViewportState = &viewportStateCI;
pipelineCI.pDepthStencilState = &depthStencilStateCI;
pipelineCI.pDynamicState = &dynamicStateCI;
pipelineCI.stageCount = static_cast<uint32_t>(shaderStages.size());
pipelineCI.pStages = shaderStages.data();
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipelines.toneMapping));
2023-05-17 14:49:05 +08:00
}
2023-05-25 16:13:37 +08:00
2023-06-06 15:52:39 +08:00
// generate two cube maps
// irradiance cube map
// prefileter environment cube map
2023-06-06 11:40:59 +08:00
void PlumageRender::generateCubemaps()
2023-05-17 14:49:05 +08:00
{
2023-06-06 11:40:59 +08:00
enum Target { IRRADIANCE = 0, PREFILTEREDENV = 1 };
2023-05-24 16:10:36 +08:00
2023-06-06 11:40:59 +08:00
for (uint32_t target = 0; target < PREFILTEREDENV + 1; target++) {
2023-05-24 16:10:36 +08:00
2023-06-06 11:40:59 +08:00
vks::TextureCubeMap cubemap;
2023-05-24 16:10:36 +08:00
2023-06-06 11:40:59 +08:00
auto tStart = std::chrono::high_resolution_clock::now();
2023-05-24 16:10:36 +08:00
2023-06-06 11:40:59 +08:00
VkFormat format;
int32_t dim;
2023-05-24 16:10:36 +08:00
2023-06-06 11:40:59 +08:00
switch (target) {
case IRRADIANCE:
format = VK_FORMAT_R32G32B32A32_SFLOAT;
dim = 64;
break;
case PREFILTEREDENV:
format = VK_FORMAT_R16G16B16A16_SFLOAT;
dim = 512;
break;
};
2023-05-24 16:10:36 +08:00
2023-06-06 11:40:59 +08:00
const uint32_t numMips = static_cast<uint32_t>(floor(log2(dim))) + 1;
2023-05-25 16:13:37 +08:00
2023-06-06 11:40:59 +08:00
// Create target cubemap
{
// Image
VkImageCreateInfo imageCI{};
imageCI.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
imageCI.imageType = VK_IMAGE_TYPE_2D;
imageCI.format = format;
imageCI.extent.width = dim;
imageCI.extent.height = dim;
imageCI.extent.depth = 1;
imageCI.mipLevels = numMips;
imageCI.arrayLayers = 6;
imageCI.samples = VK_SAMPLE_COUNT_1_BIT;
imageCI.tiling = VK_IMAGE_TILING_OPTIMAL;
imageCI.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
imageCI.flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
VK_CHECK_RESULT(vkCreateImage(device, &imageCI, nullptr, &cubemap.image));
VkMemoryRequirements memReqs;
vkGetImageMemoryRequirements(device, cubemap.image, &memReqs);
VkMemoryAllocateInfo memAllocInfo{};
memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
memAllocInfo.allocationSize = memReqs.size;
memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
VK_CHECK_RESULT(vkAllocateMemory(device, &memAllocInfo, nullptr, &cubemap.deviceMemory));
VK_CHECK_RESULT(vkBindImageMemory(device, cubemap.image, cubemap.deviceMemory, 0));
// View
VkImageViewCreateInfo viewCI{};
viewCI.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewCI.viewType = VK_IMAGE_VIEW_TYPE_CUBE;
viewCI.format = format;
viewCI.subresourceRange = {};
viewCI.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
viewCI.subresourceRange.levelCount = numMips;
viewCI.subresourceRange.layerCount = 6;
viewCI.image = cubemap.image;
VK_CHECK_RESULT(vkCreateImageView(device, &viewCI, nullptr, &cubemap.view));
// Sampler
VkSamplerCreateInfo samplerCI{};
samplerCI.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
samplerCI.magFilter = VK_FILTER_LINEAR;
samplerCI.minFilter = VK_FILTER_LINEAR;
samplerCI.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
samplerCI.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
samplerCI.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
samplerCI.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
samplerCI.minLod = 0.0f;
samplerCI.maxLod = static_cast<float>(numMips);
samplerCI.maxAnisotropy = 1.0f;
samplerCI.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;
VK_CHECK_RESULT(vkCreateSampler(device, &samplerCI, nullptr, &cubemap.sampler));
}
2023-05-24 16:10:36 +08:00
2023-06-06 11:40:59 +08:00
// FB, Att, RP, Pipe, etc.
VkAttachmentDescription attDesc{};
// Color attachment
attDesc.format = format;
attDesc.samples = VK_SAMPLE_COUNT_1_BIT;
attDesc.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
attDesc.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
attDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
attDesc.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
VkAttachmentReference colorReference = { 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
VkSubpassDescription subpassDescription{};
subpassDescription.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpassDescription.colorAttachmentCount = 1;
subpassDescription.pColorAttachments = &colorReference;
// 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_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;
// Renderpass
VkRenderPassCreateInfo renderPassCI{};
renderPassCI.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
renderPassCI.attachmentCount = 1;
renderPassCI.pAttachments = &attDesc;
renderPassCI.subpassCount = 1;
renderPassCI.pSubpasses = &subpassDescription;
renderPassCI.dependencyCount = 2;
renderPassCI.pDependencies = dependencies.data();
VkRenderPass renderpass;
VK_CHECK_RESULT(vkCreateRenderPass(device, &renderPassCI, nullptr, &renderpass));
// Create offscreen framebuffer
2023-05-25 16:13:37 +08:00
{
2023-06-06 11:40:59 +08:00
// Image
VkImageCreateInfo imageCI{};
imageCI.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
imageCI.imageType = VK_IMAGE_TYPE_2D;
imageCI.format = format;
imageCI.extent.width = dim;
imageCI.extent.height = dim;
imageCI.extent.depth = 1;
imageCI.mipLevels = 1;
imageCI.arrayLayers = 1;
imageCI.samples = VK_SAMPLE_COUNT_1_BIT;
imageCI.tiling = VK_IMAGE_TILING_OPTIMAL;
imageCI.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
imageCI.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
imageCI.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
VK_CHECK_RESULT(vkCreateImage(device, &imageCI, nullptr, &offscreen.image));
VkMemoryRequirements memReqs;
vkGetImageMemoryRequirements(device, offscreen.image, &memReqs);
VkMemoryAllocateInfo memAllocInfo{};
memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
memAllocInfo.allocationSize = memReqs.size;
memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
VK_CHECK_RESULT(vkAllocateMemory(device, &memAllocInfo, nullptr, &offscreen.memory));
VK_CHECK_RESULT(vkBindImageMemory(device, offscreen.image, offscreen.memory, 0));
// View
VkImageViewCreateInfo viewCI{};
viewCI.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewCI.viewType = VK_IMAGE_VIEW_TYPE_2D;
viewCI.format = format;
viewCI.flags = 0;
viewCI.subresourceRange = {};
viewCI.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
viewCI.subresourceRange.baseMipLevel = 0;
viewCI.subresourceRange.levelCount = 1;
viewCI.subresourceRange.baseArrayLayer = 0;
viewCI.subresourceRange.layerCount = 1;
viewCI.image = offscreen.image;
VK_CHECK_RESULT(vkCreateImageView(device, &viewCI, nullptr, &offscreen.view));
// Framebuffer
VkFramebufferCreateInfo framebufferCI{};
framebufferCI.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
framebufferCI.renderPass = renderpass;
framebufferCI.attachmentCount = 1;
framebufferCI.pAttachments = &offscreen.view;
framebufferCI.width = dim;
framebufferCI.height = dim;
framebufferCI.layers = 1;
VK_CHECK_RESULT(vkCreateFramebuffer(device, &framebufferCI, nullptr, &offscreen.framebuffer));
VkCommandBuffer layoutCmd = vulkanDevice->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);
VkImageMemoryBarrier imageMemoryBarrier{};
imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
imageMemoryBarrier.image = offscreen.image;
imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
imageMemoryBarrier.srcAccessMask = 0;
imageMemoryBarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
imageMemoryBarrier.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
vkCmdPipelineBarrier(layoutCmd, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier);
vulkanDevice->flushCommandBuffer(layoutCmd, queue, true);
}
2023-05-25 16:13:37 +08:00
2023-06-06 11:40:59 +08:00
// Descriptors
VkDescriptorSetLayout descriptorsetlayout;
VkDescriptorSetLayoutBinding setLayoutBinding = { 0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr };
VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCI{};
descriptorSetLayoutCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
descriptorSetLayoutCI.pBindings = &setLayoutBinding;
descriptorSetLayoutCI.bindingCount = 1;
VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorSetLayoutCI, nullptr, &descriptorsetlayout));
// Descriptor Pool
VkDescriptorPoolSize poolSize = { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1 };
VkDescriptorPoolCreateInfo descriptorPoolCI{};
descriptorPoolCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
descriptorPoolCI.poolSizeCount = 1;
descriptorPoolCI.pPoolSizes = &poolSize;
descriptorPoolCI.maxSets = 2;
VkDescriptorPool descriptorpool;
VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolCI, nullptr, &descriptorpool));
// Descriptor sets
VkDescriptorSet descriptorset;
VkDescriptorSetAllocateInfo descriptorSetAllocInfo{};
descriptorSetAllocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
descriptorSetAllocInfo.descriptorPool = descriptorpool;
descriptorSetAllocInfo.pSetLayouts = &descriptorsetlayout;
descriptorSetAllocInfo.descriptorSetCount = 1;
VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &descriptorSetAllocInfo, &descriptorset));
VkWriteDescriptorSet writeDescriptorSet{};
writeDescriptorSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writeDescriptorSet.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
writeDescriptorSet.descriptorCount = 1;
writeDescriptorSet.dstSet = descriptorset;
writeDescriptorSet.dstBinding = 0;
writeDescriptorSet.pImageInfo = &textures.environmentCube.descriptor;
vkUpdateDescriptorSets(device, 1, &writeDescriptorSet, 0, nullptr);
2023-05-25 16:13:37 +08:00
2023-06-06 11:40:59 +08:00
// Pipeline layout
VkPipelineLayout pipelinelayout;
VkPushConstantRange pushConstantRange{};
pushConstantRange.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
switch (target) {
case IRRADIANCE:
pushConstantRange.size = sizeof(IrradiancePushBlock);
break;
case PREFILTEREDENV:
pushConstantRange.size = sizeof(PrefilterPushBlock);
break;
};
2023-05-25 16:13:37 +08:00
2023-06-06 11:40:59 +08:00
VkPipelineLayoutCreateInfo pipelineLayoutCI{};
pipelineLayoutCI.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipelineLayoutCI.setLayoutCount = 1;
pipelineLayoutCI.pSetLayouts = &descriptorsetlayout;
pipelineLayoutCI.pushConstantRangeCount = 1;
pipelineLayoutCI.pPushConstantRanges = &pushConstantRange;
VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCI, nullptr, &pipelinelayout));
// Pipeline
VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCI{};
inputAssemblyStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
inputAssemblyStateCI.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
VkPipelineRasterizationStateCreateInfo rasterizationStateCI{};
rasterizationStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rasterizationStateCI.polygonMode = VK_POLYGON_MODE_FILL;
rasterizationStateCI.cullMode = VK_CULL_MODE_NONE;
rasterizationStateCI.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
rasterizationStateCI.lineWidth = 1.0f;
2023-05-25 16:13:37 +08:00
2023-06-06 11:40:59 +08:00
VkPipelineColorBlendAttachmentState blendAttachmentState{};
blendAttachmentState.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
blendAttachmentState.blendEnable = VK_FALSE;
VkPipelineColorBlendStateCreateInfo colorBlendStateCI{};
colorBlendStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
colorBlendStateCI.attachmentCount = 1;
colorBlendStateCI.pAttachments = &blendAttachmentState;
VkPipelineDepthStencilStateCreateInfo depthStencilStateCI{};
depthStencilStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
depthStencilStateCI.depthTestEnable = VK_FALSE;
depthStencilStateCI.depthWriteEnable = VK_FALSE;
depthStencilStateCI.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL;
depthStencilStateCI.front = depthStencilStateCI.back;
depthStencilStateCI.back.compareOp = VK_COMPARE_OP_ALWAYS;
VkPipelineViewportStateCreateInfo viewportStateCI{};
viewportStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewportStateCI.viewportCount = 1;
viewportStateCI.scissorCount = 1;
VkPipelineMultisampleStateCreateInfo multisampleStateCI{};
multisampleStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
multisampleStateCI.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
std::vector<VkDynamicState> dynamicStateEnables = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR };
VkPipelineDynamicStateCreateInfo dynamicStateCI{};
dynamicStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
dynamicStateCI.pDynamicStates = dynamicStateEnables.data();
dynamicStateCI.dynamicStateCount = static_cast<uint32_t>(dynamicStateEnables.size());
// Vertex input state
VkVertexInputBindingDescription vertexInputBinding = { 0, sizeof(glTFModel::Model::Vertex), VK_VERTEX_INPUT_RATE_VERTEX };
VkVertexInputAttributeDescription vertexInputAttribute = { 0, 0, VK_FORMAT_R32G32B32_SFLOAT, 0 };
VkPipelineVertexInputStateCreateInfo vertexInputStateCI{};
vertexInputStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vertexInputStateCI.vertexBindingDescriptionCount = 1;
vertexInputStateCI.pVertexBindingDescriptions = &vertexInputBinding;
vertexInputStateCI.vertexAttributeDescriptionCount = 1;
vertexInputStateCI.pVertexAttributeDescriptions = &vertexInputAttribute;
std::array<VkPipelineShaderStageCreateInfo, 2> shaderStages;
VkGraphicsPipelineCreateInfo pipelineCI{};
pipelineCI.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipelineCI.layout = pipelinelayout;
pipelineCI.renderPass = renderpass;
pipelineCI.pInputAssemblyState = &inputAssemblyStateCI;
pipelineCI.pVertexInputState = &vertexInputStateCI;
pipelineCI.pRasterizationState = &rasterizationStateCI;
pipelineCI.pColorBlendState = &colorBlendStateCI;
pipelineCI.pMultisampleState = &multisampleStateCI;
pipelineCI.pViewportState = &viewportStateCI;
pipelineCI.pDepthStencilState = &depthStencilStateCI;
pipelineCI.pDynamicState = &dynamicStateCI;
pipelineCI.stageCount = 2;
pipelineCI.pStages = shaderStages.data();
pipelineCI.renderPass = renderpass;
2023-06-06 17:18:45 +08:00
shaderStages[0] = loadShader(device,filePath.filterVertShaderPath, VK_SHADER_STAGE_VERTEX_BIT);
2023-06-06 11:40:59 +08:00
switch (target) {
case IRRADIANCE:
2023-06-06 17:18:45 +08:00
shaderStages[1] = loadShader(device,filePath.irradianceFragShaderPath, VK_SHADER_STAGE_FRAGMENT_BIT);
2023-06-06 11:40:59 +08:00
break;
case PREFILTEREDENV:
2023-06-06 17:18:45 +08:00
shaderStages[1] = loadShader(device,filePath.prefilterEnvmapFragShaderPath, VK_SHADER_STAGE_FRAGMENT_BIT);
2023-06-06 11:40:59 +08:00
break;
2023-05-25 16:13:37 +08:00
};
2023-06-06 11:40:59 +08:00
VkPipeline pipeline;
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipeline));
for (auto shaderStage : shaderStages) {
vkDestroyShaderModule(device, shaderStage.module, nullptr);
}
2023-05-24 16:10:36 +08:00
2023-06-06 11:40:59 +08:00
// Render cubemap
VkClearValue clearValues[1];
clearValues[0].color = { { 0.0f, 0.0f, 0.2f, 0.0f } };
VkRenderPassBeginInfo renderPassBeginInfo{};
renderPassBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
renderPassBeginInfo.renderPass = renderpass;
renderPassBeginInfo.framebuffer = offscreen.framebuffer;
renderPassBeginInfo.renderArea.extent.width = dim;
renderPassBeginInfo.renderArea.extent.height = dim;
renderPassBeginInfo.clearValueCount = 1;
renderPassBeginInfo.pClearValues = clearValues;
std::vector<glm::mat4> matrices = {
glm::rotate(glm::rotate(glm::mat4(1.0f), glm::radians(90.0f), glm::vec3(0.0f, 1.0f, 0.0f)), glm::radians(180.0f), glm::vec3(1.0f, 0.0f, 0.0f)),
glm::rotate(glm::rotate(glm::mat4(1.0f), glm::radians(-90.0f), glm::vec3(0.0f, 1.0f, 0.0f)), glm::radians(180.0f), glm::vec3(1.0f, 0.0f, 0.0f)),
glm::rotate(glm::mat4(1.0f), glm::radians(-90.0f), glm::vec3(1.0f, 0.0f, 0.0f)),
glm::rotate(glm::mat4(1.0f), glm::radians(90.0f), glm::vec3(1.0f, 0.0f, 0.0f)),
glm::rotate(glm::mat4(1.0f), glm::radians(180.0f), glm::vec3(1.0f, 0.0f, 0.0f)),
glm::rotate(glm::mat4(1.0f), glm::radians(180.0f), glm::vec3(0.0f, 0.0f, 1.0f)),
};
2023-05-24 16:10:36 +08:00
2023-06-06 11:40:59 +08:00
VkCommandBuffer cmdBuf = vulkanDevice->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, false);
2023-05-24 16:10:36 +08:00
2023-06-06 11:40:59 +08:00
VkViewport viewport{};
viewport.width = (float)dim;
viewport.height = (float)dim;
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
2023-05-24 16:10:36 +08:00
2023-06-06 11:40:59 +08:00
VkRect2D scissor{};
scissor.extent.width = dim;
scissor.extent.height = dim;
2023-05-24 16:10:36 +08:00
2023-06-06 11:40:59 +08:00
VkImageSubresourceRange subresourceRange{};
subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
subresourceRange.baseMipLevel = 0;
subresourceRange.levelCount = numMips;
subresourceRange.layerCount = 6;
2023-05-24 16:10:36 +08:00
2023-06-06 11:40:59 +08:00
// Change image layout for all cubemap faces to transfer destination
2023-05-24 16:10:36 +08:00
{
2023-06-06 11:40:59 +08:00
vulkanDevice->beginCommandBuffer(cmdBuf);
VkImageMemoryBarrier imageMemoryBarrier{};
imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
imageMemoryBarrier.image = cubemap.image;
imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
imageMemoryBarrier.srcAccessMask = 0;
imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
imageMemoryBarrier.subresourceRange = subresourceRange;
vkCmdPipelineBarrier(cmdBuf, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier);
vulkanDevice->flushCommandBuffer(cmdBuf, queue, false);
2023-05-24 16:10:36 +08:00
}
2023-06-06 11:40:59 +08:00
for (uint32_t m = 0; m < numMips; m++) {
for (uint32_t f = 0; f < 6; f++) {
2023-05-24 16:10:36 +08:00
2023-06-06 11:40:59 +08:00
vulkanDevice->beginCommandBuffer(cmdBuf);
2023-05-24 16:10:36 +08:00
2023-06-06 11:40:59 +08:00
viewport.width = static_cast<float>(dim * std::pow(0.5f, m));
viewport.height = static_cast<float>(dim * std::pow(0.5f, m));
vkCmdSetViewport(cmdBuf, 0, 1, &viewport);
vkCmdSetScissor(cmdBuf, 0, 1, &scissor);
2023-05-24 16:10:36 +08:00
2023-06-06 11:40:59 +08:00
// Render scene from cube face's point of view
vkCmdBeginRenderPass(cmdBuf, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
2023-05-24 16:10:36 +08:00
2023-06-06 11:40:59 +08:00
// Pass parameters for current pass using a push constant block
switch (target) {
case IRRADIANCE:
irradiancePushBlock.mvp = glm::perspective((float)(M_PI / 2.0), 1.0f, 0.1f, 512.0f) * matrices[f];
2023-06-06 15:52:39 +08:00
vkCmdPushConstants(cmdBuf, pipelinelayout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(IrradiancePushBlock), &irradiancePushBlock);
2023-06-06 11:40:59 +08:00
break;
case PREFILTEREDENV:
prefilterPushBlock.mvp = glm::perspective((float)(M_PI / 2.0), 1.0f, 0.1f, 512.0f) * matrices[f];
prefilterPushBlock.roughness = (float)m / (float)(numMips - 1);
2023-06-06 15:52:39 +08:00
vkCmdPushConstants(cmdBuf, pipelinelayout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(PrefilterPushBlock), &prefilterPushBlock);
2023-06-06 11:40:59 +08:00
break;
};
2023-05-24 16:10:36 +08:00
2023-06-06 11:40:59 +08:00
vkCmdBindPipeline(cmdBuf, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
vkCmdBindDescriptorSets(cmdBuf, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelinelayout, 0, 1, &descriptorset, 0, NULL);
2023-05-24 16:10:36 +08:00
2023-06-06 11:40:59 +08:00
VkDeviceSize offsets[1] = { 0 };
2023-05-24 16:10:36 +08:00
2023-06-06 11:40:59 +08:00
models.skybox.draw(cmdBuf);
2023-05-24 16:10:36 +08:00
2023-06-06 11:40:59 +08:00
vkCmdEndRenderPass(cmdBuf);
2023-05-24 16:10:36 +08:00
2023-06-06 11:40:59 +08:00
VkImageSubresourceRange subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
subresourceRange.baseMipLevel = 0;
subresourceRange.levelCount = numMips;
subresourceRange.layerCount = 6;
2023-05-24 16:10:36 +08:00
2023-06-06 11:40:59 +08:00
{
VkImageMemoryBarrier imageMemoryBarrier{};
imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
imageMemoryBarrier.image = offscreen.image;
imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
imageMemoryBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
imageMemoryBarrier.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
vkCmdPipelineBarrier(cmdBuf, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier);
}
2023-05-24 16:10:36 +08:00
2023-06-06 11:40:59 +08:00
// Copy region for transfer from framebuffer to cube face
VkImageCopy copyRegion{};
copyRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
copyRegion.srcSubresource.baseArrayLayer = 0;
copyRegion.srcSubresource.mipLevel = 0;
copyRegion.srcSubresource.layerCount = 1;
copyRegion.srcOffset = { 0, 0, 0 };
copyRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
copyRegion.dstSubresource.baseArrayLayer = f;
copyRegion.dstSubresource.mipLevel = m;
copyRegion.dstSubresource.layerCount = 1;
copyRegion.dstOffset = { 0, 0, 0 };
copyRegion.extent.width = static_cast<uint32_t>(viewport.width);
copyRegion.extent.height = static_cast<uint32_t>(viewport.height);
copyRegion.extent.depth = 1;
vkCmdCopyImage(
cmdBuf,
offscreen.image,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
cubemap.image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1,
&copyRegion);
{
VkImageMemoryBarrier imageMemoryBarrier{};
imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
imageMemoryBarrier.image = offscreen.image;
imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
imageMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
imageMemoryBarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
imageMemoryBarrier.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
vkCmdPipelineBarrier(cmdBuf, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier);
}
2023-05-24 16:10:36 +08:00
2023-06-06 11:40:59 +08:00
vulkanDevice->flushCommandBuffer(cmdBuf, queue, false);
}
}
2023-05-24 16:10:36 +08:00
2023-06-06 11:40:59 +08:00
{
vulkanDevice->beginCommandBuffer(cmdBuf);
VkImageMemoryBarrier imageMemoryBarrier{};
imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
imageMemoryBarrier.image = cubemap.image;
imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
imageMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
imageMemoryBarrier.dstAccessMask = VK_ACCESS_HOST_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT;
imageMemoryBarrier.subresourceRange = subresourceRange;
vkCmdPipelineBarrier(cmdBuf, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier);
vulkanDevice->flushCommandBuffer(cmdBuf, queue, false);
}
2023-05-24 16:10:36 +08:00
2023-06-06 11:40:59 +08:00
vkDestroyRenderPass(device, renderpass, nullptr);
vkDestroyFramebuffer(device, offscreen.framebuffer, nullptr);
vkFreeMemory(device, offscreen.memory, nullptr);
vkDestroyImageView(device, offscreen.view, nullptr);
vkDestroyImage(device, offscreen.image, nullptr);
vkDestroyDescriptorPool(device, descriptorpool, nullptr);
vkDestroyDescriptorSetLayout(device, descriptorsetlayout, nullptr);
vkDestroyPipeline(device, pipeline, nullptr);
vkDestroyPipelineLayout(device, pipelinelayout, nullptr);
cubemap.descriptor.imageView = cubemap.view;
cubemap.descriptor.sampler = cubemap.sampler;
cubemap.descriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
cubemap.device = vulkanDevice;
switch (target) {
case IRRADIANCE:
textures.irradianceCube = cubemap;
break;
case PREFILTEREDENV:
textures.prefilteredCube = cubemap;
shaderData.prefilteredCubeMipLevels = static_cast<float>(numMips);
break;
};
auto tEnd = std::chrono::high_resolution_clock::now();
auto tDiff = std::chrono::duration<double, std::milli>(tEnd - tStart).count();
std::cout << "Generating cube map with " << numMips << " mip levels took " << tDiff << " ms" << std::endl;
2023-05-24 16:10:36 +08:00
}
}
2023-06-06 15:52:39 +08:00
// generate BRDF integration map for roughness/NdotV
void PlumageRender::generateBRDFLUT()
2023-05-24 16:10:36 +08:00
{
auto tStart = std::chrono::high_resolution_clock::now();
2023-06-06 11:40:59 +08:00
const VkFormat format = VK_FORMAT_R16G16_SFLOAT;
const int32_t dim = 512;
2023-05-24 16:10:36 +08:00
// Image
2023-06-06 11:40:59 +08:00
VkImageCreateInfo imageCI{};
imageCI.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
2023-05-24 16:10:36 +08:00
imageCI.imageType = VK_IMAGE_TYPE_2D;
imageCI.format = format;
imageCI.extent.width = dim;
imageCI.extent.height = dim;
imageCI.extent.depth = 1;
imageCI.mipLevels = 1;
imageCI.arrayLayers = 1;
imageCI.samples = VK_SAMPLE_COUNT_1_BIT;
imageCI.tiling = VK_IMAGE_TILING_OPTIMAL;
imageCI.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
2023-06-06 11:40:59 +08:00
VK_CHECK_RESULT(vkCreateImage(device, &imageCI, nullptr, &textures.lutBrdf.image));
2023-05-24 16:10:36 +08:00
VkMemoryRequirements memReqs;
2023-06-06 11:40:59 +08:00
vkGetImageMemoryRequirements(device, textures.lutBrdf.image, &memReqs);
VkMemoryAllocateInfo memAllocInfo{};
memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
memAllocInfo.allocationSize = memReqs.size;
memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
VK_CHECK_RESULT(vkAllocateMemory(device, &memAllocInfo, nullptr, &textures.lutBrdf.deviceMemory));
VK_CHECK_RESULT(vkBindImageMemory(device, textures.lutBrdf.image, textures.lutBrdf.deviceMemory, 0));
// View
VkImageViewCreateInfo viewCI{};
viewCI.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
2023-05-24 16:10:36 +08:00
viewCI.viewType = VK_IMAGE_VIEW_TYPE_2D;
viewCI.format = format;
viewCI.subresourceRange = {};
viewCI.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
viewCI.subresourceRange.levelCount = 1;
viewCI.subresourceRange.layerCount = 1;
2023-06-06 11:40:59 +08:00
viewCI.image = textures.lutBrdf.image;
VK_CHECK_RESULT(vkCreateImageView(device, &viewCI, nullptr, &textures.lutBrdf.view));
2023-05-24 16:10:36 +08:00
// Sampler
2023-06-06 11:40:59 +08:00
VkSamplerCreateInfo samplerCI{};
samplerCI.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
2023-05-24 16:10:36 +08:00
samplerCI.magFilter = VK_FILTER_LINEAR;
samplerCI.minFilter = VK_FILTER_LINEAR;
samplerCI.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
samplerCI.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
samplerCI.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
samplerCI.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
samplerCI.minLod = 0.0f;
samplerCI.maxLod = 1.0f;
2023-06-06 11:40:59 +08:00
samplerCI.maxAnisotropy = 1.0f;
2023-05-24 16:10:36 +08:00
samplerCI.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;
2023-06-06 11:40:59 +08:00
VK_CHECK_RESULT(vkCreateSampler(device, &samplerCI, nullptr, &textures.lutBrdf.sampler));
2023-05-24 16:10:36 +08:00
// FB, Att, RP, Pipe, etc.
2023-06-06 11:40:59 +08:00
VkAttachmentDescription attDesc{};
2023-05-24 16:10:36 +08:00
// Color attachment
attDesc.format = format;
attDesc.samples = VK_SAMPLE_COUNT_1_BIT;
attDesc.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
attDesc.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
attDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
attDesc.finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
VkAttachmentReference colorReference = { 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
2023-06-06 11:40:59 +08:00
VkSubpassDescription subpassDescription{};
2023-05-24 16:10:36 +08:00
subpassDescription.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpassDescription.colorAttachmentCount = 1;
subpassDescription.pColorAttachments = &colorReference;
// 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_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;
2023-06-06 11:40:59 +08:00
// Create the actual renderpass
VkRenderPassCreateInfo renderPassCI{};
renderPassCI.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
2023-05-24 16:10:36 +08:00
renderPassCI.attachmentCount = 1;
renderPassCI.pAttachments = &attDesc;
renderPassCI.subpassCount = 1;
renderPassCI.pSubpasses = &subpassDescription;
renderPassCI.dependencyCount = 2;
renderPassCI.pDependencies = dependencies.data();
VkRenderPass renderpass;
VK_CHECK_RESULT(vkCreateRenderPass(device, &renderPassCI, nullptr, &renderpass));
2023-06-06 11:40:59 +08:00
VkFramebufferCreateInfo framebufferCI{};
framebufferCI.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
2023-05-24 16:10:36 +08:00
framebufferCI.renderPass = renderpass;
framebufferCI.attachmentCount = 1;
2023-06-06 11:40:59 +08:00
framebufferCI.pAttachments = &textures.lutBrdf.view;
2023-05-24 16:10:36 +08:00
framebufferCI.width = dim;
framebufferCI.height = dim;
framebufferCI.layers = 1;
VkFramebuffer framebuffer;
VK_CHECK_RESULT(vkCreateFramebuffer(device, &framebufferCI, nullptr, &framebuffer));
2023-06-06 11:40:59 +08:00
// Desriptors
2023-05-24 16:10:36 +08:00
VkDescriptorSetLayout descriptorsetlayout;
2023-06-06 11:40:59 +08:00
VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCI{};
descriptorSetLayoutCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorSetLayoutCI, nullptr, &descriptorsetlayout));
2023-05-24 16:10:36 +08:00
// Pipeline layout
VkPipelineLayout pipelinelayout;
2023-06-06 11:40:59 +08:00
VkPipelineLayoutCreateInfo pipelineLayoutCI{};
pipelineLayoutCI.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipelineLayoutCI.setLayoutCount = 1;
pipelineLayoutCI.pSetLayouts = &descriptorsetlayout;
2023-05-24 16:10:36 +08:00
VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCI, nullptr, &pipelinelayout));
// Pipeline
2023-06-06 11:40:59 +08:00
VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCI{};
inputAssemblyStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
inputAssemblyStateCI.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
VkPipelineRasterizationStateCreateInfo rasterizationStateCI{};
rasterizationStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rasterizationStateCI.polygonMode = VK_POLYGON_MODE_FILL;
rasterizationStateCI.cullMode = VK_CULL_MODE_NONE;
rasterizationStateCI.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
rasterizationStateCI.lineWidth = 1.0f;
VkPipelineColorBlendAttachmentState blendAttachmentState{};
blendAttachmentState.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
blendAttachmentState.blendEnable = VK_FALSE;
VkPipelineColorBlendStateCreateInfo colorBlendStateCI{};
colorBlendStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
colorBlendStateCI.attachmentCount = 1;
colorBlendStateCI.pAttachments = &blendAttachmentState;
VkPipelineDepthStencilStateCreateInfo depthStencilStateCI{};
depthStencilStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
depthStencilStateCI.depthTestEnable = VK_FALSE;
depthStencilStateCI.depthWriteEnable = VK_FALSE;
depthStencilStateCI.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL;
depthStencilStateCI.front = depthStencilStateCI.back;
depthStencilStateCI.back.compareOp = VK_COMPARE_OP_ALWAYS;
VkPipelineViewportStateCreateInfo viewportStateCI{};
viewportStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewportStateCI.viewportCount = 1;
viewportStateCI.scissorCount = 1;
VkPipelineMultisampleStateCreateInfo multisampleStateCI{};
multisampleStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
multisampleStateCI.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
2023-05-24 16:10:36 +08:00
std::vector<VkDynamicState> dynamicStateEnables = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR };
2023-06-06 11:40:59 +08:00
VkPipelineDynamicStateCreateInfo dynamicStateCI{};
dynamicStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
dynamicStateCI.pDynamicStates = dynamicStateEnables.data();
dynamicStateCI.dynamicStateCount = static_cast<uint32_t>(dynamicStateEnables.size());
VkPipelineVertexInputStateCreateInfo emptyInputStateCI{};
emptyInputStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
2023-05-24 16:10:36 +08:00
std::array<VkPipelineShaderStageCreateInfo, 2> shaderStages;
2023-06-06 11:40:59 +08:00
VkGraphicsPipelineCreateInfo pipelineCI{};
pipelineCI.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipelineCI.layout = pipelinelayout;
pipelineCI.renderPass = renderpass;
pipelineCI.pInputAssemblyState = &inputAssemblyStateCI;
pipelineCI.pVertexInputState = &emptyInputStateCI;
pipelineCI.pRasterizationState = &rasterizationStateCI;
pipelineCI.pColorBlendState = &colorBlendStateCI;
pipelineCI.pMultisampleState = &multisampleStateCI;
pipelineCI.pViewportState = &viewportStateCI;
pipelineCI.pDepthStencilState = &depthStencilStateCI;
pipelineCI.pDynamicState = &dynamicStateCI;
2023-05-24 16:10:36 +08:00
pipelineCI.stageCount = 2;
pipelineCI.pStages = shaderStages.data();
2023-06-06 11:40:59 +08:00
// Look-up-table (from BRDF) pipeline
shaderStages = {
2023-06-06 17:18:45 +08:00
loadShader(device,filePath.brdfVertShaderPath, VK_SHADER_STAGE_VERTEX_BIT),
loadShader(device,filePath.brdfFragShaderPath, VK_SHADER_STAGE_FRAGMENT_BIT)
2023-06-06 11:40:59 +08:00
};
2023-05-24 16:10:36 +08:00
VkPipeline pipeline;
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipeline));
2023-06-06 11:40:59 +08:00
for (auto shaderStage : shaderStages) {
vkDestroyShaderModule(device, shaderStage.module, nullptr);
}
2023-05-24 16:10:36 +08:00
// Render
VkClearValue clearValues[1];
clearValues[0].color = { { 0.0f, 0.0f, 0.0f, 1.0f } };
2023-06-06 11:40:59 +08:00
VkRenderPassBeginInfo renderPassBeginInfo{};
renderPassBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
2023-05-24 16:10:36 +08:00
renderPassBeginInfo.renderPass = renderpass;
renderPassBeginInfo.renderArea.extent.width = dim;
renderPassBeginInfo.renderArea.extent.height = dim;
renderPassBeginInfo.clearValueCount = 1;
renderPassBeginInfo.pClearValues = clearValues;
renderPassBeginInfo.framebuffer = framebuffer;
VkCommandBuffer cmdBuf = vulkanDevice->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);
vkCmdBeginRenderPass(cmdBuf, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
2023-06-06 11:40:59 +08:00
VkViewport viewport{};
viewport.width = (float)dim;
viewport.height = (float)dim;
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
VkRect2D scissor{};
scissor.extent.width = dim;
scissor.extent.height = dim;
2023-05-24 16:10:36 +08:00
vkCmdSetViewport(cmdBuf, 0, 1, &viewport);
vkCmdSetScissor(cmdBuf, 0, 1, &scissor);
vkCmdBindPipeline(cmdBuf, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
vkCmdDraw(cmdBuf, 3, 1, 0, 0);
vkCmdEndRenderPass(cmdBuf);
vulkanDevice->flushCommandBuffer(cmdBuf, queue);
vkQueueWaitIdle(queue);
vkDestroyPipeline(device, pipeline, nullptr);
vkDestroyPipelineLayout(device, pipelinelayout, nullptr);
vkDestroyRenderPass(device, renderpass, nullptr);
vkDestroyFramebuffer(device, framebuffer, nullptr);
vkDestroyDescriptorSetLayout(device, descriptorsetlayout, nullptr);
2023-06-06 11:40:59 +08:00
textures.lutBrdf.descriptor.imageView = textures.lutBrdf.view;
textures.lutBrdf.descriptor.sampler = textures.lutBrdf.sampler;
textures.lutBrdf.descriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
textures.lutBrdf.device = vulkanDevice;
2023-05-24 16:10:36 +08:00
auto tEnd = std::chrono::high_resolution_clock::now();
auto tDiff = std::chrono::duration<double, std::milli>(tEnd - tStart).count();
std::cout << "Generating BRDF LUT took " << tDiff << " ms" << std::endl;
2023-05-25 16:13:37 +08:00
}
2023-06-06 15:52:39 +08:00
2023-05-17 14:49:05 +08:00
// Prepare and initialize uniform buffer containing shader uniforms
2023-06-02 09:56:49 +08:00
void PlumageRender::prepareUniformBuffers()
2023-05-17 14:49:05 +08:00
{
2023-06-06 15:52:39 +08:00
for (auto& uniformBuffer : uniformBuffers) {
uniformBuffer.scene.create(vulkanDevice, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, sizeof(shaderDataScene));
uniformBuffer.skybox.create(vulkanDevice, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, sizeof(shaderDataSkybox));
uniformBuffer.params.create(vulkanDevice, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, sizeof(shaderData));
2023-05-25 16:13:37 +08:00
}
2023-05-17 14:49:05 +08:00
updateUniformBuffers();
}
2023-06-02 09:56:49 +08:00
void PlumageRender::updateUniformBuffers()
2023-05-17 14:49:05 +08:00
{
2023-06-06 15:52:39 +08:00
// Scene
shaderDataScene.projection = camera.matrices.perspective;
shaderDataScene.view = camera.matrices.view;
// Center and scale model
float scale = (1.0f / std::max(models.scene.aabb[0][0], std::max(models.scene.aabb[1][1], models.scene.aabb[2][2]))) * 0.5f;
glm::vec3 translate = -glm::vec3(models.scene.aabb[3][0], models.scene.aabb[3][1], models.scene.aabb[3][2]);
translate += -0.5f * glm::vec3(models.scene.aabb[0][0], models.scene.aabb[1][1], models.scene.aabb[2][2]);
shaderDataScene.model = glm::mat4(1.0f);
shaderDataScene.model[0][0] = scale;
shaderDataScene.model[1][1] = scale;
shaderDataScene.model[2][2] = scale;
shaderDataScene.model = glm::translate(shaderDataScene.model, translate);
shaderDataScene.camPos = glm::vec3(
-camera.position.z * sin(glm::radians(camera.rotation.y)) * cos(glm::radians(camera.rotation.x)),
-camera.position.z * sin(glm::radians(camera.rotation.x)),
camera.position.z * cos(glm::radians(camera.rotation.y)) * cos(glm::radians(camera.rotation.x))
);
// Skybox
shaderDataSkybox.projection = camera.matrices.perspective;
shaderDataSkybox.view = camera.matrices.view;
shaderDataSkybox.model = glm::mat4(glm::mat3(camera.matrices.view));
}
void PlumageRender::updateShaderData()
{
shaderData.lightDir = glm::vec4(
sin(glm::radians(lightSource.rotation.x)) * cos(glm::radians(lightSource.rotation.y)),
sin(glm::radians(lightSource.rotation.y)),
cos(glm::radians(lightSource.rotation.x)) * cos(glm::radians(lightSource.rotation.y)),
0.0f);
}
void PlumageRender::windowResized()
{
buildCommandBuffers();
vkDeviceWaitIdle(device);
updateUniformBuffers();
// update UI
//updateUIOverlay();
2023-05-17 14:49:05 +08:00
}
2023-06-02 09:56:49 +08:00
void PlumageRender::prepare()
2023-05-17 14:49:05 +08:00
{
VulkanExampleBase::prepare();
2023-06-06 15:52:39 +08:00
camera.type = Camera::CameraType::lookat;
camera.setPerspective(45.0f, (float)width / (float)height, 0.1f, 256.0f);
camera.rotationSpeed = 0.25f;
camera.movementSpeed = 0.1f;
camera.setPosition({ 0.0f, 0.0f, 1.0f });
camera.setRotation({ 0.0f, 0.0f, 0.0f });
waitFences.resize(renderAhead);
presentCompleteSemaphores.resize(renderAhead);
renderCompleteSemaphores.resize(renderAhead);
commandBuffers.resize(swapChain.imageCount);
uniformBuffers.resize(swapChain.imageCount);
descriptorSets.resize(swapChain.imageCount);
// Command buffer execution fences
for (auto& waitFence : waitFences) {
VkFenceCreateInfo fenceCI{ VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, nullptr, VK_FENCE_CREATE_SIGNALED_BIT };
VK_CHECK_RESULT(vkCreateFence(device, &fenceCI, nullptr, &waitFence));
}
// Queue ordering semaphores
for (auto& semaphore : presentCompleteSemaphores) {
VkSemaphoreCreateInfo semaphoreCI{ VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, nullptr, 0 };
VK_CHECK_RESULT(vkCreateSemaphore(device, &semaphoreCI, nullptr, &semaphore));
}
for (auto& semaphore : renderCompleteSemaphores) {
VkSemaphoreCreateInfo semaphoreCI{ VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, nullptr, 0 };
VK_CHECK_RESULT(vkCreateSemaphore(device, &semaphoreCI, nullptr, &semaphore));
}
// Command buffers
{
VkCommandBufferAllocateInfo cmdBufAllocateInfo{};
cmdBufAllocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
cmdBufAllocateInfo.commandPool = cmdPool;
cmdBufAllocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
cmdBufAllocateInfo.commandBufferCount = static_cast<uint32_t>(commandBuffers.size());
VK_CHECK_RESULT(vkAllocateCommandBuffers(device, &cmdBufAllocateInfo, commandBuffers.data()));
}
2023-05-17 14:49:05 +08:00
loadAssets();
2023-06-06 15:52:39 +08:00
generateBRDFLUT();
generateCubemaps();
2023-05-17 14:49:05 +08:00
prepareUniformBuffers();
setupDescriptors();
preparePipelines();
2023-06-06 15:52:39 +08:00
//ui = new UI(vulkanDevice, renderPass, queue, pipelineCache, settings.sampleCount);
//updateOverlay();
2023-05-17 14:49:05 +08:00
buildCommandBuffers();
2023-06-06 15:52:39 +08:00
2023-05-17 14:49:05 +08:00
prepared = true;
}
2023-06-02 09:56:49 +08:00
void PlumageRender::render()
2023-05-17 14:49:05 +08:00
{
2023-06-06 15:52:39 +08:00
if (!prepared) {
return;
}
//updateOverlay();
VK_CHECK_RESULT(vkWaitForFences(device, 1, &waitFences[frameIndex], VK_TRUE, UINT64_MAX));
VK_CHECK_RESULT(vkResetFences(device, 1, &waitFences[frameIndex]));
VkResult acquire = swapChain.acquireNextImage(presentCompleteSemaphores[frameIndex], &currentBuffer);
if ((acquire == VK_ERROR_OUT_OF_DATE_KHR) || (acquire == VK_SUBOPTIMAL_KHR)) {
windowResize();
}
else {
VK_CHECK_RESULT(acquire);
}
// Update UBOs
updateUniformBuffers();
UniformBufferSet currentUB = uniformBuffers[currentBuffer];
memcpy(currentUB.scene.mapped, &shaderDataScene, sizeof(shaderDataScene));
memcpy(currentUB.params.mapped, &shaderData, sizeof(shaderData));
memcpy(currentUB.skybox.mapped, &shaderDataSkybox, sizeof(shaderDataSkybox));
const VkPipelineStageFlags waitDstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
VkSubmitInfo submitInfo{};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfo.pWaitDstStageMask = &waitDstStageMask;
submitInfo.pWaitSemaphores = &presentCompleteSemaphores[frameIndex];
submitInfo.waitSemaphoreCount = 1;
submitInfo.pSignalSemaphores = &renderCompleteSemaphores[frameIndex];
submitInfo.signalSemaphoreCount = 1;
submitInfo.pCommandBuffers = &commandBuffers[currentBuffer];
submitInfo.commandBufferCount = 1;
VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, waitFences[frameIndex]));
VkResult present = swapChain.queuePresent(queue, currentBuffer, renderCompleteSemaphores[frameIndex]);
if (!((present == VK_SUCCESS) || (present == VK_SUBOPTIMAL_KHR))) {
if (present == VK_ERROR_OUT_OF_DATE_KHR) {
windowResize();
return;
}
else {
VK_CHECK_RESULT(present);
}
}
frameIndex += 1;
frameIndex %= renderAhead;
if (!paused) {
if (rotateModel) {
modelrot.y += frameTimer * 35.0f;
if (modelrot.y > 360.0f) {
modelrot.y -= 360.0f;
}
}
if ((animate) && (models.scene.animations.size() > 0)) {
animationTimer += frameTimer;
if (animationTimer > models.scene.animations[animationIndex].end) {
animationTimer -= models.scene.animations[animationIndex].end;
}
models.scene.updateAnimation(animationIndex, animationTimer);
}
updateShaderData();
if (rotateModel) {
updateUniformBuffers();
}
}
2023-05-17 14:49:05 +08:00
if (camera.updated) {
updateUniformBuffers();
}
2023-05-17 14:49:05 +08:00
}
2023-06-02 09:56:49 +08:00
void PlumageRender::viewChanged()
2023-05-17 14:49:05 +08:00
{
updateUniformBuffers();
}
2023-06-02 09:56:49 +08:00
void PlumageRender::OnUpdateUIOverlay(vks::UIOverlay *overlay)
2023-05-17 14:49:05 +08:00
{
2023-06-01 16:41:14 +08:00
GUIFunction guiFunc{};
2023-05-17 14:49:05 +08:00
if (overlay->header("Settings")) {
if (overlay->checkBox("Wireframe", &wireframe)) {
buildCommandBuffers();
}
2023-05-25 16:13:37 +08:00
if (overlay->checkBox("NormalMapping", &normalMapping))
{
}
if (overlay->checkBox("ToneMapping", &ToneMapping))
{
CreateToneMappingPipeline();
}
if (overlay->checkBox("PbrIndirect", &pbrEnabled))
{
}
2023-05-17 14:49:05 +08:00
}
2023-06-01 09:32:07 +08:00
2023-05-18 11:49:09 +08:00
if (overlay->header("Animation"))
{
2023-05-25 16:13:37 +08:00
overlay->checkBox("Pause", &paused);
2023-05-17 14:49:05 +08:00
}
2023-06-01 09:32:07 +08:00
if (overlay->header("file"))
{
if (overlay->button("select model"))
{
2023-06-01 16:41:14 +08:00
2023-06-01 10:48:46 +08:00
std::string strModelFilePath;
strModelFilePath = guiFunc.openFileFolderDialog();
filePath.glTFModelFilePath = strModelFilePath;
2023-06-02 14:29:20 +08:00
loadAssets();
2023-06-01 16:41:14 +08:00
}
if(overlay->button("select vertex Shader"))
2023-06-01 16:41:14 +08:00
{
std::string strFilePath;
strFilePath = guiFunc.openFileFolderDialog();
2023-06-01 17:26:15 +08:00
filePath.modelVertShaderPath = strFilePath;
2023-06-01 16:41:14 +08:00
loadAssets();
updateUniformBuffers();
}
if (overlay->button("select fragment Shader"))
{
2023-06-01 16:41:14 +08:00
std::string strFilePath;
strFilePath = guiFunc.openFileFolderDialog();
filePath.modelFragShaderPath = strFilePath;
loadAssets();
updateUniformBuffers();
}
}
2023-05-17 14:49:05 +08:00
}
2023-05-25 16:27:50 +08:00
2023-05-17 14:49:05 +08:00
2023-06-06 17:18:45 +08:00
PlumageRender* plumageRender;
// OS specific macros for the example main entry points
#if defined(_WIN32)
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if (plumageRender != NULL)
{
plumageRender->handleMessages(hWnd, uMsg, wParam, lParam);
}
return (DefWindowProc(hWnd, uMsg, wParam, lParam));
}
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int)
{
for (int32_t i = 0; i < __argc; i++) { PlumageRender::args.push_back(__argv[i]); };
plumageRender = new PlumageRender();
plumageRender->initVulkan();
plumageRender->setupWindow(hInstance, WndProc);
plumageRender->prepare();
plumageRender->renderLoop();
delete(plumageRender);
return 0;
}
#endif