当前位置: 首页 > news >正文

FBO中多重采样抗锯齿(MSAA MultiSampling Anti-Aliasing)

http://blog.csdn.net/xiajun07061225/article/details/7690805

 

今天在写这样一个程序,就是导入一个OBJ模型然后显示出来的时候,遇到了一个问题。我在程序中开启了多重采样,在屏幕上显示出来的效果确实有抗锯齿。但是当我用FBO离屏渲染,然后保存为BMP图像的时候,发现保存出来的BMP图像并没有抗锯齿效果。

 

问题产生原因及解决方案:

在默认帧缓冲中启用多重采样并不会导致FBO里也会启用多重采样。因此要在FBO里达到多重采样的效果,必须创建适用多重采样的FBO,而不是普通的FBO。

关于FBO介绍及使用可参考:FBO

下面分别介绍多重采样抗锯齿以及怎样在FBO中使用这一技术。

 

一、多重采样抗锯齿技术(multisampling anti-aliasing)

1、抗锯齿技术种类分类

全屏反锯齿
全屏反锯齿(full scene Anti-aliasing,简称FSAA),也称全屏抗锯齿,它指的是利用反锯齿技术对输出到显示器的满屏画面信号进行放大与采样分析并重新制作满屏画面信号输出至显示器,而不是对画面某一部分使用反锯齿技术。全屏反锯齿对于3D游戏画面有着很大的影响,它能使整个3D游戏画面变得细腻、清晰与逼真,这是是一种重要的技术应用。全屏抗锯齿反锯齿技术,有以下方法:
超级采样抗锯齿
超级采样抗锯齿(Super-Sampling Anti-aliasing,简称SSAA)此是早期抗锯齿方法,比较消耗资源,但简单直接,先把图像映射到缓存并把它放大,再用超级采样把放大后的图像像素进行采样,一般选取2个或4个邻近像素,把这些采样混合起来后,生成的最终像素,令每个像素拥有邻近像素的特征,像素与像素之间的过渡色彩,就变得近似,令图形的边缘色彩过渡趋于平滑。再把最终像素还原回原来大小的图像,并保存到帧缓存也就是显存中,替代原图像存储起来,最后输出到显示器,显示出一帧画面。这样就等于把一幅模糊的大图,通过细腻化后再缩小成清晰的小图。如果每帧都进行抗锯齿处理,游戏或视频中的所有画面都带有抗锯齿效果。[2]而将图像映射到缓存并把它放大时,放大的倍数被用于分别抗锯齿的效果,如:图1,AA后面的x2、x4、x8就是原图放大的倍数。 超级采样抗锯齿中使用的采样法一般有两种:
顺序栅格超级采样(Ordered Grid Super-Sampling,简称OGSS),采样时选取2个邻近像素。
旋转栅格超级采样(Rotated Grid Super-Sampling,简称RGSS),采样时选取4个邻近像素。


多重采样抗锯齿
多重采样抗锯齿(MultiSampling Anti-Aliasing,简称MSAA)是一种特殊的超级采样抗锯齿(SSAA)。MSAA首先来自于OpenGl。具体是MSAA只对Z缓存(Z-Buffer)和模板缓存(Stencil Buffer)中的数据进行超级采样抗锯齿的处理。可以简单理解为只对多边形的边缘进行抗锯齿处理。这样的话,相比SSAA对画面中所有数据进行处理,MSAA对资源的消耗
需求大大减弱,不过在画质上可能稍有不如SSAA。

覆盖采样抗锯齿
覆盖采样抗锯齿(CoverageSampling Anti-Aliasing,简称CSAA)

可编程过滤抗锯齿
可编程过滤抗锯齿(Custom Filter Anti-Aliasing)

 

2、多重采样抗锯齿技术详解

首先看下面这幅图,左右对比了采样多重采样抗锯齿技术和不采用的效果对比:

图一 采样多重采样抗锯齿技术前后对比

可以看出,左图边缘有很明显的锯齿状。导致这一现象的原因是:每个像素的绘制是由它是否完全位于多边形内部所决定的。如果它在多边形内部,则渲染它;否则不渲染。很显然,这是不准确的。一些像素恰好位于边缘上面。如果我们依据一个像素它位于多边形内部的区域的大小来决定它的渲染,那么获得的效果要好很多。最终像素的颜色就是多边形颜色和其外部的颜色的混合。你也许会认为这样会导致性能上的消耗,事实却是如此。但是我们可以针对每个像素使用多个采样来近似估计最终结果。

 

MSAA技术包括针对每个像素实施多个采样,然后对这些采样的结果进行混合来决定这个像素的最终值。采样点位于像素内部的不同位置。很显然,大多数的采样点会位于多边形内部,但是对于那些位于多边形边缘的像素,一些采样点会位于多边形外部。

如果对每个像素进行4次采样,那么光栅化的频率将是不进行多重采样的4倍。对于每个像素,片断着色器执行一次,输出结果由4个采样点中位于多边形内部的数量决定。

 

3、在OpenGL中实现MSAA技术

在OpenGL中实施这一技术非常简单,不需要过多的操作。它是通过使用额外的缓冲区来存储子像素样本来实现的。然后这些样本被合成以生成片断的最终颜色。

下面以在Qt中为例来进行说明,它和使用GLUT等API很相似。

(1)在创建OpenGL窗口的时候,需要选择支持MSAA的OpenGL上下文:

 

[cpp] view plain copy print ?
  1. QGLFormat format;  
  2. format.setVersion(4,0);  
  3. format.setProfile(QGLFormat::CoreProfile);  
  4. format.setSampleBuffers(true);  
  5. format.setSamples(4);  
  6. QGLWidget *glView = new QGLWidget(format);  
QGLFormat format;
format.setVersion(4,0);
format.setProfile(QGLFormat::CoreProfile);
format.setSampleBuffers(true);
format.setSamples(4);
QGLWidget *glView = new QGLWidget(format);

(2)判断多重采样缓冲区是否存在,以及对每个像素使用几个采样:

 

 

[cpp] view plain copy print ?
  1. GLint bufs, samples;  
  2. glGetIntegerv(GL_SAMPLE_BUFFERS, &bufs);  
  3. glGetIntegerv(GL_SAMPLES, &samples);  
  4. printf("MSAA: buffers = %d samples = %d\n", bufs, samples);  
GLint bufs, samples;
glGetIntegerv(GL_SAMPLE_BUFFERS, &bufs);
glGetIntegerv(GL_SAMPLES, &samples);
printf("MSAA: buffers = %d samples = %d\n", bufs, samples);

(3)启用MSAA:

 

 

[cpp] view plain copy print ?
  1. glEnable(GL_MULTISAMPLE);  
glEnable(GL_MULTISAMPLE);

(4)如果要禁用MSAA,使用以下代码:

 

 

[cpp] view plain copy print ?
  1. glDisable(GL_MULTISAMPLE);  
glDisable(GL_MULTISAMPLE);

 

 

如果使用GLUT,则使用以下代码:

 

[cpp] view plain copy print ?
  1. //申请一个采用了双重缓存,包含颜色,深度的帧缓存和多重采样。   
  2. glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH | GLUT_MULTISAMPLE);  
  3. glEnable(GL_MULTISAMPLE);//开启多重缓存   
  4. glDisable(GL_MULTISAMPLE);//关闭多重缓存  
//申请一个采用了双重缓存,包含颜色,深度的帧缓存和多重采样。
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH | GLUT_MULTISAMPLE);
glEnable(GL_MULTISAMPLE);//开启多重缓存
glDisable(GL_MULTISAMPLE);//关闭多重缓存

 

二、在FBO中使用多重采样抗锯齿技术

首先我们需要创建1个适用于多重采样的FBO:

 

[cpp] view plain copy print ?
  1.        //创建FBO:multisampling   
  2. glGenFramebuffers(1,&m_frameBufferMS);  
  3. glBindFramebuffer(GL_FRAMEBUFFER,m_frameBufferMS);  
  4.   
  5. glGenRenderbuffers(1,&m_renderBufferColorMS);  
  6. glBindRenderbuffer(GL_RENDERBUFFER,m_renderBufferColorMS);  
  7. glRenderbufferStorageMultisample(GL_RENDERBUFFER,4,  
  8.     GL_RGB,m_subImageWidth,m_subImageHeight);  
  9. glBindRenderbuffer(GL_RENDERBUFFER,0);  
  10. glFramebufferRenderbuffer(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,  
  11.     GL_RENDERBUFFER,m_renderBufferColorMS);  
  12.   
  13. glGenRenderbuffers(1,&m_renderBufferDepthMS);  
  14. glBindRenderbuffer(GL_RENDERBUFFER,m_renderBufferDepthMS);  
  15. glRenderbufferStorageMultisample(GL_RENDERBUFFER,4,  
  16.     GL_DEPTH_COMPONENT24,m_subImageWidth,m_subImageHeight);  
  17. glBindRenderbuffer(GL_RENDERBUFFER,0);  
  18. glFramebufferRenderbuffer(GL_FRAMEBUFFER,GL_DEPTH_ATTACHMENT,  
  19.     GL_RENDERBUFFER,m_renderBufferDepthMS);  
  20.   
  21. glBindFramebuffer(GL_FRAMEBUFFER,0);  
        //创建FBO:multisampling
	glGenFramebuffers(1,&m_frameBufferMS);
	glBindFramebuffer(GL_FRAMEBUFFER,m_frameBufferMS);

	glGenRenderbuffers(1,&m_renderBufferColorMS);
	glBindRenderbuffer(GL_RENDERBUFFER,m_renderBufferColorMS);
	glRenderbufferStorageMultisample(GL_RENDERBUFFER,4,
		GL_RGB,m_subImageWidth,m_subImageHeight);
	glBindRenderbuffer(GL_RENDERBUFFER,0);
	glFramebufferRenderbuffer(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,
		GL_RENDERBUFFER,m_renderBufferColorMS);

	glGenRenderbuffers(1,&m_renderBufferDepthMS);
	glBindRenderbuffer(GL_RENDERBUFFER,m_renderBufferDepthMS);
	glRenderbufferStorageMultisample(GL_RENDERBUFFER,4,
		GL_DEPTH_COMPONENT24,m_subImageWidth,m_subImageHeight);
	glBindRenderbuffer(GL_RENDERBUFFER,0);
	glFramebufferRenderbuffer(GL_FRAMEBUFFER,GL_DEPTH_ATTACHMENT,
		GL_RENDERBUFFER,m_renderBufferDepthMS);

	glBindFramebuffer(GL_FRAMEBUFFER,0);

 

记住需要在应用程序后面进行清除操作:

 

[cpp] view plain copy print ?
  1. glDeleteRenderbuffers(1,&m_renderBufferColorMS);  
  2. glDeleteRenderbuffers(1,&m_renderBufferDepthMS);  
  3. glDeleteFramebuffers(1,&m_frameBufferMS);  
	glDeleteRenderbuffers(1,&m_renderBufferColorMS);
	glDeleteRenderbuffers(1,&m_renderBufferDepthMS);
	glDeleteFramebuffers(1,&m_frameBufferMS);

 

然后在绘制物体的时候进行绑定:

 

[cpp] view plain copy print ?
  1. glBindFramebuffer(GL_FRAMEBUFFER,m_frameBuffer);  
  2. GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);  
  3. if (status != GL_FRAMEBUFFER_COMPLETE)  
  4. {  
  5.     cout << "The frame buffer status is not complete!" << endl;  
  6.     return;  
  7. }  
  8.   
  9. drawing();//draw something   
  10. glBindFramebuffer(GL_FRAMEBUFFER,0);  
glBindFramebuffer(GL_FRAMEBUFFER,m_frameBuffer);
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE)
{
    cout << "The frame buffer status is not complete!" << endl;
    return;
}

drawing();//draw something
glBindFramebuffer(GL_FRAMEBUFFER,0);
接下来我要用函数glReadPixels读取数据,然后保存为BMP图像。在这里我使用了PBO(像素缓冲区对象)。
在这里需要注意的是:不能用glReadPixels直接读取多重采样缓冲区里面的数据,否则会出现GL_INVALID_OPERATION错误。那么应该怎么做呢?

 

一个常用的方法是创建另外一个FBO。它是一个普通的FBO,用于进行传图操作(Blit).

创建用于Blit的普通FBO:

 

[cpp] view plain copy print ?
  1.        //创建普通FBO   
  2. glGenFramebuffers(1,&m_frameBuffer);  
  3. glBindFramebuffer(GL_FRAMEBUFFER,m_frameBuffer);  
  4.   
  5. glGenRenderbuffers(1,&m_renderBufferColor);  
  6. glBindRenderbuffer(GL_RENDERBUFFER,m_renderBufferColor);  
  7. glRenderbufferStorage(GL_RENDERBUFFER,GL_RGB,  
  8.     m_subImageWidth,m_subImageHeight);  
  9. glFramebufferRenderbuffer(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,  
  10.     GL_RENDERBUFFER,m_renderBufferColor);  
  11.   
  12. glGenRenderbuffers(1,&m_renderBufferDepth);  
  13. glBindRenderbuffer(GL_RENDERBUFFER,m_renderBufferDepth);  
  14. glRenderbufferStorage(GL_RENDERBUFFER,GL_DEPTH_COMPONENT,  
  15.     m_subImageWidth,m_subImageHeight);  
  16. glFramebufferRenderbuffer(GL_FRAMEBUFFER,GL_DEPTH_ATTACHMENT,  
  17.     GL_RENDERBUFFER,m_renderBufferDepth);  
  18.   
  19. glBindFramebuffer(GL_FRAMEBUFFER,0);//解除绑定  
        //创建普通FBO
	glGenFramebuffers(1,&m_frameBuffer);
	glBindFramebuffer(GL_FRAMEBUFFER,m_frameBuffer);

	glGenRenderbuffers(1,&m_renderBufferColor);
	glBindRenderbuffer(GL_RENDERBUFFER,m_renderBufferColor);
	glRenderbufferStorage(GL_RENDERBUFFER,GL_RGB,
		m_subImageWidth,m_subImageHeight);
	glFramebufferRenderbuffer(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,
		GL_RENDERBUFFER,m_renderBufferColor);

	glGenRenderbuffers(1,&m_renderBufferDepth);
	glBindRenderbuffer(GL_RENDERBUFFER,m_renderBufferDepth);
	glRenderbufferStorage(GL_RENDERBUFFER,GL_DEPTH_COMPONENT,
		m_subImageWidth,m_subImageHeight);
	glFramebufferRenderbuffer(GL_FRAMEBUFFER,GL_DEPTH_ATTACHMENT,
		GL_RENDERBUFFER,m_renderBufferDepth);

	glBindFramebuffer(GL_FRAMEBUFFER,0);//解除绑定

 

记住也要进行清除操作:

 

[cpp] view plain copy print ?
  1. glDeleteRenderbuffers(1,&m_renderBufferColor);  
  2. glDeleteRenderbuffers(1,&m_renderBufferDepth);  
  3. glDeleteFramebuffers(1,&m_frameBuffer);  
	glDeleteRenderbuffers(1,&m_renderBufferColor);
	glDeleteRenderbuffers(1,&m_renderBufferDepth);
	glDeleteFramebuffers(1,&m_frameBuffer);

 

然后绑定两个FBO,一个用于读,一个进行写入,进行blit操作:

 

[cpp] view plain copy print ?
  1.               glBindFramebuffer(GL_FRAMEBUFFER,0);  
  2.   
  3. glBindFramebuffer(GL_READ_FRAMEBUFFER,m_frameBufferMS);  
  4. status = glCheckFramebufferStatus(GL_READ_FRAMEBUFFER);  
  5. if (status != GL_FRAMEBUFFER_COMPLETE)  
  6. {  
  7.     cout << "The frame buffer status is not complete!" << endl;  
  8.     return;  
  9. }  
  10. glBindFramebuffer(GL_DRAW_FRAMEBUFFER,m_frameBuffer);  
  11. status = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);  
  12. if (status != GL_FRAMEBUFFER_COMPLETE)  
  13. {  
  14.     cout << "The frame buffer status is not complete!" << endl;  
  15.     return;  
  16. }  
  17.   
  18. glBlitFramebuffer(0,0,m_subImageWidth,m_subImageHeight,  
  19.     0,0,m_subImageWidth,m_subImageHeight,  
  20.     GL_COLOR_BUFFER_BIT,GL_LINEAR);  
  21.   
  22. glBindFramebuffer(GL_READ_FRAMEBUFFER,0);  
  23. glBindFramebuffer(GL_DRAW_FRAMEBUFFER,0);  
                glBindFramebuffer(GL_FRAMEBUFFER,0);

		glBindFramebuffer(GL_READ_FRAMEBUFFER,m_frameBufferMS);
		status = glCheckFramebufferStatus(GL_READ_FRAMEBUFFER);
		if (status != GL_FRAMEBUFFER_COMPLETE)
		{
			cout << "The frame buffer status is not complete!" << endl;
			return;
		}
		glBindFramebuffer(GL_DRAW_FRAMEBUFFER,m_frameBuffer);
		status = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);
		if (status != GL_FRAMEBUFFER_COMPLETE)
		{
			cout << "The frame buffer status is not complete!" << endl;
			return;
		}

		glBlitFramebuffer(0,0,m_subImageWidth,m_subImageHeight,
			0,0,m_subImageWidth,m_subImageHeight,
			GL_COLOR_BUFFER_BIT,GL_LINEAR);

		glBindFramebuffer(GL_READ_FRAMEBUFFER,0);
		glBindFramebuffer(GL_DRAW_FRAMEBUFFER,0);
之后,绑定普通的FBO,读取像素:
[cpp] view plain copy print ?
  1. glBindBuffer(GL_PIXEL_PACK_BUFFER,m_subImageBuffer);  
  2. glPixelStorei(GL_PACK_ALIGNMENT,1);  
  3. glBindFramebuffer(GL_FRAMEBUFFER,m_frameBuffer);  
  4. glReadPixels(0,0,m_subImageWidth,m_subImageHeight,  
  5.     GL_BGR,GL_UNSIGNED_BYTE,bufferOffset(0));  
glBindBuffer(GL_PIXEL_PACK_BUFFER,m_subImageBuffer);
glPixelStorei(GL_PACK_ALIGNMENT,1);
glBindFramebuffer(GL_FRAMEBUFFER,m_frameBuffer);
glReadPixels(0,0,m_subImageWidth,m_subImageHeight,
	GL_BGR,GL_UNSIGNED_BYTE,bufferOffset(0));

这样所有的操作就完成了。

 

 

参考资料:

StackOverflow问题1

stackoverflow问题2

stackoverflow问题3

GL_framebuffer_multisample详解

 

再分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!https://blog.csdn.net/jiangjunshow

转载于:https://www.cnblogs.com/skiwnchiwns/p/10345126.html

相关文章:

  • java序列化
  • 记利用frp配合nginx实现内网透传
  • 一本通1554【例 3】异象石
  • codeforces 140E.New Year Garland
  • 加密_散乱的密文
  • 力扣——二叉搜索树中的搜索
  • visualsvn for vs2017 初始化错误
  • 寒假开学回忆
  • 4算法与数据结构
  • C++虚继承
  • L3-009 长城 (30 分)
  • 股票
  • 如何创建一个Asp .Net Web Api项目
  • RAID LVM ISCSI
  • 在采用vue-cli Post Get
  • ES6指北【2】—— 箭头函数
  • 345-反转字符串中的元音字母
  • Docker 笔记(1):介绍、镜像、容器及其基本操作
  • extjs4学习之配置
  • Java 23种设计模式 之单例模式 7种实现方式
  • Linux下的乱码问题
  • TypeScript实现数据结构(一)栈,队列,链表
  • 百度贴吧爬虫node+vue baidu_tieba_crawler
  • 构建工具 - 收藏集 - 掘金
  • ​力扣解法汇总1802. 有界数组中指定下标处的最大值
  • ​如何防止网络攻击?
  • ​虚拟化系列介绍(十)
  • # 日期待t_最值得等的SUV奥迪Q9:空间比MPV还大,或搭4.0T,香
  • #我与Java虚拟机的故事#连载06:收获颇多的经典之作
  • #预处理和函数的对比以及条件编译
  • (C#)Windows Shell 外壳编程系列9 - QueryInfo 扩展提示
  • (DFS + 剪枝)【洛谷P1731】 [NOI1999] 生日蛋糕
  • (Matalb时序预测)PSO-BP粒子群算法优化BP神经网络的多维时序回归预测
  • (zhuan) 一些RL的文献(及笔记)
  • (附源码)spring boot公选课在线选课系统 毕业设计 142011
  • (附源码)ssm本科教学合格评估管理系统 毕业设计 180916
  • (附源码)计算机毕业设计SSM智慧停车系统
  • (南京观海微电子)——COF介绍
  • (入门自用)--C++--抽象类--多态原理--虚表--1020
  • (十八)SpringBoot之发送QQ邮件
  • .md即markdown文件的基本常用编写语法
  • .NET CORE 2.0发布后没有 VIEWS视图页面文件
  • .Net 路由处理厉害了
  • .net流程开发平台的一些难点(1)
  • .NET面试题(二)
  • @angular/cli项目构建--Dynamic.Form
  • @ModelAttribute使用详解
  • [<事务专题>]
  • [C++] cout、wcout无法正常输出中文字符问题的深入调查(1):各种编译器测试
  • [CQOI 2010]扑克牌
  • [Gym-102091E] How Many Groups
  • [LeetCode]—Add Binary 两个字符串二进制相加
  • [Linux]文件基础-如何管理文件
  • [Mac软件]Adobe XD(Experience Design) v57.1.12.2一个功能强大的原型设计软件
  • [Microsoft][SQLServer 2000 Driver for JDBC][SQLServer]对象名 'XXX' 无效