美文网首页iOS程序猿
OpenGL ES3 实现MSAA的两个坑

OpenGL ES3 实现MSAA的两个坑

作者: 皮皮Warrior | 来源:发表于2019-07-24 15:31 被阅读0次

    OpenGL ES3 实现MSAA的两个坑

    OpenGL ES3 实现MSAA

    在OpenGL ES3上实现MSAA的主要思想是创建一个用于多采样FBO,用它来接受所有渲染指令。当需要上屏时,将多采样的FBO resolve 回默认的FBO,在这个降采样过程中得到的像素值会更平滑,从而减少锯齿感。


    image

    苹果的一片文档写的足够详细:Using Multisampling to Improve Image Quality
    不过苹果用的是ES2,但MSAA的相关接口在ES3上也只是换了签名而已,迁移成本不高。

    // your default FBO
    glGenFramebuffers(1, &viewFrameBuffer);
    glGenRenderbuffers(1, &viewRenderBuffer);
    //setup MSAA Auxiliary Buffers
    glGenFramebuffers(1, &msaaFrameBuffer);
    glGenRenderbuffers(1, &msaaRenderBuffer);
    
    glBindFramebuffer(GL_FRAMEBUFFER, msaaFrameBuffer);
    glBindRenderbuffer(GL_RENDERBUFFER, msaaRenderBuffer);
    // set MSAA sample count
    int msaaSamples = 2;
    glRenderbufferStorageMultisample(GL_RENDERBUFFER, msaaSamples, GL_RGBA8, bufferWidth, bufferHeight);
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, msaaRenderBuffer);
    
    // draw calls...
    
    // resolve MSAA FBO to your default FBO
    glBindFramebuffer(GL_READ_FRAMEBUFFER, msaaFrameBuffer);
    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, viewFrameBuffer);
    glBlitFramebuffer(0, 0, bufferWidth, bufferHeight, 0, 0, bufferWidth, bufferHeight, GL_COLOR_BUFFER_BIT, GL_NEAREST);
    glFlush();
    // invalidate unneeded renderbuffer
    GLenum msaaRenderBuffer[] = {GL_COLOR_ATTACHMENT0,GL_DEPTH_ATTACHMENT,GL_STENCIL_ATTACHMENT};
    glInvalidateFramebuffer(GL_READ_FRAMEBUFFER, 3, msaaRenderBuffer);
    // present your FBO contents
    glBindRenderbuffer(GL_RENDERBUFFER, viewRenderBuffer);
    [glContext presentRenderbuffer:GL_RENDERBUFFER];
    

    遇到的问题和解决方案

    1. 偶现的闪屏

    有一些小游戏在开抗锯齿后会出现闪屏的Bug,经排查应该是FBO还没有blit完成,就被标记成了invalid,所以读不出来值了。
    glInvalidateFramebuffer可以让客户端标记哪些RBO是不需要的,这样OpenGL状态机不用再维持一些不必要的状态更新,而且在TBR/TBDR架构的GPU上,渲染时Tile不用将不再需要的数据写回显存,可以减少带宽的使用,提高性能。

    但貌似在某些驱动glBlitFramebuffer这个函数是异步的,导致FBO还没有传完,就被invalidate了,产生有偶现的黑屏。我猜测这是一些驱动的优化导致的,在iPhone 6s上会复现,在Mac和一些安卓机上没有复现。

    所以为了保证多采样FBO能正确resolve,需要在glBlitFramebuffer之后加glflush,这样可以保证命令发送到GPU后再将一些不需要的RBO标记成invalidate。

    1. 黑屏

    有小游戏开发商在设置WebGL时,将多采样的sampleCount设成了不支持的值,这在WebGL里是可以运行的,但在OpenGL里不支持的采样值会直接输出黑色像素。

    所以在创建OpenGL时,先用glGetInternalformativ读取一下当前驱动支持的采样值,若开发商设置的值不支持,会就近选一个近邻的采样值,不然产生黑屏的话很难查Bug。

    glGetInternalformativ(GL_RENDERBUFFER, GL_RGBA8, GL_NUM_SAMPLE_COUNTS, 1, &counts);
    GLint samples[counts];
    glGetInternalformativ(GL_RENDERBUFFER, GL_RGBA8, GL_SAMPLES, (GLsizei)counts, samples);
    for (int i = 0; i < sampleCounts, i++){
        printf("support sample count:%d, %d", samples[i]);
    }
    

    相关文章

      网友评论

        本文标题:OpenGL ES3 实现MSAA的两个坑

        本文链接:https://www.haomeiwen.com/subject/vdwxrctx.html