Question

The problem is that the result of the FBO copy is filled with whatever pixel is at texture coordinate 0,0 of the source texture.

  1. If I edit the shader to render a gradient based on texture coordinate position, the fragment shader fills the whole result as if it had texture coordinate 0, 0 fed into it.

  2. If I edit the triangle strip vertices, things behave as expected, so I think the camera and geometry is setup right. It's just that the 2-tri quad is all the same color when it should reflect either my input texture or at least my position-gradient shaders!

  3. I've ported this code nearly line for line from a working iOS example.

  4. This is running alongside Unity3D, so don't assume any GL settings are default, as the engine is likely fiddling with them before my code starts.

Here's the FBO copy operation

    GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mFrameBuffer);
    checkGlError("glBindFramebuffer");
    GLES20.glViewport(0, 0, TEXTURE_WIDTH*4, TEXTURE_HEIGHT*4);
    checkGlError("glViewport");
    GLES20.glDisable(GLES20.GL_BLEND);
    GLES20.glDisable(GLES20.GL_DEPTH_TEST);
    GLES20.glDepthMask(false);
    GLES20.glDisable(GLES20.GL_CULL_FACE);
    GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
    GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);
    GLES20.glPolygonOffset(0.0f, 0.0f);
    GLES20.glDisable(GLES20.GL_POLYGON_OFFSET_FILL);
    checkGlError("fbo setup");

    // Load the shaders if we have not done so
    if (mProgram <= 0) {
        createProgram();
        Log.i(TAG, "InitializeTexture created program with ID: " + mProgram);
        if (mProgram <= 0)
            Log.e(TAG, "Failed to initialize shaders!");
    }

    // Set up the program
    GLES20.glUseProgram(mProgram);
    checkGlError("glUseProgram");
    GLES20.glUniform1i(mUniforms[UNIFORM_TEXTURE], 0);
    checkGlError("glUniform1i");

    // clear the scene
    GLES20.glClearColor(0.0f,0.0f, 0.1f, 1.0f);
    checkGlError("glClearColor");
    GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);

    // Bind out source texture
    GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
    checkGlError("glActiveTexture");
    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mSourceTexture);
    checkGlError("glBindTexture");

    GLES20.glFrontFace( GLES20.GL_CW );

    // Our object to render
    ByteBuffer imageVerticesBB = ByteBuffer.allocateDirect(8 * 4);
    imageVerticesBB.order(ByteOrder.nativeOrder());
    FloatBuffer imageVertices = imageVerticesBB.asFloatBuffer();

    imageVertices.put(new float[]{
              -1.0f, -1.0f,
            1.0f,  -1.0f,
            -1.0f,  1.0f,
            1.0f,   1.0f}
    );
    imageVertices.position(0);

    // The object's texture coordinates
    ByteBuffer textureCoordinatesBB = ByteBuffer.allocateDirect(8 * 4);
    imageVerticesBB.order(ByteOrder.nativeOrder());
    FloatBuffer textureCoordinates = textureCoordinatesBB.asFloatBuffer();

    textureCoordinates.put(new float[]{
              0.0f, 1.0f,
            1.0f, 1.0f,
            0.0f, 0.0f,
            1.0f, 0.0f}
    );
    textureCoordinates.position(0); 

    // Update attribute values.
    GLES20.glEnableVertexAttribArray(ATTRIB_VERTEX);
    GLES20.glVertexAttribPointer(ATTRIB_VERTEX, 2, GLES20.GL_FLOAT, false, 0, imageVertices);
    GLES20.glEnableVertexAttribArray(ATTRIB_TEXTUREPOSITON);
    GLES20.glVertexAttribPointer(ATTRIB_TEXTUREPOSITON, 2, GLES20.GL_FLOAT, false, 0, textureCoordinates);
    // Draw the quad
    GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);

If you want to dive in, I've put up a nice gist with the update loop, setup and shaders here: https://gist.github.com/acgourley/7783624

I'm checking the result of this as an Android port to UnityFBO (MIT License) so all help is both appreciated and will be shared more broadly.

Was it helpful?

Solution

The declaration of your vertex shader output and fragment shader input do not mach for the texture coordinate varying (different precision qualifiers). Ordinarily this would not be an issue, but for reasons I will discuss below using highp in your fragment shader may come back to bite you in the butt.


Vertex shader:

attribute         vec4 position;
attribute mediump vec4 textureCoordinate;

varying   mediump vec2 coordinate;

void main()
{
  gl_Position = position;
  coordinate  = textureCoordinate.xy;
}

Fragment shader:

varying highp vec2      coordinate;

uniform       sampler2D texture;

void main()
{
  gl_FragColor = texture2D(texture, coordinate);
}


In OpenGL ES 2.0 highp is an optional feature in fragment shaders. You should not declare anything highp in a fragment shader unless GL_FRAGMENT_PRECISION_HIGH is defined by the pre-processor.

GLSL ES 1.0 Specification - 4.5.4: Available Precision Qualifiers - pp. 36

The built-in macro GL_FRAGMENT_PRECISION_HIGH is defined to one on systems supporting highp precision in the fragment language

     #define GL_FRAGMENT_PRECISION_HIGH 1

and is not defined on systems not supporting highp precision in the fragment language. When defined, this macro is available in both the vertex and fragment languages. The highp qualifier is an optional feature in the fragment language and is not enabled by #extension.


The bottom line is you need to check whether the fragment shader supports highp precision before declaring something highp or re-write your declaration in the fragment shader to use mediump. I cannot see much reason for arbitrarily increasing the precision of the vertex shader coordinates in the fragment shader, I would honestly expect to see it written as highp in both the vertex shader and fragment shader or kept mediump.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top