Antialiasing in OpenGL ES 2.0?

Is there a way to implement the Anitaliasing technique in OpenGL ES 2.0? I researched and found several methods, but there was no change in the output.

In the worst case, I was planning to implement multi-pass rendering to smooth out the edges in the fragment shader by displaying the average pixel color around each pixel, but it costs more GPU performance.

Any suggestions?

+3


source to share


2 answers


Many devices support MSAA (Multi-Sample Anti-Aliasing). To use this feature, you need to select EGLConfig

one that has multisampling.

On Android, if you are using GLSurfaceView

, you will have to implement your own EGLConfigChooser

. Then you can use the EGL functions especially eglChooseConfig()

to find the configuration you want.

The following code is untested, but it should at least sketch out how it can be implemented. In the constructor of your GLSurfaceView

derived class, before calling, setRenderer()

add:

setEGLConfigChooser(new MyConfigChooser());

      

Then we implement MyConfigChooser

. You can do it with a nested class inside 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];
        }
    }
}

      



Obviously, you will want to substitute the specific values ​​required for your configuration. It is actually much more reliable to call eglChooseConfig()

with a small set of strictly necessary attributes, let it list all the configs that match those attributes and then implement your own logic to choose the best one. The certain behavior is eglChooseConfig()

already odd (see the documentation ) and it doesn't say how the GPU manufacturers implement it.

On iOS, you can set this property on yours GLKView

to enable 4x MSAA:

[view setDrawableMultisample: GLKViewDrawableMultisample4X];

      

There are other anti-aliasing approaches you can consider:

  • Supersampling: Render a texture that is multiple (usually twice) the size of your final render surface in each direction and then shrinks it. This uses a lot of memory and the overhead is substantial. But if it meets your performance requirements, the quality will be excellent.
  • Old school: render the frame multiple times with slight offsets and average the frames. This was usually done with an accumulation buffer in the early days of OpenGL. Cumulative buffer is deprecated, but you can do the same with FBOs. For a description of the method, see the "Scene Smoothing" section in the "Framebuffer" section in the original
+18


source


On the Android platform, you can download this OpenGL source code from GDC 2011: it contains many best practices and shows you how to do multisampling, including coverage antialiasing.

What you need to do is just configure GLSurfaceView.EGLConfigChooser

and install this selection:

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

      



MultisampleConfigChooser.java

example code below:

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

      

Before using this feature, you should be aware that it will affect rendering performance and you may need to perform a full performance test.

+1


source







All Articles