Renderuj z ekranu (z FBO i RenderBuffer) i pikselowy transfer koloru, głębi, szablonu
Muszę wyrenderować ekran w OpenGL, a następnie przekazać obraz do QImage. Dodatkowo, tylko do ćwiczeń chciałbym przenieść do CPU także głębokość i bufor szablonu.
Do rysowania poza ekranem użyłem obiektu bufora ramki z buforem renderowania (a nie z teksturą, ponieważ nie potrzebuję tekstury).
Operacja transferu pikseli z buforami kolorów (rzeczywisty obraz surowy) działa. Widzę, czego oczekuję. Ale głębia i szablon nie działają .. dziwny obraz dla głębi i nic dla szablonu.
Po pierwsze, część łatwa, co właściwie rysuję:
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();
Tutaj inicjalizacja FBO i 3 renderowania bufora:
// 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);
I wreszcie 3 metody transferu pikseli:
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;
}
Tutaj 2 zrzut ekranu (kolor i głębokość, z szablonem otrzymuję pusty QImage):
Kolor jest dokładnie tym, co rysuję (odwrócony, ale myślę, że to normalne). Głębokość .. Spodziewam się białego obrazu z gradientowym szarym trójkątem. Na pewno popełniam błąd w formacie obrazu (GL_FLOAT
?) ale wypróbowałem kilka kombinacji i to jest najlepszy wynik .. fioletowe tło z jaskrawymi kolorami w środku .. iz buforem szablonu. Oczekuję białego trójkąta na czarnym tle, ale .. nie mam pojęcia dlaczego nic nie widzę ..
Ok, nigdy nie używaj samego bufora matrycowego, więc trochę się zreformowałem.
deklarując 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);
transfer pikseli na głębokość:
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;
}
szablon jest podobny, ale używap_stencil
z komponentem rgb
.. obraz wynikowy jest czarnym obrazem dla głębokości i szablonu
EDYTOWAĆdzięki odpowiedzi Nicolasa udało mi się użyć bufora renderowania dla bufora głębokości i szablonu oraz wyodrębnić komponent głębi, aby pasował doQImage::Format_ARGB32
z tym kodem:
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;
}
.. Nadal występują problemy ze składnikiem szablonu (poniższy kod nie działa: powoduje zakłócenia obrazu):
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;
}