I am trying to implement the Jump Flood Algorithm (JFA) to compute the Signed Distance Field (SDF) of a glyph atlas for font rendering. I managed to set up my framework (OpenglGL-based) for running the algorithm. My implementation employs two frame buffer objects (FBO): at each step, the first FBO serves as input for the current processing step and the second receives the result. The order is then switched achieving a ping-pong effect. The process is repeated for $n=1+\log_2(max(width, height))$ iterations. In my implementation the $0-th$ step is employed for setting the first FBO up, intermediate steps $0\lt i\lt n$ execute iterations of JFA, and the $n-th$ step is employed for computing the actual distance. The very last execution of the shader causes the result to be drawn on a third frame buffer that is then stored for later use. Eventually, I employed the SDF for font rendering. All in all, the algorithm works but the resulting SDF presents odd artifacts and the final result is very poor.

Here is a portion of the SDF computed by my shader. The artifacts I am referring to can be seen on all letters an they look like "blobs" stretching horizontally from inside each letter. This image is obtained by lowering contrast and increasing brightness.

SDF with artifacts

In this zoomed version of the O letter artifacts can be seen more clearly.

SDF with artifacts, letter O

I noticed that different results are achieved simply by inverting the direction of the stepsize: doubling the step rather than halving it. This trick mitigates the problem but doesn't solve it.

I tried to sample the center of the texel having the distance encoded (add 0.5 to integer coordinates and then normalize) but this modification did not solve the issue I am experiencing.

Here is the first version of my fragment shader, up to now it is the one giving me the best result:

#version 330 core

in vec2 TexCoord;

out vec4 color;

uniform sampler2D inputTexture;

uniform vec2 size;
uniform uint stepNumber;
uniform vec3 solid;

bool IsSeed(vec4 candidate) {
    return candidate.r>.0 && candidate.g>.0;

vec4 JFA(vec2 uv, uint k) {
    float diagonal = 99999.9;
    float stepSize = exp2(k);

    vec2 bestNeighbour = vec2(0.0);
    float bestDistance = diagonal;

    for (int x = -1; x <= 1; x++)
        for (int y = -1; y <= 1; y++) {
            vec4 candidateSeed = texture(
                uv + (vec2(x, y) * stepSize / size)
            if (IsSeed(candidateSeed)) {
                float distance = distance(uv, candidateSeed.rg);
                if (distance <= bestDistance) {
                    bestDistance = distance;
                    bestNeighbour = candidateSeed.rg;
    return vec4(bestNeighbour, 0.0, 1.0);

void main() {
    uint LAST = uint(log2(max(size.x, size.y)));
    vec2 uv = gl_FragCoord.xy/size;
    /* When the step number is 0, we are just preparing the buffer. */
    if (stepNumber==uint(0)) {
        /* Solid is the only color we consider as seeds. */
        if (texture(inputTexture, uv).rgb == solid)
            color = vec4(uv, 0.0, 1.0);
            color = vec4(vec3(0.0), 1.0);
    } else if (uint(1) <= stepNumber && stepNumber < LAST)
        /* Intermediate steps cause the algorithm to proceed. */
        color = JFA(uv, LAST - stepNumber);
    else if (stepNumber == LAST)
        /* The last iteration produces the distance field. */
        color = vec4(
            vec3(distance(uv, texture(inputTexture, uv).rg)),
    else if (stepNumber > LAST)
        color = texture(inputTexture, uv);

I do not understand what I am doing wrong. I had a look a this, tried the exact same approach but the problem is still here. All help is appreciated.

  • $\begingroup$ Have you seen this shader toy? shadertoy.com/view/4syGWK It is a good implementation that you can edit and help understand where issues are in your code. $\endgroup$
    – pmw1234
    Commented Jul 10 at 10:49
  • $\begingroup$ Hello, I am looking at it right now; I never encountered that exact example on ShaderToy. It is amazing, to say the least. $\endgroup$
    – Chaos
    Commented Jul 10 at 13:16
  • $\begingroup$ I have updated the JF algorithm, employing integer math when accessing the center of a given texel with no improvement. I also observed the problem occurs only when at least one of the dimensions of the atlas exceeds 1100 pixels. Under that threshold the SDF is perfect. $\endgroup$
    – Chaos
    Commented Jul 10 at 20:38


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.