【问题标题】:Why are the pixel colors not correct in OpenGL ES 2.0 on Android?为什么在 Android 上的 OpenGL ES 2.0 中像素颜色不正确?
【发布时间】:2012-02-14 22:49:34
【问题描述】:

(编辑:我尝试了一个示例,该示例仅绘制了一个没有任何纹理或着色器的三角形,并且我的设备上只有 OpenGL-ES 1.1,并且可以看到相同的伪影。我在模拟器中尝试了相同的示例,但那里没有伪影这可能是 Tegra 2 的问题,还是我需要设置特定的状态或在模拟器中不需要的东西?)

我正在屏幕上渲染一个像素正确的正方形,但是当我进行屏幕抓取并查看像素时,其中一些像素会略微偏离,例如抗锯齿或过滤或类似的东西。只有在放大它们时才能看到它,但这不是重点。我想在像素着色器中做一些数学运算,如果像素稍微偏离,那对我没有好处。我需要它们,就像我放入的位图中一样。

这是问题的放大屏幕截图。在白线周围,暗值比应有的稍亮:

我已经试过了:

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 );

但它没有效果。像素只是略微偏离。它很容易在暗像素和白色像素之间看到,因为它旁边的暗像素稍微亮一些,并且使用绘画程序检查 r、g、b 值也向我展示了这一点。

有人可以帮我解决这个问题吗?

更多信息:

我设备的 CPU 是 Tegra 2.0。我将一个 256x256 的正方形完全渲染为 256x256 像素(我通过使用 eclipse DDMS 进行屏幕抓取检查了这一点)。透明度关闭,位图每个像素的 alpha 值为 255。我创建了一个带有 alpha 的 32 位表面:

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

我简化了代码,但在使用以下着色器渲染时仍然可以看到问题:

    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" +
                            "}";

和命令:

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:轻微的颜色变化在没有极端放大的情况下是不可见的,但我想用这些值进行计算,这意味着它们必须是精确的。

编辑:我添加了全分辨率图像。线条上的颜色应该从 (0,0,0) 到 (255,255,255) 并且与背景渐变相反。我制作这张图片只是为了测试纹理的准确性,在那里我发现了它不是很精确的问题。您必须放大才能找出差异,这也是我发布放大图像的原因。注意:坐标与上面发布的代码不同,但问题是一样的(我尝试了不同的屏幕坐标,问题仍然存在)。

【问题讨论】:

  • 截屏可以很好地显示问题,但从您写的内容来看,我怀疑它是dithering。这是一个后处理,所以它不会影响您的像素着色器。
  • 您好,感谢您的回复,我添加了一张图片以更好地显示问题。我也不认为这仅仅是一个后处理错误,因为我在着色器中进行了一些计算并且预期值是关闭的,并指出这是问题所在。
  • 我作为一个 Android 笨蛋问:有没有什么方法可以使用 PenTile 显示器并读取从实际显示元素计算出来的颜色?
  • 它是具有普通 tft 的索尼设备(可以是 ips、mva 甚至是超级 tft,因为它具有相对较好的视角并且不会像普通 tn 面板那样反转颜色)我不不认为它是一个带有 pentile 矩阵的显示器。我使用我认为直接在内存缓冲区上工作的 eclipse DDMS 截取屏幕截图。我认为如果有任何与 pentile 相关的更改,它们不应该处于那个级别。但我不确定。
  • 不仅线条附近有“抗锯齿”效果,整个背景也是一个水平渐变(从37 -> 44)。在源纹理中应该是这样的吗?

标签: java android opengl-es textures


【解决方案1】:

好的,我想我已经找到了解决方案。

正如我已经写过的,我使用 Eclipse DDMS 制作了一个屏幕截图,这似乎是导致问题的原因。该屏幕截图不是确切的帧缓冲区,但那里正在进行某种平滑/抗锯齿。

这似乎不是 DDMS 本身的结果,而是仅在我的实际设备上使用时才会发生。当我用模拟器尝试它时,问题没有出现,所以可能是我的设备有一个 pentile 屏幕(它目前只在日本发布,我没有发现任何信息材料所以确认或否认它,但它的索尼和索尼确实做到了在设备中使用 pentile 显示器)或者它可能与 tegra 2 芯片有关,或者它可能是在传输图像时发生的某种压缩,或者可能完全出于其他原因。

无论哪种方式,当我使用以下功能时,我可以保存绝对完美的屏幕截图,因为它需要 OpenGL-ES 2.0 帧缓冲区并将其保存到 png 文件中,然后我可以使用批处理文件轻松传输该文件(“adb pull /mnt/sdcard/info/Testfile.txt") 并在我的电脑上查看:

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;
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-07-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-12
    相关资源
    最近更新 更多