Android OpenGL ES基本用法(5),自定义EGLSurfaceView


目录


Android OpenGL ES基本用法(5),自定义EGLSurfaceView_第1张图片

EGL

EGL是OpenGL ES和本地窗口系统的接口,不同平台上EGL配置是不一样的,而OpenGL的调用方式是一致的,也就是说OpenGL的跨平台特性依赖于EGL接口。

为什么要自定义GlSurfaceView

当我们需要把同一个场景渲染到不同的Surface上时,此时系统GLSurfaceView就不能满足需求了,所以我们需要自己创建EGL环境来实现渲染操作。
OpenGL整体是一个状态机,通过改变状态就能改变后续的渲染方式,而EGLContext(EgL上下文)就保存有所有状态,因此可以通过共享EGLContext来实现同一场景渲染到不同的Surface上。

创建自己的EglHelper

1、得到Egl实例:
2、得到默认的显示设备(就是窗口)
3、初始化默认显示设备
4、设置显示设备的属性
5、从系统中获取对应属性的配置
6、创建EglContext
7、创建渲染的Surface
8、绑定EglContext和Surface到显示设备中
9、刷新数据,显示渲染场景

自定义GLSurfaceView

1、继承SurfaceView,并实现其CallBack回调
2、自定义GLThread线程类,主要用于OpenGL的绘制操作
3、添加设置Surface和EglContext的方法(例如需要设置MediaCodec的Surface)
4、提供和系统GLSurfaceView相同的调用方法

EGLSurfaceView.java

package com.zhangyu.myopengl.egl;

import android.content.Context;
import android.util.AttributeSet;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

import java.lang.ref.WeakReference;

import javax.microedition.khronos.egl.EGLContext;

public abstract class EGLSurfaceView extends SurfaceView implements SurfaceHolder.Callback {
     
    private static final String TAG = "MyGlSurfaceView";
    //surface可以从外面传递进去
    private Surface surface;
    //egl上下文
    private EGLContext eglContext;
    //
    private EGLThread eglThread;
    //
    private EGLRender eglRender;
    //渲染模式,手动刷新,自动刷新,要给一个默认值,否则都没有走,默认是自动刷新60帧
    private RenderMode renderMode = RenderMode.RENDERMODE_CONTINUOUSLY;

    /**
     * 设置render mode
     * 0 手动刷新
     * 1 自动刷新
     */
    public enum RenderMode {
     
        RENDERMODE_WHEN_DIRTY,
        RENDERMODE_CONTINUOUSLY
    }

    public void setRender(EGLRender eglRender) {
     
        this.eglRender = eglRender;
    }

    public void setRenderMode(RenderMode renderMode) {
     
        this.renderMode = renderMode;
    }

    public EGLSurfaceView(Context context) {
     
        this(context, null);
    }

    public EGLSurfaceView(Context context, AttributeSet attrs) {
     
        this(context, attrs, 0);
    }

    public EGLSurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
     
        super(context, attrs, defStyleAttr);
        getHolder().addCallback(this);
    }

    public void setSurfaceAndEglContext(Surface surface, EGLContext eglContext) {
     
        this.surface = surface;
        this.eglContext = eglContext;
    }

    public EGLContext getEglContext() {
     
        if (eglThread != null) {
     
            return eglThread.getEglContext();
        }
        return null;
    }

    public void requestRender() {
     
        if (eglThread != null) {
     
            eglThread.requestRender();
        }
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
     
        if (surface == null) {
     
            surface = holder.getSurface();
        }
        eglThread = new EGLThread(new WeakReference<EGLSurfaceView>(this));
        eglThread.isCreate = true;
        eglThread.start();
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
     
        eglThread.width = width;
        eglThread.height = height;
        eglThread.isChange = true;
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
     
        eglThread.onDestory();
        eglThread = null;
        surface = null;
        eglContext = null;
    }

    public interface  EGLRender {
     
        void onSurfaceCreated();

        void onSurfaceChanged(int width, int height);

        void onDrawFrame();
    }

    static class EGLThread extends Thread {
     
        private WeakReference<EGLSurfaceView> myGlSurfaceViewWeakReference;
        private EGLHelper eglHelper = null;
        private boolean isExit = false;
        private boolean isCreate = false;
        private boolean isChange = false;
        private boolean isStart = false;
        //用来控制手动刷新
        private Object object;

        private int width;
        private int height;

        public EGLThread(WeakReference<EGLSurfaceView> myGlSurfaceViewWeakReference) {
     
            this.myGlSurfaceViewWeakReference = myGlSurfaceViewWeakReference;
        }

        @Override
        public void run() {
     
            super.run();
            isExit = false;
            isStart = false;
            object = new Object();
            eglHelper = new EGLHelper();
            eglHelper.initEgl(myGlSurfaceViewWeakReference.get().surface, myGlSurfaceViewWeakReference.get().eglContext);
            while (true) {
     
                if (isExit) {
     
                    // 释放资源
                    release();
                    break;
                }
                /**
                 * 刷新模式
                 */
                if (isStart) {
     
                    if (myGlSurfaceViewWeakReference.get().renderMode == RenderMode.RENDERMODE_WHEN_DIRTY) {
     
                        synchronized (object) {
     
                            try {
     
                                object.wait();
                            } catch (InterruptedException e) {
     
                                e.printStackTrace();
                            }
                        }
                    } else if (myGlSurfaceViewWeakReference.get().renderMode == RenderMode.RENDERMODE_CONTINUOUSLY) {
     
                        //自动刷新,每秒60帧
                        try {
     
                            Thread.sleep(1000 / 60);
                        } catch (InterruptedException e) {
     
                            e.printStackTrace();
                        }
                    }
                }
                onCreate();
                onChange(width, height);
                onDraw();
                isStart = true;
            }
        }

        /**
         * 创建,执行一次
         */
        private void onCreate() {
     
            if (isCreate && myGlSurfaceViewWeakReference.get().eglRender != null) {
     
                isCreate = false;
                myGlSurfaceViewWeakReference.get().eglRender.onSurfaceCreated();
            }
        }

        /**
         * 改变,执行一次
         *
         * @param width
         * @param height
         */
        private void onChange(int width, int height) {
     
            if (isChange && myGlSurfaceViewWeakReference.get().eglRender != null) {
     
                isChange = false;
                myGlSurfaceViewWeakReference.get().eglRender.onSurfaceChanged(width, height);
            }
        }

        /**
         * 绘制,每次循环里都要执行
         */
        private void onDraw() {
     
            if (myGlSurfaceViewWeakReference.get().eglRender != null && eglHelper != null) {
     
                myGlSurfaceViewWeakReference.get().eglRender.onDrawFrame();
                //第一次刷新的时候,需要刷新两次
                if (!isStart) {
     
                    myGlSurfaceViewWeakReference.get().eglRender.onDrawFrame();
                }
                eglHelper.swapBuffers();
            }
        }

        /**
         * 手动刷新
         * 解除掉线程里的阻塞等待
         */
        private void requestRender() {
     
            if (object != null) {
     
                synchronized (object) {
     
                    object.notifyAll();
                }
            }
        }

        public void onDestory() {
     
            isExit = true;
            requestRender();
        }

        public void release() {
     
            if (eglHelper != null) {
     
                eglHelper.destoryEgl();
                eglHelper = null;
                object = null;
                myGlSurfaceViewWeakReference = null;
            }
        }

        public EGLContext getEglContext() {
     
            if (eglHelper != null) {
     
                return eglHelper.getmEglContext();
            }
            return null;
        }
    }
}


EGLHelper.java

package com.zhangyu.myopengl.egl;

import android.opengl.EGL14;
import android.view.Surface;

import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLContext;
import javax.microedition.khronos.egl.EGLDisplay;
import javax.microedition.khronos.egl.EGLSurface;

/**
 * 1、得到Egl实例:
 * 2、得到默认的显示设备(就是窗口)
 * 3、初始化默认显示设备
 * 4、设置显示设备的属性
 * 5、从系统中获取对应属性的配置
 * 6、创建EglContext
 * 7、创建渲染的Surface
 * 8、绑定EglContext和Surface到显示设备中
 * 9、刷新数据,显示渲染场景
 */
public class EGLHelper {
     

    private EGL10 mEgl;
    private EGLDisplay mEglDisplay;
    private EGLContext mEglContext;
    private EGLSurface mEglSurface;

    public void initEgl(Surface surface, EGLContext eglContext) {
     

        //1、得到Egl实例:
        mEgl = (EGL10) EGLContext.getEGL();

        //2、得到默认的显示设备(就是窗口)
        mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
        if (mEglDisplay == EGL10.EGL_NO_DISPLAY) {
     
            throw new RuntimeException("eglGetDisplay failed");
        }

        //3、初始化默认显示设备
        int[] version = new int[2];
        if (!mEgl.eglInitialize(mEglDisplay, version)) {
     
            throw new RuntimeException("eglInitialize failed");
        }

        //4、设置显示设备的属性
        int[] attrbutes = new int[]{
     
                EGL10.EGL_RED_SIZE, 8,
                EGL10.EGL_GREEN_SIZE, 8,
                EGL10.EGL_BLUE_SIZE, 8,
                EGL10.EGL_ALPHA_SIZE, 8,
                EGL10.EGL_DEPTH_SIZE, 8,
                EGL10.EGL_STENCIL_SIZE, 8,
                EGL10.EGL_RENDERABLE_TYPE, 4,
                EGL10.EGL_NONE};

        int[] num_config = new int[1];
        if (!mEgl.eglChooseConfig(mEglDisplay, attrbutes, null, 1, num_config)) {
     
            throw new IllegalArgumentException("eglChooseConfig failed");
        }

        int numConfigs = num_config[0];
        if (numConfigs <= 0) {
     
            throw new IllegalArgumentException(
                    "No configs match configSpec");
        }

        //5、从系统中获取对应属性的配置
        EGLConfig[] configs = new EGLConfig[numConfigs];
        if (!mEgl.eglChooseConfig(mEglDisplay, attrbutes, configs, numConfigs,
                num_config)) {
     
            throw new IllegalArgumentException("eglChooseConfig#2 failed");
        }

        //6、创建EglContext
        int[] attrib_list = {
     
                EGL14.EGL_CONTEXT_CLIENT_VERSION, 2,
                EGL10.EGL_NONE
        };
        if (eglContext != null) {
     
            mEglContext = mEgl.eglCreateContext(mEglDisplay, configs[0], eglContext, attrib_list);
        } else {
     
            mEglContext = mEgl.eglCreateContext(mEglDisplay, configs[0], EGL10.EGL_NO_CONTEXT, attrib_list);
        }

        //7、创建渲染的Surface
        mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, configs[0], surface, null);

        //8、绑定EglContext和Surface到显示设备中
        if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
     
            throw new RuntimeException("eglMakeCurrent fail");
        }
    }

    /**
     * 交换缓冲区
     * 手动刷新
     *
     * @return
     */
    public boolean swapBuffers() {
     
        if (mEgl != null) {
     
            return mEgl.eglSwapBuffers(mEglDisplay, mEglSurface);
        } else {
     
            throw new RuntimeException("egl is null");
        }
    }

    public EGLContext getmEglContext() {
     
        return mEglContext;
    }

    public void destoryEgl() {
     
        if (mEgl != null) {
     

            //解绑
            mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE,
                    EGL10.EGL_NO_SURFACE,
                    EGL10.EGL_NO_CONTEXT);

            //Surface置空
            mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
            mEglSurface = null;

            //Context置空
            mEgl.eglDestroyContext(mEglDisplay, mEglContext);
            mEglContext = null;

            //停用显示设备
            mEgl.eglTerminate(mEglDisplay);
            mEglDisplay = null;
            mEgl = null;
        }
    }

}



ShaderUtil

package com.zhangyu.myopengl.egl;

import android.content.Context;
import android.opengl.GLES20;
import android.util.Log;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

public class ShaderUtil {
     
    private static final String TAG = "ShaderUtil";

    /**
     * 读取shader
     *
     * @param context
     * @param rawId
     * @return
     */
    public static String readRawTxt(Context context, int rawId) {
     
        InputStream inputStream = context.getResources().openRawResource(rawId);
        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
        StringBuffer sb = new StringBuffer();
        String line;

        try {
     
            while ((line = reader.readLine()) != null) {
     
                sb.append(line).append("\n");
            }
            reader.close();
        } catch (IOException e) {
     
            e.printStackTrace();
        }
        return sb.toString();
    }

    /**
     * 加载shader
     *
     * @param shaderType
     * @param source
     * @return
     */
    private static int loadShader(int shaderType, String source) {
     
        //创建shader
        int shader = GLES20.glCreateShader(shaderType);
        if (shader != 0) {
     
            //关联source
            GLES20.glShaderSource(shader, source);
            //编译
            GLES20.glCompileShader(shader);
            //获取编译状态
            int[] compile = new int[1];
            GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compile, 0);
            if (compile[0] != GLES20.GL_TRUE) {
     
                //失败
                Log.e(TAG, "loadShader: shader compile error");
                GLES20.glDeleteShader(shader);
                shader = 0;
            }
        }
        return shader;
    }

    /**
     * 生成程序
     *
     * @param vertexSource
     * @param fragmentSource
     * @return
     */
    public static int createProgram(String vertexSource, String fragmentSource) {
     
        int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
        if (vertexShader == 0) {
     
            return 0;
        }
        int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
        if (fragmentShader == 0) {
     
            return 0;
        }
        //创建
        int program = GLES20.glCreateProgram();
        if (program != 0) {
     
            //附加着色器
            GLES20.glAttachShader(program, vertexShader);
            GLES20.glAttachShader(program, fragmentShader);
            //连接
            GLES20.glLinkProgram(program);
            //检查连接是否成功
            int[] linkStatus = new int[1];
            GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
            if (linkStatus[0] != GLES20.GL_TRUE) {
     
                //出错
                Log.e(TAG, "createProgram: link program error");
                GLES20.glDeleteProgram(program);
                program = 0;
            }
        }
        return program;
    }
}


EGLUtils

package com.zhangyu.myopengl.egl;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLES20;
import android.opengl.GLUtils;
import android.util.Log;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

public class EGLUtils {
     
    private static final String TAG = "ShaderUtil";

    /**
     * 读取shader
     *
     * @param context
     * @param rawId
     * @return
     */
    public static String readRawTxt(Context context, int rawId) {
     
        InputStream inputStream = context.getResources().openRawResource(rawId);
        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
        StringBuffer sb = new StringBuffer();
        String line;

        try {
     
            while ((line = reader.readLine()) != null) {
     
                sb.append(line).append("\n");
            }
            reader.close();
        } catch (IOException e) {
     
            e.printStackTrace();
        }
        return sb.toString();
    }

    /**
     * 加载shader
     *
     * @param shaderType
     * @param source
     * @return
     */
    private static int loadShader(int shaderType, String source) {
     
        //创建shader
        int shader = GLES20.glCreateShader(shaderType);
        if (shader != 0) {
     
            //关联source
            GLES20.glShaderSource(shader, source);
            //编译
            GLES20.glCompileShader(shader);
            //获取编译状态
            int[] compile = new int[1];
            GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compile, 0);
            if (compile[0] != GLES20.GL_TRUE) {
     
                //失败
                Log.e(TAG, "loadShader: shader compile error");
                GLES20.glDeleteShader(shader);
                shader = 0;
            }
        }
        return shader;
    }

    /**
     * 生成程序
     *
     * @param vertexSource
     * @param fragmentSource
     * @return
     */
    public static int createProgram(String vertexSource, String fragmentSource) {
     
        int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
        if (vertexShader == 0) {
     
            return 0;
        }
        int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
        if (fragmentShader == 0) {
     
            return 0;
        }
        //创建
        int program = GLES20.glCreateProgram();
        if (program != 0) {
     
            //附加着色器
            GLES20.glAttachShader(program, vertexShader);
            GLES20.glAttachShader(program, fragmentShader);
            //连接
            GLES20.glLinkProgram(program);
            //检查连接是否成功
            int[] linkStatus = new int[1];
            GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
            if (linkStatus[0] != GLES20.GL_TRUE) {
     
                //出错
                Log.e(TAG, "createProgram: link program error");
                GLES20.glDeleteProgram(program);
                program = 0;
            }
        }
        return program;
    }


    /**
     * 创建一个图片纹理
     *
     * @param imgSrc
     * @return
     */
    public static int createImageTextureId(Context context, int imgSrc) {
     
        //创建纹理
        int[] textureIds = new int[1];
        GLES20.glGenTextures(1, textureIds, 0);
        int textureId = textureIds[0];
        //绑定纹理
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
        //环绕过滤
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
        //生成图片
        Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), imgSrc);
        //图片绑定到纹理上
        GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
        //解绑纹理
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);

        return textureId;
    }
}


你可能感兴趣的:(OpenGL)