Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why are the pixel colors not correct in OpenGL ES 2.0 on Android?

(Edit: I tried a sample that merely painted a triangle without any texture or shaders and only OpenGL-ES 1.1 on my device and could see the same artifacts. I tried the same sample in the emulator and there were no artifacts there at all. Could this be a Tegra 2 issue or do I need to set a specific state or something which is not necessary in the emulator ?)

I am rendering a pixel correct square on the screen but when I make a screengrab and look at the pixel then some of them are slightly off like antialised or filtered or something like that. You only see it when you zoom in on them but that is not the point. I want to do some math in the pixel shader and if the pixels are slightly off and thats no good to me. I need them exactly like in the bitmap I put them in.

Here is an enlarged screengrab of the problem. Around the white Line the dark values are slightly brighter than they should be:

enter image description here

I already tried:

GLES20.glDisable(GLES20.GL_DITHER);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST );
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST );
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 );

but it had no effect. The pixels are just slightly off. Its easy to see between a dark and white pixel because the dark pixel next to it is slightly brighter and checking the r,g,b values with a painting program shows me that too.

Could someone please assist me with this ?

Some more info:

My device's CPU is a Tegra 2.0. I render a 256x256 square exactly into 256x256 pixels (I checked that by taking a screen grab with eclipse DDMS). Transparency is off and the bitmap has an alpha value of 255 for each pixel. I created the surface as a 32bit surface with alpha with:

    glSurface.setEGLConfigChooser(8, 8, 8, 8, 0, 0);

I simplified the code but still can see the problem when I render with following shaders:

    String vshaderquad =    "attribute vec4 a_pos;              \n" +   //  in      position
                            "attribute vec2 a_tex;              \n" +   //  in      Texture coordinates
                            "varying vec2 vtex;                 \n" +   //  out     Texture coordinates
                            "void main(void) {                  \n" +
                            "   gl_Position = vec4(a_pos);      \n" +
                            "   vtex = a_tex;                   \n" +
                            "}";

    String fshaderquad =    "precision mediump  float;                      \n" +
                            "varying vec2       vtex;                       \n" +
                            "uniform sampler2D  samp0;                      \n" +
                            "void main() {                                  \n" +
                            "   gl_FragColor = texture2D(samp0, vtex);      \n" +
                            "}";

and commands:

GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mapid[0]);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST );
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST );
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.glUniform1i(handlesampler0, 0);
GLES20.glDisable(GLES20.GL_DITHER);



GLES20.glViewport(0, 0, screenx, screeny);

float screenx = (float)ddimx;
float screeny = (float)ddimy;
//
vdata.put(0*4+0,     0.0f * (2.0f/screenx));
vdata.put(0*4+1,   256.0f * (2.0f/screeny));
vdata.put(0*4+2,     0.0f * (1.0f/256f));
vdata.put(0*4+3,   256.0f * (1.0f/256f));
//  
vdata.put(1*4+0,     0.0f * (2.0f/screenx));
vdata.put(1*4+1,     0.0f * (2.0f/screeny));
vdata.put(1*4+2,     0.0f * (1.0f/256f));
vdata.put(1*4+3,     0.0f * (1.0f/256f));
//  
vdata.put(2*4+0,   256.0f * (2.0f/screenx));
vdata.put(2*4+1,   256.0f * (2.0f/screeny));
vdata.put(2*4+2,   256.0f * (1.0f/256f));
vdata.put(2*4+3,   256.0f * (1.0f/256f));
//  
vdata.put(3*4+0,   256.0f * (2.0f/screenx));
vdata.put(3*4+1,     0.0f * (2.0f/screeny));
vdata.put(3*4+2,   256.0f * (1.0f/256f));
vdata.put(3*4+3,     0.0f * (1.0f/256f));

GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);

PS: The slight color change is not really visible without extreme zooming in but I want to do calculations with the values and that means they just have to be precise.

Edit: I added a full resolution image. The colors on the lines should go from (0,0,0) to (255,255,255) and are inverse to the background gradient. I made this image only to test the accuracy of the texture and there I found the problem that its not really precise. You have to zoom in though to make out the differences that is why I posted the enlarged image as well. Note: The coordinates are different than in the code posted above but the problem is the same (I tried different onscreen coordinates and the problem persists).

enter image description here

like image 361
HardCoder Avatar asked Feb 14 '12 22:02

HardCoder


1 Answers

Ok, I think I have found the solution.

As I already wrote I made a screenshot with the eclipse DDMS and that seems to be causing the problem. That screenshot is not the exact framebuffer but some kind of smoothing/anti-aliasing is going on there.

This seems not to be the result of the DDMS itself but only appears to happen when used on my actual device. When I tried it with the emulator that problem didn't appear so it might be that my device has a pentile screen (its only released in Japan so far and I found no info material so confirming or denying it, but its sony and sony did use pentile displays in devices) or maybe it has to do with the tegra 2 chip or maybe its some kind of compression that happens when the image is being transfered or maybe for some other reason altogether.

Either way, when I use the following function I can save screenshots that are absolutely perfect as it takes the OpenGL-ES 2.0 Framebuffer and saves it into a png file that I can then easily transfer with a batch file ("adb pull /mnt/sdcard/info/Testfile.txt") and view on my computer:

public boolean SaveFrameBuffer(int x, int y)
{   try
    {   IntBuffer   pinbuffer   = IntBuffer.allocate(x*y*4);
        IntBuffer   poutbuffer  = IntBuffer.allocate(x*y*4);
        int         i,j,z;
        int []      pin         = pinbuffer.array();
        int []      pout        = poutbuffer.array();
        //
        GLES20.glReadPixels(0, 0, x, y, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, pinbuffer);            
        for(i=0;i<y;i++)
        {   for(j=0;j<x;j++)
            {   z = pin[(y-1-i)*x+j];
                pout[i*x+j] = (z & 0xff000000) | ((z >> 16) & 0x000000ff) | ((z << 16) & 0x00ff0000) | (z  & 0x0000ff00); 
            }
        }
        Bitmap map = Bitmap.createBitmap(x, y, Bitmap.Config.ARGB_8888);
        map.setPixels(pout, 0, x, 0,0, x, y);
        OutputStream stream = new FileOutputStream("/sdcard/info/test.png");
        map.compress(CompressFormat.PNG, 100, stream);
        stream.close();
        return true;
    } catch (Exception e)
    {   e.printStackTrace();
        return false;
    }
}
like image 56
HardCoder Avatar answered Oct 20 '22 01:10

HardCoder