添加framebuffer to ppm
parent
5e4acd3f08
commit
81c289ea9c
10
ReadMe.md
10
ReadMe.md
|
@ -10,6 +10,16 @@
|
||||||
3. 支持模型的PBR材质渲染
|
3. 支持模型的PBR材质渲染
|
||||||
4. 使用基于IBL的环境光照
|
4. 使用基于IBL的环境光照
|
||||||
|
|
||||||
|
#### 分支:add image output ToDo list
|
||||||
|
|
||||||
|
1. 添加ppm格式图片序列输出
|
||||||
|
2. 单帧输出,预览效果
|
||||||
|
3. 接入ffmpeg将图片序列转图片
|
||||||
|
|
||||||
|
#### todo list
|
||||||
|
|
||||||
|
1. 参数化模板选择:维护一个模板数组,前端发送一个数组索引
|
||||||
|
|
||||||
#### 展示
|
#### 展示
|
||||||
|
|
||||||
展示使用录制的GIF并转换为animated webp,存在**画质损失**,仅供参考
|
展示使用录制的GIF并转换为animated webp,存在**画质损失**,仅供参考
|
||||||
|
|
|
@ -625,13 +625,13 @@ void VulkanExampleBase::initVulkan()
|
||||||
for (size_t i = 0; i < args.size(); i++) {
|
for (size_t i = 0; i < args.size(); i++) {
|
||||||
if ((args[i] == std::string("-g")) || (args[i] == std::string("--gpu"))) {
|
if ((args[i] == std::string("-g")) || (args[i] == std::string("--gpu"))) {
|
||||||
char* endptr;
|
char* endptr;
|
||||||
uint32_t index = strtol(args[i + 1], &endptr, 10);
|
selectedPhysicalDeviceIndex = strtol(args[i + 1], &endptr, 10);
|
||||||
if (endptr != args[i + 1]) {
|
if (endptr != args[i + 1]) {
|
||||||
if (index > gpuCount - 1) {
|
if (selectedPhysicalDeviceIndex > gpuCount - 1) {
|
||||||
std::cerr << "Selected device index " << index << " is out of range, reverting to device 0 (use -listgpus to show available Vulkan devices)" << std::endl;
|
std::cerr << "Selected device index " << selectedPhysicalDeviceIndex << " is out of range, reverting to device 0 (use -listgpus to show available Vulkan devices)" << std::endl;
|
||||||
} else {
|
} else {
|
||||||
std::cout << "Selected Vulkan device " << index << std::endl;
|
std::cout << "Selected Vulkan device " << selectedPhysicalDeviceIndex << std::endl;
|
||||||
selectedDevice = index;
|
selectedDevice = selectedPhysicalDeviceIndex;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -102,6 +102,7 @@ protected:
|
||||||
void windowResize();
|
void windowResize();
|
||||||
public:
|
public:
|
||||||
static std::vector<const char*> args;
|
static std::vector<const char*> args;
|
||||||
|
uint32_t selectedPhysicalDeviceIndex = 0;
|
||||||
bool prepared = false;
|
bool prepared = false;
|
||||||
uint32_t width = 1280;
|
uint32_t width = 1280;
|
||||||
uint32_t height = 720;
|
uint32_t height = 720;
|
||||||
|
|
|
@ -1572,6 +1572,153 @@ PlumageRender::PlumageRender()
|
||||||
prepared = true;
|
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,std::string fileName)
|
||||||
|
{
|
||||||
|
char* imageData;
|
||||||
|
// create dst image to copy
|
||||||
|
VkImageCreateInfo imageCreateInfo(vks::initializers::imageCreateInfo());
|
||||||
|
imageCreateInfo.imageType = VK_IMAGE_TYPE_2D;
|
||||||
|
imageCreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
|
||||||
|
imageCreateInfo.extent.width = width;
|
||||||
|
imageCreateInfo.extent.height = height;
|
||||||
|
imageCreateInfo.extent.depth = 1;
|
||||||
|
imageCreateInfo.arrayLayers = 1;
|
||||||
|
imageCreateInfo.mipLevels = 1;
|
||||||
|
imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||||
|
imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||||
|
imageCreateInfo.tiling = VK_IMAGE_TILING_LINEAR;
|
||||||
|
imageCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||||
|
|
||||||
|
VkImage dstImage;
|
||||||
|
VK_CHECK_RESULT(vkCreateImage(device, &imageCreateInfo, nullptr, &dstImage));
|
||||||
|
|
||||||
|
// get memory ready to store the image
|
||||||
|
VkMemoryRequirements memRequirements;
|
||||||
|
VkMemoryAllocateInfo memAllocInfo(vks::initializers::memoryAllocateInfo());
|
||||||
|
VkDeviceMemory dstImageMemory;
|
||||||
|
vkGetImageMemoryRequirements(device, dstImage, &memRequirements);
|
||||||
|
memAllocInfo.allocationSize = memRequirements.size;
|
||||||
|
memAllocInfo.memoryTypeIndex = getMemoryTypeIndex(memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
|
||||||
|
|
||||||
|
// allocate and bind memory
|
||||||
|
VK_CHECK_RESULT(vkAllocateMemory(device, &memAllocInfo, nullptr, &dstImageMemory));
|
||||||
|
VK_CHECK_RESULT(vkBindImageMemory(device, dstImage, dstImageMemory, 0));
|
||||||
|
|
||||||
|
// blit image and copy to host visualable memory
|
||||||
|
VkCommandBufferAllocateInfo cmdBufferAllocInfo;
|
||||||
|
VkCommandBuffer copyCmd;
|
||||||
|
VK_CHECK_RESULT(vkAllocateCommandBuffers(device, &cmdBufferAllocInfo, ©Cmd));
|
||||||
|
VkCommandBufferBeginInfo cmdBufferBeginInfo(vks::initializers::commandBufferBeginInfo());
|
||||||
|
VK_CHECK_RESULT(vkBeginCommandBuffer(copyCmd, &cmdBufferBeginInfo));
|
||||||
|
|
||||||
|
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 });
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
vkCmdCopyImage(copyCmd, offscreen.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dstImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &imageCopyRegion);
|
||||||
|
|
||||||
|
// transition dst image to general layout for map memory
|
||||||
|
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});
|
||||||
|
|
||||||
|
VK_CHECK_RESULT(vkEndCommandBuffer(copyCmd));
|
||||||
|
|
||||||
|
submitWork(copyCmd, queue);
|
||||||
|
|
||||||
|
// Get layout of the image
|
||||||
|
VkImageSubresource subResource{};
|
||||||
|
subResource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||||
|
VkSubresourceLayout subResourceLayout;
|
||||||
|
|
||||||
|
vkGetImageSubresourceLayout(device, dstImage, &subResource, &subResourceLayout);
|
||||||
|
|
||||||
|
// Map image memory
|
||||||
|
vkMapMemory(device, dstImageMemory, 0, VK_WHOLE_SIZE, 0, (void**)&imageData);
|
||||||
|
|
||||||
|
imageData += subResourceLayout.offset;
|
||||||
|
|
||||||
|
std::ofstream file(fileName, std::ios::out | std::ios::binary);
|
||||||
|
|
||||||
|
// ppm header
|
||||||
|
file << "P6\n" << width << "\n" << height << "\n" << 255 << "\n";
|
||||||
|
|
||||||
|
std::vector<VkFormat> formatsBGR = { VK_FORMAT_B8G8R8A8_SRGB, VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_B8G8R8A8_SNORM };
|
||||||
|
const bool colorSwizzle = (std::find(formatsBGR.begin(), formatsBGR.end(), VK_FORMAT_R8G8B8A8_UNORM) != formatsBGR.end());
|
||||||
|
|
||||||
|
// ppm binary pixel data
|
||||||
|
for (int32_t y = 0; y < height; y++) {
|
||||||
|
unsigned int* row = (unsigned int*)imageData;
|
||||||
|
for (int32_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++;
|
||||||
|
}
|
||||||
|
imageData += subResourceLayout.rowPitch;
|
||||||
|
}
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
std::cout << "Framebuffer image saved to " << filePath << fileName << std::endl;
|
||||||
|
|
||||||
|
// Clean up resources
|
||||||
|
vkUnmapMemory(device, dstImageMemory);
|
||||||
|
vkFreeMemory(device, dstImageMemory, nullptr);
|
||||||
|
vkDestroyImage(device, dstImage, nullptr);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlumageRender::outputImageSequence(VkImage image, std::string filePath)
|
||||||
|
{
|
||||||
|
for (uint32_t i = 0; i < settings.outputFrameCount; i++)
|
||||||
|
{
|
||||||
|
std::string fileName = std::to_string(i) + "result.pmm";
|
||||||
|
std::string filePath = std::to_string(selectedPhysicalDeviceIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
void PlumageRender::render()
|
||||||
{
|
{
|
||||||
if (!prepared) {
|
if (!prepared) {
|
||||||
|
@ -1579,7 +1726,9 @@ PlumageRender::PlumageRender()
|
||||||
}
|
}
|
||||||
|
|
||||||
updateUIOverlay();
|
updateUIOverlay();
|
||||||
|
//加入写到文件的函数
|
||||||
|
//swapChainImage = swapChain.images[frameIndex];
|
||||||
|
//outputImageSequeue(swapChainImage,filePath.imageSequenceFilePath);
|
||||||
VK_CHECK_RESULT(vkWaitForFences(device, 1, &waitFences[frameIndex], VK_TRUE, UINT64_MAX));
|
VK_CHECK_RESULT(vkWaitForFences(device, 1, &waitFences[frameIndex], VK_TRUE, UINT64_MAX));
|
||||||
VK_CHECK_RESULT(vkResetFences(device, 1, &waitFences[frameIndex]));
|
VK_CHECK_RESULT(vkResetFences(device, 1, &waitFences[frameIndex]));
|
||||||
|
|
||||||
|
@ -1589,6 +1738,8 @@ PlumageRender::PlumageRender()
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
VK_CHECK_RESULT(acquire);
|
VK_CHECK_RESULT(acquire);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update UBOs
|
// Update UBOs
|
||||||
|
@ -1610,6 +1761,7 @@ PlumageRender::PlumageRender()
|
||||||
submitInfo.commandBufferCount = 1;
|
submitInfo.commandBufferCount = 1;
|
||||||
VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, waitFences[frameIndex]));
|
VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, waitFences[frameIndex]));
|
||||||
|
|
||||||
|
//显示队列
|
||||||
VkResult present = swapChain.queuePresent(queue, currentBuffer, renderCompleteSemaphores[frameIndex]);
|
VkResult present = swapChain.queuePresent(queue, currentBuffer, renderCompleteSemaphores[frameIndex]);
|
||||||
if (!((present == VK_SUCCESS) || (present == VK_SUBOPTIMAL_KHR))) {
|
if (!((present == VK_SUCCESS) || (present == VK_SUBOPTIMAL_KHR))) {
|
||||||
if (present == VK_ERROR_OUT_OF_DATE_KHR) {
|
if (present == VK_ERROR_OUT_OF_DATE_KHR) {
|
||||||
|
@ -1625,7 +1777,7 @@ PlumageRender::PlumageRender()
|
||||||
frameIndex %= renderAhead;
|
frameIndex %= renderAhead;
|
||||||
|
|
||||||
if (!paused) {
|
if (!paused) {
|
||||||
if (rotateModel) {
|
if (settings.rotateModel) {
|
||||||
modelrot.y += frameTimer * 35.0f;
|
modelrot.y += frameTimer * 35.0f;
|
||||||
if (modelrot.y > 360.0f) {
|
if (modelrot.y > 360.0f) {
|
||||||
modelrot.y -= 360.0f;
|
modelrot.y -= 360.0f;
|
||||||
|
@ -1639,7 +1791,7 @@ PlumageRender::PlumageRender()
|
||||||
models.scene.updateAnimation(animationIndex, animationTimer);
|
models.scene.updateAnimation(animationIndex, animationTimer);
|
||||||
}
|
}
|
||||||
updateShaderData();
|
updateShaderData();
|
||||||
if (rotateModel) {
|
if (settings.rotateModel) {
|
||||||
updateUniformBuffers();
|
updateUniformBuffers();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1647,6 +1799,8 @@ PlumageRender::PlumageRender()
|
||||||
updateUniformBuffers();
|
updateUniformBuffers();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlumageRender::fileDropped(std::string filename)
|
void PlumageRender::fileDropped(std::string filename)
|
||||||
|
|
|
@ -159,12 +159,19 @@ public:
|
||||||
//ttf file path
|
//ttf file path
|
||||||
std::string ttfFilePath = getAssetPath() + "/data/Roboto-Medium.ttf";
|
std::string ttfFilePath = getAssetPath() + "/data/Roboto-Medium.ttf";
|
||||||
|
|
||||||
|
// output file path
|
||||||
|
// imageSequence
|
||||||
|
std::string imageSequenceFilePath = getAssetPath() + "/output/imageSequence";
|
||||||
|
// screen shot image
|
||||||
|
std::string screenShotFilePath = getAssetPath() + "/output/screenShot";
|
||||||
|
|
||||||
} filePath;
|
} filePath;
|
||||||
|
|
||||||
bool rotateModel = false;
|
|
||||||
glm::vec3 modelrot = glm::vec3(0.0f);
|
glm::vec3 modelrot = glm::vec3(0.0f);
|
||||||
glm::vec3 modelPos = glm::vec3(0.0f);
|
glm::vec3 modelPos = glm::vec3(0.0f);
|
||||||
|
|
||||||
|
|
||||||
enum PBRWorkflows { PBR_WORKFLOW_METALLIC_ROUGHNESS = 0, PBR_WORKFLOW_SPECULAR_GLOSINESS = 1 };
|
enum PBRWorkflows { PBR_WORKFLOW_METALLIC_ROUGHNESS = 0, PBR_WORKFLOW_SPECULAR_GLOSINESS = 1 };
|
||||||
|
|
||||||
|
|
||||||
|
@ -214,6 +221,11 @@ public:
|
||||||
bool fullscreen = false;
|
bool fullscreen = false;
|
||||||
bool vsync = false;
|
bool vsync = false;
|
||||||
bool multiSampling = true;
|
bool multiSampling = true;
|
||||||
|
bool rotateModel = true;
|
||||||
|
bool enableSaveToImageSequeue = false;
|
||||||
|
uint32_t outputFrameCount = 50;
|
||||||
|
bool takeScreenShot = false;
|
||||||
|
|
||||||
VkSampleCountFlagBits sampleCount = VK_SAMPLE_COUNT_4_BIT;
|
VkSampleCountFlagBits sampleCount = VK_SAMPLE_COUNT_4_BIT;
|
||||||
} settings;
|
} settings;
|
||||||
|
|
||||||
|
@ -235,6 +247,8 @@ public:
|
||||||
const uint32_t renderAhead = 2;
|
const uint32_t renderAhead = 2;
|
||||||
uint32_t frameIndex = 0;
|
uint32_t frameIndex = 0;
|
||||||
|
|
||||||
|
//VkImage swapChainImage;
|
||||||
|
|
||||||
int32_t animationIndex = 0;
|
int32_t animationIndex = 0;
|
||||||
float animationTimer = 0.0f;
|
float animationTimer = 0.0f;
|
||||||
bool animate = true;
|
bool animate = true;
|
||||||
|
@ -331,6 +345,11 @@ public:
|
||||||
void updateShaderData();
|
void updateShaderData();
|
||||||
void windowResized();
|
void windowResized();
|
||||||
void prepare();
|
void prepare();
|
||||||
|
void submitWork(VkCommandBuffer cmdBuffer, VkQueue queue);
|
||||||
|
void writeImageToFile(std::string filePath, std::string fileName);
|
||||||
|
void outputImageSequence(VkImage image,std::string filePath);
|
||||||
|
void outputScreenShot(VkImage image, std::string filePath);
|
||||||
|
uint32_t getMemoryTypeIndex(uint32_t typeBits, VkMemoryPropertyFlags properties);
|
||||||
virtual void render();
|
virtual void render();
|
||||||
virtual void updateUIOverlay();
|
virtual void updateUIOverlay();
|
||||||
virtual void fileDropped(std::string filename);
|
virtual void fileDropped(std::string filename);
|
||||||
|
|
Loading…
Reference in New Issue