zoukankan      html  css  js  c++  java
  • OpenGL ES3使用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的调用给注释掉即可,非常方便~

  • 相关阅读:
    【BZOJ】4011: [HNOI2015]落忆枫音
    【BZOJ】1187: [HNOI2007]神奇游乐园
    【CERC2007】机器排序
    【NOI2004】郁闷的出纳员
    【USACO】奶牛跑步2
    【HNOI2004】宠物收养所
    【NOI2009】植物大战僵尸
    Xn数列
    骨牌覆盖
    【JSOI2008】球形空间产生器
  • 原文地址:https://www.cnblogs.com/zenny-chen/p/5058575.html
Copyright © 2011-2022 走看看