2012-02-17 2 views
1

У меня проблема с моими шейдерами glsl на устройстве Android 2.3.4 Tegra 2.Почему текстуры-координаты GLSL в моем шейдере не линейны?

Я хочу сделать некоторые вычисления, чтобы создать координаты текстуры в шейдере фрагмента в 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); 
+0

Попробуйте изменить GL_NEAREST на GL_LINEAR –

+0

Я сделал, и это не повлияло. – HardCoder

ответ

1

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

Спасибо за ваш ответ. Когда моя третья тестовая программа возвращает 127, но это также может быть связано с некоторой ошибкой округления (не уверен). Тот же шейдер на моем рабочем столе работает и возвращает ожидаемые значения. Может быть, что-то я могу сделать, чтобы добиться ожидаемого поведения? Я хочу индексировать текстуру (значение байта -> 8-битная точность) вычисляемые координаты текстуры, и я не уверен, как их вычислить, чтобы учитывать округление. – HardCoder

0

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

precision mediump float; 

к:

precision highp float; 

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

Смежные вопросы