OpenglEs之EGL环境搭建

Posted FlyerGo

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OpenglEs之EGL环境搭建相关的知识,希望对你有一定的参考价值。

前言

前面我们发布了一系列的入门教程,例如C++系列的指针扫盲、多线程的使用等,JNI入门系列,ffmpeg入门系列等,有感兴趣的童鞋们可以关注往回自行查阅。

今天我们的主题依然是音视频开发的范畴,做过音视频开发的都知道Opengl也是音视频开发中的一项重要技能,特别是涉及到视频录制、特效处理、画质渲染细分功能。因此后续笔者打算再出一系列的Opengl ES的学习笔记,
希望能与大家共同温故知新。

因为前面介绍了一些NDK和C++的教程,所以为了巩固,后续的一些demo多以NDK的形式呈现给大家,使用Opengl ES3的版本。

今天我们的主题是Opengl ES的第一篇–>EGL

EGL是什么

众所周知,Opengl是跨平台的,那么面对各种平台的差异性,Opengl是如何抹平而做到跨平台的呢?这也许就是EGL的功劳吧,简单地说EGL就是Opengl和平台各平台之间的一个适配器,是一系列的接口,具体实现是由具体的设备厂商实现的。

EGL 是渲染 API(如 OpenGL ES)和原生窗口系统之间的接口.通常来说,OpenGL 是一个操作 GPU 的 API,它通过驱动向 GPU 发送相关指令,控制图形渲染管线状态机的运行状态,但是当涉及到与本地窗口系统进行交互时,就需要这么一个中间层,且它最好是与平台无关的,
因此 EGL 被设计出来,作为 OpenGL 和原生窗口系统之间的桥梁。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cQLExV1o-1662517101245)(https://flyer-blog.oss-cn-shenzhen.aliyuncs.com/opengl%E4%B8%8Eegl.png)]

EGL API 是独立于 OpenGL 各版本标准的独立的一套 API,其主要作用是为 OpenGL 指令 创建上下文 Context 、绘制目标 Surface 、配置 FrameBuffer 属性、Swap 提交绘制结果等。EGL提供如下机制:

  • 与设备的原生窗口系统通信
  • 查询绘图表面的可用类型和配置
  • 创建绘图表面
  • 在 OpenGL ES 和其他图形渲染 API 之间同步渲染
  • 管理纹理贴图等渲染资源

下面这张图可简要看出EGL的接口能力:

EGL创建流程

想要在安卓上使用Opengl ES我们可以直接使用GLSurfaceView来进行Opengl的渲染,因为GLSurfaceView内部给我们封装好了EGL环境和渲染线程。如果我们想要更高的拓展性,我们也使用SurfaceView,然后参考SurfaceView中的EGL环境搭建、线程模型来
自行搭建Opengl ES的渲染环境。

本着学习探索的目的,我们尝试在NDK搭建EGL环境。

下面这张图展示的是安卓系统上EGL的主要使用API:

需要说明的一点是EGL是单线程模型的,也就说说EGL环境的创建、渲染操作、EGL环境的销毁都必须在同一个线程内完成,否则是无效的。当然我们可以通过共享EGL上下文来做多多线程渲染,但这些都是后话了…

任何OpenGL ES应用程序都必须在开始渲染之前使用EGL执行如下任务:

  1. 查询并初始化设备商可用的显示器。
  2. 创建渲染表面。
    EGL中创建的表面可以分类为屏幕上的表面或者屏幕外的表面。屏幕上的表面连接到原生窗口系统,而屏幕外的表面是不显示但是可以用作渲染表面的像素缓冲区。这些表面可以用来渲染纹理,并可以在多个Khronos API之间共享。
  3. 创建渲染上下文。
    EGL是创建OpenGL ES渲染上下文所必需的。这个上下文必须连接到合适的表面才能开始渲染。

下面是EGL环境创建的主要流程:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VFqBdZSh-1662517101247)(https://flyer-blog.oss-cn-shenzhen.aliyuncs.com/egl%E5%88%9B%E5%BB%BA%E6%B5%81%E7%A8%8B.png)]

说完烦躁的基础理论,那就放码过来吧!!!

使用android Studio创建一个Native工程,然后配置好CMakeLists.txt引入相关库:


cmake_minimum_required(VERSION 3.18.1)

project("learn")

#找到包含所有的cpp文件
file(GLOB allCpp *.cpp **/**.cpp **/**/**.cpp  **/**/**/**.cpp  **/**/**/**/**.cpp)

add_library( # Sets the name of the library.
        # 库名称
        learn

        SHARED

        $allCpp)

target_link_libraries(
        learn
        # 引入egl
        egl
        # 引入gles 3
        GLESv3
        # 安卓相关库
        android
        # 安卓log
        log)

下面我们创建一个与Native映射的EGLHelper类:

package com.fly.opengles.learn.egl;

import android.view.Surface;

public class EGLHelper 
    protected long nativePtr;

    public void surfaceCreated(Surface surface) 
        nativePtrInit();
        n_surfaceCreated(nativePtr,surface);
    

    public void surfaceChanged(int width, int height) 
        nativePtrInit();
        n_surfaceChanged(nativePtr,width,height);
    

    public void surfaceDestroyed() 
        if(nativePtr != 0)
            n_surfaceDestroyed(nativePtr);
            nativePtr = 0;
        
    

    private void nativePtrInit()
        if(nativePtr == 0)
            nativePtr = n_nativePtrInit();
        
    

    private native long n_nativePtrInit();
    private native void n_surfaceCreated(long nativePtr,Surface surface);
    private native void n_surfaceChanged(long nativePtr,int width, int height);
    private native void n_surfaceDestroyed(long nativePtr);

然后自定义一个MySurfaceView继承于SurfaceView,在它的Callback回调方法中对EGL进行操作:

public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback 

    private EGLHelper eglHelper;

    public MySurfaceView(Context context) 
        this(context,null);
    

    public MySurfaceView(Context context, AttributeSet attrs) 
        super(context, attrs);
        eglHelper = new EGLHelper();
        getHolder().addCallback(this);
    

    @Override
    public void surfaceCreated(@NonNull SurfaceHolder surfaceHolder) 
        eglHelper.surfaceCreated(surfaceHolder.getSurface());
    

    @Override
    public void surfaceChanged(@NonNull SurfaceHolder surfaceHolder, int i, int w, int h) 
        eglHelper.surfaceChanged(w,h);
    

    @Override
    public void surfaceDestroyed(@NonNull SurfaceHolder surfaceHolder) 
        eglHelper.surfaceDestroyed();
    

测试效果时,我们在布局中使用我们自定义好MySurfaceView即可,自此java层代码编写完毕,在NDK层我们将EGL环境创建完毕后即可通过MySurfaceView看到渲染结果。

为了方便调试和debug,我们定义Log.h日志工具:

#ifndef NDK_OPENGLES_LEARN_LOG_H
#define NDK_OPENGLES_LEARN_LOG_H

#include "android/log.h"

#define LOGD(FORMAT, ...) __android_log_print(ANDROID_LOG_DEBUG, "fly_learn_opengl", FORMAT, ##__VA_ARGS__);
#define LOGE(FORMAT, ...) __android_log_print(ANDROID_LOG_ERROR, "fly_learn_opengl", FORMAT, ##__VA_ARGS__);

#endif //NDK_OPENGLES_LEARN_LOG_H

将EGL的相关操作封装在类C++的类EglHelper中:

EglHelper.h

#ifndef NDK_OPENGLES_LEARN_EGLHELPER_H
#define NDK_OPENGLES_LEARN_EGLHELPER_H

#include "EGL/egl.h"

class EglHelper 

public:
    EGLDisplay  mEglDisplay;
    EGLSurface  mEglSurface;
    EGLConfig  mEglConfig;
    EGLContext mEglContext;

public:
    EglHelper();
    ~EglHelper();
    int initEgl(EGLNativeWindowType win);
    int swapBuffers();
    void destroyEgl();
;


#endif

EglHelper.cpp主要实现如下,EGL的主要创建过程在函数initEgl中,具体看注释:

#include "EglHelper.h"
#include "../utils/Log.h"

EglHelper::EglHelper() 

    mEglDisplay = EGL_NO_DISPLAY;
    mEglSurface = EGL_NO_SURFACE;
    mEglContext = EGL_NO_CONTEXT;
    mEglConfig = NULL;


EglHelper::~EglHelper() 
    destroyEgl();


int EglHelper::initEgl(EGLNativeWindowType window) 

    //1、获取显示设备
    mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
    if(mEglDisplay == EGL_NO_DISPLAY)
    
        LOGE("eglGetDisplay error");
        return -1;
    
    // 2、 EGL初始化
    EGLint *version = new EGLint[2];
    if(!eglInitialize(mEglDisplay, &version[0], &version[1]))
    
        LOGE("eglInitialize error");
        return -1;
    

    //3、 资源配置,例如颜色位数等
    const EGLint attribs[] = 
            EGL_RED_SIZE, 8,
            EGL_GREEN_SIZE, 8,
            EGL_BLUE_SIZE, 8,
            EGL_ALPHA_SIZE, 8,
            EGL_DEPTH_SIZE, 8,
            EGL_STENCIL_SIZE, 8,
            EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
            EGL_NONE
    ;

    EGLint num_config;
    if(!eglChooseConfig(mEglDisplay, attribs, NULL, 1, &num_config))
    
        LOGE("eglChooseConfig  error 1");
        return -1;
    

    //4、ChooseConfig
    if(!eglChooseConfig(mEglDisplay, attribs, &mEglConfig, num_config, &num_config))
    
        LOGE("eglChooseConfig  error 2");
        return -1;
    

    // 5、创建上下文
    int attrib_list[] = 
            EGL_CONTEXT_CLIENT_VERSION, 2,
            EGL_NONE
    ;

    mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT, attrib_list);

    if(mEglContext == EGL_NO_CONTEXT)
    
        LOGE("eglCreateContext  error");
        return -1;
    

    //6、创建渲染的Surface
    mEglSurface = eglCreateWindowSurface(mEglDisplay, mEglConfig, window, NULL);
    if(mEglSurface == EGL_NO_SURFACE)
    
        LOGE("eglCreateWindowSurface  error");
        return -1;
    

    // 7、使用
    if(!eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext))
    
        LOGE("eglMakeCurrent  error");
        return -1;
    
    LOGD("egl init success! ");
    return 0;


int EglHelper::swapBuffers() 

    if(mEglDisplay != EGL_NO_DISPLAY && mEglSurface != EGL_NO_SURFACE)
    
        if(eglSwapBuffers(mEglDisplay, mEglSurface))
        
            return 0;
        
    
    return -1;


void EglHelper::destroyEgl() 

    if(mEglDisplay != EGL_NO_DISPLAY)
    
        eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
    
    if(mEglDisplay != EGL_NO_DISPLAY && mEglSurface != EGL_NO_SURFACE)
    
        eglDestroySurface(mEglDisplay, mEglSurface);
        mEglSurface = EGL_NO_SURFACE;
    
    if(mEglDisplay != EGL_NO_DISPLAY && mEglContext != EGL_NO_CONTEXT)
        eglDestroyContext(mEglDisplay, mEglContext);
        mEglContext = EGL_NO_CONTEXT;
    
    if(mEglDisplay != EGL_NO_DISPLAY)
    
        eglTerminate(mEglDisplay);
        mEglDisplay = EGL_NO_DISPLAY;
    

自己EGL环境创建完毕,我们通过JNI调用起来看看效果,native-lib.cpp:

#include <jni.h>
#include <string>
#include "eglhelper/EglHelper.h"
#include <cstdint>
#include "android/native_window.h"
#include "android/native_window_jni.h"
#include "GLES3/gl3.h"

jlong eglHelperNativePtrInit(JNIEnv *env, jobject thiz) 
    EglHelper *eglHelper = new EglHelper();
    return reinterpret_cast<uintptr_t>(eglHelper);


void eglSurfaceCreated(JNIEnv *env, jobject thiz,jlong native_ptr, jobject surface) 
    if(native_ptr != 0)
        EglHelper *eglHelper = reinterpret_cast<EglHelper *>(native_ptr);
        ANativeWindow *nativeWindow = ANativeWindow_fromSurface(env, surface);
        eglHelper->initEgl(nativeWindow);
    


void eglSurfaceChanged(JNIEnv *env, jobject thiz,jlong native_ptr, jint width,jint height) 

    if(native_ptr != 0)
        //设置视口大小
        glViewport(0, 0, width, height);
        // 绿色清屏
//        glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
        // 蓝色清屏
        glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);
        EglHelper *eglHelper = reinterpret_cast<EglHelper *>(native_ptr);
        eglHelper->swapBuffers();
    


void eglSurfaceDestroyed(JNIEnv *env, jobject thiz,jlong native_ptr) 
    if(native_ptr != 0)
        EglHelper *eglHelper = reinterpret_cast<EglHelper *>(native_ptr);
        delete eglHelper;
    


static JNINativeMethod nativeMethod_EGLHelper[] = 
        // Java中的函数名
        "n_nativePtrInit",
                // 函数签名信息
         "()J",
                // native的函数指针
         (jlong *) (eglHelperNativePtrInit),

        "n_surfaceCreated",
                // 函数签名信息
         "(JLandroid/view/Surface;)V",
                // native的函数指针
         (void *) (eglSurfaceCreated),

        "n_surfaceChanged",
                // 函数签名信息
         "(JII)V",
                // native的函数指针
         (void *) (eglSurfaceChanged),

        "n_surfaceDestroyed",
                // 函数签名信息
         "(J)V",
                // native的函数指针
         (void *) (eglSurfaceDestroyed),
;

static int RegisterNativeMethods(JNIEnv *env, const char *className, JNINativeMethod *methods, int methodNum)

    jclass clazz = env->FindClass(className);
    if (clazz == NULL)
    
        return JNI_FALSE;
    
    if (env->RegisterNatives(clazz, methods, methodNum) < 0)
    
        return JNI_FALSE;
    
    return JNI_TRUE;


// 类库加载时自动调用
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reversed)

    JNIEnv *env = NULL;
    // 初始化JNIEnv
    if(vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6) != JNI_OK)
        return JNI_FALSE;
    
    // 动态注册
    RegisterNativeMethods(env,"com/fly/opengles/learn/egl/EGLHelper",nativeMethod_EGLHelper,sizeof(nativeMethod_EGLHelper) / sizeof(JNINativeMethod) );
    // 返回JNI使用的版本
    return JNI_VERSION_1_6;

上述native-lib.cpp涉及到了之前介绍过的JNI函数签名、动态注册等相关知识点,忘记了的童鞋可往回看之前的记录。

如无意外,运行看到的是一个蓝屏画面则说明EGL环境搭建成功了,后续开启你的Opengl炫酷之旅吧!!!

推荐阅读

JNI基础简介
JNI之数组与字符串的使用
JNI之动态注册与静态注册
JNI之访问java属性和方法
JNI之缓存与引用
JNI之异常处理
JNI之常用技巧与陷阱

关注我,一起进步,人生不止coding!!!

OpenglEs之三角形绘制

在前面我们已经在NDK层搭建好了EGL环境,也介绍了一些着色器相关的理论知识,那么这次我们就使用已经搭配的EGL绘制一个三角形吧。

在Opengl ES的世界中,无论多复杂的形状都是由点、线或三角形组成的。因此三角形的绘制在Opengl ES中相当重要,犹比武林高手的内功心法…

坐标系
在Opengl ES中有很多坐标系,今天我们首先了解一些标准化的设备坐标。

标准化设备坐标(Normalized Device Coordinates, NDC),一旦你的顶点坐标已经在顶点着色器中处理过,它们就是标准化设备坐标了,
标准化设备坐标是一个x、y和z的值都在-1.0到1.0的之间,任何落在-1和1范围外的坐标都会被丢弃/裁剪,不会显示在你的屏幕上。

如下图,在在标准化设备坐标中,假设有一个正方形的屏幕,那么屏幕中心就是坐标原点,左上角就是坐标(-1,1),右下角则是坐标(1,-1)。
标准化设备坐标

上代码
这里需要说明亮点:

在后续的实战例子中,经常会复用到前面介绍的demo的代码,因此如果是复用之前的代码逻辑,为了节省篇幅,笔者就不重复贴了。
在demo中为了简洁,并没有开启子线程作为GL线程,很明显这是不对,实际开发中都应该开启子线程对Opengl进行操作。
首先为了后续方便使用,我们在Java层和C++分别创建一个BaseOpengl的基类:

BaseOpengl.java

public class BaseOpengl

// 三角形
public static final int DRAW_TYPE_TRIANGLE = 0;

public long glNativePtr;
protected EGLHelper eglHelper;
protected int drawType;

public BaseOpengl(int drawType) 
    this.drawType = drawType;
    this.eglHelper = new EGLHelper();


public void surfaceCreated(Surface surface) 
    eglHelper.surfaceCreated(surface);


public void surfaceChanged(int width, int height) 
    eglHelper.surfaceChanged(width,height);


public void surfaceDestroyed() 
    eglHelper.surfaceDestroyed();


public void release()
    if(glNativePtr != 0)
        n_free(glNativePtr,drawType);
        glNativePtr = 0;
    


public void onGlDraw()
    if(glNativePtr == 0)
        glNativePtr = n_gl_nativeInit(eglHelper.nativePtr,drawType);
    
    if(glNativePtr != 0)
        n_onGlDraw(glNativePtr,drawType);
    


// 绘制
private native void n_onGlDraw(long ptr,int drawType);
protected native long n_gl_nativeInit(long eglPtr,int drawType);
private native void n_free(long ptr,int drawType);


下面是C++的BaseOpengl:

BaseOpengl.h

#ifndef NDK_OPENGLES_LEARN_BASEOPENGL_H
#define NDK_OPENGLES_LEARN_BASEOPENGL_H
#include “…/eglhelper/EglHelper.h”
#include “GLES3/gl3.h”
#include

class BaseOpengl
public:
EglHelper *eglHelper;
GLint program0;

public:
BaseOpengl();
// 析构函数必须是虚函数
virtual ~BaseOpengl();
// 加载着色器并链接成程序
void initGlProgram(std::string ver,std::string fragment);
// 绘制
virtual void onDraw() = 0;
;

#endif //NDK_OPENGLES_LEARN_BASEOPENGL_H
注意基类的析构函数一定要是虚函数,为什么?如果不是虚函数的话则会导致无法完全析构,具体原因请大家面向搜索引擎编程。

BaseOpengl.cpp

#include “BaseOpengl.h”
#include “…/utils/ShaderUtils.h”

BaseOpengl::BaseOpengl()

void BaseOpengl::initGlProgram(std::string ver, std::string fragment)
program = createProgram(ver.c_str(),fragment.c_str());

BaseOpengl::~BaseOpengl()
eglHelper = nullptr;
if(program != 0)
glDeleteProgram(program);


然后使用BaseOpengl自定义一个SurfaceView,为MyGLSurfaceView:

public class MyGLSurfaceView extends SurfaceView implements SurfaceHolder.Callback

public BaseOpengl baseOpengl;
private OnDrawListener onDrawListener;

public MyGLSurfaceView(Context context) 
    this(context,null);


public MyGLSurfaceView(Context context, AttributeSet attrs) 
    super(context, attrs);
    getHolder().addCallback(this);


public void setBaseOpengl(BaseOpengl baseOpengl) 
    this.baseOpengl = baseOpengl;


public void setOnDrawListener(OnDrawListener onDrawListener) 
    this.onDrawListener = onDrawListener;


@Override
public void surfaceCreated(@NonNull SurfaceHolder surfaceHolder) 
    if(null != baseOpengl)
        baseOpengl.surfaceCreated(surfaceHolder.getSurface());
    


@Override
public void surfaceChanged(@NonNull SurfaceHolder surfaceHolder, int i, int w, int h) 
    if(null != baseOpengl)
        baseOpengl.surfaceChanged(w,h);
    
    if(null != onDrawListener)
        onDrawListener.onDrawFrame();
    


@Override
public void surfaceDestroyed(@NonNull SurfaceHolder surfaceHolder) 
    if(null != baseOpengl)
        baseOpengl.surfaceDestroyed();
    


public interface OnDrawListener
    void onDrawFrame();


有了以上基类,既然我们的目标是绘制一个三角形,那么我们在Java层和C++层再新建一个TriangleOpengl的类吧,他们都继承TriangleOpengl:

TriangleOpengl.java

public class TriangleOpengl extends BaseOpengl

public TriangleOpengl() 
    super(BaseOpengl.DRAW_TYPE_TRIANGLE);


C++ TriangleOpengl类,TriangleOpengl.h:

#ifndef NDK_OPENGLES_LEARN_TRIANGLEOPENGL_H
#define NDK_OPENGLES_LEARN_TRIANGLEOPENGL_H
#include “BaseOpengl.h”

class TriangleOpengl: public BaseOpengl
public:
TriangleOpengl();
virtual ~TriangleOpengl();
virtual void onDraw();

private:
GLint positionHandle-1;
GLint colorHandle-1;
;

#endif //NDK_OPENGLES_LEARN_TRIANGLEOPENGL_H
TriangleOpengl.cpp:

#include “TriangleOpengl.h”
#include “…/utils/Log.h”

// 定点着色器
static const char *ver = “#version 300 es\\n”
“in vec4 aColor;\\n”
“in vec4 aPosition;\\n”
“out vec4 vColor;\\n”
“void main() \\n”
" vColor = aColor;\\n"
" gl_Position = aPosition;\\n"
“”;

// 片元着色器
static const char *fragment = “#version 300 es\\n”
“precision mediump float;\\n”
“in vec4 vColor;\\n”
“out vec4 fragColor;\\n”
“void main() \\n”
" fragColor = vColor;\\n"
“”;

// 三角形三个顶点
const static GLfloat VERTICES[] =
0.0f,0.5f,
-0.5f,-0.5f,
0.5f,-0.5f
;

// rgba
const static GLfloat COLOR_ICES[] =
0.0f,0.0f,1.0f,1.0f
;

TriangleOpengl::TriangleOpengl():BaseOpengl()
initGlProgram(ver,fragment);
positionHandle = glGetAttribLocation(program,“aPosition”);
colorHandle = glGetAttribLocation(program,“aColor”);
LOGD(“program:%d”,program);
LOGD(“positionHandle:%d”,positionHandle);
LOGD(“colorHandle:%d”,colorHandle);

TriangleOpengl::~TriangleOpengl() noexcept

void TriangleOpengl::onDraw()
LOGD(“TriangleOpengl onDraw”);
glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(program);
/**
* size 几个数字表示一个点,显示是两个数字表示一个点
* normalized 是否需要归一化,不用,这里已经归一化了
* stride 步长,连续顶点之间的间隔,如果顶点直接是连续的,也可填0
*/
glVertexAttribPointer(positionHandle,2,GL_FLOAT,GL_FALSE,0,VERTICES);
// 启用顶点数据
glEnableVertexAttribArray(positionHandle);

// 这个不需要glEnableVertexAttribArray
glVertexAttrib4fv(colorHandle, COLOR_ICES);

glDrawArrays(GL_TRIANGLES,0,3);

glUseProgram(0);

// 禁用顶点
glDisableVertexAttribArray(positionHandle);
if(nullptr != eglHelper)
    eglHelper->swapBuffers();

LOGD("TriangleOpengl onDraw--end");

在前面的章节中我们介绍了着色器的创建、编译、链接等,但是缺少了具体使用方式,这里我们补充说明一下。

着色器的使用只要搞懂如何传递数据给着色器中变量。首先我们需要获取到着色器程序中的变量,然后赋值。

我们看上面的TriangleOpengl.cpp的构造函数:

TriangleOpengl::TriangleOpengl():BaseOpengl()
initGlProgram(ver,fragment);
// 获取aPosition变量
positionHandle = glGetAttribLocation(program,“aPosition”);
// 获取aColor
colorHandle = glGetAttribLocation(program,“aColor”);
LOGD(“program:%d”,program);
LOGD(“positionHandle:%d”,positionHandle);
LOGD(“colorHandle:%d”,colorHandle);

由上,我们通过函数glGetAttribLocation获取了变量aPosition和aColor的句柄,这里我们定义的aPosition和aColor是向量变量,如果我们定义的是uniform统一变量的话,则需要使用函数glGetUniformLocation获取统一变量句柄。
有了这些变量句柄,我们就可以通过这些变量句柄传递函数给着色器程序了,具体可参考TriangleOpengl.cpp的onDraw函数。

此外如果变量是一个统一变量(uniform)的话,则通过一系列的 glUniform…函数传递参数。

这里说明一下函数glVertexAttribPointer的stride参数,一般情况下不会用到,传递0即可,但是如果需要提高性能,例如将顶点坐标和纹理/颜色坐标等放在同一个数组中传递,则需要使用到这个stride参数了,目前顶点坐标数组和其他数组是分离的,暂时可以不管。

在Activity中调用一下测试结果:

public class DrawTriangleActivity extends AppCompatActivity

private TriangleOpengl mTriangleOpengl;

@Override
protected void onCreate(Bundle savedInstanceState) 
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_draw_triangle);
    MyGLSurfaceView glSurfaceView = findViewById(R.id.my_gl_surface_view);
    mTriangleOpengl = new TriangleOpengl();
    glSurfaceView.setBaseOpengl(mTriangleOpengl);
    glSurfaceView.setOnDrawListener(new MyGLSurfaceView.OnDrawListener() 
        @Override
        public void onDrawFrame() 
            mTriangleOpengl.onGlDraw();
        
    );


@Override
protected void onDestroy() 
    if(null != mTriangleOpengl)
        mTriangleOpengl.release();
    
    super.onDestroy();


如果运行起来,看到一个蓝色的三角形,则说明三角形绘制成功啦!
运行结果

源码
想来还是不贴源码链接了,纸上得来终觉浅,绝知此事要躬行。很多时候就是这样,你看着觉得很简单,实际如何还得动手敲,只有在敲的过程中出了问题,然后你解决了,只是才算是你的。

在这个系列完毕后再贴出整个项目demo的代码吧。

以上是关于OpenglEs之EGL环境搭建的主要内容,如果未能解决你的问题,请参考以下文章

OPENGL ES 2.0 知识串讲 ——OPENGL ES 详解I(绑定 SHADER)

Android视频编码和直播推流教程-杨万里-专题视频课程

Android OpenGLES2.0(十五)——利用EGL后台处理图像

Android OpenGL 开发---EGL 的使用

我的OpenGL学习进阶之旅EGL简介

OPENGL ES 2.0 知识串讲 ——OPENGL ES 详解I(绑定 SHADER)