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