Отрисовка вне экрана (с FBO и RenderBuffer) и передача цвета, глубины, трафарета в пикселях
Мне нужно отрисовать за пределами экрана в OpenGL, а затем передать изображение в QImage. Плюс, просто для тренировки я хотел бы перенести в процессор также глубину и буфер трафарета.
Для рисования вне экрана я использовал Frame Buffer Object с Render Buffer (а не с текстурой, потому что мне не нужна текстура).
Пиксельная передача с цветными буферами (фактическое необработанное изображение) работает .. Я вижу то, что я ожидаю. Но глубина и трафарет не работают .. странное изображение для глубины и ничего для трафарета.
Во-первых, самое простое, что я на самом деле рисую:
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glTranslatef(-1.5f,0.0f,-6.0f);
glBegin(GL_TRIANGLES);
glColor3f(1.0f,0.0f,0.0f);
glVertex3f( 0.0f, 1.0f, 0.0f);
glColor3f(0.0f,1.0f,0.0f);
glVertex3f(-1.0f,-1.0f, -1.0f);
glColor3f(0.0f,0.0f,1.0f);
glVertex3f( 1.0f,-1.0f, 0.0f);
glEnd();
Здесь инициализация FBO и буфера рендеринга 3:
// frame buffer object
glGenFramebuffers(1, &fboId);
glBindFramebuffer(GL_FRAMEBUFFER, fboId);
// render buffer as color buffer
glGenRenderbuffers(1, &colorBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, colorBuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA, width, height);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
// attach render buffer to the fbo as color buffer
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorBuffer);
// render buffer as depth buffer
glGenRenderbuffers(1, &depthBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, depthBuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
// attach render buffer to the fbo as depth buffer
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthBuffer);
glGenRenderbuffers(1, &stencilBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, stencilBuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX, width, height);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
// attach render buffer to the fbo as stencil buffer
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_BUFFER, GL_RENDERBUFFER, stencilBuffer);
И, наконец, здесь 3 метода для передачи пикселей:
QImage FBO::getImage()
{
// this is called Pixel Transfer operation: http://www.opengl.org/wiki/Pixel_Transfer
uchar *pixels;
pixels = new uchar[width * height * 4];
for(int i=0; i < (width * height * 4) ; i++ ) {
pixels[i] = 0;
}
glBindFramebuffer(GL_FRAMEBUFFER, fboId);
glReadPixels( 0,0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
QImage qi = QImage(pixels, width, height, QImage::Format_ARGB32);
qi = qi.rgbSwapped();
return qi;
}
QImage FBO::getDepth()
{
// this is called Pixel Transfer operation: http://www.opengl.org/wiki/Pixel_Transfer
uchar *pixels;
pixels = new uchar[width * height * 4];
for(int i=0; i < (width * height * 4) ; i++ ) {
pixels[i] = 0;
}
glBindFramebuffer(GL_FRAMEBUFFER, fboId);
glBindRenderbuffer(GL_RENDERBUFFER, depthBuffer);
glReadPixels( 0,0, width, height, GL_DEPTH_COMPONENT, GL_FLOAT, pixels);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
QImage qi = QImage(pixels, width, height, QImage::Format_ARGB32);
qi = qi.rgbSwapped();
return qi;
}
QImage FBO::getStencil()
{
// this is called Pixel Transfer operation: http://www.opengl.org/wiki/Pixel_Transfer
uchar *pixels;
pixels = new uchar[width * height * 4];
for(int i=0; i < (width * height * 4) ; i++ ) {
pixels[i] = 0;
}
glBindFramebuffer(GL_FRAMEBUFFER, fboId);
glBindRenderbuffer(GL_RENDERBUFFER, stencilBuffer);
glReadPixels( 0,0, width, height, GL_STENCIL_INDEX, GL_FLOAT, pixels);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
QImage qi = QImage(pixels, width, height, QImage::Format_ARGB32);
qi = qi.rgbSwapped();
return qi;
}
Вот скриншот 2 (цвет и глубина, с трафаретом я получаю пустой QImage):
Цветной это именно то, что я рисую (перевернул, но я думаю, что это нормально). Глубина .. Я ожидаю белое изображение с градиентом серого треугольника .. Наверняка я делаю какую-то ошибку в формате изображения (GL_FLOAT
?) но я пробовал некоторые комбинации, и это лучший результат ... фиолетовый фон с блестящими цветами внутри него ... и с трафаретным буфером ... я ожидаю контур белого треугольника на черном фоне, но .. понятия не имею, почему я ничего не вижу ..
Ладно, никогда не используйте только трафаретный буфер, поэтому я немного изменил рефакторинг.
при объявлении FBO:
// render buffer for both depth and stencil buffer
glGenRenderbuffers(1, &depthStencilBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, depthStencilBuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
// attach render buffer to the fbo as depth buffer
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, depthStencilBuffer);
пиксельная передача глубины:
QImage FBO::getDepth()
{
std::vector<uchar> pixels;
pixels.reserve(width * height*4);
for(int i=0; i < (width * height*4) ; i++ ) {
pixels.push_back(0);
}
glBindFramebuffer(GL_FRAMEBUFFER, fboId);
glBindRenderbuffer(GL_RENDERBUFFER, depthStencilBuffer);
glReadPixels( 0,0, width, height, GL_DEPTH24_STENCIL8, GL_UNSIGNED_BYTE, pixels.data());
glBindFramebuffer(GL_FRAMEBUFFER, 0);
std::vector<uchar> _pixels;
_pixels.reserve(width * height*4);
for(int i=0; i < (width * height*4) ; i++ ) {
uchar p_red = pixels[i];
uchar p_green = pixels[i+1];
uchar p_blue = pixels[i+2];
uchar p_stencil = pixels[i+3];
_pixels.push_back(p_red);
_pixels.push_back(p_green);
_pixels.push_back(p_blue);
_pixels.push_back(255); // alpha
}
QImage qi = QImage(_pixels.data(), width, height, QImage::Format_ARGB32);
//qi = qi.rgbSwapped();
return qi;
}
трафарет похож, но с использованиемp_stencil
с компонентом RGB
.. В результате получается черное изображение как для глубины, так и для трафарета.
РЕДАКТИРОВАТЬблагодаря ответу Николаса мне удалось использовать буфер визуализации как для буфера глубины, так и для буфера трафарета, и извлечь компонент глубины, чтобы он соответствовалQImage::Format_ARGB32
с этим кодом:
QImage FBO1::getDepth()
{
// sizeof( GLuint ) = 4 byte
// sizeof( uchar ) = 1 byte
std::vector<GLuint> pixels(width * height);
glBindFramebuffer(GL_FRAMEBUFFER, fboId);
glBindRenderbuffer(GL_RENDERBUFFER, depthStencilBuffer);
glReadPixels( 0,0, width, height, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, pixels.data());
glBindFramebuffer(GL_FRAMEBUFFER, 0);
std::vector<uchar> _pixels;
for(int i=0; i < (width * height) ; i++ ) {
GLuint color = pixels[i];
float temp = (color & 0xFFFFFF00) >> 8; // 24 bit of depth component
// remap temp that goes from 0 to 0xFFFFFF (24 bits) to
// _temp that goes from 0 to 0xFF (8 bits)
float _temp = (temp / 0xFFFFFF) * 0xFF;
_pixels.push_back((uchar)_temp);
_pixels.push_back((uchar)_temp);
_pixels.push_back((uchar)_temp);
_pixels.push_back(255);
}
QImage qi = QImage(_pixels.data(), width, height, QImage::Format_ARGB32);
qi = qi.rgbSwapped();
return qi;
}
.. Еще некоторые проблемы с компонентом трафарета (следующий код не работает: он производит глюки изображения):
QImage FBO1::getStencil()
{
// sizeof( GLuint ) = 4 byte
// sizeof( uchar ) = 1 byte
std::vector<GLuint> pixels(width * height);
glBindFramebuffer(GL_FRAMEBUFFER, fboId);
glBindRenderbuffer(GL_RENDERBUFFER, depthStencilBuffer);
glReadPixels( 0,0, width, height, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, pixels.data());
glBindFramebuffer(GL_FRAMEBUFFER, 0);
std::vector<uchar> _pixels;
for(int i=0; i < (width * height); i++ ) {
GLuint color = pixels[i];
uchar temp = (color & 0x000000FF); // last 8 bit of depth component
_pixels.push_back(temp);
_pixels.push_back(temp);
_pixels.push_back(temp);
_pixels.push_back(255);
}
QImage qi = QImage(_pixels.data(), width, height, QImage::Format_ARGB32);
qi = qi.rgbSwapped();
return qi;
}