关于OpenGL Framebuffer Object、glReadPixels与离屏渲染
最近写论文需要用到离屏渲染(主要是因为模型太大普通窗口绘制根本做不了),于是翻阅了红宝书查了下相关api和用法。中文版的红宝书可读性有点差,很多地方翻译地晦涩,但好歹读起来比较快,主要相关章节为第8章和第10章(可以连带把第9章读完以后写GLSL会顺利成章)。貌似superbible可读性更强,但红宝书讲得也差不多了就没再继续看。
由于红宝书过于学术,想动手还是最好查查网上的资料,于是把一些还可以的资料列一下。
关于FBO:
关于glReadPixels:
OpenGL中位图的操作(glReadPixels,glDrawPixels等)
关于在FBO中使用多重采样:
【OpenGL】FBO中多重采样抗锯齿(MSAA:MultiSampling Anti-Aliasing)
总结上面的参考资料,并主要参照红宝书的代码,离屏渲染的代码如下(经测试确实可用):
void *GlWidget::offScreenRender(string file_path) {
int render_width = *window_width_, render_height = *window_height_;
enum { Color, Depth, NumRenderbuffers };
// multi-sampled frame buffer object as the draw target
GLuint framebuffer_ms, renderbuffer_ms[NumRenderbuffers];
// generate color and depth render buffers and allocate storage for the multi-sampled FBO
glGenRenderbuffers(NumRenderbuffers, renderbuffer_ms);
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer_ms[Color]);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, ,
GL_RGBA8, render_width, render_height);
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer_ms[Depth]);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, ,
GL_DEPTH_COMPONENT24, render_width, render_height);
// generate frame buffer object for the multi-sampled FBO
glGenFramebuffers(, &framebuffer_ms);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer_ms);
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_RENDERBUFFER, renderbuffer_ms[Color]);
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
GL_RENDERBUFFER, renderbuffer_ms[Depth]);
if (glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE) {
// draw
glViewport(, , render_width, render_height);
glDrawBuffer(GL_COLOR_ATTACHMENT0); // set draw to the created color render buffer
glEnable(GL_MULTISAMPLE); // antialiasing
glEnable(GL_DEPTH_TEST);
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClearDepth(1.0f);
HMeshModel model;
bool draw_success = drawModel(model, true, file_path.c_str());
// copy to memory
if (draw_success) {
// single-sampled frame buffer object as the server-side copy target and copy-to-memory source
GLuint framebuffer, renderbuffer[NumRenderbuffers];
// generate color and depth render buffers and allocate storage for the single-sampled FBO
glGenRenderbuffers(NumRenderbuffers, renderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer[Color]);
glRenderbufferStorage(GL_RENDERBUFFER,
GL_RGBA, render_width, render_height);
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer[Depth]);
glRenderbufferStorage(GL_RENDERBUFFER,
GL_DEPTH_COMPONENT24, render_width, render_height);
// generate frame buffer object
glGenFramebuffers(, &framebuffer);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer);
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_RENDERBUFFER, renderbuffer[Color]);
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
GL_RENDERBUFFER, renderbuffer[Depth]);
glDrawBuffer(GL_COLOR_ATTACHMENT0);
if (glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE) {
// set up to read from the multi-sampled FBO
glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer_ms);
// copy from the multi-sampled FBO to the single-sampled FBO
glBlitFramebuffer(
, , render_width, render_height,
, , render_width, render_height,
GL_COLOR_BUFFER_BIT, GL_NEAREST);
// create memory storage for the pixel array
int image_bytes = alignInteger(render_width*, BMP_ROW_ALIGN) * render_height;
unsigned char *image = new unsigned char[image_bytes];
// copy pixels to memory from the single-sampled frame bffer
glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer); // set up to read from the single-sampled FBO
glBindBuffer(GL_PIXEL_PACK_BUFFER, ); // do not copy into any server side buffer object
glReadBuffer(GL_COLOR_ATTACHMENT0);
glPixelStorei(GL_PACK_ALIGNMENT, BMP_ROW_ALIGN);
glGetError();
glReadPixels(, , render_width, render_height,
GL_BGR, GL_UNSIGNED_BYTE, image);
GLenum error = checkGLError(__FILE__, __LINE__);
// write to file
string image_path = getFilename(file_path.c_str()) + "_off_screen.bmp";
writeBMP(image_path.c_str(), render_width, render_height, BMP_BGR, image);
delete[] image;
} else {
cout << internalErrorPrefix() << "): frame buffer object not complete" << endl;
}
// delete the created frame buffer objects and render buffer objects
glDeleteRenderbuffers(NumRenderbuffers, renderbuffer);
glDeleteFramebuffers(, &framebuffer);
}
} else {
cout << internalErrorPrefix() << "): frame buffer object not complete" << endl;
}
// delete the created frame buffer objects and render buffer objects
glDeleteRenderbuffers(NumRenderbuffers, renderbuffer_ms);
glDeleteFramebuffers(, &framebuffer_ms);
// bind the read and draw frame buffer to the default (window)
glBindFramebuffer(GL_READ_FRAMEBUFFER, );
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, );
// restore the opengl status for window buffer drawing
initializeGL();
glViewport(, , window_width_, window_height_);
}
window_width_和window_height_分别是窗口的宽高,checkGLError是一个获取openGL错误的函数。虽然函数是一个QT的GLWidget类的一个成员函数,不过由于实际上与类本身的关联并不大,故比较容易粘贴移植到其他场景下(之所以没有做一个可复用的版本可能是觉得这样做起来实在太麻烦而且容易出错,也许以后可以写一个)。使用时把drawModel函数替换成自己的其他可能只要稍微修改下就好了。另附上一些支撑的函数和自己从网上修改得来的写BMP文件的代码:
template<typename T>
T alignInteger (T n, T divisor) {
if (n % divisor != )
n = n - n % divisor + divisor; return n;
} const int BMP_ROW_ALIGN = ;
enum BMPPixelType { BMP_BGR = , BMP_BGRA = };
const int BMP_HEADER_LEN = ;
const static unsigned char pixel_bytes[] = { /*BGR*/, /*BGRA*/ };
const static unsigned char pixel_bits[] = { /*BGR*/, /*BGRA*/ }; bool writeBMP(
const char *file_path, const int width, const int height,
const BMPPixelType pixel_type, const unsigned char *pdata
){
unsigned char header[BMP_HEADER_LEN] = {
0x42, 0x4d, , , , , , , , ,
, , , , , , , , , , , , , , , , , , pixel_bits[pixel_type], ,
, , , , , , , , , , , , , , , , , , , ,
, , ,
}; long file_size = (long)width * (long)height * pixel_bytes[pixel_type] + ;
header[] = (unsigned char)(file_size &0x000000ff);
header[] = (file_size >> ) & 0x000000ff;
header[] = (file_size >> ) & 0x000000ff;
header[] = (file_size >> ) & 0x000000ff; long w = width;
header[] = w & 0x000000ff;
header[] = (w >> ) &0x000000ff;
header[] = (w >> ) &0x000000ff;
header[] = (w >> ) &0x000000ff; long h = height;
header[] = h &0x000000ff;
header[] = (h >> ) &0x000000ff;
header[] = (h >> ) &0x000000ff;
header[] = (h >> ) &0x000000ff; FILE *pfile = NULL; pfile = fopen(file_path, "wb");
if (pfile == NULL) {
fprintf(stderr,
"#error(%s, line %d): open file '%s' for write failed\n",
__FILE__, __LINE__, file_path);
return false;
} fwrite(header, sizeof(unsigned char), , pfile);
int row_length, total_length;
row_length = width * pixel_bytes[pixel_type]; // 每行数据长度大致为图象宽度乘以每像素字节数
row_length = alignInteger(row_length, BMP_ROW_ALIGN); // 修正LineLength使其为4的倍数
total_length = row_length * height; // 数据总长 = 每行长度 * 图象高度 fwrite(pdata, sizeof(unsigned char), (size_t)(long)total_length, pfile); // 释放内存和关闭文件
fclose(pfile);
return true;
}
关于OpenGL Framebuffer Object、glReadPixels与离屏渲染的更多相关文章
- IOS 中openGL使用教程4(openGL ES 入门篇 | 离屏渲染)
通常情况下,我们使用openGL将渲染好的图片绘制到屏幕上,但有时候我们不想显示处理结果,这时候就需要使用离屏渲染了. 正常情况下,我们将屏幕,也就是一个CAEAGLLayer对象作为渲染目标,离屏渲 ...
- Android OpenGL ES 离屏渲染(offscreen render)
通常在Android上使用OpenGL ES,都是希望把渲染后的结果显示在屏幕上,例如图片处理.模型显示等.这种情况下,只需要使用Android API中提供的GLSurfaceView类和Rende ...
- opengl离屏渲染(不需要和窗口绑定,仅当作一个可以渲染一张图片的API使用)+ opencv显示
具体过程参考的是这篇BLOG: http://wiki.woodpecker.org.cn/moin/lilin/swig-glBmpContext 这一片BLOG的代码有个 BOOL SaveBmp ...
- OpenGL于MFC使用汇总(三)——离屏渲染
有时直接创建OpenGL形式不适合,或者干脆不同意然后创建一个表单,正如我现在这个项目,创建窗体不显示,它仅限于主框架.而我只是ActiveX里做一些相关工作,那仅仅能用到OpenGL的离屏渲染技术了 ...
- iOS-----openGL--openGL ES iOS 入门篇4---> 离屏渲染
http://www.cnblogs.com/CoderAlex/p/6604618.html 通常情况下,我们使用openGL将渲染好的图片绘制到屏幕上,但有时候我们不想显示处理结果,这时候就需要使 ...
- WebGL简易教程(十三):帧缓存对象(离屏渲染)
目录 1. 概述 2. 示例 2.1. 着色器部分 2.2. 初始化/准备工作 2.2.1. 着色器切换 2.2.2. 帧缓冲区 2.3. 绘制函数 2.3.1. 初始化顶点数组 2.3.2. 传递非 ...
- NDK OpenGLES3.0 开发(五):FBO 离屏渲染
什么是 FBOFBO(Frame Buffer Object)即帧缓冲区对象,实际上是一个可添加缓冲区的容器,可以为其添加纹理或渲染缓冲区对象(RBO). FBO 本身不能用于渲染,只有添加了纹理或者 ...
- iOS 离屏渲染的研究
GPU渲染机制: CPU 计算好显示内容提交到 GPU,GPU 渲染完成后将渲染结果放入帧缓冲区,随后视频控制器会按照 VSync 信号逐行读取帧缓冲区的数据,经过可能的数模转换传递给显示器显示. G ...
- iOS离屏渲染简书
更详细地址https://zsisme.gitbooks.io/ios-/content/chapter15/offscreen-rendering.html(包含了核心动画) GPU渲染机制: CP ...
随机推荐
- js之隔行换色
HTML <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <ti ...
- Linux系统下位(Ubuntu 11.04) 下安装配置 JDK 7安装步骤指导
第一步:下载jdk下载内容为:jdk分为:jdk-7-linux-i586.tar.gzjdk-7u51-linux-x64.tar.gz(适合64位Linux操作系统) hadoop版本为:hado ...
- 《selenium2 python 自动化测试实战》(16)——js操作补充
js修改readonly属性 我们看到这里日期框标签中有readonly属性,如果我们直接send_keys就无法输入内容,这时我们需要先去掉readonly属性: js ='document.get ...
- 《selenium2 python 自动化测试实战》(11)——selenium安装版本
先和大家说一下selenium环境的问题,大家可以在cmd里先看一下自己的selenium版本: 回车,就可以安装了. 本来想和大家说如何跳过验证码进行登录的,结果好多朋友加我问我环境配置的问题,所以 ...
- hdu4261 Estimation[暴力dp+对顶堆]
https://vjudge.net/problem/HDU-4261 对于一个长2000的数列划分最多25个块,每块代价为块内每个数与块内中位数差的绝对值之和,求最小总代价. 套路化地,设$f[i] ...
- 关于浏览器和IIS基础的简单理解
浏览器 输入域名或者IP地址,按回车访问后:发生了什么??IIS是如何工作的?为什么能这么工作?? 1 浏览器和IIS 分别是两个应用程序:浏览器访问网址实际就是 两个应用程序的数据交互往来: ...
- springboot各种集成
fastjson 两个方法 @Configuration public class WebMvcConfigurer extends WebMvcConfigurerAdapter { @Overri ...
- Eclipse编译问题
问题现象:Maven编译ok,Eclipse始终存在编译错误,点了工程的刷新,没用,点了Eclipse上面的菜单Project -> Clean,也没用.后来看了下工作空间项目目录,发现.cla ...
- Jenkins的项目管理
新建Item 使用Jenkins最重要的是能够创建一些工作流,除了部署,还能做很多流程上的事情.同样,一条条项目建起来需要做一定的管理,在Jenkins首页Jenkins->新建可以按自己的需要 ...
- python--logging库学习_第二波
用Python写代码的时候,在想看的地方写个print xx 就能在控制台上显示打印信息,这样子就能知道它是什么了,但是当我需要看大量的地方或者在一个文件中查看的时候,这时候print就不大方便了,所 ...