关于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 ...
随机推荐
- OC中使用单例模式
static Config * instance = nil; +(Config *) Instance { @synchronized(self) { if(nil == instance) { [ ...
- SVM处理多分类问题(one-versus-rest和one-versus-one的不同)
SVM算法最初是为二值分类问题设计的,当处理多类问题时,就需要构造合适的多类分类器. 目前,构造SVM多类分类器的方法主要有两类:一类是直接法,直接在目标函数上进行修改,将多个分类面的参数求解合并到一 ...
- PHP中echo(),print(),print_r()之间的区别?
echo是PHP语句, print和print_r是函数,语句没有返回值,函数可以有返回值(即便没有用) print只能打印出简单类型变量的值(如int,string) print_r可以打印出复杂类 ...
- CODEVS3013 单词背诵 【Hash】【MAP】
CODEVS3013 单词背诵 题目描述 Description 灵梦有n个单词想要背,但她想通过一篇文章中的一段来记住这些单词. 文章由m个单词构成,她想在文章中找出连续的一段,其中包含最多的她想要 ...
- 在iOS上实现二维码功能
http://blog.csdn.net/abcmx/article/details/8011904 如今二维码随处可见,无论是实物商品还是各种礼券都少不了二维码的身影.而手机等移动设备又成为二维码的 ...
- Balanced Substring
You are given a string s consisting only of characters 0 and 1. A substring [l, r] of s is a string ...
- php7+Redis+Windows7安装 (phpstudy)
1.首先去github网站上下载https://github.com/dmajkic/redis/downloads: 2.根据实际情况,将64bit的内容cp到自定义盘符目录,如D:\Redis; ...
- java 短链接生成
package shorurl; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang. ...
- 【转】linux内核态和用户态的区别
原文网址:http://www.mike.org.cn/articles/linux-kernel-mode-and-user-mode-distinction/ 内核态与用户态是操作系统的两种运行级 ...
- VirtulBox安装虚拟机(鼠标点击时)0x00000000指令引用的0x00000000内存该内存不能为written错误解决方案
这个错误并不是所有人都会用到,我用的是WIN7系统,公司的电脑.查找了很多原因后,发现的确是由于系统主题被破解过的原因. 手工恢复风险太高.通过下面的工具就可以直接恢复.UniversalThemeP ...