2284 lines
92 KiB
C++
2284 lines
92 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 running 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, brdflut)
|
||
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::submitWork(VkCommandBuffer cmdBuffer, VkQueue queue)
|
||
{
|
||
VkSubmitInfo submitInfo = vks::initializers::submitInfo();
|
||
submitInfo.commandBufferCount = 1;
|
||
submitInfo.pCommandBuffers = &cmdBuffer;
|
||
VkFenceCreateInfo fenceInfo = vks::initializers::fenceCreateInfo();
|
||
VkFence fence;
|
||
VK_CHECK_RESULT(vkCreateFence(device, &fenceInfo, nullptr, &fence));
|
||
VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, fence));
|
||
VK_CHECK_RESULT(vkWaitForFences(device, 1, &fence, VK_TRUE, UINT64_MAX));
|
||
vkDestroyFence(device, fence, nullptr);
|
||
}
|
||
|
||
// todo :根据physicalDeviceIndex确定子文件夹路径,frameIndex确定fileName
|
||
void PlumageRender::writeImageToFile(std::string filePath)
|
||
{
|
||
|
||
bool screenshotSaved = false;
|
||
bool supportsBlit = true;
|
||
|
||
// Check blit support for source and destination
|
||
VkFormatProperties formatProps;
|
||
|
||
// Check if the device supports blitting from optimal images (the swapchain images are in optimal format)
|
||
vkGetPhysicalDeviceFormatProperties(physicalDevice, swapChain.colorFormat, &formatProps);
|
||
if (!(formatProps.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT)) {
|
||
std::cerr << "Device does not support blitting from optimal tiled images, using copy instead of blit!" << std::endl;
|
||
supportsBlit = false;
|
||
}
|
||
|
||
// Check if the device supports blitting to linear images
|
||
vkGetPhysicalDeviceFormatProperties(physicalDevice, VK_FORMAT_R8G8B8A8_UNORM, &formatProps);
|
||
if (!(formatProps.linearTilingFeatures & VK_FORMAT_FEATURE_BLIT_DST_BIT)) {
|
||
std::cerr << "Device does not support blitting to linear tiled images, using copy instead of blit!" << std::endl;
|
||
supportsBlit = false;
|
||
}
|
||
|
||
// Source for the copy is the last rendered swapchain image
|
||
VkImage srcImage = swapChain.images[currentBuffer];
|
||
|
||
// Create the linear tiled destination image to copy to and to read the memory from
|
||
VkImageCreateInfo imageCreateCI(vks::initializers::imageCreateInfo());
|
||
imageCreateCI.imageType = VK_IMAGE_TYPE_2D;
|
||
// Note that vkCmdBlitImage (if supported) will also do format conversions if the swapchain color format would differ
|
||
imageCreateCI.format = VK_FORMAT_R8G8B8A8_UNORM;
|
||
imageCreateCI.extent.width = width;
|
||
imageCreateCI.extent.height = height;
|
||
imageCreateCI.extent.depth = 1;
|
||
imageCreateCI.arrayLayers = 1;
|
||
imageCreateCI.mipLevels = 1;
|
||
imageCreateCI.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||
imageCreateCI.samples = VK_SAMPLE_COUNT_1_BIT;
|
||
imageCreateCI.tiling = VK_IMAGE_TILING_LINEAR;
|
||
imageCreateCI.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||
// Create the image
|
||
VkImage dstImage;
|
||
VK_CHECK_RESULT(vkCreateImage(device, &imageCreateCI, nullptr, &dstImage));
|
||
// Create memory to back up the image
|
||
VkMemoryRequirements memRequirements;
|
||
VkMemoryAllocateInfo memAllocInfo(vks::initializers::memoryAllocateInfo());
|
||
VkDeviceMemory dstImageMemory;
|
||
vkGetImageMemoryRequirements(device, dstImage, &memRequirements);
|
||
memAllocInfo.allocationSize = memRequirements.size;
|
||
// Memory must be host visible to copy from
|
||
memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
|
||
VK_CHECK_RESULT(vkAllocateMemory(device, &memAllocInfo, nullptr, &dstImageMemory));
|
||
VK_CHECK_RESULT(vkBindImageMemory(device, dstImage, dstImageMemory, 0));
|
||
|
||
// Do the actual blit from the swapchain image to our host visible destination image
|
||
VkCommandBuffer copyCmd = vulkanDevice->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);
|
||
|
||
// Transition destination image to transfer destination layout
|
||
vks::tools::insertImageMemoryBarrier(
|
||
copyCmd,
|
||
dstImage,
|
||
0,
|
||
VK_ACCESS_TRANSFER_WRITE_BIT,
|
||
VK_IMAGE_LAYOUT_UNDEFINED,
|
||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||
VkImageSubresourceRange{ VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 });
|
||
|
||
// Transition swapchain image from present to transfer source layout
|
||
vks::tools::insertImageMemoryBarrier(
|
||
copyCmd,
|
||
srcImage,
|
||
VK_ACCESS_MEMORY_READ_BIT,
|
||
VK_ACCESS_TRANSFER_READ_BIT,
|
||
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
|
||
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||
VkImageSubresourceRange{ VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 });
|
||
|
||
// If source and destination support blit we'll blit as this also does automatic format conversion (e.g. from BGR to RGB)
|
||
if (supportsBlit)
|
||
{
|
||
// Define the region to blit (we will blit the whole swapchain image)
|
||
VkOffset3D blitSize;
|
||
blitSize.x = width;
|
||
blitSize.y = height;
|
||
blitSize.z = 1;
|
||
VkImageBlit imageBlitRegion{};
|
||
imageBlitRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||
imageBlitRegion.srcSubresource.layerCount = 1;
|
||
imageBlitRegion.srcOffsets[1] = blitSize;
|
||
imageBlitRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||
imageBlitRegion.dstSubresource.layerCount = 1;
|
||
imageBlitRegion.dstOffsets[1] = blitSize;
|
||
|
||
// Issue the blit command
|
||
vkCmdBlitImage(
|
||
copyCmd,
|
||
srcImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||
dstImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||
1,
|
||
&imageBlitRegion,
|
||
VK_FILTER_NEAREST);
|
||
}
|
||
else
|
||
{
|
||
// Otherwise use image copy (requires us to manually flip components)
|
||
VkImageCopy imageCopyRegion{};
|
||
imageCopyRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||
imageCopyRegion.srcSubresource.layerCount = 1;
|
||
imageCopyRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||
imageCopyRegion.dstSubresource.layerCount = 1;
|
||
imageCopyRegion.extent.width = width;
|
||
imageCopyRegion.extent.height = height;
|
||
imageCopyRegion.extent.depth = 1;
|
||
|
||
// Issue the copy command
|
||
vkCmdCopyImage(
|
||
copyCmd,
|
||
srcImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||
dstImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||
1,
|
||
&imageCopyRegion);
|
||
}
|
||
|
||
// Transition destination image to general layout, which is the required layout for mapping the image memory later on
|
||
vks::tools::insertImageMemoryBarrier(
|
||
copyCmd,
|
||
dstImage,
|
||
VK_ACCESS_TRANSFER_WRITE_BIT,
|
||
VK_ACCESS_MEMORY_READ_BIT,
|
||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||
VK_IMAGE_LAYOUT_GENERAL,
|
||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||
VkImageSubresourceRange{ VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 });
|
||
|
||
// Transition back the swap chain image after the blit is done
|
||
vks::tools::insertImageMemoryBarrier(
|
||
copyCmd,
|
||
srcImage,
|
||
VK_ACCESS_TRANSFER_READ_BIT,
|
||
VK_ACCESS_MEMORY_READ_BIT,
|
||
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
|
||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||
VkImageSubresourceRange{ VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 });
|
||
|
||
vulkanDevice->flushCommandBuffer(copyCmd, queue);
|
||
|
||
// Get layout of the image (including row pitch)
|
||
VkImageSubresource subResource{ VK_IMAGE_ASPECT_COLOR_BIT, 0, 0 };
|
||
VkSubresourceLayout subResourceLayout;
|
||
vkGetImageSubresourceLayout(device, dstImage, &subResource, &subResourceLayout);
|
||
|
||
// Map image memory so we can start copying from it
|
||
const char* data;
|
||
vkMapMemory(device, dstImageMemory, 0, VK_WHOLE_SIZE, 0, (void**)&data);
|
||
data += subResourceLayout.offset;
|
||
|
||
|
||
if (settings.outputPNGimage)
|
||
{
|
||
stbi_write_png(filePath.c_str(), width, height, 4, data, static_cast<int>(subResourceLayout.rowPitch));
|
||
}
|
||
else
|
||
{
|
||
std::ofstream file(filePath, std::ios::out | std::ios::binary);
|
||
|
||
// ppm header
|
||
file << "P6\n" << width << "\n" << height << "\n" << 255 << "\n";
|
||
|
||
// If source is BGR (destination is always RGB) and we can't use blit (which does automatic conversion), we'll have to manually swizzle color components
|
||
bool colorSwizzle = false;
|
||
// Check if source is BGR
|
||
// Note: Not complete, only contains most common and basic BGR surface formats for demonstration purposes
|
||
if (!supportsBlit)
|
||
{
|
||
std::vector<VkFormat> formatsBGR = { VK_FORMAT_B8G8R8A8_SRGB, VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_B8G8R8A8_SNORM };
|
||
colorSwizzle = (std::find(formatsBGR.begin(), formatsBGR.end(), swapChain.colorFormat) != formatsBGR.end());
|
||
}
|
||
// ppm binary pixel data
|
||
for (uint32_t y = 0; y < height; y++)
|
||
{
|
||
unsigned int* row = (unsigned int*)data;
|
||
for (uint32_t x = 0; x < width; x++)
|
||
{
|
||
if (colorSwizzle)
|
||
{
|
||
file.write((char*)row + 2, 1);
|
||
file.write((char*)row + 1, 1);
|
||
file.write((char*)row, 1);
|
||
}
|
||
else
|
||
{
|
||
file.write((char*)row, 3);
|
||
}
|
||
row++;
|
||
}
|
||
data += subResourceLayout.rowPitch;
|
||
}
|
||
file.close();
|
||
}
|
||
|
||
std::cout << "Screenshot saved to " << filePath << std::endl;
|
||
|
||
// Clean up resources
|
||
vkUnmapMemory(device, dstImageMemory);
|
||
vkFreeMemory(device, dstImageMemory, nullptr);
|
||
vkDestroyImage(device, dstImage, nullptr);
|
||
|
||
screenshotSaved = true;
|
||
|
||
}
|
||
|
||
void PlumageRender::outputImageSequence()
|
||
{
|
||
|
||
if (savedFrameCounter == settings.startFrameCount)
|
||
{
|
||
std::cout << "clean up directory for image sequence generation" << std::endl;
|
||
removeImageSequence();
|
||
}
|
||
|
||
filePath.deviceSpecFilePath = filePath.imageOutputPath + "/device" + std::to_string(selectedPhysicalDeviceIndex);
|
||
|
||
if (savedFrameCounter > settings.outputFrameCount)
|
||
{
|
||
if (signal.imageSequenceOutputComplete) // 避免重复改变为true
|
||
{
|
||
return;
|
||
}
|
||
signal.imageSequenceOutputComplete = true;
|
||
|
||
std::string fileName;
|
||
|
||
if (settings.outputPNGimage)
|
||
{
|
||
fileName = "/%dresult.png";
|
||
}
|
||
else
|
||
{
|
||
fileName = "/%dresult.ppm";
|
||
}
|
||
|
||
filePath.totalImageOutputPath = filePath.deviceSpecFilePath + fileName;
|
||
return;
|
||
}
|
||
if (_access(filePath.deviceSpecFilePath.c_str(), 0) == -1)
|
||
{
|
||
std::filesystem::create_directories(filePath.deviceSpecFilePath.c_str());
|
||
}
|
||
std::string fileNameSuffix;
|
||
if (settings.outputPNGimage)
|
||
{
|
||
fileNameSuffix = "result.png";
|
||
}
|
||
else
|
||
{
|
||
fileNameSuffix = "result.ppm";
|
||
}
|
||
std::string fileName ="/" + std::to_string(savedFrameCounter) + fileNameSuffix;
|
||
filePath.totalImageOutputPath = filePath.deviceSpecFilePath + fileName;
|
||
//std::cout << outputPath << std::endl;
|
||
writeImageToFile(filePath.totalImageOutputPath.c_str());
|
||
savedFrameCounter++;
|
||
}
|
||
|
||
void PlumageRender::imageSequenceToVideo()
|
||
{
|
||
if (!signal.imageSequenceOutputComplete)
|
||
{
|
||
return;
|
||
}
|
||
if (signal.imageSequenceToVideoComplete)
|
||
{
|
||
return;
|
||
}
|
||
std::string deviceFilePath = filePath.videoOutputPath + "/device" + std::to_string(selectedPhysicalDeviceIndex);
|
||
if (_access(deviceFilePath.c_str(), 0) == -1)
|
||
{
|
||
std::filesystem::create_directories(deviceFilePath.c_str());
|
||
}
|
||
|
||
std::string resultVideoPath = deviceFilePath + "/result.mp4";
|
||
|
||
std::string commandLineImageSequencePath = filePath.totalImageOutputPath;
|
||
//std::string commandLineCodecAndResultPath = resultVideoPath;
|
||
std::string commandLineFrameRate = std::to_string(settings.videoFrameRate);
|
||
#if defined(_WIN32)
|
||
std::string commandLine = filePath.image2videoBatFilePath + " " + commandLineFrameRate + " " + commandLineImageSequencePath + " " + resultVideoPath;
|
||
#else
|
||
std::string commandLine = filePath.image2videoShFilePath + " " + commandLineFrameRate + " " + commandLineImageSequencePath + " " + resultVideoPath;
|
||
#endif
|
||
std::cout << commandLine << std::endl;
|
||
std::system(commandLine.c_str());
|
||
|
||
signal.imageSequenceToVideoComplete = true;
|
||
std::cout << "vidoe codec complete,saved in:" << resultVideoPath << std::endl;
|
||
std::cout << "star to clean up image sequence" << std::endl;
|
||
removeImageSequence();
|
||
}
|
||
|
||
void PlumageRender::removeImageSequence()
|
||
{
|
||
if (savedFrameCounter != settings.startFrameCount)
|
||
{
|
||
if (!signal.imageSequenceToVideoComplete)
|
||
{
|
||
return;
|
||
}
|
||
|
||
}
|
||
if (std::filesystem::exists(filePath.deviceSpecFilePath))
|
||
{
|
||
for (const auto& entry : std::filesystem::directory_iterator(filePath.deviceSpecFilePath))
|
||
{
|
||
if (std::filesystem::is_directory(entry.path()))
|
||
{
|
||
std::filesystem::remove_all(entry.path());
|
||
}
|
||
else
|
||
{
|
||
std::filesystem::remove(entry.path());
|
||
}
|
||
}
|
||
std::filesystem::remove(filePath.deviceSpecFilePath);
|
||
std::cout << "clean up complete" << std::endl;
|
||
}
|
||
return;
|
||
|
||
}
|
||
|
||
uint32_t PlumageRender::getMemoryTypeIndex(uint32_t typeBits, VkMemoryPropertyFlags properties)
|
||
{
|
||
VkPhysicalDeviceMemoryProperties deviceMemoryProperties;
|
||
vkGetPhysicalDeviceMemoryProperties(physicalDevice, &deviceMemoryProperties);
|
||
for (uint32_t i = 0; i < deviceMemoryProperties.memoryTypeCount; i++)
|
||
{
|
||
if ((typeBits & 1) == 1)
|
||
{
|
||
if ((deviceMemoryProperties.memoryTypes[i].propertyFlags & properties) == properties)
|
||
{
|
||
return i;
|
||
}
|
||
}
|
||
typeBits >>= 1;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
void PlumageRender::render()
|
||
{
|
||
if (!prepared) {
|
||
return;
|
||
}
|
||
|
||
updateUIOverlay();
|
||
//加入写到文件的函数
|
||
//swapChainImage = swapChain.images[frameIndex];
|
||
//outputImageSequeue(swapChainImage,filePath.imageSequenceFilePath);
|
||
|
||
|
||
outputImageSequence();
|
||
|
||
VK_CHECK_RESULT(vkWaitForFences(device, 1, &waitFences[frameIndex], VK_TRUE, UINT64_MAX));
|
||
|
||
imageSequenceToVideo();
|
||
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 (settings.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 (settings.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;
|
||
bool boolTitleWindowShow = false;
|
||
ImGui::NewFrame();
|
||
|
||
ImGui::SetNextWindowPos(ImVec2(10000, 10000));
|
||
//ImGui::SetNextWindowSize(ImVec2(200 * scale, (models.scene.animations.size() > 0 ? 440 : 360) * scale), ImGuiSetCond_Always);
|
||
ImGui::Begin("", nullptr, ImGuiWindowFlags_NoFocusOnAppearing|ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoBringToFrontOnFocus);
|
||
|
||
ImGui::PushItemWidth(100.0f * scale);
|
||
|
||
|
||
|
||
|
||
if(gui->beginMainMenuBar()) {
|
||
if (gui->beginMenu(chineseUI.menuFile))
|
||
{
|
||
if (gui->menuItem(chineseUI.menuOpenNewModel))
|
||
{
|
||
std::wstring filename = L"";
|
||
wchar_t buffer[MAX_PATH];
|
||
OPENFILENAMEW ofn;
|
||
ZeroMemory(&buffer, sizeof(buffer));
|
||
ZeroMemory(&ofn, sizeof(ofn));
|
||
ofn.lStructSize = sizeof(ofn);
|
||
ofn.lpstrFilter = L"glTF files\0*.gltf;*.glb\0";
|
||
ofn.lpstrFile = buffer;
|
||
ofn.nMaxFile = MAX_PATH;
|
||
ofn.lpstrTitle = L"Select a glTF file to load";
|
||
ofn.Flags = OFN_DONTADDTORECENT | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR;
|
||
if (GetOpenFileNameW(&ofn)) {
|
||
filename = buffer;
|
||
|
||
}
|
||
|
||
if (!filename.empty()) {
|
||
vkDeviceWaitIdle(device);
|
||
std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
|
||
std::string stringFilename = converter.to_bytes(filename);
|
||
loadScene(stringFilename);
|
||
setupDescriptors();
|
||
updateCBs = true;
|
||
signal.imageSequenceOutputComplete = false;
|
||
signal.imageSequenceToVideoComplete = false;
|
||
savedFrameCounter = 1;
|
||
}
|
||
}
|
||
gui->endMenu();
|
||
}
|
||
if (gui->beginMenu(chineseUI.menuEnvironment))
|
||
{
|
||
if (gui->beginMenu(chineseUI.menuEnvironmentConfig))
|
||
{
|
||
if (gui->combo(chineseUI.environmentMap, selectedEnvironment, environments)) {
|
||
vkDeviceWaitIdle(device);
|
||
loadEnvironment(environments[selectedEnvironment]);
|
||
setupDescriptors();
|
||
updateCBs = true;
|
||
}
|
||
if (gui->checkbox(chineseUI.environmentBackGround, &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;
|
||
}
|
||
gui->endMenu();
|
||
}
|
||
gui->endMenu();
|
||
}
|
||
if (gui->beginMenu("debug"))
|
||
{
|
||
if (gui->beginMenu(chineseUI.menuDebugInput))
|
||
{
|
||
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;
|
||
}
|
||
gui->endMenu();
|
||
}
|
||
if (gui->beginMenu("PBR"))
|
||
{
|
||
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;
|
||
}
|
||
gui->endMenu();
|
||
}
|
||
|
||
if (gui->beginMenu(chineseUI.menuDebugFrameRate))
|
||
{
|
||
gui->text("%.1d fps (%.2f ms)", lastFPS, (1000.0f / lastFPS));
|
||
gui->endMenu();
|
||
}
|
||
gui->endMenu();
|
||
}
|
||
if (gui->beginMenu(chineseUI.menuAnimation))
|
||
{
|
||
if (models.scene.animations.size() > 0)
|
||
{
|
||
if (gui->beginMenu(chineseUI.menuAnimationActivation))
|
||
{
|
||
gui->checkbox(chineseUI.pauseAnimation, &animate);
|
||
gui->endMenu();
|
||
}
|
||
if (gui->beginMenu(chineseUI.menuAnimationAnimationSequence))
|
||
{
|
||
std::vector<std::string> animationNames;
|
||
for (auto animation : models.scene.animations) {
|
||
animationNames.push_back(animation.name);
|
||
}
|
||
gui->combo(chineseUI.animationSeq, &animationIndex, animationNames);
|
||
gui->endMenu();
|
||
}
|
||
}
|
||
else
|
||
{
|
||
gui->text(chineseUI.menuAnimationNoAnimation);
|
||
}
|
||
gui->endMenu();
|
||
}
|
||
|
||
gui->endMainMenuBar();
|
||
}
|
||
|
||
|
||
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
|