android openGL ES blur effect gives line on screen

34 views Asked by At

I am trying to implement a post-processing blur effect on a map drawn using openGL on my android app project.

What I try to do is render my map in a texture, then render the texture on screen using a fragment shader that apply my gaussian blur in a single pass. I have successfully render the texture making a passthrough shader, however applying a simple gaussian blur gives my a 5 pixel line in the middle of my screen, and I cannot figure it out (been trying for several hours)

Here are my shaders:

var vertexShaderCode = "" +
        "precision mediump float;" +
        "attribute vec2 inPos;" +
        "varying   vec2 pos;" +

        "void main()" +
        "{" +
        "    pos = inPos;" +
        "    gl_Position = vec4( inPos, 0.0, 1.0 );" +
        "}"

var fragmentShaderCode = "" +
        "precision mediump float;" +
        "uniform sampler2D sample_texture;" +
        "uniform vec2 u_textureSize;" +
        "varying vec2 pos;" +

        "float normpdf(in float x, in float sigma)" +
        "{" +
        "    return 0.39894*exp(-0.5*x*x/(sigma*sigma))/sigma;" +
        "}" +

        "void main()" +
        "{" +
        "    /* Return colour. */" +
        "    vec2 texC     = pos.st * 0.5 + 0.5;" +
        "    vec4 texCol   = texture2D(sample_texture, texC);" +

        "int mSize = 11;" +
        "int kSize = int((float(mSize) - 1.0) / 2.0);" +
        "float kernel[11];" +
        "vec3 final_color = vec3(0.0, 0, 0);" +

        // Create the kernel
        "float sigma = 10.0;" +
        "float Z = 0.0;" +
        "for (int j = 0; j <= kSize; ++j)" +
        "{" +
        "    kernel[kSize + j] = kernel[kSize - j] = normpdf(float(j), sigma);" +
        "}" +

        //get the normalization factor (as the gaussian has been clamped)
        "for (int j = 0; j < mSize; ++j)" +
        "{" +
        "    Z += kernel[j];" +
        "}" +

        //read out the texels
        "for (int i = -kSize; i <= kSize; ++i)" +
        "{" +
        "    for (int j = -kSize; j <= kSize; ++j)" +
        "    {" +
        "        vec2 tmp = texC + vec2(float(i), float(j)) / u_textureSize;" +
        "        final_color += kernel[kSize + j] * kernel[kSize + i] * texture2D(sample_texture, tmp).rgb;" +
        "    }" +
        "}" +

        "gl_FragColor = vec4(final_color / (Z*Z), 1.0);" +
        // This gives my a working passthrough shader
        //"    gl_FragColor = texCol;" +
        "}"

Here is how I create the texture and use it back after rendering the scene:

    /* Generate objects. */
    GLES20.glGenFramebuffers(1, framebufferObjectIdPtr)
    GLES20.glGenTextures    (1, originalTextureObjectIdPtr)

    /* Bind generated framebuffer and texture objects to specific binding points. */
    GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, framebufferObjectIdPtr[0])
    GLES20.glBindTexture  (GLES20.GL_TEXTURE_2D,    originalTextureObjectIdPtr[0])
    GLES20.glTexImage2D   (GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, width, height, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null)
    GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE)
    GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE)
    GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR)
    GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR)

    /* attach generated texture objects to the framebuffer object at specific binding points. */
    GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, originalTextureObjectIdPtr[0], 0)

    /* Clear the framebuffer's content. */
    GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT or GLES20.GL_DEPTH_BUFFER_BIT)

    /* Draw scene. */

    /* Get texture sample uniform location. */
    var sampleTextureUniformLocation = GLES20.glGetUniformLocation(mProgram, "sample_texture");
    val locTexSize: Int = glGetUniformLocation(mProgram, "u_textureSize")

    val texSize = ByteBuffer.allocateDirect(8).run {
        order(ByteOrder.nativeOrder())
        asFloatBuffer().apply {
            put(floatArrayOf(width.toFloat(), height.toFloat()))
            position(0)
        }
    }
    /* Set uniform value. */
    GLES20.glUniform1i(sampleTextureUniformLocation, 0);
    GLES20.glUniform2fv(locTexSize, 1, texSize)

    GLES20.glUseProgram(mProgram)

    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, originalTextureObjectIdPtr[0]);
    GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0)

    GLES20.glEnableVertexAttribArray(quadPositionHandle)

    GLES20.glDrawArrays( GLES20.GL_TRIANGLE_STRIP, 0, 4 )
    GLES20.glDisableVertexAttribArray(quadPositionHandle)

I was thinking that my error would be in the fragment shader line where I compute the final color, but computing wrongly the adjacent texture coordinates should give me wrong result but everywhere on the screen, what I get is an horizontal 5 pixel brown line in the middle of the screen. Did I miss something in the framebuffer init that may cause a wrong layout ? On the other hand, the passthrough works...

Can someone point me on the right direction ?

0

There are 0 answers