OpenGL: проблемы с рендерингом в текстуру и объект фреймбуфера

Я хочу визуализировать сцену с изначально пустой текстурой. Для этого я использую объект Framebuffer, к которому прикрепляю пустую 2-мерную текстуру и буфер глубины. После настройки, что касается тестирования, я рисую на сцене простой четырехугольник. Каждая вершина имеет разный цвет, поэтому я в конечном итоге ожидаю, что в текстуре будет интерполированный по цвету четырехугольник. Затем я использую эту текстуру, содержащую четырехугольник, и накладываю ее на другой четырехугольник. Итак, у меня всегда есть четырехугольник в моем кадровом буфере по умолчанию, у которого есть текстура, содержащая цветной четырехугольник. Надеюсь, это не слишком сбивает с толку ...

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

Спасибо Уолтер


Вот код, который у меня есть: // создать объект буфера кадра glGenFramebuffers (1, & frameBufferObject);

// create depth buffer
glGenRenderbuffers(1, &depthAttachment);

// create empty texture
int width = 512;
int height = 512;
int numberOfChannels = 3;
GLuint internalFormat = GL_RGB;
GLuint format = GL_RGB;

unsigned char* texels = new unsigned char[width * height * numberOfChannels];

glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, GL_UNSIGNED_BYTE, texels);

glGenerateMipmap(GL_TEXTURE_2D);

delete[] texels;
texels = NULL;

// activate & bind empty texture
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);

// attach empty texture to framebuffer object
glBindFramebuffer(GL_FRAMEBUFFER, frameBufferObject);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);

// define render targets (empty texture is at GL_COLOR_ATTACHMENT0)
glDrawBuffers(1, GL_COLOR_ATTACHMENT0);

// attach depth buffer
glBindRenderbuffer(GL_RENDERBUFFER, depthAttachment);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthAttachment);

// use framebuffer object
glBindFramebuffer(GL_FRAMEBUFFER, frameBufferObject);

// draw the colored quad into the initially empty texture
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);

// store attibutes
glPushAttrib(GL_VIEWPORT_BIT | GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

// reset viewport
glViewport(0, 0, width, height);

// make background yellow
glClearColor(1.0f, 1.0f, 0.0f, 1.0f);

// draw quad into texture attached to frame buffer object
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
glBegin(GL_QUADS);
    glColor4f(1.0f, 1.0f, 1.0f, 1.0f); glVertex2f(0.0f, 100.0f); // top left
    glColor4f(1.0f, 0.0f, 0.0f, 1.0f); glVertex2f(0.0f, 0.0f); // bottom left
    glColor4f(0.0f, 1.0f, 0.0f, 1.0f); glVertex2f(100.0f, 0.0f); // bottom right
    glColor4f(0.0f, 0.0f, 1.0f, 1.0f); glVertex2f(100.0f, 100.0f); // top right
glEnd();

// restore attributes
glPopAttrib();

glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);

// use default framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, 0);

// clear default framebuffer
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

// draw the scene
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();

glColor4f(1.0, 1.0, 1.0, 1.0);

// begin texture mapping
glEnable(GL_TEXTURE_2D);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

glBegin(GL_QUADS);
    glNormal3d(0.0f, 0.0f, 1.0f);
    glTexCoord2f(0.0f, 0.0f); glVertex3f(0.0f, 50.0f, -100.0f);    // top left
    glTexCoord2f(0.0f, 1.0f); glVertex3f(0.0f, 0.0f, -100.0f);     // bottom left
    glTexCoord2f(1.0f, 1.0f); glVertex3f(50.0f, 0.0f, -100.0f);    // bottom right
    glTexCoord2f(1.0f, 0.0f); glVertex3f(50.0f, 50.0f, -100.0f);   // top right
glEnd();

glDisable(GL_TEXTURE_2D);

glPopMatrix();

// swap buffers (I forgot to mention that I use SDL)
SDL_GL_SwapBuffers();

Ожидаемый результат:

  • Текстура с цветным квадратом на желтом фоне
  • Эта текстура отображается на другой четырехугольник.

Фактический результат:

  • Серая текстура
  • Текстура может быть успешно наложена на другой квад

РЕДАКТИРОВАТЬ: я не упомянул об обработке ошибок. Вот как я проверяю ошибки. Он включает дополнительные случаи, предложенные Полом С. Спасибо за это.

GLuint status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
switch(status) {
    case GL_FRAMEBUFFER_COMPLETE:
        return;
        break;

case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
    throw FramebufferIncompleteException("An attachment could not be bound to frame buffer object!");
    break;

case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
    throw FramebufferIncompleteException("Attachments are missing! At least one image (texture) must be bound to the frame buffer object!");
    break;

case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:
    throw FramebufferIncompleteException("The dimensions of the buffers attached to the currently used frame buffer object do not match!");
    break;

case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:
    throw FramebufferIncompleteException("The formats of the currently used frame buffer object are not supported or do not fit together!");
    break;

case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
    throw FramebufferIncompleteException("A Draw buffer is incomplete or undefinied. All draw buffers must specify attachment points that have images attached.");
    break;

case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
    throw FramebufferIncompleteException("A Read buffer is incomplete or undefinied. All read buffers must specify attachment points that have images attached.");
    break;

case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE:
    throw FramebufferIncompleteException("All images must have the same number of multisample samples.");
    break;

case GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS :
    throw FramebufferIncompleteException("If a layered image is attached to one attachment, then all attachments must be layered attachments. The attached layers do not have to have the same number of layers, nor do the layers have to come from the same kind of texture.");
    break;

case GL_FRAMEBUFFER_UNSUPPORTED:
    throw FramebufferIncompleteException("Attempt to use an unsupported format combinaton!");
    break;

default:
    throw FramebufferIncompleteException("Unknown error while attempting to create frame buffer object!");
    break;
}

РЕДАКТИРОВАТЬ 2: это метод, который я использую для проверки GL-ошибок.

checkForGLErrors(string sourceFile, int line)
    GLenum error = glGetError();
    ostringstream o;

    switch(error) {
        case GL_NO_ERROR:
            return;
            break;

        case GL_INVALID_ENUM:
            o<<"OpenGL Error in "<<sourceFile<<" at line "<<line<<": Invalid enum!"<<endl;
            throw GLErrorException(o.str());
            break;

        case GL_INVALID_VALUE:
            o<<"OpenGL Error in "<<sourceFile<<" at line "<<line<<": Invalid value!"<<endl;
            throw GLErrorException(o.str());
            break;

        case GL_INVALID_OPERATION:
            o<<"OpenGL Error in "<<sourceFile<<" at line "<<line<<": Invalid operation!"<<endl;
            throw GLErrorException(o.str());
            break;

        case GL_STACK_OVERFLOW:
            o<<"OpenGL Error in "<<sourceFile<<" at line "<<line<<": Stack overflow!"<<endl;
            throw GLErrorException(o.str());
            break;

        case GL_STACK_UNDERFLOW:
            o<<"OpenGL Error in "<<sourceFile<<" at line "<<line<<": Stack underflow!"<<endl;
            throw GLErrorException(o.str());
            break;

        case GL_OUT_OF_MEMORY:
            o<<"OpenGL Error in "<<sourceFile<<" at line "<<line<<": Out Of memory!"<<endl;
            throw GLErrorException(o.str());
            break;

        case GL_TABLE_TOO_LARGE:
            o<<"OpenGL Error in "<<sourceFile<<" at line "<<line<<": Table too large!"<<endl;
            throw GLErrorException(o.str());
            break;

        default:
            o<<"OpenGL Error in "<<sourceFile<<" at line "<<line<<": Unknown error!"<<endl;
            throw GLErrorException(o.str());
            break;
    }
}

person Walter    schedule 24.11.2010    source источник
comment
Какую версию OpenGL вы используете? Функции объекта фреймбуфера не имеют суффикса производителя, поэтому я склонен верить 3.0–3.3, но подозрительно. Помните, что есть существенные различия между основными функциями OpenGL 3, GL_ARB_framebuffer_object и GL_EXT_framebuffer_object. Они не равны! Кроме того, недостаточно просто вызвать glCheckFramebufferStatus (). Убедитесь, что вы обрабатываете и обычные ошибки OpenGL с помощью glGetError ()   -  person    schedule 25.11.2010
comment
Ваше предположение верно. Я использую OpenGL 3.1. Я должен был упомянуть об этом раньше. Спасибо за подсказку glGetError (). Я включил это в свой код. Я добавил метод в листинги кода выше. Я думаю, что проверю все ошибки, которые могут быть вызваны OpenGL 3.1, не так ли? Я проверил весь код на наличие ошибок, но вроде ничего нет.   -  person Walter    schedule 25.11.2010
comment
У меня все чаще возникает подозрение, что это не приложение содержит ошибки, а может быть какая-то библиотека или расширение отсутствует или неправильно настроено, соответственно. Не уверен, что это поможет, но это моя система: Windows 7, OpenGL 3.1, NVidia Quadro FX 3700. Я использую GLEW 1.5.7, SDL 1.2, GL_EXT_framebuffer_object, GL_EXT_texture3D, GL_ARB_draw_buffers ... Любое идеи ценятся.   -  person Walter    schedule 26.11.2010


Ответы (3)


Я решил эту проблему.

Итак, вот что я делаю. Может быть, есть какой-нибудь лучший / умный способ сделать это, но сейчас он работает отлично. Не стесняйтесь предлагать адаптации ...

Я не перечисляю обработку ошибок ниже. Вы можете найти это далее в этом вопросе.


// create frame buffer object
glGenFramebuffers(1, frameBufferObject);

// create empty texture
int width = 512;
int height = 512;
int numberOfChannels = 3;
GLuint internalFormat = GL_RGB8;
GLuint format = GL_RGB;

unsigned char* texels = new unsigned char[width * height * numberOfChannels];

glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, GL_UNSIGNED_BYTE, texels);

delete[] texels;
texels = NULL;

// draw the colored quad into the initially empty texture
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);

// store attibutes
glPushAttrib(GL_VIEWPORT_BIT | GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

// reset viewport
glViewport(0, 0, width, height);

// setup modelview matrix
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();

// setup projection matrix
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();

// setup orthogonal projection
glOrtho(-width / 2, width / 2, -height / 2, height / 2, 0, 1000);

// bind framebuffer object
glBindFramebuffer(GL_FRAMEBUFFER, frameBufferObject);

// attach empty texture to framebuffer object
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);

// check framebuffer status (see above)

// bind framebuffer object (IMPORTANT! bind before adding color attachments!)
glBindFramebuffer(GL_FRAMEBUFFER, frameBufferObject);

// define render targets (empty texture is at GL_COLOR_ATTACHMENT0)
glDrawBuffers(1, GL_COLOR_ATTACHMENT0); // you can of course have more than only 1 attachment

// activate & bind empty texture
// I figured activating and binding must take place AFTER attaching texture as color attachment
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);

// clear color attachments
glClear(GL_COLOR_BUFFER_BIT);

// make background yellow
glClearColor(1.0f, 1.0f, 0.0f, 1.0f);

// draw quad into texture attached to frame buffer object
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
glBegin(GL_QUADS);
    glColor4f(1.0f, 1.0f, 1.0f, 1.0f); glVertex2f(0.0f, 100.0f); // top left
    glColor4f(1.0f, 0.0f, 0.0f, 1.0f); glVertex2f(0.0f, 0.0f); // bottom left
    glColor4f(0.0f, 1.0f, 0.0f, 1.0f); glVertex2f(100.0f, 0.0f); // bottom right
    glColor4f(0.0f, 0.0f, 1.0f, 1.0f); glVertex2f(100.0f, 100.0f); // top right
glEnd();

// reset projection matrix
glMatrixMode(GL_PROJECTION);
glPopMatrix();

// reset modelview matrix
glMatrixMode(GL_MODELVIEW);
glPopMatrix();

// restore attributes
glPopAttrib();

glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);

// I guess, now it's OK to create MipMaps

// draw the scene
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();

glColor4f(1.0, 1.0, 1.0, 1.0);

// begin texture mapping
glEnable(GL_TEXTURE_2D);
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

    // normal faces "camera"
    glNormal3d(0.0f, 0.0f, 1.0f);

    glBegin(GL_QUADS);
        glNormal3d(0.0f, 0.0f, 1.0f);
        glTexCoord2f(0.0f, 0.0f); glVertex3f(0.0f, 50.0f, -100.0f);    // top left
        glTexCoord2f(0.0f, 1.0f); glVertex3f(0.0f, 0.0f, -100.0f);     // bottom left
        glTexCoord2f(1.0f, 1.0f); glVertex3f(50.0f, 0.0f, -100.0f);    // bottom right
        glTexCoord2f(1.0f, 0.0f); glVertex3f(50.0f, 50.0f, -100.0f);   // top right
    glEnd();

glDisable(GL_TEXTURE_2D);

glPopMatrix();

// finish rendering
glFlush();
glFinish();

// swap buffers (I forgot to mention that I use SDL)
SDL_GL_SwapBuffers();

// do the clean up!

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

Надеюсь, это будет полезно для кого-нибудь, Уолтер

person Walter    schedule 03.12.2010
comment
Похоже, это было прикрепление буфера глубины, но я бы ожидал, что это выдаст ошибку, если бы это было так. - person Paul S; 15.12.2010
comment
вы должны использовать malloc для больших массивов, иначе вы буквально переполните стек;) - person P i; 20.05.2011

Как ни странно, glDrawBuffers и glFramebufferTexture2D не похоже на спецификации! Первый принимает массив GLenum, а не GLenum (проверьте предупреждения: он может быть приведен без уведомления!). А второй - всего 4 аргумента в спецификации!

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

void glDrawBuffers(GLsizei  n,  const GLenum * bufs);
person tibur    schedule 24.11.2010
comment
Хороший улов. Один glFramebufferTexture2D связан с тем, что версия EXT имеет 5 аргументов, а версия Core - 4. GL_TEXTURE_2D не требуется. - person Paul S; 24.11.2010
comment
Что ж, это правда, что glFramebufferTexture2D принимает только 4 аргумента. Но если я опущу GL_TEXTURE_2D, я не смогу скомпилировать приложение. ошибка C2198: «PFNGLFRAMEBUFFERTEXTURE2DPROC»: слишком мало аргументов для вызова. Думаю, есть еще несколько неправильных / отсутствующих. Я собираюсь проверить, действительно ли у меня последняя версия glew.h - person Walter; 25.11.2010
comment
Меня немного смущает другое утверждение. Разве glDrawBuffers (1, GL_GL_COLOR_ATTACHMENTn) не в порядке? Цитата: Выходное значение фрагментного шейдера записывается во вложенный n-й цвет текущего фреймбуфера. n может варьироваться от 0 до значения GL_MAX_COLOR_ATTACHMENTS. - person Walter; 25.11.2010
comment
Я добавил подпись для функции glDrawBuffers. Я думаю, вам нужно использовать: GLenum att = GL_GL_COLOR_ATTACHMENT;, а затем glDrawBuffers(1,&att);, поскольку glDrawBuffers ожидает массив GLenum в качестве параметра. - person tibur; 25.11.2010
comment
Думаю, вы правы в том, что с glDrawBuffers () что-то не так. У меня здесь две разные спецификации. Тот, который вы дали, и еще один, написанный на немецком языке. Последний говорит в основном то же самое, но не упоминает, что я могу использовать GL_COLOR_ATTACHMENTn. Вместо этого он говорит, что можно использовать GL_AUXi, что, как ни странно, не упоминается в английских спецификациях. Однако GL_AUXi автоматически выдает ошибку! Это дает GL_INVALID_OPERATION. То же самое для GL_FRONT_LEFT, GL_FRONT_RIGHT, GL_BACK_LEFT и GL_BACK_RIGHT. Только GL_COLOR_ATTACHMENTn на самом деле не вызывает ошибки. - person Walter; 25.11.2010
comment
Согласно спецификациям, существует 3 случая, когда возникает ошибка GL_INVALID_OPERATION. Я могу подтвердить два из них. Однако меня интересует вот это: (цитата) GL_INVALID_OPERATION генерируется, если какая-либо из записей в bufs (кроме GL_NONE) указывает на буфер цвета, который не существует в текущем контексте GL. Интересно, действительно ли мне нужно регистрировать GL_AUXi или GL_FRONT_LEFT и последователей в качестве цветовых буферов. Имеет ли это хоть какой-то смысл? Если да, то как мне это сделать? - person Walter; 25.11.2010

Я тоже ожидаю, что происходит:

  • Вы выполняете рендеринг с незанятого уровня MIP-карты. Вы вызываете glGenerateMipmap задолго до того, как визуализируете цветной квадрат. Просто попробуйте GL_NEAREST сейчас.
  • Вы получаете сообщение об ошибке при создании объекта фреймбуфера. Если это произойдет, ваш рендеринг просто упадет в черную дыру.

Вот фрагмент кода, который я использую для проверки состояния буфера кадра. Он использует версию FBO EXT и находится в Objective-C (отсюда и строки @ ""), но это должно быть очевидно. Это происходит после того, как я вызвал glBindFramebuffer, glFramebufferTexture2D и glDrawBuffers.

    GLenum framebufferStatus = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);

    switch (framebufferStatus) {
        case GL_FRAMEBUFFER_COMPLETE_EXT: break;
        case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:
            NSLog(@"Framebuffer Object %d Error: Attachment Point Unconnected", i);
            break;
        case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT:
            NSLog(@"Framebuffer Object %d Error: Missing Attachment", i);
            break;
        case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:
            NSLog(@"Framebuffer Object %d Error: Dimensions do not match", i);
            break;
        case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:
            NSLog(@"Framebuffer Object %d Error: Formats", i);
            break;
        case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:
            NSLog(@"Framebuffer Object %d Error: Draw Buffer", i);
            break;
        case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:
            NSLog(@"Framebuffer Object %d Error: Read Buffer", i);
            break;
        case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
            NSLog(@"Framebuffer Object %d Error: Unsupported Framebuffer Configuration", i);
            break;
        default:
            NSLog(@"Framebuffer Object %d Error: Unkown Framebuffer Object Failure", i);
            break;
    }

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

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

person Paul S    schedule 24.11.2010
comment
Извините, я пропустил обработку ошибок в приведенном выше коде. Я проверяю статус фреймбуфера. Однако я решил, что не проверяю все возможные ошибки. Я забыл проверить GL_FRAMEBUFFER_INCOMPLETE_DIMENSION_EXT и GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT, которые, конечно, очень существенны. Я добавил эти два случая. К сожалению, это не изменило ситуацию и не вернет сообщение об ошибке. Я добавил свою проверку ошибок в приведенный выше код. - person Walter; 25.11.2010
comment
Я попробовал GL_NEAREST вместо GL_LINEAR и пропустил создание mipmap. Тоже не работает. - person Walter; 25.11.2010
comment
Я теперь также не активирую и не привязываю текстуры перед фактическим наложением текстуры. Все еще такой же... - person Walter; 25.11.2010