美文网首页Android开发经验谈Android技术知识IT@程序员猿媛
android-opengles3.0开发【4】使用EGL绘制图

android-opengles3.0开发【4】使用EGL绘制图

作者: 昵称真难选 | 来源:发表于2019-05-10 08:41 被阅读2次

    简介

    前面几篇文章通过 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。

    项目地址

    相关文章

      网友评论

        本文标题:android-opengles3.0开发【4】使用EGL绘制图

        本文链接:https://www.haomeiwen.com/subject/fcqooqtx.html