昨晚花费了我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(, &mFramebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); // Create a color renderbuffer, allocate storage for it, and attach it to the framebuffer.
glGenRenderbuffers(, &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(, &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(, &mMSAAFramebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, mMSAAFramebuffer); glGenRenderbuffers(, &mMSAARenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, mMSAARenderbuffer);
// 4 samples for color
glRenderbufferStorageMultisample(GL_RENDERBUFFER, , GL_RGBA8, mBackingWidth, mBackingHeight);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, mMSAARenderbuffer); glGenRenderbuffers(, &mMSAADepthRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, mMSAADepthRenderbuffer);
// 4 samples for depth
glRenderbufferStorageMultisample(GL_RENDERBUFFER, , 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(, , 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, , (GLenum[]){GL_DEPTH_ATTACHMENT}); glResolveMultisampleFramebufferAPPLE(); glDiscardFramebufferEXT(GL_READ_FRAMEBUFFER, , (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, , (GLenum[]){GL_DEPTH_ATTACHMENT}); // Copy the read fbo(multisampled framebuffer) to the draw fbo(single-sampled framebuffer)
glBlitFramebuffer(, , mBackingWidth, mBackingHeight, , , mBackingWidth, mBackingHeight, GL_COLOR_BUFFER_BIT, GL_NEAREST); glInvalidateFramebuffer(GL_READ_FRAMEBUFFER, , (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(多重采样抗锯齿)的方法的更多相关文章

  1. Unity3D学习(七):Unity多重采样抗锯齿设置无效的解决办法

    前言 学习Shader的过程中发现模型锯齿严重,于是去Edit--Project Settings--Quality选项下将反锯齿设置为了8X Multi Sampling.结果没有任何改变,如图: ...

  2. Unity3d 超级采样抗锯齿 Super Sampling Anti-Aliasing

    Super Sampling Anti-AliasingSSAA算是在众多抗锯齿算法中比较昂贵的一种了,年代也比较久远,但是方法比较简单,主要概括为两步1.    查找边缘2.    模糊边缘这是一种 ...

  3. 多重采样(MultiSample)下的FBO反锯齿 【转】

    在三维渲染的过程中,锯齿总是让人讨厌的东西.抗锯齿的一种采用方式是多重采样,本文主要小记一下FBO与多重采样的关系.——ZwqXin.com 首先,关于FBO(Frame Buffer Object) ...

  4. openGL线型和线宽以及线的抗锯齿

    openGL线型和线宽以及线抗锯齿 一. 线宽 Opengl的线宽设置:glLineWidth(width); width为float类型值,在0~10.0,大于10以上按10来处理. 若开启线的反走 ...

  5. osg如何设置抗锯齿(反走样,反锯齿)

    首先抗锯齿是什么? 举个最简单的例子 你用windows画图软件画一根直线(准确说这个叫做线段),当水平或者垂直的时候,如下图,这是绝对完美的 但是当线段出现倾斜时,就无法做到完美了此时就会出现锯齿 ...

  6. 处理 CALayer 变形后的抗锯齿问题

    处理锯齿当然要用抗锯齿,iOS 可以通过修改 Plist 实现全局抗锯齿,但是这样容易出现性能问题. 所以就要使用对单个 Layer 开启抗锯齿的方法 layer.allowsEdgeAntialia ...

  7. 【Three.js】模型抗锯齿处理

    1.锯齿消除方法 three.js参考使用官方demo发现模型渲染有锯齿,这种情况在旋转视角时候就非常明显. 抗锯齿的方法,很简单,只需要配置render两个属性即可: renderer = new ...

  8. 在qt的QOpenGLWidget开启opengl的抗锯齿

    在QOpenGLWidget的构造函数添加下面几句代码即可 QSurfaceFormat surfaceFormat; surfaceFormat.setSamples();//多重采样 setFor ...

  9. OpenGL之抗锯齿 以及 线宽的设置

    转自原文 OpenGL之抗锯齿 以及 线宽的设置 抗锯齿 1.线的抗锯齿 glEnable(GL_LINE_SMOOTH); //启用 glHint(GL_LINE_SMOOTH,GL_NICEST) ...

随机推荐

  1. LeetCode初级算法--字符串02:字符串中的第一个唯一字符

    LeetCode初级算法--字符串02:字符串中的第一个唯一字符 搜索微信公众号:'AI-ming3526'或者'计算机视觉这件小事' 获取更多算法.机器学习干货 csdn:https://blog. ...

  2. (二)WCF的Binding模型

    上篇博客对WCF中的基础知识进行了介绍,先从概念上知道了WCF的一些理论,在abc模型中B是Binding,WCF为我们提供了多种绑定机制,我们先从了解各种绑定机制开始,只有知道之后才能在实践中更好的 ...

  3. python_并发编程——多进程

    from multiprocessing import Process import os def func1(): print('子进程1',os.getpid()) #子进程:获取当前进程的进程号 ...

  4. C# 4.0 新特性(.NET Framework 4.0 与 Visual Studio 2010 )

    一.dynamic binding:动态绑定 在通过 dynamic 类型实现的操作中,该类型的作用是不在编译时类型检查,而是在运行时解析这些操作.dynamic 类型简化了对 COM API(例如 ...

  5. C语言实验二——位运算

    问题 线性反馈移位寄存器 Linear feedback shift register(LFSR),是指给定前一状态,将该输出的线性函数再用作输入的移位寄存器.异或运算是最常见的单比特线性函数:对寄存 ...

  6. Zabbix 短信报警示例

    Zabbix 短信报警 示例: 注意zabbix 脚本文件默认放置目录是 alertscripts (zabbix 动作调用脚本目录) # 编辑 zabbix_server.conf # AlertS ...

  7. JS发送验证码;并设置cookie

    Tool.send_code = function(obj) { var isCheck = true, form = $('#editInfo_Form'); var mobile = form.f ...

  8. C二维数组用指针地址遍历

    #include <stdio.h> #include <stdlib.h> int main(){ int a = 100; void *p = &a; printf ...

  9. Problem 5 素数筛法+并查集

    $des$ 给定一个长度为 $n$ 的正整数序列 ${a_i }$.将 ${1,2,...,n}$ 划分成两个非空集合 $S.T$,使得 $gcd(\prod_{i \in S} a_i, \prod ...

  10. Spoj PRIME1 - Prime Generator

    题意翻译 求给定的两个数之间的素数 Translated by @kaiming 题目描述 Peter wants to generate some prime numbers for his cry ...