颜色混合

片段着色器返回的片段颜色需要和原来帧缓冲中对应像素的颜色进行混合。混合的方式有下面两种:

  • 混合旧值和新值产生最终的颜色

  • 使用位运算组合旧值和新值

有两个用于配置颜色混合的结构体。第一个是VkPipelineColorBlendAttachmentState结构体,可以用它来对每个绑定的帧缓冲进行单独的颜色混合配置。第二个是VkPipelineColorBlendStateCreateInfo结构体,可以用它来进行全局的颜色混合配置。对于我们的教程,只使用了一个帧缓冲:

VkPipelineColorBlendAttachmentState colorBlendAttachment = {};
colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
colorBlendAttachment.blendEnable = VK_FALSE;
colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE; // Optional
colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO; // Optional
colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD; // Optional
colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; // Optional
colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; // Optional
colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD; // Optional

通过VkPipelineColorBlendAttachmentState结构体,我们可以对绑定的帧缓冲进行第一类混合方式的配置。第一类混合方式的运算过程类似下面的代码:

if (blendEnable) {
    finalColor.rgb = (srcColorBlendFactor * newColor.rgb) <colorBlendOp> (dstColorBlendFactor * oldColor.rgb);
    finalColor.a = (srcAlphaBlendFactor * newColor.a) <alphaBlendOp> (dstAlphaBlendFactor * oldColor.a);
} else {
    finalColor = newColor;
}

finalColor = finalColor & colorWriteMask;

如果blendEnable成员变量被设置为VK_FALSE,就不会进行混合操作。否则,就会执行指定的混合操作计算新的颜色值。计算出的新的颜色值会按照colorWriteMask的设置决定写入到帧缓冲的颜色通道。

通常,我们使用颜色混合是为了进行alpha混合来实现半透明效果。这时finalColor应该按照下面的方式计算:

finalColor.rgb = newAlpha * newColor + (1 - newAlpha) * oldColor;
finalColor.a = newAlpha.a;

上面的运算,可以通过下面的设置实现:

colorBlendAttachment.blendEnable = VK_TRUE;
colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD;
colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD;

读者可以在Vulkan的规范文档中找到所有VkBlendFactor和VkBlendOp枚举可以进行的运算。

VkPipelineColorBlendStateCreateInfo结构体使用了一个VkPipelineColorBlendAttachmentState结构体数组指针来指定每个帧缓冲的颜色混合设置,还提供了用于设置全局混合常量的成员变量。

VkPipelineColorBlendStateCreateInfo colorBlending = {};
colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
colorBlending.logicOpEnable = VK_FALSE;
colorBlending.logicOp = VK_LOGIC_OP_COPY; // Optional
colorBlending.attachmentCount = 1;
colorBlending.pAttachments = &colorBlendAttachment;
colorBlending.blendConstants[0] = 0.0f; // Optional
colorBlending.blendConstants[1] = 0.0f; // Optional
colorBlending.blendConstants[2] = 0.0f; // Optional
colorBlending.blendConstants[3] = 0.0f; // Optional

如果想要使用第二种混合方式(位运算),那么就需要将logicOpEnable成员变量设置为 VK_TRUE。然后使用logicOp成员变量指定要使用的位运算。需要注意,这样设置后会自动禁用第一种混合方式,就跟对每个绑定的帧缓冲设置blendEnable成员变量为VK_FALSE一样。colorWriteMask成员变量的设置在第二种混合方式下仍然起作用,可以决定哪些颜色通道能够被写入帧缓冲。我们也可以禁用所有这两种混合模式,这种情况下,片段颜色会直接覆盖原来帧缓冲中存储的颜色值。