2D Bloom Shader Produces Seam

I have been working on a very very basic bloom shader. The basic idea is that before rendering a frame, I will render it onto a RenderTarget. Then, I render the RenderTarget onto the screen with the bloom shader (with Point filtering because it’s a pixel art game).

However, I can’t figure out why it produces a diagonal (from bottom-left to top-right) seam on the entire screen like this:

In the pixel shader, I find the HSV (similar to HSL) of surrounding pixels and add the Value component of HSV (the V is also multiplied by some weight based on distance) to the current pixel’s Value component. This will brighten the current pixel based on how bright its neighbors are.

Below is my pixel shader. Note that the methods that convert between RGB and HSV are tested and correct. Also, the bloom effect itself is working properly, the only issue is the seam from the bottom-left corner to the top-right corner of the screen. I know there are better ways to implement bloom shaders like with Gaussian Blur, but I want to focus on this simple one for now.

float strength; // passed in from C#
float2 textureSize; // passed in from C#

Texture2D SpriteTexture;
sampler2D SpriteTextureSampler = sampler_state
{
    Texture = <SpriteTexture>;
};

// SOME UNRELATED CODE HERE............

float4 MainPS(VertexShaderOutput input) : COLOR
{
    float2 coords = input.TextureCoordinates;
    float4 color = tex2D(SpriteTextureSampler, coords);
    float3 hsv = RGBtoHSV(color.rgb); // get HSV of this pixel
    
    float2 offset = 1.0f / textureSize; // this is how big a pixel is in UV

    int neighbors = 3; // how many neighbors in each direction to sample
    for (int i = -neighbors; i <= neighbors; i++)
    {
        for (int j = -neighbors; j <= neighbors; j++)
        {
            // sample with the given offset
            if (i == 0 && j == 0)
                continue;
            float2 neighborCoords = coords + float2(offset.x * i, offset.y * j);// get neighbor position in UV
            float4 neighborColor = tex2D(SpriteTextureSampler, neighborCoords);// get neighbor RGB
            float3 neighborHSV = RGBtoHSV(neighborColor.rgb);// get neighbor HSV
            // calculate weight based on the distance to neighbor (closer = more weight)
            float weight = (sqrt(neighbors * neighbors * 2) - length(float2(i, j))) * strength / sqrt(neighbors * neighbors * 2);
            // add the neighbor's influence to the current pixel's V component, which can brighten this pixel
            hsv.z += neighborHSV.z * weight;
        }
    }
    color = float4(HSVtoRGB(hsv), color.a);
    
    return color * input.Color;
}
// SOME UNRELATED CODE HERE..............

After some testing, I found that if I increase neighbors, it will make the seam more visible. Also, if I set strength to 0 in C#, the seam will disappear (but I don’t want to set strength to 0 because that would get rid of the bloom effect entirely).

Check this

The seam is (probably) the edge of the 2 triangles.

1 Like

Thanks for the reply :slight_smile: !
I think you are right, the seam is probably caused by the two triangles that form the quad for the RenderTexture. Does this mean that the pixel shader will run twice for all pixels on the diagonal? Is there a simple way to prevent this repeated computation? If not, is it possible to use only one triangle that covers the entire RenderTexture instead of using a quad in MonoGame (like how they did in WebGL in the StackOverflow link)?

The 2 triangles should be drawing between the same 2 points though, so I don’t see how that would produce a line.

Have you made sure the points are the same points, and don’t just “have the same values”?
Floating point inaccuracy, is what I’m getting at.