有过android开发经验应该或多或少地听说过OpengGL,甚至也行使用过framework提供的opengl包做过一些2d,3d相关的绘制,但事实上Android还提供了NDK方面的opengl能力,这个能力更接近真实的opengl,网上这部分的文章并不多,因此打算写一篇NDK相关的OpenGL。
什么是OpenGL
按照官方文档的说法:
OpenGL(Open Graphics Library开发图形接口)是一个跨平台的图形API,用于指定3D图形处理硬件中的标准软件接口。
但在移动平台端我们说的OpenGL更确切地说是OpenGL ES。因为OpenGL一般用于在图形工作站,PC端使用,由于性能各方面原因,在移动端使用OpenGL基本带不动。为此,Khronos公司就为OpenGL提供了一个子集,OpenGL ES(OpenGl for Embedded System)。
环境准备
提供Surface
要想在android上使用NDK的OpenGL,我们需要提供一个画布给NDK,这个画布其实就是一个SurfaceView,在framework层构造一个SurfaceView并获取其中的Surface传给NDK
<?xml version="1.0" encoding="utf-8"?>
<SurfaceView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/sv_main"
>
</SurfaceView>
mSurfaceView = findViewById(R.id.sv_main);
mSurfaceView = findViewById(R.id.sv_main);
if(null != mSurfaceView) {
mSurfaceView.setZOrderMediaOverlay(true);
mSurfaceView.getHolder().setFormat(PixelFormat.RGBA_8888);
mSurfaceView.getHolder().addCallback(new SurfaceHolder.Callback2() {
@Override
public void surfaceRedrawNeeded(@NonNull SurfaceHolder surfaceHolder) {
}
@Override
public void surfaceCreated(@NonNull SurfaceHolder surfaceHolder) {
// native方法传递给NDK OpenGLClass.getInstance().onSurfaceCreateNative(surfaceHolder.getSurface());
}
@Override
public void surfaceChanged(@NonNull SurfaceHolder surfaceHolder, int i, int i1, int i2) {
}
@Override
public void surfaceDestroyed(@NonNull SurfaceHolder surfaceHolder) {
}
});
}
获取当前平台窗口
接下来开始都是NDK部分的处理
ANativeWindow* m_CurrentNativeWindow = ANativeWindow_fromSurface(env, surface);
上面获取到的ANativeWindow就是android平台用于与openGL渲染的本地窗口,不同平台有不同的本地窗口,比如Window系统对应的就是Window,获取窗口之后接下去要做的将本地窗口与OpenGL渲染做环境准备和绑定,这里我们通常会引入EGL。
EGL简单理解就是OpenGL渲染和本地窗口系统之间的中间层,通过它我们可以屏蔽不同平台不同窗口的处理差异。
获取 EGLDisplay 对象,建立与本地窗口系统的连接
EGLDisplay eglDisplay; /**< 系统显示 ID 或句柄。 */
eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
获取 EGLConfig 对象,确定渲染表面的配置信息
EGLint eglConfigNum = 0;
EGLint configSpec[] = {
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_NONE
};
int initConfig = eglChooseConfig(eglDisplay, configSpec, &eglConfig, 1, &eglConfigNum);
if (EGL_TRUE != initConfig)
{
LOGD("eglChooseConfig failed!");
return;
}
创建渲染表面 EGLSurface
EGLSurface eglSurface = eglCreateWindowSurface(eglDisplay, eglConfig, m_CurrentNativeWindow, NULL);
创建渲染上下文EGLContext
const EGLint ctxAttr[] = {
EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE
};
EGLContext eglContext = eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT, ctxAttr);
绑定EGLDisplay,EGLSurface,EGLContext
if (EGL_TRUE != eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext))
{
LOGD("eglMakeCurrent failed!");
return;
}
当我们完成EGLDisplay,EGLSurface,EGLContext三者的绑定之后,OpenGL ES的环境就安装好了。接下去可以直接使用OpenGL ES的接口进行渲染绘制了。
简单画个背景
// 绘制功能,这里指简单绘制背景
glClearColor(1.0, 1.0, 0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
glFlush();
// 交换缓冲
eglSwapBuffers(eglDisplay, eglSurface);
因为这篇文章重点介绍怎么使用NDK的opengl,因此绘制部分我们就简单绘制一个黄色背景。
依赖配置
前面我们用到的NDK相关的方法需要配置对应的依赖才能保证编译通过,比如ANativeWindow_fromSurface以及egl相关接口,因此在NDK的CMakeList里具体新增了如下的依赖
target_link_libraries( # Specifies the target library.
# 制定目标库.
OpenGLUI
android # 保证能编译过ANativeWindow_fromSurface
EGL #编译过相关egl接口
GLESv2 #编译过相关opengl es接口
# Links the target library to the log library
# included in the NDK.
${log-lib} )
源码
上面说了这么多,demo我也写了一个,传送门。