android-opengles3.0開發【4】使用EGL繪製圖形
簡介
前面幾篇文章通過 GLSurfaceView 進行 opengles 的渲染,使用簡單。但是不夠靈活,一個 opengl 只能渲染一個 GLSurfaceView,一旦 GLSurfaceView 銷燬,對應的 opengl 也會銷燬。
使用 EGL 可以避免上述缺點。
EGL 時渲染 API 和平臺原生視窗系統之間的介面,主要任務是:
- 查詢並初始化裝置的可用顯示器。
- 建立渲染表面。
- 建立渲染上下文。
EGL 使用流程
EGL 使用主要步驟很清晰,每個步驟都有相應的方法進行操作。
- 與視窗系統通訊,獲取顯示器:eglGetDisplay
- 初始化EGL:eglInitialize
- 根據需要,讓EGL 選擇合適的配置:eglChooseConfig
- 建立上下文:eglCreateContext
- 建立渲染區域:EGL視窗:eglCreateWindowSurface
- 指定當前上下文:eglMakeCurrent
- 載入著色器、連線程式、繫結資料到屬性進行渲染(使用的資料、著色器之類的和前幾篇文章一樣)
private void createEGL(){ //獲取顯示裝置 eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); if (eglDisplay == EGL_NO_DISPLAY){ throw new RuntimeException("egl error:" + eglGetError()); } //初始化EGL int[] version = new int[2]; if (!eglInitialize(eglDisplay, version,0,version,1)){ throw new RuntimeException("egl error:" + eglGetError()); } //EGL選擇配置 int[] configAttribList = { EGL_BUFFER_SIZE, 32, EGL_ALPHA_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_RED_SIZE, 8, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_NONE }; int[] numConfig = new int[1]; EGLConfig[] configs = new EGLConfig[1]; if(!eglChooseConfig(eglDisplay, configAttribList, 0, configs,0, configs.length, numConfig,0)){ throw new RuntimeException("egl error:" + eglGetError()); } eglConfig = configs[0]; //建立ELG上下文 int[] contextAttribList = { EGL_CONTEXT_CLIENT_VERSION,2, EGL_NONE }; eglContext = eglCreateContext(eglDisplay, eglConfig,EGL_NO_CONTEXT,contextAttribList,0); if (eglContext == EGL_NO_CONTEXT){ throw new RuntimeException("egl error:" + eglGetError()); } } public void render(Surface surface, int width, int height){ //建立螢幕上渲染區域:EGL視窗 int[] surfaceAttribList = {EGL_NONE}; EGLSurface eglSurface = eglCreateWindowSurface(eglDisplay, eglConfig, surface, surfaceAttribList, 0); //指定當前上下文 eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext); //獲取著色器 int vertexShader = loadShader(GL_VERTEX_SHADER, VERTEX_SHADER_SOURCE); int fragmentShader = loadShader(GL_FRAGMENT_SHADER, FRAGMENT_SHADER_SOURCE); //建立並連線程式 int program = createAndLinkProgram(vertexShader, fragmentShader); //設定清除渲染時的顏色 glClearColor(1.0f, 1.0f, 1.0f, 0.0f); //設定視口 glViewport(0, 0, width, height); //獲取頂點、顏色資料 FloatBuffer vertexBuffer = getVertextBuffer(); FloatBuffer vertexColorBuffer = getVertexColorBuffer(); //擦除螢幕 glClear(GL_COLOR_BUFFER_BIT); //使用程式 glUseProgram(program); //繫結頂點、顏色資料到指定屬性位置 int vposition = glGetAttribLocation(program, "vPosition"); glVertexAttribPointer(vposition,3,GL_FLOAT,false,0,vertexBuffer); glEnableVertexAttribArray(vposition); int aColor = glGetAttribLocation(program, "aColor"); glEnableVertexAttribArray(aColor); glVertexAttribPointer(aColor, 4, GL_FLOAT, false, 0, vertexColorBuffer); //繪製 glDrawArrays(GL_TRIANGLES,0,3); //交換 surface 和顯示器快取 eglSwapBuffers(eglDisplay, eglSurface); //釋放 eglDestroySurface(eglDisplay, eglSurface); }
基於執行緒實現渲染器
opengles 渲染是基於執行緒的,需要自己實現一個管理 opengles 環境和渲染的執行緒的渲染器。
public class EGLRender extends HandlerThread { private EGLConfig eglConfig; private EGLDisplay eglDisplay; private EGLContext eglContext; public EGLRender() { super("ELGRender"); } private void createEGL(){ //程式碼在上面 } private void destroyEGL(){ eglDestroyContext(eglDisplay, eglContext); eglContext = EGL_NO_CONTEXT; eglDisplay = EGL_NO_DISPLAY; } @Override public synchronized void start() { super.start(); new Handler(getLooper()).post(new Runnable() { @Override public void run() { createEGL(); } }); } public void release(){ new Handler(getLooper()).post(new Runnable() { @Override public void run() { destroyEGL(); quit(); } }); } public void render(Surface surface, int width, int height){ //程式碼在上面 } }
使用 SurfaceView 進行顯示
佈局檔案
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".fragment.EGLFragment"> <SurfaceView android:id="@+id/surfaceView" android:layout_width="match_parent" android:layout_height="match_parent" /> </RelativeLayout>
將渲染器與佈局中的 SurfaceView 進行關聯。
public class EGLFragment extends Fragment { private SurfaceView surfaceView; private EGLRender eglRender; public EGLFragment() { // Required empty public constructor } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment return inflater.inflate(R.layout.fragment_egl, container, false); } @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); eglRender = new EGLRender(); eglRender.start(); surfaceView = view.findViewById(R.id.surfaceView); surfaceView.getHolder().addCallback(new SurfaceHolder.Callback() { @Override public void surfaceCreated(SurfaceHolder holder) { } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { eglRender.render(holder.getSurface(), width, height); } @Override public void surfaceDestroyed(SurfaceHolder holder) { } }); } @Override public void onDestroy() { eglRender.release(); eglRender = null; super.onDestroy(); } }
總結
本文梳理了 EGL 的使用流程,基於執行緒自定義了 EGL 渲染器,將內容顯示到 SurfaceView。