Рендеринг пинг-понга между двумя FBO завершается неудачно после первого кадра.

Я пытаюсь создать два FBO и реализовать рендеринг в пинг-понге. Но у меня только первый кадр работает правильно. Я пытаюсь смоделировать игру в жизнь, и после первого кадра я получаю только черный экран. Не могли бы вы помочь мне проверить это? Я потратил часы на эту проблему.

редактировать

Возможно я не описал ясно. На самом деле, я хочу использовать textureB в качестве текстуры и визуализировать ее в textureA, затем использовать textureA для рендеринга на экран, а затем наоборот.

Edit Я вижу первый кадр, который является textureB. После прохождения фрагмента шейдер становится черным. Сначала я подозреваю, что фрагментный шейдер изменен таким образом, чтобы вернуть только черный цвет к белому и белый к черному. Это все еще становится черным.

Настройте ФБО и текстуру

glEnable(GL_TEXTURE_2D);
    glGenTextures(1, &textureA);
    glBindTexture(GL_TEXTURE_2D, textureA);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA,
                 GL_UNSIGNED_BYTE, NULL);

    glGenTextures(1, &textureB);
    glBindTexture(GL_TEXTURE_2D, textureB);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

    data=(GLubyte*)malloc(256*256*4*sizeof(GLubyte));
    GLubyte val;
    for (int i = 0; i < 256 * 256 * 4; i+=4) {   
        if (rand()%10 ==1) 
            { val = 0; } 
        else 
            { val = 255; }
        data[i] = data[i+1] = data[i+2] = val;
        data[i+3] = 255;
    }
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);

    glGenFramebuffers(1, &fboA);
    glBindFramebuffer(GL_FRAMEBUFFER, fboA);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureA, 0);

    glGenFramebuffers(1, &fboB);
    glBindFramebuffer(GL_FRAMEBUFFER, fboB);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureB, 0);

Render Loop

if ([context API] == kEAGLRenderingAPIOpenGLES2) {


        if(counter%2==0)
        {
            glUseProgram(automateProg);
            glBindFramebuffer(GL_FRAMEBUFFER, fboA);
            glActiveTexture(GL_TEXTURE0);
            glBindTexture(GL_TEXTURE_2D, textureB);
            glUniform1i(AUTOMATE_TEXT, 0);
            glUniform1f(DU, 1.0/256);
            glUniform1f(DV, 1.0/256);
            // Update attribute values.
            glVertexAttribPointer(ATTRIB_VERTEX_2, 2, GL_FLOAT, 0, 0, squareVertices);
            glEnableVertexAttribArray(ATTRIB_VERTEX_2);

            glVertexAttribPointer(ATTRIB_TEXCOORD_2, 2, GL_FLOAT, GL_FALSE, 0, texCoord);    
            //glEnableVertexAttribArray(ATTRIB_TEXCOORD_2);
            glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
            if (![self validateProgram:automateProg]) {
                NSLog(@"Failed to validate program: %d", automateProg);
                return;
            }

            glBindFramebuffer(GL_FRAMEBUFFER, 0);
            glUseProgram(0);
        }
        else
        {
            glUseProgram(automateProg);            
            glBindFramebuffer(GL_FRAMEBUFFER, fboB);
            glActiveTexture(GL_TEXTURE0);
            glBindTexture(GL_TEXTURE_2D, textureA);
            glUniform1i(AUTOMATE_TEXT, 0);
            glUniform1f(DU, 1.0/256);
            glUniform1f(DV, 1.0/256);
            // Update attribute values.
            glVertexAttribPointer(ATTRIB_VERTEX_2, 2, GL_FLOAT, 0, 0, squareVertices);
            glEnableVertexAttribArray(ATTRIB_VERTEX_2);
            glVertexAttribPointer(ATTRIB_TEXCOORD_2, 2, GL_FLOAT, GL_FALSE, 0, texCoord); 
            //glEnableVertexAttribArray(ATTRIB_TEXCOORD_2);
            glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
            if (![self validateProgram:automateProg]) {
                NSLog(@"Failed to validate program: %d", automateProg);
                return;
            }

            glBindFramebuffer(GL_FRAMEBUFFER, 0);
            glUseProgram(0);
        }

        [(EAGLView *)self.view setFramebuffer];
        glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        if (counter % 2 == 0) {
            glUseProgram(normalProg);
            glActiveTexture(GL_TEXTURE0);
            glBindTexture(GL_TEXTURE_2D, textureB);
            glUniform1i(NORMAL_TEXT, 0);
            glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, 0, 0, squareVertices);
            glEnableVertexAttribArray(ATTRIB_VERTEX);
            glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, 0, texCoord); 
            glEnableVertexAttribArray(ATTRIB_TEXCOORD);
            glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
            if (![self validateProgram:normalProg]) {
                NSLog(@"Failed to validate program: %d", normalProg);
                return;
            }
            glUseProgram(0);

        } else {
            glUseProgram(normalProg);
            glActiveTexture(GL_TEXTURE0);
            glBindTexture(GL_TEXTURE_2D, textureA);
            glUniform1i(NORMAL_TEXT, 0);
            glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, 0, 0, squareVertices);
            glEnableVertexAttribArray(ATTRIB_VERTEX);
            glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, 0, texCoord); 
            glEnableVertexAttribArray(ATTRIB_TEXCOORD);
            glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
            if (![self validateProgram:normalProg]) {
                NSLog(@"Failed to validate program: %d", normalProg);
                return;
            }
            glUseProgram(0);
        }
        counter++;

[(EAGLView *)self.view presentFramebuffer];

Фрагмент шейдера

precision mediump float;
varying vec2 v_texCoord;
uniform sampler2D tex; //the input texture
uniform float du; //the width of the cells
uniform float dv; //the height of the cells
    void main() {
        int count = 0;

        vec4 C = texture2D( tex, v_texCoord );
        vec4 E = texture2D( tex, vec2(v_texCoord.x + du, v_texCoord.y) );
        vec4 N = texture2D( tex, vec2(v_texCoord.x, v_texCoord.y + dv) );
        vec4 W = texture2D( tex, vec2(v_texCoord.x - du, v_texCoord.y) );
        vec4 S = texture2D( tex, vec2(v_texCoord.x, v_texCoord.y - dv) );
        vec4 NE = texture2D( tex, vec2(v_texCoord.x + du, v_texCoord.y + dv) );
        vec4 NW = texture2D( tex, vec2(v_texCoord.x - du, v_texCoord.y + dv) );
        vec4 SE = texture2D( tex, vec2(v_texCoord.x + du, v_texCoord.y - dv) );
        vec4 SW = texture2D( tex, vec2(v_texCoord.x - du, v_texCoord.y - dv) );

        if (E.r == 1.0) { count++; }
        if (N.r == 1.0) { count++; }
        if (W.r == 1.0) { count++; }
        if (S.r == 1.0) { count++; }
        if (NE.r == 1.0) { count++; }
        if (NW.r == 1.0) { count++; }
        if (SE.r == 1.0) { count++; }
        if (SW.r == 1.0) { count++; }

        if ( (count == 2 || count == 3)) {
            gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); //cell lives...
        } else {
            gl_FragColor = vec4(0.0,0.0,0.0, 1.0); //cell dies...
        }
    }

3 ответа

Правильно ли я понимаю ваш код, что вы хотите визуализировать результат в текстуре в первом if-else-блокировать и визуализировать этот результат на экране во втором if-else-блок? Если так, то похоже, что у вас есть ошибка в том, как вы организовали свой ввод и вывод для начала. Вот что происходит в вашем первом проходе (я сократил ваш код):

if(counter%2==0)
{
    glBindFramebuffer(GL_FRAMEBUFFER, fboA); // will render to textureA
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, textureB); // textureB is our input
} else {
    ...
}

if (counter % 2 == 0) {
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, textureB); // textureB still as input? not textureA?
} else {
    ...
}

... и вот что происходит во втором проходе:

if(counter%2==0)
{
    ...
} else {
    glBindFramebuffer(GL_FRAMEBUFFER, fboB); // will render to textureB
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, textureA); // textureA as input
}

if (counter % 2 == 0) {
    ...
} else {
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, textureA); // textureA as input again?
}

Причина, по которой вы видите что-то в первом кадре, заключается в том, что вы фактически визуализируете свои входные данные, а не результат первого прохода. И причина, по которой у вас во втором проходе черный экран, может заключаться в том, что ваш фрагментный шейдер работает неправильно. Судя по вашему шейдерному коду, ошибка в доступе к соседним текселям кажется наиболее вероятной причиной этого. Можете ли вы предоставить значения duа также dv?

Также я не думаю, что использование только одного текстурного блока должно создавать проблемы, как Брэд указывал ранее. Я не уверен в этом, хотя.

Примечание: для пинг-понга вы должны рассмотреть возможность создания ваших FBO в виде массива, чтобы сделать ваш код более читабельным.

РЕДАКТИРОВАТЬ:

У меня проблемы с настройкой униформы du а также dv с glUniform1f(), пытаться glUniform1i() (вам нужно сыграть с float() тогда в вашем шейдере) или glUniform1fv() вместо. Однажды у меня была такая же проблема с драйверами PowerVR GLES2, где эта функция ничего не делала и заставляла униформу быть 0.0,

У вас есть две текстуры, с которыми вы хотели бы иметь дело, но я вижу, что здесь используется только одна текстура. Возможно, если вы привязали свою текстуру FBO к одному текстурному блоку, используя следующий код:

glBindFramebuffer(GL_FRAMEBUFFER, fboA);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, textureA);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, textureB);

или же

glBindFramebuffer(GL_FRAMEBUFFER, fboB);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, textureB);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, textureA);

перед рендерингом каждого кадра он будет правильно читать одну текстуру, привязанную к блоку 0, и выводить через FBO в другую текстуру в блоке 1.

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

Если необходимо получить доступ к цветовой плоскости кадра, текстура должна быть прикреплена к буферу кадра, в который записывается. Если текстура, прикрепленная к буферу кадра, должна быть прочитана в шейдере, то текст должен быть привязан к блоку textur, а индекс блока textue должен быть установлен на юниформу сэмплера текстуры шейдера.

Поскольку вы не можете одновременно читать из буфера кадров и записывать в тот же буфер кадров (это приведет к неопределенному поведению), вы должны рисовать для чтения из одного буфера кадров и для записи во второй буфер кадров.

После каждого кадра буферы кадров должны менять свое место. Буфер, из которого было прочитано, станет буфером, в который будет производиться запись, а буфер, в который была произведена запись, станет буфером, из которого будут прочитаны.

Создайте текстуры для вложений кадрового буфера:

      GLuint colorTexture[2];
glGenTextures( 2, &colorTexture[0] );
glBindTexture( GL_TEXTURE_2D, colorTexture[0] );
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL );
glBindTexture( GL_TEXTURE_2D, colorTexture[1] );
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL );

Создайте буферы кадров:

      GLuint frameBuffer[2];
glGenFramebuffers( 2, &frameBuffer[0] );
glBindFramebuffer( GL_FRAMEBUFFER, frameBuffer[0] );
glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTexture[0], 0 );
glBindFramebuffer( GL_FRAMEBUFFER, frameBuffer[1] );
glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTexture[1], 0 );

Обратите внимание: если требуется буфер глубины или даже буфер трафарета, то , или же должен быть прикреплен к буферу кадра.

Поскольку каждый кадр отрисовывается в буфер кадра, вам необходимо реализовать постобработку, которая переносит цветовую плоскость из буфера кадра в буфер рисования.
Это можно сделать с помощьюglBlitFramebuffer, которые передают прямоугольник значений пикселей из одной области буфера кадра чтения в другую область буфера кадра отрисовки.

       glBindFramebuffer( GL_READ_FRAMEBUFFER, frameBuffer[ ... ]  );
 glBindFramebuffer( GL_DRAW_FRAMEBUFFER, 0 );
 glBlitFramebuffer( 0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST ); 

Ваш основной цикл рендеринга должен выглядеть примерно так:

      int drawFB = 0;
while ( /* ... */ )
{
    int readFB = 1 - drawFB;

    glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer[drawFB]);
    glActiveTexture(GL_TEXTURE0 + 1);
    glBindTexture(GL_TEXTURE_2D, colorTexture[readFB]);

    glProgramUse( /* shader program object */ );
    glUniform1i( /* texture sampler 2D location */, 1 ); 

    // do the drawing
    // ...

    // post processing
    glBindFramebuffer( GL_READ_FRAMEBUFFER, frameBuffer[drawFB]  );
    glBindFramebuffer( GL_DRAW_FRAMEBUFFER, 0 );
    glBlitFramebuffer( 0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST );

    drawFB = 1 - drawFB;
}

В качестве альтернативы вы также можете использовать 1 буфер кадра с 2 цветовыми плоскостями и 2 прикрепленными текстурами. Активируйте поочередно первую или вторую цветовую плоскость:

      GLuint frameBuffer;
glGenFramebuffers( 1, &frameBuffer );
glBindFramebuffer( GL_FRAMEBUFFER, frameBuffer );
glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTexture[0], 0 );
glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, colorTexture[1], 0 );

int drawFB = 0;
while ( /* ... */ )
{
    int readFB = 1 - drawFB;

    glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
    glDrawBuffer( drawFB == 0 ? GL_COLOR_ATTACHMENT0 : GL_COLOR_ATTACHMENT1 );
    glActiveTexture(GL_TEXTURE0 + 1);
    glBindTexture(GL_TEXTURE_2D, colorTexture[readFB]);

    glProgramUse( /* shader program object */ );
    glUniform1i( /* texture sampler 2D location */, 1 ); 

    // do the drawing
    // ...

    // post processing
    glBindFramebuffer( GL_READ_FRAMEBUFFER, frameBuffer[drawFB]  );
    glBindFramebuffer( GL_DRAW_FRAMEBUFFER, 0 );
    glBlitFramebuffer( 0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST );

    drawFB = 1 - drawFB;
}

См. следующий простой пример WebGL для демонстрации процесса:

Другие вопросы по тегам