Почему GLSL-текстуры в моем шейдере не линейны? - PullRequest
1 голос
/ 17 февраля 2012

У меня проблема с моими шейдерами glsl на моем устройстве Android 2.3.4 Tegra 2.

Я хочу провести некоторые вычисления, чтобы создать координаты текстуры в фрагментном шейдере в шейдере glsl, и обнаружил проблему, попытался сузить ее и поэтому создал тестовую программу. Я рендерил текстуру шириной 256 пикселей с точностью до 256 пикселей на экране, сохраняю кадровый буфер и проверяю его попиксельно (сравни с исходным кодом), и он отображается правильно. В соответствии со спецификацией opengl, когда я использую координаты текстуры от 0,0 до 1,0, следует выбирать каждый пиксель в центре (0,5 / 256, 1,5 / 256, 2,5 / 256 и т. Д.), Что кажется правильным.

Затем я записал x текстурные координаты в текстуру и также проверил их. Результаты несколько отклонились, и я не знаю почему. Вместо того чтобы получить градиент 0..255, который я ожидал, я получил 1..128,128..255. Это означает, что значение 0 пропускается, а значение 128 появляется дважды, что означает, что они не являются совершенно линейными. Если значение было округлено каким-либо образом, то для меня это не очевидно. Я также проверил субпиксельные значения для текстурных координат, и они - то, что я ожидал. Их значение всегда равно 0,5 / 256, что должно быть половиной пикселя и обеспечивать правильную выборку текстуры в пикселе.

Опять же, текстура, отображаемая с этими координатами, является именно тем, что я хочу, но сами координаты не соответствуют ожиданиям. Это проблема для меня, потому что я хочу рендерить с вычисленными координатами текстуры, и, хотя я рассчитываю их так, чтобы они были правильными, полученная текстура неверна. Мне нужно, чтобы эти координаты были правильными до уровня пикселей, и я не знаю, почему они отключены.

Кто-нибудь знает, что вызывает такое поведение и как я могу его исправить?

Это фрагментный шейдер, который корректно отображает текстуру:

precision mediump   float;
varying vec2        vtex;
uniform sampler2D   samp0;
void main()     {
            gl_FragColor = texture2D(samp0, vtex);
        };

Это фрагментный шейдер, который создает градиент с координатами текстуры 1..128,128..255, а не 0..255, как я ожидал:

precision mediump   float;
varying vec2        vtex;
uniform sampler2D   samp0;
void main()     {
            gl_FragColor = vec4(vtex.x, 0, 0, 1.0);
        };

Это фрагментный шейдер, который создает цвет для субпиксельных значений координат текстуры и доставляет текстуру со всеми значениями 127, как я ожидал, и которая должна быть центром пикселей:

precision mediump   float;
varying vec2        vtex;
uniform sampler2D   samp0;
void main()     {
            gl_FragColor = vec4(fract(vtex.x * 256.0), 0, 0, 1.0);
        };

Остальные тестовые программы абсолютно идентичны. Я только меняю шейдеры. Вот некоторые важные фрагменты кода:

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


//  Initialization

    GLES20.glUseProgram(shader);
    //  Vertex
    vdata.position(0);
    GLES20.glVertexAttribPointer(handlepos, 2, GLES20.GL_FLOAT, false, 4*4, vdata);
    GLES20.glEnableVertexAttribArray(handlepos);
    vdata.position(2);
    GLES20.glVertexAttribPointer(handletex, 2, GLES20.GL_FLOAT, false, 4*4, vdata);
    GLES20.glEnableVertexAttribArray(handletex);
    //  Texture
    GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, maplookupid[1]);
    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_REPEAT );
    GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT );
    GLES20.glUniform1i(handlesampler0, 0);
    GLES20.glDisable(GLES20.GL_DITHER);

//  Rendering

    GLES20.glViewport(0, 0, screenx, screeny);
    //
    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/256.0f));
    vdata.put(0*4+3,   256.0f * (1.0f/256.0f));
    //  
    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/256.0f));
    vdata.put(1*4+3,     0.0f * (1.0f/256.0f));
    //  
    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/256.0f));
    vdata.put(2*4+3,   256.0f * (1.0f/256.0f));
    //  
    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/256.0f));
    vdata.put(3*4+3,     0.0f * (1.0f/256.0f));

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

Ответы [ 2 ]

1 голос
/ 18 февраля 2012

Похоже, у вас проблемы с округлением. Поскольку вы, очевидно, используете 8-битный буфер цвета, цвета преобразуются в значения пикселей, умножая их на 255. Таким образом, значение вашей текстуры 0,5 / 256 умножится на 255, чтобы получить 0,498 для значения пикселя, что в идеальном мире (вычисление с бесконечной точностью на графическом процессоре и округление до ближайшего) будет преобразовано в 0-байтовое значение.

Фактически, из того факта, что вы получаете 1..128,128..255 (128 - повторяющееся значение), это выглядит как округление преобразования вверх (поскольку (127.5 / 256) * 255 равно 127.002 и (128.5 / 256) ) * 255 - 127,998 - это два пикселя, которые преобразуются в 128). Если это то, что происходит, я ожидаю, что ваша третья тестовая программа будет производить 128 в каждом пикселе, а не 127 (так как 0,5 * 255 == 127,5)

В результате вы не можете ожидать чрезвычайно точных вычислений от графического процессора - существует множество источников ошибок округления.

0 голосов
/ 19 марта 2012

Вы можете изменить следующую строку кода:

precision mediump float;

до:

precision highp float;

в коде фрагмента шейдера.

...