【问题标题】:Anti-aliasing/smoothing/supersampling 2d images with opengl使用opengl对2d图像进行抗锯齿/平滑/超级采样
【发布时间】:2015-01-08 01:09:33
【问题描述】:

我正在使用 OpenGL 中的 2D 图形 - 分形和其他有趣的东西 ^_^。我的基本设置是渲染几个三角形来填充屏幕并使用片段着色器在它们上绘制很酷的东西。我想稍微平滑一下,所以我开始研究超级采样。如何去做这件事对我来说并不明显。这是我迄今为止尝试过的......

首先,我查看了Apple docs on anti-aliasing。我更新了我的像素格式初始化:

NSOpenGLPixelFormatAttribute attrs[] =
{
    NSOpenGLPFADoubleBuffer,
    NSOpenGLPFADepthSize, 24,
    NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion4_1Core,
    NSOpenGLPFASupersample,
    NSOpenGLPFASampleBuffers, 1,
    NSOpenGLPFASamples, 4,
    0
};

我还添加了glEnable(GL_MULTISAMPLE); 行。 GL_MULTISAMPLE_FILTER_HINT_NV 似乎没有定义(文档似乎已经过时了),所以我不确定在那里做什么。

这使我的渲染速度变慢,但似乎没有进行抗锯齿处理,因此我尝试了OpenGL Wiki on Multisampling 中描述的“Render-to-FBO”方法。我尝试了很多变体,结果多种多样:成功渲染(似乎没有抗锯齿),将垃圾渲染到屏幕上(有趣!),崩溃(应用程序消失,我得到一个系统对话框关于图形问题),并使我的笔记本电脑除了光标之外没有响应(在硬重启后得到关于图形问题的系统对话框)。

我在绘制之前检查了帧缓冲区的状态,所以我知道这不是问题所在。而且我确定我正在使用硬件而不是软件进行渲染 - 在其他帖子中看到了该建议。

我已经花了相当多的时间在这上面,但仍然不太明白如何处理这个问题。我想要一些帮助的一件事是如何查询 GL 以查看是否正确启用了超级采样,或者如何判断我的片段着色器被调用了多少次等等。我也对某些调用的位置有点困惑go - 我发现的大多数示例只是说明要调用哪些方法,但没有指定哪些方法需要在 draw 回调中执行。任何人都有一个简单的 SSAA 示例,带有 OpenGL 3 或 4 和 OSX...或其他可以尝试的东西?

编辑:绘图代码 - 超级坏(不要评判我),但供参考:

- (void)draw
{
    glBindVertexArray(_vao);    // todo: is this necessary? (also in init)
    glBufferData(GL_ARRAY_BUFFER, 12 * sizeof(GLfloat), points, GL_STATIC_DRAW);

    glGenTextures( 1, &_tex );
    glBindTexture( GL_TEXTURE_2D_MULTISAMPLE, _tex );
    glTexImage2DMultisample( GL_TEXTURE_2D_MULTISAMPLE, 4, GL_RGBA8, _width * 2, _height * 2, false );

    glGenFramebuffers( 1, &_fbo );
    glBindFramebuffer( GL_FRAMEBUFFER, _fbo );
    glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, _tex, 0 );

    GLint status;
    status = glCheckFramebufferStatus( GL_FRAMEBUFFER );
    if (status != GL_FRAMEBUFFER_COMPLETE) {
        NSLog(@"incomplete buffer 0x%x", status);
        return;
    }

    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
    glBindFramebuffer(GL_READ_FRAMEBUFFER, _fbo);
    glDrawBuffer(GL_BACK);
    glClear(GL_COLOR_BUFFER_BIT);
    glDrawArrays(GL_TRIANGLES, 0, 6);
    glBlitFramebuffer(0, 0, _width * 2, _height * 2, 0, 0, _width, _height, GL_COLOR_BUFFER_BIT, GL_LINEAR);

    glDeleteTextures(1, &_tex);
    glDeleteFramebuffers(1, &_fbo);
    glBindFramebuffer( GL_FRAMEBUFFER, 0 );
}

更新: 我根据以下 Reto 的建议更改了代码:

glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _fbo);
glClear(GL_COLOR_BUFFER_BIT);
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindFramebuffer(GL_READ_FRAMEBUFFER, _fbo);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glBlitFramebuffer(0, 0, _width * 2, _height * 2, 0, 0, _width, _height,
                  GL_COLOR_BUFFER_BIT, GL_LINEAR);

这导致程序将垃圾渲染到屏幕上。然后我去掉了* 2 乘数,它仍然在屏幕上画了垃圾。然后我关闭了与多重/超级采样相关的NSOpenGLPFA 选项,它正常渲染,没有抗锯齿。

我也尝试使用非多重采样纹理,但不断收到不完整的附件错误。我不确定这是否是由于 OpenGL wiki 上提到的 NVidia 问题(将发表评论,因为我没有足够的代表来发布超过 2 个链接)或其他原因。如果有人可以提出一种方法来找出附件不完整的原因,那将非常非常有帮助。

最后,我尝试使用渲染缓冲区而不是纹理,发现在 glRenderbufferStorage 中指定大于视口大小的宽度和高度似乎无法按预期工作。

GLuint rb;
glGenRenderbuffers(1, &rb);
glBindRenderbuffer(GL_RENDERBUFFER, rb);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, _width * 2, _height * 2);

// ...

glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _fbo);
glClear(GL_COLOR_BUFFER_BIT);
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindFramebuffer(GL_READ_FRAMEBUFFER, _fbo);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glBlitFramebuffer(0, 0, _width * 2, _height * 2, 0, 0, _width, _height,
                  GL_COLOR_BUFFER_BIT, GL_LINEAR);

... 呈现在屏幕左下角的 1/4 处。不过好像不是更流畅...

更新 2: 将视口大小翻倍,并没有更流畅。转动NSOpenGLPFASupersample 仍然会导致它在屏幕上绘制垃圾。 >.

更新 3:我是个白痴,它完全更流畅。它只是看起来不太好,因为我使用的是丑陋的配色方案。而且我必须将所有坐标加倍,因为视口是 2x。那好吧。我仍然希望得到一些帮助来理解为什么 NSOpenGLPFASupersample 会导致如此疯狂的行为......

【问题讨论】:

  • NVidia + 不完整的纹理问题:opengl.org/wiki/Common_Mistakes#Render_To_Texture
  • 您能分享一下您的 Mandelbrot 项目吗?我试图弄清楚如何在 OS X 上通过直接像素操作来制作 2d 图形,并且真的很难到达任何地方:(
  • @DanielLucraft 我会尝试将它放到 Github 上,但与此同时,我建议查看 Apple 的 GLEssentials example code。我发现它对入门非常有用。
  • 好的,谢谢,会检查一下!

标签: macos cocoa opengl antialiasing


【解决方案1】:

您在此处的调用顺序看起来不像您想要的那样:

glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glBindFramebuffer(GL_READ_FRAMEBUFFER, _fbo);
glDrawBuffer(GL_BACK);
glClear(GL_COLOR_BUFFER_BIT);
glDrawArrays(GL_TRIANGLES, 0, 6);
glBlitFramebuffer(0, 0, _width * 2, _height * 2, 0, 0, _width, _height, GL_COLOR_BUFFER_BIT, GL_LINEAR);

当您调用glClear()glDrawArrays() 时,您当前的绘制帧缓冲区(由上次调用glBindFramebuffer(GL_DRAW_FRAMEBUFFER, ...) 决定)是默认帧缓冲区。所以你永远不会渲染到 FBO。让我对上面的内容进行注释:

// Set draw framebuffer to default (0) framebuffer. This is where the rendering will go.
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
// Set read framebuffer to the FBO.
glBindFramebuffer(GL_READ_FRAMEBUFFER, _fbo);
// This is redundant, GL_BACK is the default draw buffer for the default framebuffer.
glDrawBuffer(GL_BACK);
// Clear the current draw framebuffer, which is the default framebuffer.
glClear(GL_COLOR_BUFFER_BIT);
// Draw to the current draw framebuffer, which is the default framebuffer.
glDrawArrays(GL_TRIANGLES, 0, 6);
// Copy from read framebuffer (which is the FBO) to the draw framebuffer (which is the
// default framebuffer). Since no rendering was done to the FBO, this will copy garbage
// into the default framebuffer, wiping out what was previously rendered.
glBlitFramebuffer(0, 0, _width * 2, _height * 2, 0, 0, _width, _height,
                  GL_COLOR_BUFFER_BIT, GL_LINEAR);

要使其正常工作,您需要在渲染时将绘制帧缓冲区设置为 FBO,然后将读取帧缓冲区设置为 FBO,并将绘制帧缓冲区设置为副本的默认值:

glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _fbo);
glClear(GL_COLOR_BUFFER_BIT);
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindFramebuffer(GL_READ_FRAMEBUFFER, _fbo);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glBlitFramebuffer(0, 0, _width * 2, _height * 2, 0, 0, _width, _height,
                  GL_COLOR_BUFFER_BIT, GL_LINEAR);

回顾:

  • 绘图命令写入GL_DRAW_FRAMEBUFFER
  • glBlitFramebuffer()GL_READ_FRAMEBUFFER 复制到GL_DRAW_FRAMEBUFFER

关于代码的更多评论:

  • 由于您要创建两倍大小的多重采样纹理,因此您同时使用多重采样和超级采样:

    glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, _tex);
    glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4, GL_RGBA8,
                            _width * 2, _height * 2, false);
    

    这是完全合法的。但它... 很多采样。如果您只想进行超级采样,则可以使用常规纹理。

  • 您可以为 FBO 颜色目标使用渲染缓冲区而不是纹理。没有太大的优势,但它更简单,并且可能更有效。如果您想稍后对结果进行采样,则只需要使用纹理作为附件,这里不是这种情况。

【讨论】:

  • 非常感谢您提供如此详细的回复。不幸的是,它还不完全在那里......我会用我尝试过的事情和结果来更新这个问题。
  • 再次感谢。这是我的 Mandelbrot 现在的样子:dl.dropboxusercontent.com/u/24793835/thankyou.png。这是 700 次迭代,每单位缩放 25600 像素。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-31
  • 1970-01-01
相关资源
最近更新 更多