在OpenGL ES 2.0中进行抗锯齿?

有没有办法在OpenGL ES 2.0中实现Anitaliasing技术? 我已经看到了,发现了很less的方法,但是输出没有变化。

在最坏的情况下,我已经计划实现多遍渲染,通过显示每个像素周围的像素的平均颜色来平滑片段着色器中的边缘,但是它会花费更多的GPU性能。

有什么build议么 ?

许多设备支持MSAA(多样本反锯齿)。 要利用此function,您必须select具有多重采样的EGLConfig

在Android上,如果使用GLSurfaceView ,则必须实现自己的EGLConfigChooser 。 然后你可以使用EGL函数,特别是eglChooseConfig()来find你喜欢的configuration。

下面的代码是未经testing的,但至less应该草拟如何实现。 在你的GLSurfaceView派生类的构造函数中, 调用setRenderer() 之前 ,添加:

 setEGLConfigChooser(new MyConfigChooser()); 

然后实现MyConfigChooser 。 你可以在你的GLSurfaceView中使这个嵌套类:

 class MyConfigChooser implements GLSurfaceView.EGLConfigChooser { @Override public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) { int attribs[] = { EGL10.EGL_LEVEL, 0, EGL10.EGL_RENDERABLE_TYPE, 4, // EGL_OPENGL_ES2_BIT EGL10.EGL_COLOR_BUFFER_TYPE, EGL10.EGL_RGB_BUFFER, EGL10.EGL_RED_SIZE, 8, EGL10.EGL_GREEN_SIZE, 8, EGL10.EGL_BLUE_SIZE, 8, EGL10.EGL_DEPTH_SIZE, 16, EGL10.EGL_SAMPLE_BUFFERS, 1, EGL10.EGL_SAMPLES, 4, // This is for 4x MSAA. EGL10.EGL_NONE }; EGLConfig[] configs = new EGLConfig[1]; int[] configCounts = new int[1]; egl.eglChooseConfig(display, attribs, configs, 1, configCounts); if (configCounts[0] == 0) { // Failed! Error handling. return null; } else { return configs[0]; } } } 

您显然想要replace您的configuration所需的特定值。 实际上,要调用eglChooseConfig()eglChooseConfig()严格必要的属性,让它枚举所有匹配这些属性的configuration,然后实现自己的逻辑来select其中的最好的属性,会更加健壮。 eglChooseConfig()已定义的行为已经很奇怪了(参见文档 ),并没有告诉GPU供应商如何实现它。

在iOS上,您可以在GLKView上设置此属性以启用4x MSAA:

 [view setDrawableMultisample: GLKViewDrawableMultisample4X]; 

还有其他的抗锯齿方法可以考虑:

  • 超采样:渲染到每个方向上的最终渲染表面大小倍数(通常是两倍)的纹理,然后对其进行下采样。 这使用了大量的内存,开销很大。 但是,如果它符合您的性能要求,质量将是优秀的。
  • 老学校:用微小的偏移量多次渲染帧,并对帧进行平均。 这在OpenGL的早期阶段通常使用累积缓冲区来完成。 积累缓冲已经过时了,但是你可以对FBO做同样的事情。 有关该方法的说明,请参阅原始红皮书中的“Framebuffer”下的“场景抗锯齿”部分。

在Android平台上,您可以从GDC 2011下载这个OpenGL演示应用程序源代码:它包含许多最佳实践,并向您展示如何进行多重采样,包括覆盖抗锯齿。

你需要做的只是自定义GLSurfaceView.EGLConfigChooser并设置这个select器:

 // Set this chooser before calling setRenderer() setEGLConfigChooser(new MultisampleConfigChooser()); setRenderer(mRenderer); 

下面的MultisampleConfigChooser.java示例代码:

 package com.example.gdc11; import javax.microedition.khronos.egl.EGL10; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.egl.EGLDisplay; import android.opengl.GLSurfaceView; import android.util.Log; // This class shows how to use multisampling. To use this, call // myGLSurfaceView.setEGLConfigChooser(new MultisampleConfigChooser()); // before calling setRenderer(). Multisampling will probably slow down // your app -- measure performance carefully and decide if the vastly // improved visual quality is worth the cost. public class MultisampleConfigChooser implements GLSurfaceView.EGLConfigChooser { static private final String kTag = "GDC11"; @Override public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) { mValue = new int[1]; // Try to find a normal multisample configuration first. int[] configSpec = { EGL10.EGL_RED_SIZE, 5, EGL10.EGL_GREEN_SIZE, 6, EGL10.EGL_BLUE_SIZE, 5, EGL10.EGL_DEPTH_SIZE, 16, // Requires that setEGLContextClientVersion(2) is called on the view. EGL10.EGL_RENDERABLE_TYPE, 4 /* EGL_OPENGL_ES2_BIT */, EGL10.EGL_SAMPLE_BUFFERS, 1 /* true */, EGL10.EGL_SAMPLES, 2, EGL10.EGL_NONE }; if (!egl.eglChooseConfig(display, configSpec, null, 0, mValue)) { throw new IllegalArgumentException("eglChooseConfig failed"); } int numConfigs = mValue[0]; if (numConfigs <= 0) { // No normal multisampling config was found. Try to create a // converage multisampling configuration, for the nVidia Tegra2. // See the EGL_NV_coverage_sample documentation. final int EGL_COVERAGE_BUFFERS_NV = 0x30E0; final int EGL_COVERAGE_SAMPLES_NV = 0x30E1; configSpec = new int[]{ EGL10.EGL_RED_SIZE, 5, EGL10.EGL_GREEN_SIZE, 6, EGL10.EGL_BLUE_SIZE, 5, EGL10.EGL_DEPTH_SIZE, 16, EGL10.EGL_RENDERABLE_TYPE, 4 /* EGL_OPENGL_ES2_BIT */, EGL_COVERAGE_BUFFERS_NV, 1 /* true */, EGL_COVERAGE_SAMPLES_NV, 2, // always 5 in practice on tegra 2 EGL10.EGL_NONE }; if (!egl.eglChooseConfig(display, configSpec, null, 0, mValue)) { throw new IllegalArgumentException("2nd eglChooseConfig failed"); } numConfigs = mValue[0]; if (numConfigs <= 0) { // Give up, try without multisampling. configSpec = new int[]{ EGL10.EGL_RED_SIZE, 5, EGL10.EGL_GREEN_SIZE, 6, EGL10.EGL_BLUE_SIZE, 5, EGL10.EGL_DEPTH_SIZE, 16, EGL10.EGL_RENDERABLE_TYPE, 4 /* EGL_OPENGL_ES2_BIT */, EGL10.EGL_NONE }; if (!egl.eglChooseConfig(display, configSpec, null, 0, mValue)) { throw new IllegalArgumentException("3rd eglChooseConfig failed"); } numConfigs = mValue[0]; if (numConfigs <= 0) { throw new IllegalArgumentException("No configs match configSpec"); } } else { mUsesCoverageAa = true; } } // Get all matching configurations. EGLConfig[] configs = new EGLConfig[numConfigs]; if (!egl.eglChooseConfig(display, configSpec, configs, numConfigs, mValue)) { throw new IllegalArgumentException("data eglChooseConfig failed"); } // CAUTION! eglChooseConfigs returns configs with higher bit depth // first: Even though we asked for rgb565 configurations, rgb888 // configurations are considered to be "better" and returned first. // You need to explicitly filter the data returned by eglChooseConfig! int index = -1; for (int i = 0; i < configs.length; ++i) { if (findConfigAttrib(egl, display, configs[i], EGL10.EGL_RED_SIZE, 0) == 5) { index = i; break; } } if (index == -1) { Log.w(kTag, "Did not find sane config, using first"); } EGLConfig config = configs.length > 0 ? configs[index] : null; if (config == null) { throw new IllegalArgumentException("No config chosen"); } return config; } private int findConfigAttrib(EGL10 egl, EGLDisplay display, EGLConfig config, int attribute, int defaultValue) { if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) { return mValue[0]; } return defaultValue; } public boolean usesCoverageAa() { return mUsesCoverageAa; } private int[] mValue; private boolean mUsesCoverageAa; } 

在使用此function之前,您应该知道这会影响渲染效率,并且可能需要执行完整的性能testing。