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。

項目地址

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章