Search code examples
c++graphicsvulkan

Vulkan offscreen renderer question regarding an example code from Sacha Willems


Can someone explain how Sascha Willem's offscreen example works?

In his offscreen example it looks like the scene is SUPPOSED to be first rendered onto an offscreen framebuffer and then added onto the second render. However I can't quite see how this happens, in the command buffer building code I don't see how the offscreen framebuffer ends up being presented.

In a different example raytracingbasic I see the following code which I feel is easier to understand (I believe here the ray traced image simply gets copied onto the swapchain presentation image, thereby allowing for presentation onto the screen)

vkCmdCopyImage(drawCmdBuffers[i], storageImage.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, swapChain.images[i], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &copyRegion);

Solution

  • There is no presentation of the offscreen image. The offscreen image is displayed in the second render pass by drawing a mirror plane that samples from the offscreen image.

    Copying the image like in the ray tracing sample you mentioned is not possible in that case, sine the mirror plane is a 3D object and not a full screen triangle that never changes size or orientation.

    This is done by setting up a combined image sampler descriptor from the offscreen image in setupDescriptorSet:

    std::vector<VkWriteDescriptorSet> writeDescriptorSets =
    {
        // Binding 0 : Vertex shader uniform buffer
        vks::initializers::writeDescriptorSet(
            descriptorSets.mirror,
            VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
            0,
            &uniformBuffers.vsMirror.descriptor),
        // Binding 1 : Fragment shader texture sampler
        vks::initializers::writeDescriptorSet(
            descriptorSets.mirror,
            VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
            1,
            &offscreenPass.descriptor),
    };
    

    The sampling itself is then done in the fragment shader (mirror.frag):

    layout (binding = 1) uniform sampler2D samplerColor;
    ...
        if (gl_FrontFacing) 
        {
            // Only render mirrored scene on front facing (upper) side of mirror surface
            vec4 reflection = vec4(0.0);
            for (int x = -3; x <= 3; x++)
            {
                for (int y = -3; y <= 3; y++)
                {
                    reflection += texture(samplerColor, vec2(projCoord.s + x * blurSize, projCoord.t + y * blurSize)) / 49.0;
                }
            }
            outFragColor += reflection;
        };