I'have to render off screen in OpenGL and then pass the image to a QImage. Plus, just for exercise I'd like to transfer to the CPU also the depth and the stencil buffer.
For drawing offscreen I've used Frame Buffer Object with Render Buffer (and not with texture because I don't need the texture).
Pixel transfer operation with color buffers (the actual raw image) works.. I see what I'm expecting. But depth and stencil are not working.. strange image for depth and nothing with for the stencil.
First, the easy part, what I'm actually drawing:
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();
Here the initalization of the FBO and of the 3 render buffer:
// 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);
And finally here 3 methods for the pixel transfer:
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;
}
Here the 2 screenshot (color and depth, with stencil I get an empty QImage):
The color one is exactly what I'm drawing (flipped but it is normal I think). The depth.. I'm expecting a white image with a gradient gray triangle.. For sure I'm making some mistake in the format of the image (GL_FLOAT
?) but I've tried some combinations and this is the best result.. a violet background with a glitchy colors inside it.. and with the stencil buffer.. i'm expecting a white triangle outline in a black background but.. no idea why I don't see anything..
EDIT:
Ok, never use stencil buffer alone, so I've refactored a little bit.
when declaring the 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);
pixel transfer of the depth:
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;
}
the stencil is similar but using p_stencil
with rgb component
.. the result image is a black image for both depth and stencil
EDIT
thanks to Nicolas answer I managed to use a renderbuffer for both depth and stencil buffer and extract the depth component to fit a QImage::Format_ARGB32
with this code:
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;
}
.. Still some problems with stencil component (the following code does not work: it produces glitches image):
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;
}