1847 lines
78 KiB
C++
1847 lines
78 KiB
C++
|
|
|
|
|
|
#ifndef TINYGLTF_IMPLEMENTATION
|
|
#define TINYGLTF_IMPLEMENTATION
|
|
#endif
|
|
#ifndef STB_IMAGE_IMPLEMENTATION
|
|
#define STB_IMAGE_IMPLEMENTATION
|
|
#endif
|
|
|
|
#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
|
|
#define TINYGLTF_NO_STB_IMAGE_WRITE
|
|
#endif
|
|
|
|
|
|
#include "render.h"
|
|
//#include "VulkanUtils.hpp"
|
|
//#include "assetLoader.h"
|
|
|
|
|
|
|
|
PlumageRender::PlumageRender()
|
|
{
|
|
title = "plumage render";
|
|
|
|
}
|
|
|
|
|
|
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;
|
|
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);
|
|
}
|
|
}
|
|
|
|
void PlumageRender::buildCommandBuffers()
|
|
{
|
|
VkCommandBufferBeginInfo cmdBufferBeginInfo{};
|
|
cmdBufferBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
|
|
|
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 };
|
|
}
|
|
|
|
VkRenderPassBeginInfo renderPassBeginInfo{};
|
|
renderPassBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
|
|
renderPassBeginInfo.renderPass = renderPass;
|
|
renderPassBeginInfo.renderArea.offset.x = 0;
|
|
renderPassBeginInfo.renderArea.offset.y = 0;
|
|
renderPassBeginInfo.renderArea.extent.width = width;
|
|
renderPassBeginInfo.renderArea.extent.height = height;
|
|
renderPassBeginInfo.clearValueCount = settings.multiSampling ? 3 : 2;
|
|
renderPassBeginInfo.pClearValues = clearValues;
|
|
|
|
for (uint32_t i = 0; i < commandBuffers.size(); ++i) {
|
|
renderPassBeginInfo.framebuffer = frameBuffers[i];
|
|
|
|
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);
|
|
}
|
|
|
|
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
|
|
gui->draw(currentCB);
|
|
|
|
vkCmdEndRenderPass(currentCB);
|
|
VK_CHECK_RESULT(vkEndCommandBuffer(currentCB));
|
|
}
|
|
}
|
|
|
|
|
|
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();
|
|
}
|
|
|
|
void PlumageRender::loadAssets()
|
|
{
|
|
const std::string assetpath = getAssetPath();
|
|
|
|
if (_access(assetpath.c_str(),0) != 0) {
|
|
std::string msg = "Could not locate asset path in \"" + assetpath + "\".\nMake sure binary is run from correct relative directory!";
|
|
std::cerr << msg << std::endl;
|
|
system("pause");
|
|
//exit(-1);
|
|
}
|
|
else {
|
|
std::string msg = "asset path get " + assetpath;
|
|
std::cout << msg << std::endl;
|
|
}
|
|
|
|
readDirectory(assetpath + "environments", "*.ktx", environments, false);
|
|
|
|
textures.empty.loadFromFile(PlumageRender::filePath.emptyEnvmapFilePath, VK_FORMAT_R8G8B8A8_UNORM, vulkanDevice, queue);
|
|
|
|
std::string sceneFile = filePath.glTFModelFilePath;
|
|
std::string envMapFile = filePath.envMapFilePath;
|
|
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(PlumageRender::filePath.skyboxModleFilePath, vulkanDevice, queue);
|
|
|
|
loadEnvironment(envMapFile.c_str());
|
|
}
|
|
|
|
void PlumageRender::setupNodeDescriptorSet(glTFModel::Node* node)
|
|
{
|
|
/*
|
|
This sample uses separate descriptor sets (and layouts) for the matrices and materials (textures)
|
|
*/
|
|
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);
|
|
}
|
|
for (auto& child : node->children) {
|
|
setupNodeDescriptorSet(child);
|
|
}
|
|
}
|
|
|
|
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++;
|
|
}
|
|
}
|
|
}
|
|
|
|
std::vector<VkDescriptorPoolSize> poolSizes = {
|
|
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, (4 + meshCount) * swapChain.imageCount },
|
|
{ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, imageSamplerCount * swapChain.imageCount }
|
|
};
|
|
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));
|
|
|
|
/*
|
|
Descriptor sets
|
|
*/
|
|
|
|
// Scene (matrices and environment maps)
|
|
{
|
|
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 },
|
|
};
|
|
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);
|
|
}
|
|
}
|
|
|
|
// 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 },
|
|
};
|
|
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;
|
|
}
|
|
}
|
|
|
|
if (material.pbrWorkflows.specularGlossiness) {
|
|
if (material.extension.diffuseTexture) {
|
|
imageDescriptors[0] = material.extension.diffuseTexture->descriptor;
|
|
}
|
|
if (material.extension.specularGlossinessTexture) {
|
|
imageDescriptors[1] = material.extension.specularGlossinessTexture->descriptor;
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
// Model node (matrices)
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
}
|
|
|
|
void PlumageRender::preparePipelines()
|
|
{
|
|
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
|
|
};
|
|
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
|
|
};
|
|
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;
|
|
vertexInputStateCI.vertexAttributeDescriptionCount = static_cast<uint32_t>(vertexInputAttributes.size());
|
|
vertexInputStateCI.pVertexAttributeDescriptions = vertexInputAttributes.data();
|
|
|
|
// Pipelines
|
|
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 = static_cast<uint32_t>(shaderStages.size());
|
|
pipelineCI.pStages = shaderStages.data();
|
|
|
|
if (settings.multiSampling) {
|
|
multisampleStateCI.rasterizationSamples = settings.sampleCount;
|
|
}
|
|
|
|
// Skybox pipeline (background cube)
|
|
shaderStages = {
|
|
loadShader(device,PlumageRender::filePath.skyboxVertShaderPath, VK_SHADER_STAGE_VERTEX_BIT),
|
|
loadShader(device,PlumageRender::filePath.skyboxFragShaderPath, VK_SHADER_STAGE_FRAGMENT_BIT)
|
|
};
|
|
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipelines.skybox));
|
|
for (auto shaderStage : shaderStages) {
|
|
vkDestroyShaderModule(device, shaderStage.module, nullptr);
|
|
}
|
|
|
|
// PBR pipeline
|
|
shaderStages = {
|
|
loadShader(device,PlumageRender::filePath.pbrVertShaderPath, VK_SHADER_STAGE_VERTEX_BIT),
|
|
loadShader(device,PlumageRender::filePath.pbrFragShaderPath, VK_SHADER_STAGE_FRAGMENT_BIT)
|
|
};
|
|
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);
|
|
}
|
|
//Create Tone Mapping render pipeline
|
|
//CreateToneMappingPipeline();
|
|
}
|
|
|
|
// generate two cube maps
|
|
// irradiance cube map
|
|
// prefileter environment cube map
|
|
void PlumageRender::generateCubemaps()
|
|
{
|
|
enum Target { IRRADIANCE = 0, PREFILTEREDENV = 1 };
|
|
|
|
for (uint32_t target = 0; target < PREFILTEREDENV + 1; target++) {
|
|
|
|
vks::TextureCubeMap cubemap;
|
|
|
|
auto tStart = std::chrono::high_resolution_clock::now();
|
|
|
|
VkFormat format;
|
|
int32_t dim;
|
|
|
|
switch (target) {
|
|
case IRRADIANCE:
|
|
format = VK_FORMAT_R32G32B32A32_SFLOAT;
|
|
dim = 64;
|
|
break;
|
|
case PREFILTEREDENV:
|
|
format = VK_FORMAT_R16G16B16A16_SFLOAT;
|
|
dim = 512;
|
|
break;
|
|
};
|
|
|
|
const uint32_t numMips = static_cast<uint32_t>(floor(log2(dim))) + 1;
|
|
|
|
// 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));
|
|
}
|
|
|
|
// 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
|
|
{
|
|
// 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);
|
|
}
|
|
|
|
// 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);
|
|
|
|
// 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;
|
|
};
|
|
|
|
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;
|
|
|
|
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;
|
|
|
|
shaderStages[0] = loadShader(device, PlumageRender::filePath.filterVertShaderPath, VK_SHADER_STAGE_VERTEX_BIT);
|
|
switch (target) {
|
|
case IRRADIANCE:
|
|
shaderStages[1] = loadShader(device, PlumageRender::filePath.irradianceFragShaderPath, VK_SHADER_STAGE_FRAGMENT_BIT);
|
|
break;
|
|
case PREFILTEREDENV:
|
|
shaderStages[1] = loadShader(device, PlumageRender::filePath.prefilterEnvmapFragShaderPath, VK_SHADER_STAGE_FRAGMENT_BIT);
|
|
break;
|
|
};
|
|
VkPipeline pipeline;
|
|
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipeline));
|
|
for (auto shaderStage : shaderStages) {
|
|
vkDestroyShaderModule(device, shaderStage.module, nullptr);
|
|
}
|
|
|
|
// 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)),
|
|
};
|
|
|
|
VkCommandBuffer cmdBuf = vulkanDevice->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, false);
|
|
|
|
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;
|
|
|
|
VkImageSubresourceRange subresourceRange{};
|
|
subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
subresourceRange.baseMipLevel = 0;
|
|
subresourceRange.levelCount = numMips;
|
|
subresourceRange.layerCount = 6;
|
|
|
|
// Change image layout for all cubemap faces to transfer destination
|
|
{
|
|
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);
|
|
}
|
|
|
|
for (uint32_t m = 0; m < numMips; m++) {
|
|
for (uint32_t f = 0; f < 6; f++) {
|
|
|
|
vulkanDevice->beginCommandBuffer(cmdBuf);
|
|
|
|
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);
|
|
|
|
// Render scene from cube face's point of view
|
|
vkCmdBeginRenderPass(cmdBuf, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
|
|
|
|
// 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];
|
|
vkCmdPushConstants(cmdBuf, pipelinelayout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(IrradiancePushBlock), &irradiancePushBlock);
|
|
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);
|
|
vkCmdPushConstants(cmdBuf, pipelinelayout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(PrefilterPushBlock), &prefilterPushBlock);
|
|
break;
|
|
};
|
|
|
|
vkCmdBindPipeline(cmdBuf, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
|
|
vkCmdBindDescriptorSets(cmdBuf, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelinelayout, 0, 1, &descriptorset, 0, NULL);
|
|
|
|
VkDeviceSize offsets[1] = { 0 };
|
|
|
|
models.skybox.draw(cmdBuf);
|
|
|
|
vkCmdEndRenderPass(cmdBuf);
|
|
|
|
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;
|
|
|
|
{
|
|
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);
|
|
}
|
|
|
|
// 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,
|
|
©Region);
|
|
|
|
{
|
|
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);
|
|
}
|
|
|
|
vulkanDevice->flushCommandBuffer(cmdBuf, queue, false);
|
|
}
|
|
}
|
|
|
|
{
|
|
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);
|
|
}
|
|
|
|
|
|
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;
|
|
}
|
|
}
|
|
// generate BRDF integration map for roughness/NdotV
|
|
void PlumageRender::generateBRDFLUT()
|
|
{
|
|
auto tStart = std::chrono::high_resolution_clock::now();
|
|
|
|
const VkFormat format = VK_FORMAT_R16G16_SFLOAT;
|
|
const int32_t dim = 512;
|
|
|
|
// 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.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
|
|
VK_CHECK_RESULT(vkCreateImage(device, &imageCI, nullptr, &textures.lutBrdf.image));
|
|
VkMemoryRequirements memReqs;
|
|
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;
|
|
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;
|
|
viewCI.image = textures.lutBrdf.image;
|
|
VK_CHECK_RESULT(vkCreateImageView(device, &viewCI, nullptr, &textures.lutBrdf.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 = 1.0f;
|
|
samplerCI.maxAnisotropy = 1.0f;
|
|
samplerCI.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;
|
|
VK_CHECK_RESULT(vkCreateSampler(device, &samplerCI, nullptr, &textures.lutBrdf.sampler));
|
|
|
|
// 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_SHADER_READ_ONLY_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;
|
|
|
|
// Create the actual 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));
|
|
|
|
VkFramebufferCreateInfo framebufferCI{};
|
|
framebufferCI.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
|
|
framebufferCI.renderPass = renderpass;
|
|
framebufferCI.attachmentCount = 1;
|
|
framebufferCI.pAttachments = &textures.lutBrdf.view;
|
|
framebufferCI.width = dim;
|
|
framebufferCI.height = dim;
|
|
framebufferCI.layers = 1;
|
|
|
|
VkFramebuffer framebuffer;
|
|
VK_CHECK_RESULT(vkCreateFramebuffer(device, &framebufferCI, nullptr, &framebuffer));
|
|
|
|
// Desriptors
|
|
VkDescriptorSetLayout descriptorsetlayout;
|
|
VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCI{};
|
|
descriptorSetLayoutCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
|
|
VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorSetLayoutCI, nullptr, &descriptorsetlayout));
|
|
|
|
// Pipeline layout
|
|
VkPipelineLayout pipelinelayout;
|
|
VkPipelineLayoutCreateInfo pipelineLayoutCI{};
|
|
pipelineLayoutCI.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
|
|
pipelineLayoutCI.setLayoutCount = 1;
|
|
pipelineLayoutCI.pSetLayouts = &descriptorsetlayout;
|
|
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;
|
|
|
|
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());
|
|
|
|
VkPipelineVertexInputStateCreateInfo emptyInputStateCI{};
|
|
emptyInputStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
|
|
|
|
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 = &emptyInputStateCI;
|
|
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();
|
|
|
|
// Look-up-table (from BRDF) pipeline
|
|
shaderStages = {
|
|
loadShader(device,PlumageRender::filePath.brdfVertShaderPath, VK_SHADER_STAGE_VERTEX_BIT),
|
|
loadShader(device,PlumageRender::filePath.brdfFragShaderPath, VK_SHADER_STAGE_FRAGMENT_BIT)
|
|
};
|
|
VkPipeline pipeline;
|
|
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipeline));
|
|
for (auto shaderStage : shaderStages) {
|
|
vkDestroyShaderModule(device, shaderStage.module, nullptr);
|
|
}
|
|
|
|
// Render
|
|
VkClearValue clearValues[1];
|
|
clearValues[0].color = { { 0.0f, 0.0f, 0.0f, 1.0f } };
|
|
|
|
VkRenderPassBeginInfo renderPassBeginInfo{};
|
|
renderPassBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
|
|
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);
|
|
|
|
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;
|
|
|
|
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);
|
|
|
|
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;
|
|
|
|
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;
|
|
}
|
|
|
|
// Prepare and initialize uniform buffer containing shader uniforms
|
|
void PlumageRender::prepareUniformBuffers()
|
|
{
|
|
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));
|
|
}
|
|
updateUniformBuffers();
|
|
}
|
|
|
|
void PlumageRender::updateUniformBuffers()
|
|
{
|
|
// 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();
|
|
}
|
|
|
|
void PlumageRender::prepare()
|
|
{
|
|
VulkanExampleBase::prepare();
|
|
|
|
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()));
|
|
}
|
|
|
|
loadAssets();
|
|
generateBRDFLUT();
|
|
generateCubemaps();
|
|
prepareUniformBuffers();
|
|
setupDescriptors();
|
|
preparePipelines();
|
|
|
|
gui = new UI(vulkanDevice, renderPass, queue, pipelineCache, settings.sampleCount);
|
|
updateUIOverlay();
|
|
|
|
buildCommandBuffers();
|
|
|
|
prepared = true;
|
|
}
|
|
|
|
void PlumageRender::render()
|
|
{
|
|
if (!prepared) {
|
|
return;
|
|
}
|
|
|
|
updateUIOverlay();
|
|
|
|
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], ¤tBuffer);
|
|
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();
|
|
}
|
|
}
|
|
if (camera.updated) {
|
|
updateUniformBuffers();
|
|
}
|
|
|
|
}
|
|
|
|
void PlumageRender::fileDropped(std::string filename)
|
|
{
|
|
vkDeviceWaitIdle(device);
|
|
loadScene(filename);
|
|
setupDescriptors();
|
|
buildCommandBuffers();
|
|
}
|
|
|
|
void PlumageRender::updateUIOverlay()
|
|
{
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
|
|
ImVec2 lastDisplaySize = io.DisplaySize;
|
|
io.DisplaySize = ImVec2((float)width, (float)height);
|
|
io.DeltaTime = frameTimer;
|
|
|
|
io.MousePos = ImVec2(mousePos.x, mousePos.y);
|
|
io.MouseDown[0] = mouseButtons.left;
|
|
io.MouseDown[1] = mouseButtons.right;
|
|
|
|
gui->pushConstBlock.scale = glm::vec2(2.0f / io.DisplaySize.x, 2.0f / io.DisplaySize.y);
|
|
gui->pushConstBlock.translate = glm::vec2(-1.0f);
|
|
|
|
bool updateShaderParams = false;
|
|
bool updateCBs = false;
|
|
float scale = 1.0f;
|
|
ImGui::NewFrame();
|
|
|
|
ImGui::SetNextWindowPos(ImVec2(10, 10));
|
|
ImGui::SetNextWindowSize(ImVec2(200 * scale, (models.scene.animations.size() > 0 ? 440 : 360) * scale), ImGuiSetCond_Always);
|
|
ImGui::Begin("Plumage Render", nullptr, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove);
|
|
ImGui::PushItemWidth(100.0f * scale);
|
|
|
|
|
|
gui->text("%.1d fps (%.2f ms)", lastFPS, (1000.0f / lastFPS));
|
|
// TO DO : language switch
|
|
if (gui->header(chineseUI.model)) {
|
|
if (gui->button(chineseUI.openNewModel)) {
|
|
std::string filename = "";
|
|
char buffer[MAX_PATH];
|
|
OPENFILENAME ofn;
|
|
ZeroMemory(&buffer, sizeof(buffer));
|
|
ZeroMemory(&ofn, sizeof(ofn));
|
|
ofn.lStructSize = sizeof(ofn);
|
|
ofn.lpstrFilter = "glTF files\0*.gltf;*.glb\0";
|
|
ofn.lpstrFile = buffer;
|
|
ofn.nMaxFile = MAX_PATH;
|
|
ofn.lpstrTitle = "Select a glTF file to load";
|
|
ofn.Flags = OFN_DONTADDTORECENT | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR;
|
|
if (GetOpenFileNameA(&ofn)) {
|
|
filename = buffer;
|
|
}
|
|
if (!filename.empty()) {
|
|
vkDeviceWaitIdle(device);
|
|
loadScene(filename);
|
|
setupDescriptors();
|
|
updateCBs = true;
|
|
}
|
|
}
|
|
if (gui->combo(chineseUI.environmentMap, selectedEnvironment, environments)) {
|
|
vkDeviceWaitIdle(device);
|
|
loadEnvironment(environments[selectedEnvironment]);
|
|
setupDescriptors();
|
|
updateCBs = true;
|
|
}
|
|
}
|
|
|
|
if (gui->header(chineseUI.environment)) {
|
|
if (gui->checkbox("Background", &displayBackground)) {
|
|
updateShaderParams = true;
|
|
}
|
|
if (gui->slider("Exposure", &shaderData.exposure, 0.1f, 10.0f)) {
|
|
updateShaderParams = true;
|
|
}
|
|
if (gui->slider("Gamma", &shaderData.gamma, 0.1f, 4.0f)) {
|
|
updateShaderParams = true;
|
|
}
|
|
if (gui->slider("IBL", &shaderData.scaleIBLAmbient, 0.0f, 1.0f)) {
|
|
updateShaderParams = true;
|
|
}
|
|
}
|
|
|
|
if (gui->header("Debug ")) {
|
|
const std::vector<std::string> debugNamesInputs = {
|
|
"none", "Base color", "Normal", "Occlusion", "Emissive", "Metallic", "Roughness"
|
|
};
|
|
if (gui->combo(chineseUI.debugInput, &debugViewInputs, debugNamesInputs)) {
|
|
shaderData.debugViewInputs = static_cast<float>(debugViewInputs);
|
|
updateShaderParams = true;
|
|
}
|
|
const std::vector<std::string> debugNamesEquation = {
|
|
"none", "Diff (l,n)", "F (l,h)", "G (l,v,h)", "D (h)", "Specular"
|
|
};
|
|
if (gui->combo(chineseUI.debugPBREquation, &debugViewEquation, debugNamesEquation)) {
|
|
shaderData.debugViewEquation = static_cast<float>(debugViewEquation);
|
|
updateShaderParams = true;
|
|
}
|
|
}
|
|
|
|
if (models.scene.animations.size() > 0) {
|
|
if (gui->header(chineseUI.animation)) {
|
|
gui->checkbox(chineseUI.pauseAnimation, &animate);
|
|
std::vector<std::string> animationNames;
|
|
for (auto animation : models.scene.animations) {
|
|
animationNames.push_back(animation.name);
|
|
}
|
|
gui->combo(chineseUI.animationSeq, &animationIndex, animationNames);
|
|
}
|
|
}
|
|
|
|
ImGui::PopItemWidth();
|
|
ImGui::End();
|
|
ImGui::Render();
|
|
|
|
ImDrawData* imDrawData = ImGui::GetDrawData();
|
|
|
|
// Check if ui buffers need to be recreated
|
|
if (imDrawData) {
|
|
VkDeviceSize vertexBufferSize = imDrawData->TotalVtxCount * sizeof(ImDrawVert);
|
|
VkDeviceSize indexBufferSize = imDrawData->TotalIdxCount * sizeof(ImDrawIdx);
|
|
|
|
bool updateBuffers = (gui->vertexBuffer.buffer == VK_NULL_HANDLE) || (gui->vertexBuffer.count != imDrawData->TotalVtxCount) || (gui->indexBuffer.buffer == VK_NULL_HANDLE) || (gui->indexBuffer.count != imDrawData->TotalIdxCount);
|
|
|
|
if (updateBuffers) {
|
|
vkDeviceWaitIdle(device);
|
|
if (gui->vertexBuffer.buffer) {
|
|
gui->vertexBuffer.destroy();
|
|
}
|
|
gui->vertexBuffer.create(vulkanDevice, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, vertexBufferSize);
|
|
gui->vertexBuffer.count = imDrawData->TotalVtxCount;
|
|
if (gui->indexBuffer.buffer) {
|
|
gui->indexBuffer.destroy();
|
|
}
|
|
gui->indexBuffer.create(vulkanDevice, VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, indexBufferSize);
|
|
gui->indexBuffer.count = imDrawData->TotalIdxCount;
|
|
}
|
|
|
|
// Upload data
|
|
ImDrawVert* vtxDst = (ImDrawVert*)gui->vertexBuffer.mapped;
|
|
ImDrawIdx* idxDst = (ImDrawIdx*)gui->indexBuffer.mapped;
|
|
for (int n = 0; n < imDrawData->CmdListsCount; n++) {
|
|
const ImDrawList* cmd_list = imDrawData->CmdLists[n];
|
|
memcpy(vtxDst, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert));
|
|
memcpy(idxDst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx));
|
|
vtxDst += cmd_list->VtxBuffer.Size;
|
|
idxDst += cmd_list->IdxBuffer.Size;
|
|
}
|
|
|
|
gui->vertexBuffer.flush();
|
|
gui->indexBuffer.flush();
|
|
|
|
updateCBs = updateCBs || updateBuffers;
|
|
}
|
|
|
|
if (lastDisplaySize.x != io.DisplaySize.x || lastDisplaySize.y != io.DisplaySize.y) {
|
|
updateCBs = true;
|
|
}
|
|
|
|
if (updateCBs) {
|
|
vkDeviceWaitIdle(device);
|
|
buildCommandBuffers();
|
|
vkDeviceWaitIdle(device);
|
|
}
|
|
|
|
if (updateShaderParams) {
|
|
updateShaderData();
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|