GPU渲染完数据在显存,回传内存的唯一方式glReadPixels函数
显存也被叫做显示内存、帧缓存,它是用来存储显示芯片处理过或者即将读取的渲染数据。如同计算机的内存一样,显存是用来存储图形数据的硬件。在显示器上显示出的画面是由一个个的像素点构成的,而每个像素点都以4至64位的数据来控制它的亮度和色彩,这些点构成一帧的图形画面。为了保持画面流畅,要输出和要处理的多幅帧的像素数据必须通过显存来保存,达到缓冲效果,再交由显示芯片和CPU调配,最后把运算结果转化为图形输出到显示器上
glReadPixels:读取一些像素。当前可以简单理解为“把已经绘制好的像素(它可能已经被保存到显卡的显存中)读取到内存”。
注意:glReadPixels实际上是从缓冲区中读取数据,如果使用了双缓冲区,则默认是从正在显示的缓冲(即前缓冲)中读取,而绘制工作是默认绘制到后缓冲区的。因此,如果需要读取已经绘制好的像素,往往需要先交换前后缓冲。
颜色缓冲区
OpenGL在绘制图元时,先是在一个缓冲区中完成渲染,然后再把渲染结果交换到屏幕上。我们把这两个缓冲区称为前颜色缓冲区(屏幕)和后颜色缓冲区。在默认情况下,OpenGL命令是在后颜色缓冲区进行渲染的。当然,也可以直接在前颜色缓冲区中进行渲染。
若要在前颜色缓冲区中进行渲染,第一种方法是直接告诉OpenGL希望在前颜色缓冲区中进行绘图,可以调用下面这个函数来实现这个目的:
void glDrawBuffer(Glenum mode);
如果参数mode指定为GL_FRONT,OpenGL就会在前颜色缓冲区中进行渲染;
如果参数mode指定为GL_BACK,那么渲染将在后颜色缓冲区中进行。
在前颜色缓冲区进行渲染的第二种方法是在OpenGL被初始化时简单地不要求进行双缓冲区渲染。进行单缓冲区渲染时,如果希望把渲染结果实际绘制到屏幕上,需要调用glFlush()或glFinsh(),这点非常重要。
OpenGL实现除了支持单纯的前颜色缓冲区和后颜色缓冲区之外,还支持其他模式,如用于立体渲染的左和右缓冲区以及辅助缓冲区。
方法一、保存为图片
QImage* img=new QImage(WINDOW_WIDTH,WINDOW_HEIGHT,QImage::Format_ARGB32);
uchar* tmpBIT = img->bits();
//从颜色缓冲区中读取数据
int tmpPixelSize = WINDOW_WIDTH*WINDOW_HEIGHT * 4;
char* tmpPixelsBuffer = (char*)malloc(tmpPixelSize);
glReadPixels(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT,GL_RGBA, GL_UNSIGNED_BYTE, tmpPixelsBuffer);
for (int y = WINDOW_HEIGHT-1; y >=0 ; y--)
{
for (int x = 0; x < WINDOW_WIDTH; x++)
{
//蓝色
tmpBIT[0] = tmpPixelsBuffer[(y*WINDOW_WIDTH + x) * 4 + 2];
//绿色
tmpBIT[1] = tmpPixelsBuffer[(y*WINDOW_WIDTH + x) * 4 + 1];
//红色
tmpBIT[2] = tmpPixelsBuffer[(y*WINDOW_WIDTH + x) * 4 + 0];
tmpBIT[3] = 100;//不起作用
tmpBIT += 4;
}
}
//从深度缓冲区读取数据
int tmpPixelSize = WINDOW_WIDTH*WINDOW_HEIGHT;
float* tmpPixelsBuffer = new float[ tmpPixelSize ];
//GLfloat* tmpPixelsBuffer = new GLfloat[ tmpPixelSize ];
//glReadBuffer(GL_NONE);
//glReadBuffer(GL_BACK);
glReadPixels(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT,GL_DEPTH_COMPONENT, GL_FLOAT, tmpPixelsBuffer);
GLenum aaa = glGetError();
for (int y = WINDOW_HEIGHT-1; y >=0 ; y--)
{
for (int x = 0; x < WINDOW_WIDTH; x++)
{
float iii=tmpPixelsBuffer[(y*WINDOW_WIDTH + x) + 0];
iii*=255;
//蓝色
tmpBIT[0] = iii;
//绿色
tmpBIT[1] = iii;
//红色
tmpBIT[2] = iii;
tmpBIT[3] = 255;//不起作用
tmpBIT += 4;
}
}
img->save("a.jpg");
方法二、输出到纹理
1、配置FBO,使颜色、深度输出到纹理
glGenFramebuffers(1, &m_fbo);
// Создаем буфер глубины
glGenTextures(1, &m_shadowMap);
glBindTexture(GL_TEXTURE_2D, m_shadowMap);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, WindowWidth, WindowHeight, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(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);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D,
m_shadowMap, 0);
//增加一个颜色缓冲区
glGenTextures(1, &m_shadowColor);
glBindTexture(GL_TEXTURE_2D, m_shadowColor);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, WindowWidth, WindowHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_fbo);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
m_shadowColor, 0);
glDrawBuffer(GL_COLOR_ATTACHMENT0);
// 不允许读写颜色缓冲区,也会禁用片元着色器
//glDrawBuffer(GL_NONE);
//glReadBuffer(GL_NONE);
GLenum Status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (Status != GL_FRAMEBUFFER_COMPLETE){
printf("FB error, status: 0x%x\n", Status);
return false;
}
return true;
2、在默认帧缓冲区(或其他帧缓冲区)使用纹理
glActiveTexture(TextureUnit);
glBindTexture(GL_TEXTURE_2D, m_shadowMap);
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)