关于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 ...
随机推荐
- learn go anonymous function
package main // 参考文档: // https://github.com/Unknwon/the-way-to-go_ZH_CN/blob/master/eBook/06.8.md im ...
- learn go ifelse
package main // 参考文档: // https://github.com/Unknwon/the-way-to-go_ZH_CN/blob/master/eBook/05.1.md im ...
- 非在线PDF转图片!!!
关于非在线 由于这次要转的是身份证,不是阴谋论,防人之心还是要有的.万一呢. 关于工具 试了好多工具,有一家软件竟然是反过来的,即图片转pdf.也给搜了出来,主要的是下载页面还显示的pdf转图片,啊呸 ...
- swift 3新特性总结
swift新特性之String 参考自[Swift 3.0 变化汇总系列总结-String] 使用方法:直接CMD+F搜索相应的函数或关键字符串,比较修改代码. 重要: /// 使用String的方法 ...
- vue实现简单评分效果
- stm32寄存器版学习笔记07 ADC
STM32F103RCT有3个ADC,12位主逼近型模拟数字转换器,有18个通道,可测量16个外部和2个内部信号源.各通道的A/D转换可以单次.连续.扫描或间断模式执行. 1.通道选择 stm32把A ...
- Could Not Launch Appium Inspector
环境: macOS High Sierra 10.13.2 appium GUI 1.5.3 出现如上图报错时,尝试将App Path和Device Name勾选,如下图:
- LaTex初学
先用三句话来介绍什么是LaTeX:1.LaTeX是一类用于编辑和排版的软件,用于生成PDF文档.2.LaTeX编辑和排版的核心思想在于,通过\section和\paragraph等语句,规定了每一句话 ...
- Portainer docker 可视化管理工具
1. 快速使用 docker run -d -p 9000:9000 portainer/portainer 2. docker swarm 模式 docker service create \ ...
- 请求MWS报错401:Access Denied
跑MWS接口,报错: Caught Exception: Access denied Response Status Code: Error Code: AccessDenied Error Type ...