Search code examples
c++glslvulkan

Vulkan transparency strangeness


I'm modifying a particle system that had been written by a colleague to render by manual blend / set-pixel in the compute pipeline (!) to draw using 2D graphics. I am seeing some strange issues with transparency.

I've checked most of the possible sources of error - I've definitely enabled blending and the operations look legit:

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_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;

I definitely see transparency in the output. Above certain level of alpha (around 0.2), the image renders correctly. Below that value, the particle renders as solid white.

Rendered particles

The particle texture looks fine in GIMP. If I adjust my fragment shader to show the alpha values, it also looks like what I'd expect:

Shader showing alpha

The vertex and fragment shaders are trivial:

#version 450

layout(binding = 0) uniform UniformBufferObject {
    mat4 proj;
} ubo;

layout(location = 0) in vec2 inPosition;
layout(location = 1) in vec2 inTexCoord;
layout(location = 2) in vec4 inColor;

layout(location = 0) out vec4 fragColor;
layout(location = 1) out vec2 fragTexCoord;

void main()
{
    gl_Position = ubo.proj * vec4(inPosition, 0.0, 1.0);
    fragColor = inColor;
    fragTexCoord = inTexCoord;
}

//----------------------------------------

#version 450

layout(binding = 1) uniform sampler2D texSampler;

layout(location = 0) in vec4 fragColor;
layout(location = 1) in vec2 fragTexCoord;

layout(location = 0) out vec4 outColor;

void main()
{
    outColor = texture(texSampler, fragTexCoord);
}

The rendering code itself:

CommandBufferBeginInfo const beginInfo{};
VK_CHECK_RESULT(vkBeginCommandBuffer(vkCommandBuffer_, &beginInfo));

RenderPassBeginInfo renderPassInfo{};
renderPassInfo.renderPass = *renderPass_;
renderPassInfo.framebuffer = framebuffer_->getVulkanFramebuffer();
renderPassInfo.renderArea.offset = { 0, 0 };
renderPassInfo.renderArea.extent.width = uint32_t(windowSize_.x);
renderPassInfo.renderArea.extent.height = uint32_t(windowSize_.y);

constexpr VkClearValue clearColor = { {{0.0f, 0.0f, 0.0f, 1.0f}} };
renderPassInfo.clearValueCount = 1;
renderPassInfo.pClearValues = &clearColor;
vkCmdBeginRenderPass(vkCommandBuffer_, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
vkCmdBindPipeline(vkCommandBuffer_, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline_);

const VkBuffer vertexBuffers[] = { backgroundVertexBuffer_->getVulkanBuffer(),  particleVertexBuffer_->getVulkanBuffer() };
const VkDescriptorSet descriptorSets[] = { *backgroundDescriptorSet_, *particleDescriptorSet_ };
constexpr VkDeviceSize offsets[] = { 0 };
vkCmdBindIndexBuffer(vkCommandBuffer_, indexBuffer_->getVulkanBuffer(), 0, VK_INDEX_TYPE_UINT16);

// Draw Background
vkCmdBindVertexBuffers(vkCommandBuffer_, 0, 1, &vertexBuffers[0], offsets);
vkCmdBindDescriptorSets(vkCommandBuffer_, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipelineLayout_, 0, 1,
&descriptorSets[0], 0, nullptr);
vkCmdDrawIndexed(vkCommandBuffer_, uint32_t(std::size(Quad::indices)), 1, 0, 0, 0);

// Draw particles.
vkCmdBindVertexBuffers(vkCommandBuffer_, 0, 1, &vertexBuffers[1], offsets);
vkCmdBindDescriptorSets(vkCommandBuffer_, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipelineLayout_, 0, 1,
&descriptorSets[1], 0, nullptr);
for (auto i = 0u; i < effect_->getParticleCount(); ++i)
{
    vkCmdDrawIndexed(vkCommandBuffer_, uint32_t(std::size(Quad::indices)), 1, 0, int32_t(i) * 4, 0);
}

vkCmdEndRenderPass(vkCommandBuffer_);
VK_CHECK_RESULT(vkEndCommandBuffer(vkCommandBuffer_));

Not sure what else it could be? I have no depth buffer so don't have a depth subpass. Could it be that? Doesn't really explain what I see. Any suggestions?


Solution

  • Ok well it was a problem with the blend settings for alpha, which require identical setup to the colour channels for this to work. Apologies for the bandwidth stolen.

    colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
    colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;