我有一个启用透明的GLFW窗口,Vulkan Swapchain使用VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR
,图形管道使用Normal Blend (Src * One) + (Dst * One_Minus_Src)
。
这分为三个部分,窗口/交换链/图形spipeline创建。另请参阅项目的Github以获取更多信息:https://github.com/Yaazarai/TinyVulkan-Dynamic
问题是这样的:透明帧缓冲区可以工作,但它不考虑Alpha通道,因为唯一支持的交换链合成Alpha模式是VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR
,这使得帧缓冲区在合成多个窗口曲面时始终具有Alpha分量1。
我已经检查了每个复合模式,当不使用VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR
时,Vulkan总是出错。改变混合模式也不起作用,因为帧缓冲区的alpha分量总是1。
Validation Layer: Validation Error: [ VUID-VkSwapchainCreateInfoKHR-compositeAlpha-01280 ] Object 0: handle = 0x2655f873250, type = VK_OBJECT_TYPE_DEVICE; | MessageID = 0xb0051a12 | vkCreateSwapchainKHR() called with a non-supported pCreateInfo->compositeAlpha (i.e. VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR). Supported values are:
VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR
. The Vulkan spec states: compositeAlpha must be one of the bits present in the supportedCompositeAlpha member of the VkSurfaceCapabilitiesKHR structure returned by vkGetPhysicalDeviceSurfaceCapabilitiesKHR for the surface
// Creates a Vulkan Swapchain with the supported Surface Capabilities based
// on the GLFW Vulkan Window surface passed to the VkPhysicalDevice.
void CreateSwapChainImages(uint32_t width = 0, uint32_t height = 0) {
TinyVkSwapChainSupporter swapChainSupport = QuerySwapChainSupport(renderDevice.physicalDevice);
VkSurfaceFormatKHR surfaceFormat = QuerySwapSurfaceFormat(swapChainSupport.formats);
VkPresentModeKHR presentMode = QuerySwapPresentMode(swapChainSupport.presentModes);
VkExtent2D extent = QuerySwapExtent(swapChainSupport.capabilities);
uint32_t imageCount = std::min(swapChainSupport.capabilities.maxImageCount, std::max(swapChainSupport.capabilities.minImageCount, static_cast<uint32_t>(bufferingMode)));
if (width != 0 && height != 0) {
extent = {
std::min(std::max((uint32_t)width, swapChainSupport.capabilities.minImageExtent.width), swapChainSupport.capabilities.maxImageExtent.width),
std::min(std::max((uint32_t)height, swapChainSupport.capabilities.minImageExtent.height), swapChainSupport.capabilities.maxImageExtent.height)
};
} else {
extent = QuerySwapExtent(swapChainSupport.capabilities);
}
if (swapChainSupport.capabilities.maxImageCount > 0 && imageCount > swapChainSupport.capabilities.maxImageCount)
imageCount = swapChainSupport.capabilities.maxImageCount;
VkSwapchainCreateInfoKHR createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
createInfo.surface = renderDevice.presentationSurface;
createInfo.minImageCount = imageCount;
createInfo.imageFormat = surfaceFormat.format;
createInfo.imageColorSpace = surfaceFormat.colorSpace;
createInfo.imageExtent = extent;
createInfo.imageArrayLayers = 1; // Change when developing VR or other 3D stereoscopic applications
createInfo.imageUsage = imageUsage;
TinyVkQueueFamily indices = TinyVkQueueFamily::FindQueueFamilies(renderDevice.physicalDevice, renderDevice.presentationSurface);
uint32_t queueFamilyIndices[] = { indices.graphicsFamily.value(), indices.presentFamily.value() };
if (indices.graphicsFamily != indices.presentFamily) {
createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
createInfo.queueFamilyIndexCount = 2;
createInfo.pQueueFamilyIndices = queueFamilyIndices;
} else {
createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
createInfo.queueFamilyIndexCount = 0; // Optional
createInfo.pQueueFamilyIndices = nullptr; // Optional
}
createInfo.preTransform = swapChainSupport.capabilities.currentTransform;
createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
createInfo.presentMode = presentMode;
createInfo.clipped = VK_TRUE;
createInfo.oldSwapchain = swapChain;
if (vkCreateSwapchainKHR(renderDevice.logicalDevice, &createInfo, nullptr, &swapChain) != VK_SUCCESS)
throw std::runtime_error("TinyVulkan: Failed to create swap chain!");
vkGetSwapchainImagesKHR(renderDevice.logicalDevice, swapChain, &imageCount, nullptr);
images.resize(imageCount);
vkGetSwapchainImagesKHR(renderDevice.logicalDevice, swapChain, &imageCount, images.data());
imageFormat = surfaceFormat.format;
imageExtent = extent;
}
// Creates the graphics pipeline with the appropriate Normal Blend mod
void CreateGraphicsPipeline() {
///////////////////////////////////////////////////////////////////////////////////////////////////////
/////////// This section specifies that MvkVertex provides the vertex layout description ///////////
const VkVertexInputBindingDescription bindingDescription = vertexDescription.binding;
const std::vector<VkVertexInputAttributeDescription>& attributeDescriptions = vertexDescription.attributes;
VkPipelineVertexInputStateCreateInfo vertexInputInfo{};
vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vertexInputInfo.vertexBindingDescriptionCount = 1;
vertexInputInfo.pVertexBindingDescriptions = &bindingDescription;
vertexInputInfo.vertexAttributeDescriptionCount = static_cast<uint32_t>(attributeDescriptions.size());
vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data();
///////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////
VkPipelineLayoutCreateInfo pipelineLayoutInfo{};
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipelineLayoutInfo.pushConstantRangeCount = 0;
uint32_t pushConstantRangeCount = static_cast<uint32_t>(pushConstantRanges.size());
if (pushConstantRangeCount > 0) {
pipelineLayoutInfo.pushConstantRangeCount = pushConstantRangeCount;
pipelineLayoutInfo.pPushConstantRanges = pushConstantRanges.data();
}
if (descriptorBindings.size() > 0) {
VkDescriptorSetLayoutCreateInfo descriptorCreateInfo{};
descriptorCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
descriptorCreateInfo.flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR;
descriptorCreateInfo.bindingCount = static_cast<uint32_t>(descriptorBindings.size());
descriptorCreateInfo.pBindings = descriptorBindings.data();
if (vkCreateDescriptorSetLayout(renderDevice.logicalDevice, &descriptorCreateInfo, nullptr, &descriptorLayout) != VK_SUCCESS)
throw std::runtime_error("TinyVulkan: Failed to create push descriptor bindings! ");
pipelineLayoutInfo.setLayoutCount = 1;
pipelineLayoutInfo.pSetLayouts = &descriptorLayout;
}
if (vkCreatePipelineLayout(renderDevice.logicalDevice, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS)
throw std::runtime_error("TinyVulkan: Failed to create graphics pipeline layout!");
///////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////
VkPipelineInputAssemblyStateCreateInfo inputAssembly{};
inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
inputAssembly.topology = vertexTopology;
inputAssembly.primitiveRestartEnable = VK_FALSE;
VkPipelineViewportStateCreateInfo viewportState{};
viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewportState.viewportCount = 1;
viewportState.scissorCount = 1;
viewportState.flags = 0;
VkPipelineRasterizationStateCreateInfo rasterizer{};
rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rasterizer.depthClampEnable = VK_FALSE;
rasterizer.rasterizerDiscardEnable = VK_FALSE;
rasterizer.polygonMode = polgyonTopology;
rasterizer.lineWidth = 1.0f;
rasterizer.cullMode = VK_CULL_MODE_BACK_BIT;
rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE;
rasterizer.depthBiasEnable = VK_FALSE;
VkPipelineMultisampleStateCreateInfo multisampling{};
multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
multisampling.sampleShadingEnable = VK_FALSE;
multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
VkPipelineColorBlendStateCreateInfo colorBlending{};
colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
colorBlending.logicOpEnable = VK_FALSE;
colorBlending.logicOp = VK_LOGIC_OP_COPY;
colorBlending.attachmentCount = 1;
VkPipelineColorBlendAttachmentState blendDescription = colorBlendState;
colorBlending.pAttachments = &blendDescription;
colorBlending.blendConstants[0] = 0.0f;
colorBlending.blendConstants[1] = 0.0f;
colorBlending.blendConstants[2] = 0.0f;
colorBlending.blendConstants[3] = 0.0f;
const std::array<VkDynamicState, 2> dynamicStateEnables = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR };
dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
dynamicState.flags = 0;
dynamicState.pDynamicStates = dynamicStateEnables.data();
dynamicState.dynamicStateCount = static_cast<uint32_t>(dynamicStateEnables.size());
dynamicState.pNext = nullptr;
VkPipelineRenderingCreateInfoKHR renderingCreateInfo{};
renderingCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR;
renderingCreateInfo.colorAttachmentCount = 1;
renderingCreateInfo.pColorAttachmentFormats = &imageFormat;
renderingCreateInfo.depthAttachmentFormat = QueryDepthFormat();
VkPipelineDepthStencilStateCreateInfo depthStencilInfo{};
depthStencilInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
depthStencilInfo.depthTestEnable = enableDepthTesting;
depthStencilInfo.depthWriteEnable = enableDepthTesting;
depthStencilInfo.depthCompareOp = VK_COMPARE_OP_LESS;
depthStencilInfo.depthBoundsTestEnable = VK_FALSE;
depthStencilInfo.minDepthBounds = 0.0f; // Optional
depthStencilInfo.maxDepthBounds = 1.0f; // Optional
depthStencilInfo.stencilTestEnable = VK_FALSE;
depthStencilInfo.front = {}; // Optional
depthStencilInfo.back = {}; // Optional
///////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////
VkGraphicsPipelineCreateInfo pipelineInfo{};
pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipelineInfo.stageCount = static_cast<uint32_t>(shaderStages.shaderCreateInfo.size());
pipelineInfo.pStages = shaderStages.shaderCreateInfo.data();
pipelineInfo.pVertexInputState = &vertexInputInfo;
pipelineInfo.pInputAssemblyState = &inputAssembly;
pipelineInfo.pViewportState = &viewportState;
pipelineInfo.pRasterizationState = &rasterizer;
pipelineInfo.pMultisampleState = &multisampling;
pipelineInfo.pColorBlendState = &colorBlending;
pipelineInfo.pDepthStencilState = &depthStencilInfo;
pipelineInfo.pDynamicState = &dynamicState;
pipelineInfo.pNext = &renderingCreateInfo;
pipelineInfo.layout = pipelineLayout;
pipelineInfo.renderPass = nullptr;
pipelineInfo.subpass = 0;
pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; // Optional
pipelineInfo.basePipelineIndex = -1; // Optional
if (vkCreateGraphicsPipelines(renderDevice.logicalDevice, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS)
throw std::runtime_error("TinyVulkan: Failed to create graphics pipeline!");
}
// Creates the GLFW transparent framebuffer enabled window.
TinyVkWindow(std::string title, int width, int height, bool resizable, bool transparentFramebuffer = false, bool hasMinSize = false, int minWidth = 200, int minHeight = 200) {
onDispose.hook(callback<bool>([this](bool forceDispose){this->Disposable(forceDispose); }));
onWindowResized.hook(callback<GLFWwindow*, int, int>([this](GLFWwindow* hwnd, int width, int height) { if (hwnd != hwndWindow) return; hwndWidth = width; hwndHeight = height; }));
onWindowPositionMoved.hook(callback<GLFWwindow*, int, int>([this](GLFWwindow* hwnd, int xpos, int ypos) { if (hwnd != hwndWindow) return; hwndXpos = xpos; hwndYpos = ypos; }));
hwndWindow = InitiateWindow(title, width, height, resizable, transparentFramebuffer);
glfwSetWindowUserPointer(hwndWindow, this);
glfwSetFramebufferSizeCallback(hwndWindow, TinyVkWindow::OnFrameBufferNotifyReSizeCallback);
glfwSetWindowPosCallback(hwndWindow, TinyVkWindow::OnWindowPositionCallback);
if (hasMinSize) glfwSetWindowSizeLimits(hwndWindow, minWidth, minHeight, GLFW_DONT_CARE, GLFW_DONT_CARE);
InitGLFWInput();
}
这里有一个奇怪的地方,透明的帧缓冲区可以工作,帧缓冲区的VkClearColor也可以清除窗口表面,但正如所述,alpha分量被忽略了。这意味着曲面合成期间的Alpha分量由透明颜色的RGB分量的值确定,如下所示,给定暗紫色(RGBA)0.1, 0.0, 0.1, 0.0
的透明颜色。正确的结果应该是透明的,黑色的,没有由于0.0阿尔法不尊重,所以我们得到紫色代替。
1条答案
按热度按时间mrphzbgm1#
好吧,我不完全理解预乘alpha颜色的数学,但一切实际上都按预期工作(感谢Discord图形编程)。问题是,使用预乘alpha时,零alpha分量不是有效的颜色,因此当使用SwapChain渲染时,颜色合成器的行为是意外的-正如前面所说的,这是因为我不能在SwapChain上使用
VK_COMPOSITE_PREMULITPLIED_ALPHA
。然后,解决方案是使用渲染到纹理模型进行渲染,然后将其输出到SwapChain以获得所需的结果。然而,如果你想要显式透明的窗口(GIF录制应用程序或其他东西的Idk),那么预乘alpha实际上是可取的,因为现在你的背景清晰的颜色(如下面的图2所示)与合成清晰的背景预乘。使用render-to-texture然后render-to-swapchain时情况并非如此(下图1)-您会注意到输出图像更暗,因为它是用图像渲染器预乘以黑色的。
这个答案是由图形编程/Vulkan Discord社区提供的。
参考:https://ciechanow.ski/alpha-compositing/