关于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 ...
随机推荐
- Git详解之八 Git与其他系统
以下内容转载自:http://www.open-open.com/lib/view/open1328070454218.html Git 与其他系统 世界不是完美的.大多数时候,将所有接触到的项目全部 ...
- C++面向对象的编程思想机器人
C++的面向对象的编程思想如下,一般情况为一个类中包含了这个对象的所有属性与函数,直接调用这个对象就可以对这个对象执行它可以使用的任何操作. #include <iostream> cla ...
- POJ3682King Arthur's Birthday Celebration(数学期望||概率DP)
King Arthur is an narcissist who intends to spare no coins to celebrate his coming K-th birthday. Th ...
- java并发--CountDownLatch、CyclicBarrier和Semaphore
在java 1.5中,提供了一些非常有用的辅助类来帮助我们进行并发编程,比如CountDownLatch,CyclicBarrier和Semaphore,今天我们就来学习一下这三个辅助类的用法. 以下 ...
- 通过Excel生成批量SQL语句,处理大量数据的好办法
我们经常会遇到这样的要求:用户给发过来一些数据,要我们直接给存放到数据库里面,有的是Insert,有的是Update等等,少量的数据我们可以采取最原始的办法,也就是在SQL里面用Insert into ...
- Windows下Redis的使用
Redis介绍 Redis是一个开源的使用ANSI C语言编写.支持网络.可基于内存亦可持久化的日志型.Key-Value数据库,和Memcached类似,它支持存储的value类型相对更多,包括st ...
- nginx unit 安装试用
1. yum 源 nano /etc/yum.repos.d/unit.repo 内容 [unit] name=unit repo baseurl=https://packages.nginx.org ...
- scrapy docker 基本部署使用
1. 简单项目 pip install scrapy scrapy startproject appdemo 2. 项目代码 a. 项目代码结构 ├── Dockerfile ├── READ ...
- vault key 管理工具
Vault is a tool for securely accessing secrets. A secret is anything that you want to tightly contro ...
- c# 启动关闭sql服务
static void Main(string[] args) { ServiceController sc = new ServiceController("MSSQL$SQLEXPRES ...