【问题标题】:Antialiasing in OpenGL ES 2.0?OpenGL ES 2.0 中的抗锯齿?
【发布时间】:2014-11-20 09:23:33
【问题描述】:

有没有办法在 OpenGL ES 2.0 中实现抗锯齿技术?我看了看,发现的方法很少,但输出没有变化。

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

有什么建议吗?

【问题讨论】:

    标签: android ios opengl-es opengl-es-2.0 irrlicht


    【解决方案1】:

    很多设备都支持 MSAA(多采样抗锯齿)。要利用此功能,您必须选择具有多重采样的EGLConfig

    在 Android 上,如果您使用 GLSurfaceView,则必须实现自己的 EGLConfigChooser。然后您可以使用 EGL 函数,尤其是 eglChooseConfig() 来找到您喜欢的配置。

    以下代码未经测试,但至少应该概述如何实现。在您的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];
            }
        }
    }
    

    您显然希望替换配置所需的特定值。实际上,使用一小组严格必要的属性调用eglChooseConfig() 会更加健壮,让它枚举与这些属性匹配的所有配置,然后实现您自己的逻辑以从中选择最好的。 eglChooseConfig() 的定义行为已经有点奇怪了(参见 documentation),并且不知道 GPU 供应商如何实现它。

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

    [view setDrawableMultisample: GLKViewDrawableMultisample4X];
    

    您可以考虑其他抗锯齿方法:

    • 超级采样:在每个方向上渲染为最终渲染表面大小的倍数(通常是两倍)的纹理,然后对其进行下采样。这会占用大量内存,并且开销很大。但是,如果它符合您的性能要求,那么质量将非常出色。
    • 老派:以轻微偏移多次渲染帧,然后平均帧。这通常在 OpenGL 早期使用累积缓冲区完成。累积缓冲区已过时,但您可以对 FBO 做同样的事情。有关该方法的说明,请参见原始Red Book 中“帧缓冲区”下的“场景抗锯齿”部分。

    【讨论】:

      【解决方案2】:

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

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

      // 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;
      }
      

      在使用此功能之前,您应该知道这会影响渲染效率,并且可能需要进行全面的性能测试。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多