Страницы

Поиск по вопросам

четверг, 23 января 2020 г.

Проблема в OpenGL с неверными координатами вывода изображения

#java #android #opengl #opengles


Всем привет! Возникла проблема с glViewport в OpenGL ES 2.0 под Android.
Я реализовал следующий алгоритм. Изображение генерируется в текстуру с использованием
FBO. Шейдер, который генерирует изображение, также принимает на вход предыдущую отрисованную
текстуру того же размера. Шейдер использует ее для более быстрой генерации следующей
текстуры. 
Для всего этого у меня есть 2 фреймбуфера (+ экранный фреймбуфер), к каждому из которых
приаттачена 1 текстура. После каждого цикла отрисовки ссылки на фреймбуферы меняются
местами, чтобы каждый раз не копировать предыдущее изображение.
Размер текстуры степени 2, т.е. размеры текстуры и экрана не совпадают.

Итак, проблема в том, что на экран рендерится не вся текстура, а лишь ее часть.

Код отрисовки:

//Меняем местами ссылки на буферы
evenRender = !evenRender;
int srcFB = evenRender? 0:1;
int dstFB = evenRender? 1:0;

//Привязываем буфер, куда будет генерироваться изображение
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, frameBufferID[dstFB]);
//Выбираем размеры окна вывода как размеры экрана устройства
**GLES20.glViewport(0, 0, mScreenWidth, mScreenHeight);**
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

//Выбираем предыдущую отрисованную текстуру
GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + srcFB);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, frameTextureID[srcFB]);

//Устанавливаем шейдер для генерации изображения
GLES20.glUseProgram(sp_ImageGeneration);

int mPositionHandle = GLES20.glGetAttribLocation(sp_ImageGeneration, "vPosition");
GLES20.glVertexAttribPointer(mPositionHandle, 3, GLES20.GL_FLOAT, false, 0, offscreen_vertexBuffer);
GLES20.glEnableVertexAttribArray(mPositionHandle);

int mTexCoordLoc = GLES20.glGetAttribLocation(sp_ImageGeneration, "a_texCoord");
GLES20.glVertexAttribPointer ( mTexCoordLoc, 2, GLES20.GL_FLOAT, false, 0, offscreen_uvBuffer);
GLES20.glEnableVertexAttribArray(mTexCoordLoc);

...

//Отправляем шейдеру предыдущую отрисованную текстуру
int mSamplerLoc = GLES20.glGetUniformLocation (sp_ImageGeneration, "prev_frame" );
GLES20.glUniform1i(mSamplerLoc, srcFB);

GLES20.glDrawElements(GLES20.GL_TRIANGLES, indices.length, GLES20.GL_UNSIGNED_SHORT,
drawListBuffer);

GLES20.glDisableVertexAttribArray(mPositionHandle);
GLES20.glDisableVertexAttribArray(mTexCoordLoc);

//Привязываем экранный буфер
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
GLES20.glViewport(0, 0, mScreenWidth, mScreenHeight);
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

//Выбираем текстуру в том буфере, куда генерировалось изображение
GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + dstFB);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, frameTextureID[dstFB]);

// Устанавливаем простой шейдер
GLES20.glUseProgram(sp_Image);

mPositionHandle = GLES20.glGetAttribLocation(sp_Image, "vPosition");
GLES20.glVertexAttribPointer(mPositionHandle, 3, GLES20.GL_FLOAT, false, 0, vertexBuffer);
GLES20.glEnableVertexAttribArray(mPositionHandle);
mTexCoordLoc = GLES20.glGetAttribLocation(sp_Image, "a_texCoord" );
GLES20.glVertexAttribPointer ( mTexCoordLoc, 2, GLES20.GL_FLOAT, false, 0, uvBuffer);
GLES20.glEnableVertexAttribArray(mTexCoordLoc);

...

//Отправляем шейдеру текстуру, куда генерировалось изображение
mSamplerLoc = GLES20.glGetUniformLocation (sp_Image, "s_texture" );
GLES20.glUniform1i(mSamplerLoc, dstFB);

GLES20.glDrawElements(GLES20.GL_TRIANGLES, indices.length, GLES20.GL_UNSIGNED_SHORT,
drawListBuffer);

GLES20.glDisableVertexAttribArray(mPositionHandle);
GLES20.glDisableVertexAttribArray(mTexCoordLoc);
GLES20.glUseProgram(0);

GLES20.glActiveTexture(0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);


Координаты треугольников, на которые происходит отрисовка:

float koefWidth = (float)texSize / mScreenWidth;
float koefHeight = (float)texSize / mScreenHeight;

vertices = new float[] {
        0, 0, 0f,
        0, mScreenHeight * koefHeight, 0f,
        mScreenWidth * koefWidth, mScreenHeight * koefHeight, 0f,
        mScreenWidth * koefWidth, 0, 0f
};

offscreen_vertices = new float[] {
        0, 0, 0f,
        0, mScreenHeight * koefHeight* koefHeight, 0f,
        mScreenWidth * koefWidth * koefWidth , mScreenHeight * koefHeight* koefHeight, 0f,
        mScreenWidth * koefWidth * koefWidth, 0, 0f
};


texSize - размер текстуры. Например, mScreenWidth = 800, mScreenHeight = 600, texSize
= 1024.

Координаты текстур uvs и offscreen_uvs одинаковы:

uvs = new float[] {
            0, 0,
            0, 1,
            1, 1,
            1, 0,
    };


Фреймбуферы и текстуры для них были созданы так:

    frameBufferID = new int[2];
    frameTextureID = new int[2];

    GLES20.glGenFramebuffers(2, frameBufferID, 0);
    GLES20.glGenTextures(2, frameTextureID, 0);
    GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, frameBufferID[0]);
    GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, frameTextureID[0]);

    GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGB, texSize, texSize,
0, GLES20.GL_RGB, GLES20.GL_UNSIGNED_BYTE, null);
    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.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
    GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
    GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0,
GLES20.GL_TEXTURE_2D, frameTextureID[0], 0);

    //Next framebuffer
    GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, frameBufferID[1]);
    GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + 1);
    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, frameTextureID[1]);
    GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGB, texSize, texSize,
0, GLES20.GL_RGB, GLES20.GL_UNSIGNED_BYTE, null);
    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.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
    GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
    GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0,
GLES20.GL_TEXTURE_2D, frameTextureID[1], 0);


Я очень долго пробовал менять значения в glViewport, выделенном звездочками, а также
координаты треугольников. Но тогда либо возникали графические артефакты, либо сильно
падала производительность.
Заранее спасибо!
    


Ответы

Ответ 1



Вопрос решен. Оказалось, "графические артефакты" были связаны со спецификой работы моего шейдера. Он берет текстуры из фреймбуфера, но я не учитывал то, что эти текстуры были уменьшены glViewport'ом. Из-за этого данные текстур обрабатывались неправильно, что и выливалось в артефакты результирующего изображения. Решилось добавлением в шейдер соответствующих коэффициентов при обработке текстур. Эта проблема мучила меня на протяжении недели, и теперь я очень рад! :)

Комментариев нет:

Отправить комментарий