OpenGL ES3使用MSAA(多重采样抗锯齿)的方法

摘要:
1.包括头文件<2.后缀为“APPLE”、“EXT”和“OES”的函数和常量已消失;如果包含&lt,则可以调用glDiscardFramebufferEXT;然而,直接使用官方标准接口更可靠;接下来,我提供了一个相对完整的使用示例(带有部分Objective-C代码)我们看到,上述代码定义了两组FBO和RBO;一种是用于绘制到目标窗口(无MSAA);

昨晚花费了我2个多小时的时间终于把OpenGL ES3.0中的MSAA给搞定了。在OpenGL ES2.0中,Khronos官方没有引入标准的MSAA全屏抗锯齿的方法,而Apple则采用了自己的GL_APPLE_framebuffer_multisample的扩展来实现MSAA。在iOS中,OpenGL ES3.0之前使用MSAA的方法可以参见Apple的官方OpenGL ES开发者指南,写得非常详细:

https://developer.apple.com/library/ios/documentation/3DDrawing/Conceptual/OpenGLES_ProgrammingGuide/WorkingwithEAGLContexts/WorkingwithEAGLContexts.html#//apple_ref/doc/uid/TP40008793-CH103-SW4

而对于OpenGL ES3.0,GL_APPLE_framebuffer_multisample扩展已经失效,不能再使用了。于是我在网上搜了许多资料,不过有帮助的不多,比较有方向性的文章是OpenGL官方wiki上关于多重采样的介绍:https://www.opengl.org/wiki/Multisampling

不过这篇文章针对的是OpenGL,与OpenGL ES稍微有些差异。于是本人借助Apple的文档结合这篇官维,终于把它捣鼓出来了。

其实,大部分代码与Apple官方所描述的差不多,有几个需要改动的地方:

1、要包含头文件<OpenGLES/ES3/gl.h>。如果是之前的OpenGL ES2.0,那么所包含的是<OpenGLES/ES2/gl.h>和<OpenGLES/ES2/glext.h>。

2、带‘APPLE’、‘EXT’以及‘OES’后缀的函数以及常量都没有了。改起来非常简单,直接把后缀给删了即可,比如原来的‘glRenderbufferStorageMultisampleAPPLE’改为‘glRenderbufferStorageMultisample’;原来的‘GL_RGBA8_OES’改为‘GL_RGBA8’。

3、在绘制时,用‘glBlitFramebuffer’来取代‘glResolveMultisampleFramebufferAPPLE’。

4、用‘glInvalidateFramebuffer’来取代‘glDiscardFramebufferEXT’。这个接口非常有用!使用和没使用速度能相差1倍之多!这里得感谢Apple的Xcode以及OpenGL ES Analysis的profile工具,使得我能查到之前的glDiscardFramebufferEXT被啥取代了……否则,如果包含<OpenGLES/ES2/glext.h>然后调用glDiscardFramebufferEXT也没啥问题。不过直接用官方标准的接口会更可靠些,至少更有可移植性些,呵呵。

下面我提供比较完整的使用范例(带有部分的Objective-C代码):

先是头文件

//  MyGLLayer.h
//  CADemo
//
//  Created by Zenny Chen on 14-8-19.
//  Copyright (c) 2014年 Adwo. All rights reserved.
//

@import QuartzCore;

#import <OpenGLES/ES3/gl.h>

@interface MyGLLayer : CAEAGLLayer
{
@private
    
    /* The pixel dimensions of the backbuffer */
    GLint mBackingWidth;
    GLint mBackingHeight;
    
    EAGLContext *mContext;
    
    /* OpenGL names for the renderbuffer and framebuffers used to render to this view */
    GLuint mFramebuffer, mRenderbuffer, mDepthRenderbuffer;
    
    GLuint mMSAAFramebuffer, mMSAARenderbuffer, mMSAADepthRenderbuffer;
    
    CADisplayLink *mDisplayLink;
}

我们看到以上代码定义了两组FBO和RBO,一组是用于绘制到目标窗口的(不带MSAA的),另一组是用于图形渲染的,采用MSAA。在最后绘制时会把MSAA的FBO像素拷贝到单样本的FBO,用于显示。

以下是源文件的主要代码片段:

- (instancetype)init
{
    self = [super init];
    
    self.opaque = YES;
    
    self.contentsScale = [UIScreen mainScreen].scale;
    
    // Optionally configure the surface properties of the rendering surface by assigning a new dictionary of
    // values to the drawableProperties property of the CAEAGLLayer object.
    self.drawableProperties = @{
                                kEAGLDrawablePropertyRetainedBacking : @NO,
                                kEAGLDrawablePropertyColorFormat : kEAGLColorFormatRGBA8
                                };
    
    // Set OpenGL ES context,use GL ES3 profile
    mContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
    
    return self;
}

- (BOOL)createFramebuffer
{
    // Create the framebuffer and bind it so that future OpenGL ES framebuffer commands are directed to it.
    glGenFramebuffers(1, &mFramebuffer);
    glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
    
    // Create a color renderbuffer, allocate storage for it, and attach it to the framebuffer.
    glGenRenderbuffers(1, &mRenderbuffer);
    glBindRenderbuffer(GL_RENDERBUFFER, mRenderbuffer);
    
    // Create the color renderbuffer and call the rendering context to allocate the storage on our Core Animation layer.
    // The width, height, and format of the renderbuffer storage are derived from the bounds and properties of the CAEAGLLayer object
    // at the moment the renderbufferStorage:fromDrawable: method is called.
    [mContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:self];
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, mRenderbuffer);
    
    // Retrieve the height and width of the color renderbuffer.
    glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &mBackingWidth);
    glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &mBackingHeight);
    
    // Perform similar steps to create and attach a depth renderbuffer.
    glGenRenderbuffers(1, &mDepthRenderbuffer);
    glBindRenderbuffer(GL_RENDERBUFFER, mDepthRenderbuffer);
    glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, mBackingWidth, mBackingHeight);
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, mDepthRenderbuffer);
    
    // The following is MSAA settings
    glGenFramebuffers(1, &mMSAAFramebuffer);
    glBindFramebuffer(GL_FRAMEBUFFER, mMSAAFramebuffer);
    
    glGenRenderbuffers(1, &mMSAARenderbuffer);
    glBindRenderbuffer(GL_RENDERBUFFER, mMSAARenderbuffer);
    // 4 samples for color
    glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_RGBA8, mBackingWidth, mBackingHeight);
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, mMSAARenderbuffer);
    
    glGenRenderbuffers(1, &mMSAADepthRenderbuffer);
    glBindRenderbuffer(GL_RENDERBUFFER, mMSAADepthRenderbuffer);
    // 4 samples for depth
    glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_DEPTH_COMPONENT16, mBackingWidth, mBackingHeight);
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, mMSAADepthRenderbuffer);
    
    // Test the framebuffer for completeness.
    if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
    {
        NSLog(@"failed to make complete framebuffer object %x", glCheckFramebufferStatus(GL_FRAMEBUFFER));
        return NO;
    }
    
    glViewport(0, 0, mBackingWidth, mBackingHeight);
    
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);

   // Do other settings...
    
    return YES;
}

- (void)drawLayer:(CADisplayLink*)link
{    
    glBindFramebuffer(GL_FRAMEBUFFER, mMSAAFramebuffer);
    glBindRenderbuffer(GL_RENDERBUFFER, mMSAARenderbuffer);
    
    // Draw something here...
    [self drawModels];
    
    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mFramebuffer);
    glBindFramebuffer(GL_READ_FRAMEBUFFER, mMSAAFramebuffer);
    
#if 0
    // OpenGL ES 2.0 Apple multisampling
    
    // Discard the depth buffer from the read fbo. It is no more necessary.
    glDiscardFramebufferEXT(GL_READ_FRAMEBUFFER, 1, (GLenum[]){GL_DEPTH_ATTACHMENT});
    
    glResolveMultisampleFramebufferAPPLE();
    
    glDiscardFramebufferEXT(GL_READ_FRAMEBUFFER, 1, (GLenum[]){GL_COLOR_ATTACHMENT0});
#else
    // OpenGL ES3.0 Core multisampling
    
    // Discard the depth buffer from the read fbo. It is no more necessary.
    glInvalidateFramebuffer(GL_READ_FRAMEBUFFER, 1, (GLenum[]){GL_DEPTH_ATTACHMENT});
    
    // Copy the read fbo(multisampled framebuffer) to the draw fbo(single-sampled framebuffer)
    glBlitFramebuffer(0, 0, mBackingWidth, mBackingHeight, 0, 0, mBackingWidth, mBackingHeight, GL_COLOR_BUFFER_BIT, GL_NEAREST);
    
    glInvalidateFramebuffer(GL_READ_FRAMEBUFFER, 1, (GLenum[]){GL_COLOR_ATTACHMENT0});
#endif
    
    glBindRenderbuffer(GL_RENDERBUFFER, mRenderbuffer);
    
    // Assuming you allocated a color renderbuffer to point at a Core Animation layer, you present its contents by making it the current renderbuffer
    // and calling the presentRenderbuffer: method on your rendering context.
    [mContext presentRenderbuffer:GL_RENDERBUFFER];
}

大致使用流程如上述代码所示。我用11寸的MacBook Air上模拟器看,效果十分明显(因为MacBook Air不是retina屏)。上述demo中使用了4个样本,基本够用了。

如果各位要看非MSAA版本,只需要把drawLayer:方法下面第一行代码改为:‘glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);’;然后把对glBlitFramebuffer的调用给注释掉即可,非常方便~

免责声明:文章转载自《OpenGL ES3使用MSAA(多重采样抗锯齿)的方法》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇如何在Eclipse中配置python开发环境arcengine动态显示所需字段值下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

微信默认表情符号的代码对照表

之前有不少朋友问我,微信公众平台如何发带有表情符号的图片,其实只需要在信息里面插入表情代码就可以使用的。 微信表情对照表如下: 表情图片 字符串代码 替换关键字 /::) 微笑 /::~ 伤心 /::B 美女 /::| 发呆 /:8-) 墨镜 /::< 哭 /::$ 羞 /::X 哑 /::...

电赛菜鸟营培训(五)——OLED屏幕的使用

一、取模软件的使用        首先进行设置         然后可以生成显示这个字母的代码,列优先,先按列画8行,然后再继续画下一列。汉字为16*16,字母为8*8,对应生成相应个数的ox代码。 二、STM32烤写OLED #include "stm32f10x.h" #include "stm32f10x_gpio.h" #include...

AutoCAD中程序创建Ribbon界面执行AutoCAD命令

在基于AutoCAD做二次开发时,常见的教程讲的都是在DLL中定义一些自定义命令,然后通过netload命令加载这个DLL,通过执行自定义命令的方式来执行我们的自定义功能。这样的方式在在学习中是显得很简单,但用在正式产品中就显得太业余了,没有专业精神。当然更professional的当然是和AutoCAD一样,提供一些基于Ribbon的用户界面来调用我们的...

前后端分离的变化,以及前后端对接需要关注的点

1. 前言 随着互联网的高速发展,前端页面的展示、交互体验越来越灵活,响应体验也要求越来越高,后端服务的高并发、高可用、高性能、高扩展等特性的要求也愈加苛刻,从而导致前后端研发各自专注于自己擅长的领域深耕细作。 然而这带来了新的问题: 前后端的对接界面双方却关注甚少,没有任何接口约定规范情况下各自撸起袖子就是干,导致我们在产品项目开发过程中,前后端的接口联...

WPF中元素拖拽的两个实例

  今天结合之前做过的一些拖拽的例子来对这个方面进行一些总结,这里主要用两个例子来说明在WPF中如何使用拖拽进行操作,元素拖拽是一个常见的操作,第一个拖拽的例子是将ListBox中的子元素拖拽到ListView的某一个节点,从而将该子元素作为当前节点的子节点。第二个例子就是将ListView的某一项拖拽到另外一项上从而使两个子项位置互换,这两个例子的原理类...

LINUX 上 实现SNTP (时间同步)

Network Time Protocol (NTP) 也是RHCE新增的考试要求. 学习的时候也顺便复习了一下如何设置Linux的时间,现在拿出来和大家分享 设置NTP服务器不难但是NTP本身是一个很复杂的协议. 这里只是简要地介绍一下实践方法和上次一样,下面的实验都在RHEL5上运行  https://bbs.csdn.net/topics/33019...