【OpenGL ES】不用GLSurfaceView,如何渲染图像

1 前言

​ Android 中,GLSurfaceView 封装了 EGL 环境,使得我们省去了复杂的 EGL 环境搭建。如果我们不用 GLSurfaceView,该如何渲染 OpenGL ES 图像?在回答此问题前,我们先了解下 EGL。

​ EGL 是 Khronos Group 定义的平台无关接口,作为 OpenGL ES 和本地窗口系统之间的桥梁,主要功能如下。

  • 管理图形上下文
  • 创建和管理渲染表面 (surface)
  • 同步渲染与平台显示系统

​ EGL 提供了两种渲染方式,分别是离屏渲染和窗口渲染,分别对应 eglCreatePbufferSurface 和 eglCreateWindowSurface。对于离屏渲染方案,详见 → EGL+FBO离屏渲染。eglCreateWindowSurface 函数的源码如下。

cpp 复制代码
public static EGLSurface eglCreateWindowSurface(EGLDisplay dpy,
    EGLConfig config, Object win, int[] attrib_list, int offset){
    Surface sur = null;
    if (win instanceof SurfaceView) {
        SurfaceView surfaceView = (SurfaceView)win;
        sur = surfaceView.getHolder().getSurface();
    } else if (win instanceof SurfaceHolder) {
        SurfaceHolder holder = (SurfaceHolder)win;
        sur = holder.getSurface();
    } else if (win instanceof Surface) {
        sur = (Surface) win;
    }

    EGLSurface surface;
    if (sur != null) {
        surface = _eglCreateWindowSurface(dpy, config, sur, attrib_list, offset);
    } else if (win instanceof SurfaceTexture) {
        surface = _eglCreateWindowSurfaceTexture(dpy, config,
                win, attrib_list, offset);
    } else {
        throw new java.lang.UnsupportedOperationException(
            "eglCreateWindowSurface() can only be called with an instance of " +
            "Surface, SurfaceView, SurfaceTexture or SurfaceHolder at the moment, " +
            "this will be fixed later.");
    }

    return surface;
}

​ 主要留意 win 参数,可以看到它可以是 SurfaceView、SurfaceHolder、Surface,本质都是为了获取 Surface。因此我们提供了以下两种渲染图像的方案。

  • 继承 SurfaceView 方案:自定义一个 View 继承 SurfaceView,并实现 SurfaceHolder.Callback 接口,在 surfaceCreated 方法中将 this 或 getHolder() 传给 eglCreateWindowSurface 函数。
  • 继承 TextureView 方案:自定义一个 View 继承 TextureView,并实现 TextureView.SurfaceTextureListener 接口,在 onSurfaceTextureAvailable 方法中会提供 SurfaceTexture,我们可以创建一个 Surface,并将 SurfaceTexture 传给 Surface,然后将创建的 Surface 传给 eglCreateWindowSurface 函数。

​ 本文完整代码详见 → 不用GLSurfaceView,如何渲染图像。

2 继承 SurfaceView 方案

​ 自定义一个 View 继承 SurfaceView,并实现 SurfaceHolder.Callback 接口,在 surfaceCreated 方法中将 this 或 getHolder() 传给 eglCreateWindowSurface 函数。

​ 由于 GLSurfaceView 继承 SurfaceView,所以该方案很容易想到,只需要将 GLSurfaceView 的核心代码扣出来就行。

​ MainActivity.java

java 复制代码
package com.zhyan8.egldemo;

import android.os.Bundle;

import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {
    private EGLSurfaceView mEglSurfaceView;

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mEglSurfaceView = new EGLSurfaceView(this);
        setContentView(mEglSurfaceView);
        mEglSurfaceView.setRenderer(new MyRenderer(this));
    }

    @Override
    protected void onResume() {
        super.onResume();
        mEglSurfaceView.requestRender();
        //mEglSurfaceView.startRender();
    }

    @Override
    protected void onPause() {
        super.onPause();
        mEglSurfaceView.stopRender();
    }
}

​ EGLSurfaceView.java

java 复制代码
package com.zhyan8.egldemo;

import android.content.Context;
import android.opengl.EGL14;
import android.opengl.EGLConfig;
import android.opengl.EGLContext;
import android.opengl.EGLDisplay;
import android.opengl.EGLExt;
import android.opengl.EGLSurface;
import android.util.Log;
import android.view.Choreographer;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

import androidx.annotation.NonNull;

/**
 * @author little fat sheep
 * 承载EGL环境的View, 类比GLSurfaceView
 */
public class EGLSurfaceView extends SurfaceView implements SurfaceHolder.Callback {
    private static final String TAG = "EGLSurfaceView";

    protected EGLDisplay mEGLDisplay;
    protected EGLConfig mEGLConfig;
    protected EGLContext mEGLContext;
    protected EGLSurface mEGLSurface;
    protected Context mContext;
    protected Renderer mRenderer;
    protected boolean mFirstCreateSurface = true;
    private Choreographer mChoreographer = Choreographer.getInstance();

    public EGLSurfaceView(Context context) {
        super(context);
        mContext = context;
        getHolder().addCallback(this);
    }

    // 设置渲染器
    public void setRenderer(Renderer renderer) {
        mRenderer = renderer;
    }

    // 开始持续渲染
    public void startRender() {
        Log.i(TAG, "startRender");
        mChoreographer.removeFrameCallback(mFrameCallback);
        mChoreographer.postFrameCallback(mFrameCallback);
    }

    // 暂停持续渲染
    public void stopRender() {
        Log.i(TAG, "stopRender");
        mChoreographer.removeFrameCallback(mFrameCallback);
    }

    // 请求渲染一帧
    public void requestRender() {
        mFrameCallback.doFrame(System.nanoTime());
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        createDisplay();
        createConfig();
        createContext();
    }

    @Override
    public void surfaceCreated(@NonNull SurfaceHolder holder) { // 每次activity resume都会调用一次
        Log.i(TAG, "surfaceCreated, surface=" + holder.getSurface());
        createSurface();
        makeCurrent();
        if (mFirstCreateSurface) {
            mRenderer.onSurfaceCreated();
            mFirstCreateSurface = false;
        }
    }

    @Override
    public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {
        Log.i(TAG, "surfaceChanged, width=" + width + ", height=" + height);
        mRenderer.onSurfaceChanged(width, height);
    }

    @Override
    public void surfaceDestroyed(@NonNull SurfaceHolder holder) { // 每次activity pause都会调用一次
        Log.i(TAG, "surfaceDestroyed");
        if (mEGLDisplay != null && mEGLDisplay != EGL14.EGL_NO_DISPLAY) {
            // 与显示设备解绑
            EGL14.eglMakeCurrent(mEGLDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT);
            // 销毁 EGLSurface
            if (mEGLSurface != null && mEGLSurface != EGL14.EGL_NO_SURFACE) {
                EGL14.eglDestroySurface(mEGLDisplay, mEGLSurface);
                checkoutConfig("eglDestroySurface");
                mEGLSurface = null;
            }
        }
    }

    @Override
    public void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        Log.i(TAG, "onDetachedFromWindow");
        stopRender();
        getHolder().removeCallback(this);
        if (mEGLDisplay != null && mEGLDisplay != EGL14.EGL_NO_DISPLAY) {
            // 与显示设备解绑
            EGL14.eglMakeCurrent(mEGLDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT);
            // 销毁 EGLSurface
            if (mEGLSurface != null && mEGLSurface != EGL14.EGL_NO_SURFACE) {
                EGL14.eglDestroySurface(mEGLDisplay, mEGLSurface);
                checkoutConfig("eglDestroySurface");
                mEGLSurface = null;
            }
            // 销毁 EGLContext
            if (mEGLContext != null && mEGLContext != EGL14.EGL_NO_CONTEXT) {
                EGL14.eglDestroyContext(mEGLDisplay, mEGLContext);
                checkoutConfig("eglDestroyContext");
                mEGLContext = null;
            }
            // 销毁 EGLDisplay (显示设备)
            EGL14.eglTerminate(mEGLDisplay);
            checkoutConfig("eglTerminate");
            mEGLDisplay = null;
        }
    }

    // 1.创建EGLDisplay
    private void createDisplay() {
        mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
        int[] versions = new int[2];
        EGL14.eglInitialize(mEGLDisplay, versions,0, versions, 1);
        checkoutConfig("eglInitialize");
    }

    // 2.创建EGLConfig
    private void createConfig() {
        if (mEGLDisplay != null && mEGLDisplay != EGL14.EGL_NO_DISPLAY) {
            EGLConfig[] configs = new EGLConfig[1];
            int[] configNum = new int[1];
            EGL14.eglChooseConfig(mEGLDisplay, mEGLConfigAttrs, 0, configs, 0,1,  configNum, 0);
            if (configNum[0] > 0) {
                mEGLConfig = configs[0];
            }
            checkoutConfig("eglChooseConfig");
        }
    }

    // 3.创建EGLContext
    private void createContext() {
        if (mEGLConfig != null) {
            mEGLContext = EGL14.eglCreateContext(mEGLDisplay, mEGLConfig, EGL14.EGL_NO_CONTEXT, mEGLContextAttrs, 0);
            checkoutConfig("eglCreateContext");
        }
    }

    // 4.创建EGLSurface
    private void createSurface() {
        if (mEGLContext != null && mEGLContext != EGL14.EGL_NO_CONTEXT) {
            int[] eglSurfaceAttrs = { EGL14.EGL_NONE };
            //mEGLSurface = EGL14.eglCreateWindowSurface(mEGLDisplay, mEGLConfig, getHolder(), eglSurfaceAttrs, 0);
            mEGLSurface = EGL14.eglCreateWindowSurface(mEGLDisplay, mEGLConfig, this, eglSurfaceAttrs, 0);
            checkoutConfig("eglCreateWindowSurface");
        }
    }

    // 5.绑定EGLSurface和EGLContext到显示设备(EGLDisplay)
    private void makeCurrent() {
        if (mEGLSurface != null && mEGLSurface != EGL14.EGL_NO_SURFACE) {
            EGL14.eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext);
            checkoutConfig("eglMakeCurrent");
        }
    }

    private void checkoutConfig(String tag) {
        int error = EGL14.eglGetError();
        if (error != EGL14.EGL_SUCCESS) {
            Log.e(TAG, tag + " error=0x" + Integer.toHexString(error));
        }
    }

    // EGLConfig参数
    private int[] mEGLConfigAttrs = {
            EGL14.EGL_RED_SIZE, 8,
            EGL14.EGL_GREEN_SIZE, 8,
            EGL14.EGL_BLUE_SIZE, 8,
            EGL14.EGL_ALPHA_SIZE, 8,
            EGL14.EGL_DEPTH_SIZE, 8,
            //EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
            EGL14.EGL_RENDERABLE_TYPE, EGLExt.EGL_OPENGL_ES3_BIT_KHR,
            EGL14.EGL_NONE
    };

    // EGLContext参数
    private int[] mEGLContextAttrs = {
            EGL14.EGL_CONTEXT_CLIENT_VERSION, 3,
            EGL14.EGL_NONE
    };

    Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
        @Override
        public void doFrame(long frameTimeNanos) {
            mChoreographer.postFrameCallback(mFrameCallback);
            if (mEGLSurface != null) {
                mRenderer.onDrawFrame();
                EGL14.eglSwapBuffers(mEGLDisplay, mEGLSurface);
                checkoutConfig("eglSwapBuffers");
            }
        }
    };

    /**
     * @author little fat sheep
     * 渲染器接口, 类比GLSurfaceView.Renderer
     */
    interface Renderer {
        void onSurfaceCreated();
        void onSurfaceChanged(int width, int height);
        void onDrawFrame();
    }
}

​ MyRenderer.java

java 复制代码
package com.zhyan8.egldemo;

import android.content.Context;
import android.opengl.GLES30;

import java.nio.FloatBuffer;

public class MyRenderer implements EGLSurfaceView.Renderer {
    private FloatBuffer vertexBuffer;
    private FloatBuffer textureBuffer;
    private MyGLUtils mGLUtils;
    private int mTextureId;

    public MyRenderer(Context context) {
        mGLUtils = new MyGLUtils(context);
        getFloatBuffer();
    }

    @Override
    public void onSurfaceCreated() {
        //设置背景颜色
        GLES30.glClearColor(0.1f, 0.2f, 0.3f, 0.4f);
        //编译着色器
        final int vertexShaderId = mGLUtils.compileShader(GLES30.GL_VERTEX_SHADER, R.raw.vertex_shader);
        final int fragmentShaderId = mGLUtils.compileShader(GLES30.GL_FRAGMENT_SHADER, R.raw.fragment_shader);
        //链接程序片段
        int programId = mGLUtils.linkProgram(vertexShaderId, fragmentShaderId);
        GLES30.glUseProgram(programId);
        mTextureId = mGLUtils.loadTexture(R.raw.girl);
    }

    @Override
    public void onSurfaceChanged(int width, int height) {
        //设置视图窗口
        GLES30.glViewport(0, 0, width, height);
    }

    @Override
    public void onDrawFrame() {
        //将颜色缓冲区设置为预设的颜色
        GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT);
        //启用顶点的数组句柄
        GLES30.glEnableVertexAttribArray(0);
        GLES30.glEnableVertexAttribArray(1);
        //准备顶点坐标和纹理坐标
        GLES30.glVertexAttribPointer(0, 3, GLES30.GL_FLOAT, false, 0, vertexBuffer);
        GLES30.glVertexAttribPointer(1, 2, GLES30.GL_FLOAT, false, 0, textureBuffer);
        //激活纹理
        GLES30.glActiveTexture(GLES30.GL_TEXTURE);
        //绑定纹理
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mTextureId);
        //绘制贴图
        GLES30.glDrawArrays(GLES30.GL_TRIANGLE_FAN, 0, 4);
        //禁止顶点数组句柄
        GLES30.glDisableVertexAttribArray(0);
        GLES30.glDisableVertexAttribArray(1);
    }

    private void getFloatBuffer() {
        float[] vertex = new float[] {
                1f, 1f, 0f,     //V0
                -1f, 1f, 0f,    //V1
                -1f, -1f, 0f,   //V2
                1f, -1f, 0f     //V3
        };
        float[] texture = {
                1f, 0f,     //V0
                0f, 0f,     //V1
                0f, 1.0f,   //V2
                1f, 1.0f    //V3
        };
        vertexBuffer = mGLUtils.getFloatBuffer(vertex);
        textureBuffer = mGLUtils.getFloatBuffer(texture);
    }
}

​ MyGLUtils.java

java 复制代码
package com.zhyan8.egldemo;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLES30;
import android.opengl.GLUtils;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

public class MyGLUtils {
    private Context mContext;
    private Bitmap mBitmap;

    public MyGLUtils(Context context) {
        mContext = context;
    }

    public FloatBuffer getFloatBuffer(float[] floatArr) {
        FloatBuffer fb = ByteBuffer.allocateDirect(floatArr.length * Float.BYTES)
                .order(ByteOrder.nativeOrder())
                .asFloatBuffer();
        fb.put(floatArr);
        fb.position(0);
        return fb;
    }

    //通过代码片段编译着色器
    public int compileShader(int type, String shaderCode){
        int shader = GLES30.glCreateShader(type);
        GLES30.glShaderSource(shader, shaderCode);
        GLES30.glCompileShader(shader);
        return shader;
    }

    //通过外部资源编译着色器
    public int compileShader(int type, int shaderId){
        String shaderCode = readShaderFromResource(shaderId);
        return compileShader(type, shaderCode);
    }

    //链接到着色器
    public int linkProgram(int vertexShaderId, int fragmentShaderId) {
        final int programId = GLES30.glCreateProgram();
        //将顶点着色器加入到程序
        GLES30.glAttachShader(programId, vertexShaderId);
        //将片元着色器加入到程序
        GLES30.glAttachShader(programId, fragmentShaderId);
        //链接着色器程序
        GLES30.glLinkProgram(programId);
        return programId;
    }

    //从shader文件读出字符串
    private String readShaderFromResource(int shaderId) {
        InputStream is = mContext.getResources().openRawResource(shaderId);
        BufferedReader br = new BufferedReader(new InputStreamReader(is));
        String line;
        StringBuilder sb = new StringBuilder();
        try {
            while ((line = br.readLine()) != null) {
                sb.append(line);
                sb.append("\n");
            }
            br.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return sb.toString();
    }

    //加载纹理贴图
    public int loadTexture(int resourceId) {
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inScaled = false;
        mBitmap = BitmapFactory.decodeResource(mContext.getResources(), resourceId, options);
        final int[] textureIds = new int[1];
        // 生成纹理id
        GLES30.glGenTextures(1, textureIds, 0);
        // 绑定纹理到OpenGL
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureIds[0]);
        GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_LINEAR_MIPMAP_LINEAR);
        GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_LINEAR);
        // 加载bitmap到纹理中
        GLUtils.texImage2D(GLES30.GL_TEXTURE_2D, 0, mBitmap, 0);
        // 生成MIP贴图
        GLES30.glGenerateMipmap(GLES30.GL_TEXTURE_2D);
        // 取消绑定纹理
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0);
        return textureIds[0];
    }
}

​ vertex_shader.glsl

cpp 复制代码
attribute vec4 a_position;
attribute vec2 a_texCoord;
varying vec2 v_texCoord;
void main() {
     gl_Position = a_position;
     v_texCoord = a_texCoord;
}

​ fragment_shader.glsl

cpp 复制代码
precision mediump float;
uniform sampler2D u_texture;
varying vec2 v_texCoord;
void main() {
     gl_FragColor = texture2D(u_texture, v_texCoord);
}

​ 运行效果如下。

3 继承 TextureView 方案

​ 自定义一个 View 继承 TextureView,并实现 TextureView.SurfaceTextureListener 接口,在 onSurfaceTextureAvailable 方法中会提供 SurfaceTexture,我们可以创建一个 Surface,并将 SurfaceTexture 传给 Surface,然后将创建的 Surface 传给 eglCreateWindowSurface 函数。

​ 前段时间在看 Rive 的源码,详见 → rive-android源码分析,了解到 Rive 底层是通过 OpenGL ES 渲染图像,并且也没有使用 GLSurfaceView,由此借鉴而来。该方案主要参考 Rive 中 RiveTextureView 的实现,eglCreateWindowSurface 参考 thread_state_egl.cpp。

​ 与第二节相比,只有 EGLSurfaceView 类有差异,因此本节仅展示 EGLSurfaceView 的代码。

​ EGLSurfaceView.java

java 复制代码
package com.zhyan8.egldemo;

import android.content.Context;
import android.graphics.SurfaceTexture;
import android.opengl.EGL14;
import android.opengl.EGLConfig;
import android.opengl.EGLContext;
import android.opengl.EGLDisplay;
import android.opengl.EGLExt;
import android.opengl.EGLSurface;
import android.util.Log;
import android.view.Choreographer;
import android.view.Surface;
import android.view.TextureView;

import androidx.annotation.NonNull;

/**
 * @author little fat sheep
 * 承载EGL环境的View, 类比GLSurfaceView
 */
public class EGLSurfaceView extends TextureView implements TextureView.SurfaceTextureListener {
    private static final String TAG = "EGLSurfaceView";

    protected EGLDisplay mEGLDisplay;
    protected EGLConfig mEGLConfig;
    protected EGLContext mEGLContext;
    protected EGLSurface mEGLSurface;
    protected Context mContext;
    protected Surface mSurface;
    protected Renderer mRenderer;
    private Choreographer mChoreographer = Choreographer.getInstance();

    public EGLSurfaceView(Context context) {
        super(context);
        mContext = context;
        setSurfaceTextureListener(this);
    }

    // 设置渲染器
    public void setRenderer(Renderer renderer) {
        mRenderer = renderer;
    }

    // 开始持续渲染
    public void startRender() {
        Log.i(TAG, "startRender");
        mChoreographer.removeFrameCallback(mFrameCallback);
        mChoreographer.postFrameCallback(mFrameCallback);
    }

    // 暂停持续渲染
    public void stopRender() {
        Log.i(TAG, "stopRender");
        mChoreographer.removeFrameCallback(mFrameCallback);
    }

    // 请求渲染一帧
    public void requestRender() {
        mFrameCallback.doFrame(System.nanoTime());
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        Log.i(TAG, "onAttachedToWindow");
        createDisplay();
        createConfig();
        createContext();
    }

    @Override
    public void onSurfaceTextureAvailable(@NonNull SurfaceTexture surface, int width, int height) {
        Log.i(TAG, "onSurfaceTextureAvailable");
        mSurface = new Surface(surface);
        createSurface();
        makeCurrent();
        mRenderer.onSurfaceCreated();
        mRenderer.onSurfaceChanged(width, height);
    }

    @Override
    public void onSurfaceTextureSizeChanged(@NonNull SurfaceTexture surface, int width, int height) {
        Log.i(TAG, "onSurfaceTextureSizeChanged, width=" + width + ", height=" + height);
        mRenderer.onSurfaceChanged(width, height);
    }

    @Override
    public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surface) {
        Log.i(TAG, "onSurfaceTextureDestroyed");
        return false;
    }

    @Override
    public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surface) {
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        Log.i(TAG, "onDetachedFromWindow");
        stopRender();
        setSurfaceTextureListener(null);
        mSurface.release();
        if (mEGLDisplay != null && mEGLDisplay != EGL14.EGL_NO_DISPLAY) {
            // 与显示设备解绑
            EGL14.eglMakeCurrent(mEGLDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT);
            // 销毁 EGLSurface
            if (mEGLSurface != null && mEGLSurface != EGL14.EGL_NO_SURFACE) {
                EGL14.eglDestroySurface(mEGLDisplay, mEGLSurface);
                checkoutConfig("eglDestroySurface");
                mEGLSurface = null;
            }
            // 销毁 EGLContext
            if (mEGLContext != null && mEGLContext != EGL14.EGL_NO_CONTEXT) {
                EGL14.eglDestroyContext(mEGLDisplay, mEGLContext);
                checkoutConfig("eglDestroyContext");
                mEGLContext = null;
            }
            // 销毁 EGLDisplay (显示设备)
            EGL14.eglTerminate(mEGLDisplay);
            checkoutConfig("eglTerminate");
            mEGLDisplay = null;
        }
    }

    // 1.创建EGLDisplay
    private void createDisplay() {
        mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
        int[] versions = new int[2];
        EGL14.eglInitialize(mEGLDisplay, versions,0, versions, 1);
        checkoutConfig("eglInitialize");
    }

    // 2.创建EGLConfig
    private void createConfig() {
        if (mEGLDisplay != null && mEGLDisplay != EGL14.EGL_NO_DISPLAY) {
            EGLConfig[] configs = new EGLConfig[1];
            int[] configNum = new int[1];
            EGL14.eglChooseConfig(mEGLDisplay, mEGLConfigAttrs, 0, configs, 0,1,  configNum, 0);
            if (configNum[0] > 0) {
                mEGLConfig = configs[0];
            }
            checkoutConfig("eglChooseConfig");
        }
    }

    // 3.创建EGLContext
    private void createContext() {
        if (mEGLConfig != null) {
            mEGLContext = EGL14.eglCreateContext(mEGLDisplay, mEGLConfig, EGL14.EGL_NO_CONTEXT, mEGLContextAttrs, 0);
            checkoutConfig("eglCreateContext");
        }
    }

    // 4.创建EGLSurface
    private void createSurface() {
        if (mEGLContext != null && mEGLContext != EGL14.EGL_NO_CONTEXT) {
            int[] eglSurfaceAttrs = { EGL14.EGL_NONE };
            mEGLSurface = EGL14.eglCreateWindowSurface(mEGLDisplay, mEGLConfig, mSurface, eglSurfaceAttrs, 0);
            checkoutConfig("eglCreateWindowSurface");
        }
    }

    // 5.绑定EGLSurface和EGLContext到显示设备(EGLDisplay)
    private void makeCurrent() {
        if (mEGLSurface != null && mEGLSurface != EGL14.EGL_NO_SURFACE) {
            EGL14.eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext);
            checkoutConfig("eglMakeCurrent");
        }
    }

    private void checkoutConfig(String tag) {
        int error = EGL14.eglGetError();
        if (error != EGL14.EGL_SUCCESS) {
            Log.e(TAG, tag + " error=0x" + Integer.toHexString(error));
        }
    }

    // EGLConfig参数
    private int[] mEGLConfigAttrs = {
            EGL14.EGL_RED_SIZE, 8,
            EGL14.EGL_GREEN_SIZE, 8,
            EGL14.EGL_BLUE_SIZE, 8,
            EGL14.EGL_ALPHA_SIZE, 8,
            EGL14.EGL_DEPTH_SIZE, 8,
            //EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
            EGL14.EGL_RENDERABLE_TYPE, EGLExt.EGL_OPENGL_ES3_BIT_KHR,
            EGL14.EGL_NONE
    };

    // EGLContext参数
    private int[] mEGLContextAttrs = {
            EGL14.EGL_CONTEXT_CLIENT_VERSION, 3,
            EGL14.EGL_NONE
    };

    Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
        @Override
        public void doFrame(long frameTimeNanos) {
            mChoreographer.postFrameCallback(mFrameCallback);
            if (mEGLSurface != null) {
                mRenderer.onDrawFrame();
                EGL14.eglSwapBuffers(mEGLDisplay, mEGLSurface);
                checkoutConfig("eglSwapBuffers");
            }
        }
    };

    /**
     * @author little fat sheep
     * 渲染器接口, 类比GLSurfaceView.Renderer
     */
    interface Renderer {
        void onSurfaceCreated();
        void onSurfaceChanged(int width, int height);
        void onDrawFrame();
    }
}

​ 运行效果如下。

​ 声明:本文转自【OpenGL ES】不用GLSurfaceView,如何渲染图像。

相关推荐
:mnong1 天前
Web 三维可视化技术对比:ParaView Web vs 本项目 wasm + Three.js
前端·webgl·opengl·opencl·科学可视化
扶尔魔ocy2 天前
【qt openGL入门笔记】OSG实现显示飞机模型飞行的点迹(二)
qt·opengl·osg
扶尔魔ocy2 天前
【qt openGL入门笔记】OSG实现飞机模型在网格图上飞行(三)
opengl·osg
扶尔魔ocy3 天前
【qt openGL入门笔记】OSG实现飞机模型在网格图上飞行
qt·opengl·osg
扶尔魔ocy7 天前
【qt openGL入门笔记】FPS相机
qt·opengl
扶尔魔ocy7 天前
qt openGL入门笔记】FPS相机和轨道相机
qt·opengl
扶尔魔ocy9 天前
【qt openGL入门笔记】基础光照
qt·opengl
扶尔魔ocy9 天前
【qt openGL入门笔记】坐标系统和摄像机
qt·opengl
茉莉玫瑰花茶12 天前
OpenGL [ 纹理 ]
开发语言·c++·opengl
mmsx24 天前
Android 地图十万要素不卡顿:空间网格 + 渐进式加载的移动端实践
android·大数据·opengl