MSAA多重采样demo
多重采样抗锯齿(MSAA,Multisample Anti-Aliasing)是一种用于减少图形渲染中锯齿效应的技术。
原理:
锯齿效应(又称走样)是指在光栅显示器上渲染图形时,本应平滑的几何边缘或色彩过渡处,因像素的离散化采样而呈现出阶梯状或参差不齐的视觉瑕疵。这种由连续信号到离散像素的映射失配,会显著降低图像的视觉质量和真实感。

如图所示,当我们渲染一个三角形时,每个像素的中心会设定一个采样点,用于判定该像素是否被三角形区域所覆盖(即是否属于待渲染区域)。
如果红色的采样点位于三角形内部,就会为该像素生成一个对应的片段(Fragment);反之,即使三角形覆盖了像素的局部区域,只要采样点未被覆盖,系统便不会为该像素生成片段。

多重采样抗锯齿通过在渲染过程中对图像进行额外的抽样来解决这个问题。

多重采样抗锯齿技术的核心,在于为每个像素分配多个采样点来判断三角形的覆盖情况。在三角形边缘附近的像素中,其最终片段的颜色将不再仅由单一的像素中心采样点决定,而是综合该像素内所有被覆盖采样点的信息共同计算得出,从而有效消除了边缘“一刀切”式的锯齿状过渡。
多重采样抗锯齿实现:
代码实现部分:
EGL 设置多重采样
我们知道 EGL 创建 OpenGL 的渲染上下文,会调用一系列的 egl 函数,例如 eglGetDisplay() ,eglInitialize() , eglChooseConfig() 等。
其中 eglChooseConfig 时会设置一个 attrib_list 用于确定表面的配置信息,我们可以在这个 attrib_list 设置多重采样的信息。
public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display)
{
/* From the Khronos definitions */
final int EGL_OPENGL_ES2_BIT = 4;
int[] attribs =
{
EGL10.EGL_LEVEL, 0,
EGL10.EGL_RED_SIZE, redSize,
EGL10.EGL_GREEN_SIZE, greenSize,
EGL10.EGL_BLUE_SIZE, blueSize,
EGL10.EGL_ALPHA_SIZE, 0,
EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL10.EGL_SAMPLE_BUFFERS, 1,
EGL10.EGL_SAMPLES, getNumberOfSamples(),
EGL10.EGL_DEPTH_SIZE, getDepthSize(),
EGL10.EGL_NONE
};
int[] num_config = new int[1];
egl.eglChooseConfig(display, attribs, null, 0, num_config);
int numConfigs = num_config[0];
if (numConfigs <= 0)
{
Log.e(LOG_TAG, "No EGL configs were found.");
return null;
}
EGLConfig[] configs = new EGLConfig[numConfigs];
egl.eglChooseConfig(display, attribs, configs, numConfigs, num_config);
return SelectAnEGLConfig(egl, display, configs);
}
然后,系统将遵循标准流程来创建用于实际绘制的渲染表面(EGLSurface)以及管理 OpenGL ES 状态的核心上下文(EGLContext)。
在配置选择阶段,可以指定 EGL_SAMPLES 参数来启用多重采样抗锯齿(MSAA)。该参数决定了每个像素的采样数量,采样数越多,图形边缘的平滑效果越好。通常建议设置为 2 或 4。数值越高,GPU 的计算与内存开销也越大,部分硬件可能无法支持 8 以上的采样数,强行设置可能导致上下文创建失败。
创建和销毁 OpenGL ES 的渲染上下文(EGLContext)
protected class TheEGLContextFactory implements GLSurfaceView.EGLContextFactory
{
public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig config)
{
/* From the Khronos definitions */
final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
int error;
while ((error = egl.eglGetError()) != EGL10.EGL_SUCCESS)
{
Log.e(LOG_TAG, String.format("Before TheEGLContextFactory.createContext(): EGL error: 0x%x", error));
}
/* Use the value of glesVersion */
int[] attribs = {EGL_CONTEXT_CLIENT_VERSION, glesVersion.getValue(), EGL10.EGL_NONE };
EGLContext context = egl.eglCreateContext(display, config, EGL10.EGL_NO_CONTEXT, attribs);
while ((error = egl.eglGetError()) != EGL10.EGL_SUCCESS)
{
Log.e(LOG_TAG, String.format("After TheEGLContextFactory.createContext(): EGL error: 0x%x", error));
}
return context;
}
public void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context)
{
/* Allow the derived classes to destroy their resources*/
destroyContextCallback();
egl.eglDestroyContext(display, context);
}
}
借助egl.eglDestroyContext调用标准的 EGL 函数来销毁上下文本身。
调用native层

通过类似
public static native void step();
这些 native 关键字声明的方法,就是 Java 通向 C/C++ 世界的大门。它们只有声明,没有实现,因为实现体在动态链接库(.so 文件)中。
native层

Native 层(C++)的 JNI 函数实现,它直接对应并实现了你在 Java 层声明的那些 native 方法。
设置Android.bp
jni的Android.bp:
将cpp编译出来的库为libmultisample_fb0'

以及demo根目录的Android.bp,将so库打包进apk:

完整版,参考MSAA demo:
[https://github.com/Whale12138/MultisampledFBO-demo/tree/main]
demo 运行案例:


浙公网安备 33010602011771号