八、EGL实践

news2024/12/26 21:10:40

第一部分基础概念

1)引入

之前的OpenGLes应用的开发都是使用类GLSurfaceView ,然而GLSurfaceView 是内部已经实现了EGL的封装,也就是对Display,surface,context的管理。因此我们也很方便的利用GLSurfaceView.Renderer接口的实现对opengl esAPI进行渲染绘制。然而我们也可以自己去初始化创建EGL而不依赖于GLSurfaceView ,在Native层对EGL进行封装,实现图片的后台渲染,可以更高效的利用GPU完成对图形的处理。

2)EGL介绍

EGL是opengl es和本地窗口系统Native Windows System 之间的通信接口。

opengl es其实是一个操作 GPU 的 API,它通过驱动向 GPU 发送相关指令,控制图形渲染管线状态机的运行状态。

而最后opengles实现后的结果需展示到本地窗口的时候就需要中间层了,也就是EGL。

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

主要作用有:

与设备的原生窗口系统通信

查询绘制图表面的可用类型和配置

创建绘图表面

在OpenGL ES和其他图形渲染API之间同步渲染

管理纹理贴图等渲染资源

OpenGL ES 的平台无关性正是借助 EGL 实现的,EGL 屏蔽了不同平台的差异(Apple 提供了自己的 EGL API 的 iOS 实现,自称 EAGL)。

本地窗口相关的 API 提供了访问本地窗口系统的接口,而 EGL 可以创建渲染表面 EGLSurface ,同时提供了图形渲染上下文 EGLContext,用来进行状态管理,接下来 OpenGL ES 就可以在这个渲染表面上绘制。

在这里插入图片描述

从图片可以看出EGL其实包含实际的显示设备,SurFace也就是对应帧缓冲一样的,存储图像的内存空间,还有一个自我管理的上下文存储一些OpenGL ES 绘图的状态信息。

3)渲染的步骤

1、获取 EGLDisplay 对象,建立与本地窗口系统的连接

eglGetDisplay 为原生窗口系统 displayId 获取一个 EGL display 连接,在 OpenGL ES 与本地窗口系统之间架起了一座沟通的桥梁

/* 描述:获取 EGLDisplay 对象 显示设备
 * 参数:
 *   displayId:系统显示类型
 *
 * 返回值:成功返回 EGLDisplay 对象,失败返回 EGL_NO_DISPLAY
 * EGLDisplay eglGetDisplay(EGLNativeDisplayType displayId);
 */
EGLDisplay m_eglDisplay;
m_eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);

2、初始化 EGL 方法

EGLint eglMajVers, eglMinVers;//返回 EGL 主次版本号
eglInitialize(m_eglDisplay, &eglMajVers, &eglMinVers)

3、获取 EGLConfig 对象,确定渲染表面的配置信息

确定可用的 Surface 的配置

一旦初始化了EGL,就可以确定可用渲染表面的类型和配置了。有两种方法:两种方法均得到 EGLConfig 对象。EGLConfig 包含了渲染表面的所有信息,包括可用颜色、缓冲区等其他特性。

【1】先使用 eglGetConfigs 查询每个配置,再使用 eglGetConfigAttrib 找出最好的选择

【2】指定一组需求,使用 eglChooseChofig 让 EGL 推荐最佳配置

EGLConfig  m_eglConf;
EGLint numConfigs;
// EGL config attributes
const EGLint confAttr[] =
{
    EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT_KHR,
    EGL_SURFACE_TYPE,EGL_PBUFFER_BIT,//EGL_WINDOW_BIT EGL_PBUFFER_BIT we will create a pixelbuffer surface
    EGL_RED_SIZE,   8,
    EGL_GREEN_SIZE, 8,
    EGL_BLUE_SIZE,  8,
    EGL_ALPHA_SIZE, 8,// if you need the alpha channel
    EGL_DEPTH_SIZE, 16,// if you need the depth buffer
    EGL_STENCIL_SIZE,8,
    EGL_NONE
};
eglChooseConfig(m_eglDisplay, confAttr, &m_eglConf, 1, &numConfigs)

4、创建渲染表面 EGLSurface

通过 EGLDisplay 和 EGLConfig ,调用 eglCreateWindowSurface 或eglCreatePbufferSurface 方法创建渲染表面,得到 EGLSurface,其中eglCreateWindowSurface 用于创建屏幕上渲染区域,eglCreatePbufferSurface 用于创建屏幕外渲染区域。

const EGLint surfaceAttr[] = {
    EGL_WIDTH, 1,
    EGL_HEIGHT,1,
    EGL_NONE
};
m_eglSurface = eglCreatePbufferSurface(m_eglDisplay, m_eglConf, surfaceAttr);

5、创建渲染上下文 EGLContext

const EGLint ctxAttr[] = {
    EGL_CONTEXT_CLIENT_VERSION, 2,
    EGL_NONE
};
m_eglCtx = eglCreateContext(m_eglDisplay, m_eglConf, EGL_NO_CONTEXT, ctxAttr);

6、绑定上下文

通过 eglMakeCurrent 方法将 EGLSurface、EGLContext、EGLDisplay 三者绑定,绑定成功之后 OpenGLES 环境就创建好了,接下来便可以进行渲染。

eglMakeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglCtx)

系统通常还支持另外两种Surface:PixmapSurface和PBufferSurface,这两种都不是可显示的Surface,PixmapSurface是保存在系统内存中的位图,PBuffer则是保存在显存中的帧。

7、交换缓冲

OpenGLES 绘制结束后,使用 eglSwapBuffers 方法交换前后缓冲,将绘制内容显示到屏幕上,而屏幕外的渲染不需要调用此方法。

8、释放 EGL 环境

if (m_eglDisplay != EGL_NO_DISPLAY) {
   eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
   eglDestroyContext(m_eglDisplay, m_eglCtx);
   eglDestroySurface(m_eglDisplay, m_eglSurface);
   eglReleaseThread();
   eglTerminate(m_eglDisplay);
}

m_eglDisplay = EGL_NO_DISPLAY;
m_eglSurface = EGL_NO_SURFACE;
m_eglCtx = EGL_NO_CONTEXT;

9、GLES30.glReadPixels()

读取一些像素。当前可以简单理解为“把已经绘制好的像素(它可能已经被保存到显卡的显存中)读取到内存

void glReadPixels(GLint x,
    GLint y,
    GLsizei width,
    GLsizei height,
    GLenum format,
    GLenum type,
    GLvoid * data);
前四个参数可以得到一个矩形,该矩形所包括的像素都会被读取出来。
X Y指定从帧缓冲区读取的第一个像素的窗口坐标。 此位置是矩形像素块的左下角。
width height指定像素矩形的尺寸。 一个宽度和高度对应于单个像素。
format 指定像素数据的格式。 接受以下符号值:GL_ALPHA,GL_RGB和GL_RGBA。
type 指定像素数据的数据类型。 必须是GL_UNSIGNED_BYTE,GL_UNSIGNED_SHORT_5_6_5,GL_UNSIGNED_SHORT_4_4_4_4或GL_UNSIGNED_SHORT_5_5_5_1之一
data 返回像素数据。
注意:glReadPixels实际上是从缓冲区中读取数据,如果使用了双缓冲区,则默认是从正在显示的缓冲(即前缓冲)中读取,而绘制工作是默认绘制到后缓冲区的。因此,如果需要读取已经绘制好的像素,往往需要先交换前后缓冲。

第二部分编写代码

1)java部分

界面部分:使用ImageView显示图片

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="466dp"
        android:layout_height="700dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:srcCompat="@drawable/awesomeface" />

</androidx.constraintlayout.widget.ConstraintLayout>

MainActivity.java代码

package com.example.fiveegldraw;

import androidx.appcompat.app.AppCompatActivity;

import android.content.res.AssetManager;
import android.opengl.GLES30;
import android.opengl.GLException;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.widget.ImageView;

import java.nio.IntBuffer;

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "EGLActivity";
    private ImageView mImageView;
    private NativeEglRender mBgRender;
    private AssetManager mrg;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mImageView = (ImageView) findViewById(R.id.imageView);
        mrg = getResources().getAssets();

        mBgRender = new NativeEglRender();
        mBgRender.native_InitScene(mrg);//将AssetManager对象传下去
        mBgRender.native_EglRenderInit();//调到Native里面的EGL初始化去
        mBgRender.native_EglRenderDraw();//调用底层OPENGL的离屏渲染
        mImageView.setImageBitmap(createBitmapFromGLSurface(0, 0, 512, 512));//读取显存图片到内存进行显示
    }
    @Override
    protected void onResume() {
        super.onResume();
    }

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

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mBgRender.native_EglRenderUnInit();
    }

    private Bitmap createBitmapFromGLSurface(int x, int y, int w, int h) {
        int bitmapBuffer[] = new int[w * h];
        int bitmapSource[] = new int[w * h];
        IntBuffer intBuffer = IntBuffer.wrap(bitmapBuffer);
        intBuffer.position(0);
        try {
            //glReadPixels从显存读取数据到内存中
            GLES30.glReadPixels(x, y, w, h, GLES30.GL_RGBA, GLES30.GL_UNSIGNED_BYTE,intBuffer);
            int offset1, offset2;
            for (int i = 0; i < h; i++) {
                offset1 = i * w;
                offset2 = (h - i - 1) * w;
                for (int j = 0; j < w; j++) {
                    int texturePixel = bitmapBuffer[offset1 + j];
                    int blue = (texturePixel >> 16) & 0xff;
                    int red = (texturePixel << 16) & 0x00ff0000;
                    int pixel = (texturePixel & 0xff00ff00) | red | blue;
                    bitmapSource[offset2 + j] = pixel;
                }
            }
        } catch (GLException e) {
            return null;
        }
        return Bitmap.createBitmap(bitmapSource, w, h, Bitmap.Config.ARGB_8888);
    }
}

NativeEglRender类

package com.example.fiveegldraw;

public class NativeEglRender {
    static {
        System.loadLibrary("native-lib");
    }
    public static native void native_InitScene(Object mrg);
    public native void native_EglRenderInit();
    public native void native_EglRenderDraw();
    public native void native_EglRenderUnInit();
}

2)着色器代码

顶点着色器

#version 300 es
layout (location = 0) in vec4 a_position;
layout (location = 1) in vec2 a_texCoord;

out vec2 v_texCoord;

void main()
{
    gl_Position = a_position;
    v_texCoord = a_texCoord;
}

片段着色器

#version 300 es
precision highp float;
layout(location = 0) out vec4 outColor;
in vec2 v_texCoord;
uniform sampler2D s_TextureMap;
uniform vec2 u_texSize;
void main() {
    float size = u_texSize.x / 75.0;
    float radius = size * 0.5;
    vec2 fragCoord = v_texCoord * u_texSize.xy;
    vec2 quadPos = floor(fragCoord.xy / size) * size;
    vec2 quad = quadPos/u_texSize.xy;
    vec2 quadCenter = (quadPos + size/2.0);
    float dist = length(quadCenter - fragCoord.xy);

    if (dist > radius) {
        outColor = vec4(0.25);
    } else {
        outColor = texture(s_TextureMap, v_texCoord);
    }
}

3)C++部分

NativeGL.cpp,jni中实现java接口

//
// Created by CreatWall_zhouwen on 2023/4/11.
//

#include "jni.h"
#include <android/log.h>
#include <GLES3/gl3.h>
#include "EGLDraw.h"
#include "Util.h"
#include "ReadFileUtil.h"
#define NATIVE_RENDER_CLASS_NAME "com/example/fiveegldraw/NativeEglRender"
#define TAG "GLTRIANGLE"
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_fiveegldraw_NativeEglRender
 * Method:    native_InitScene
 * Signature: (Ljava/lang/Object;)V
 */
JNIEXPORT void JNICALL native_InitScene(JNIEnv *env, jobject instance, jobject msg)
{
   g_mrg = AAssetManager_fromJava(env, msg);
}

/*
 * Class:     com_example_fiveegldraw_NativeEglRender
 * Method:    native_EglRenderInit
 * Signature: ()V
 */
JNIEXPORT void JNICALL native_EglRenderInit(JNIEnv *env, jobject instance)
{
   EGLDraw::GetInstance();
   int width = 0, height = 0;
   unsigned char *img = ReadBMP("awesomeface.bmp", width, height);
   EGLDraw::GetInstance()->CreateGlesEnv();//创建EGL环境
   EGLDraw::GetInstance()->getTexturedata(img, width, height);//加载图片数据
   EGLDraw::GetInstance()->CreateProgram(
         reinterpret_cast<const char *>(LoadFileContent("vertex.vs")),
         reinterpret_cast<const char *>(LoadFileContent("fFboShader.fs")));//创建openngl工程
}
/*
 * Class:     com_example_fiveegldraw_NativeEglRender
 * Method:    native_EglRenderDraw
 * Signature: ()V
 */
JNIEXPORT void JNICALL native_EglRenderDraw(JNIEnv *env, jobject instance)
{
   EGLDraw::GetInstance()->Draw();//opengl绘画
}
/*
 * Class:     com_example_fiveegldraw_NativeEglRender
 * Method:    native_EglRenderUnInit
 * Signature: ()V
 */
JNIEXPORT void JNICALL native_EglRenderUnInit(JNIEnv *env, jobject instance)
{
   EGLDraw::GetInstance()->UnInit();
}

#ifdef __cplusplus
}
#endif

static JNINativeMethod g_RenderMethods[] = {
      {"native_InitScene",             "(Ljava/lang/Object;)V",       (void *)(native_InitScene)},
      {"native_EglRenderInit",             "()V",       (void *)(native_EglRenderInit)},
      {"native_EglRenderDraw",        "()V",       (void *)(native_EglRenderDraw)},
      {"native_EglRenderUnInit",        "()V",       (void *)(native_EglRenderUnInit)},
};

static int RegisterNativeMethods(JNIEnv *env, const char *className, JNINativeMethod *methods, int methodNum)
{
   LOGD("RegisterNativeMethods");
   jclass clazz = env->FindClass(className);
   if (clazz == NULL)
   {
      LOGD("RegisterNativeMethods fail. clazz == NULL");
      return JNI_FALSE;
   }
   if (env->RegisterNatives(clazz, methods, methodNum) < 0)
   {
      LOGD("RegisterNativeMethods fail");
      return JNI_FALSE;
   }
   return JNI_TRUE;
}

static void UnregisterNativeMethods(JNIEnv *env, const char *className)
{
   LOGD("UnregisterNativeMethods");
   jclass clazz = env->FindClass(className);
   if (clazz == NULL)
   {
      LOGD("UnregisterNativeMethods fail. clazz == NULL");
      return;
   }
   if (env != NULL)
   {
      env->UnregisterNatives(clazz);
   }
}

// call this func when loading lib
extern "C" jint JNI_OnLoad(JavaVM *jvm, void *p)
{
   LOGD("===== JNI_OnLoad =====");
   jint jniRet = JNI_ERR;
   JNIEnv *env = NULL;
   if (jvm->GetEnv((void **) (&env), JNI_VERSION_1_6) != JNI_OK)
   {
      return jniRet;
   }

   jint regRet = RegisterNativeMethods(env, NATIVE_RENDER_CLASS_NAME, g_RenderMethods,
                              sizeof(g_RenderMethods) /
                              sizeof(g_RenderMethods[0]));
   if (regRet != JNI_TRUE)
   {
      return JNI_ERR;
   }

   return JNI_VERSION_1_6;
}

extern "C" void JNI_OnUnload(JavaVM *jvm, void *p)
{
   JNIEnv *env = NULL;
   if (jvm->GetEnv((void **) (&env), JNI_VERSION_1_6) != JNI_OK)
   {
      return;
   }

   UnregisterNativeMethods(env, NATIVE_RENDER_CLASS_NAME);
}

EGLDraw类

//
// Created by CreatWall_zhouwen on 2023/4/24.
//

#ifndef FIVEEGLDRAW_EGLDRAW_H
#define FIVEEGLDRAW_EGLDRAW_H


#include <GLES3/gl3.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>

class EGLDraw {
public:
    EGLDraw();
    ~EGLDraw();
    int CreateGlesEnv();
    void CreateProgram(const char *ver, const char * fragfbo);//创建工程并初始化
    void getTexturedata(unsigned char *data, int width, int height);//从NDK那边获取图片数据传过来
    static EGLDraw* GetInstance();
    static void DestroyInstance();
    void Draw();//绘画 就是执行离屏渲染再将离屏渲染的结果显示
    void UnInit();
private:
    GLuint m_FboVertexShader;//FBO的顶点着色器和片段着色器
    GLuint m_FboFragmentShader;
    GLuint m_FboProgramObj;//FBO工程ID
    GLuint m_ImageTextureId;//图片数据的纹理ID
    GLuint m_FboTextureId;//FBO绑定的空数据纹理ID
    GLint m_FboSamplerLoc;//FBO片段着色器中的采样器值的位置
    GLint m_TexSizeLoc;
    GLuint m_FboId;//FBO的ID
    unsigned char *texturedata;
    int texturewidth, textureheight;
    GLuint m_VaoId;//存放顶点数据
    GLuint m_VboIds[3];//0表示顶点坐标缓冲区,1表示离屏渲染纹理坐标缓冲区,2表示纹理索引坐标缓冲区

    EGLConfig  m_eglConf;
    EGLSurface m_eglSurface;
    EGLContext m_eglCtx;
    EGLDisplay m_eglDisplay;
};



#endif //FIVEEGLDRAW_EGLDRAW_H

cpp代码

//
// Created by CreatWall_zhouwen on 2023/4/24.
//

#include "EGLDraw.h"
#include "Util.h"
#include "GLUtil.h"
EGLDraw* m_pContext = nullptr;
#define TAG "DRAWTEXTURE"
//顶点坐标
GLfloat vVertices[] = {
        -1.0f, -1.0f, 0.0f,
        1.0f, -1.0f, 0.0f,
        -1.0f,  1.0f, 0.0f,
        1.0f,  1.0f, 0.0f,
};
//纹理坐标
GLfloat vFboTexCoors[] = {
        0.0f, 0.0f,
        1.0f, 0.0f,
        0.0f, 1.0f,
        1.0f, 1.0f,
};
GLushort indices[] = { 0, 1, 2, 1, 3, 2 };//三角形的索引数组

EGLDraw::EGLDraw() {
     m_FboVertexShader = GL_NONE;//FBO的顶点着色器和片段着色器
     m_FboFragmentShader= GL_NONE;
     m_FboProgramObj= GL_NONE;//FBO工程ID
     m_ImageTextureId= GL_NONE;//图片数据的纹理ID
     m_FboTextureId= GL_NONE;//FBO绑定的空数据纹理ID
     m_FboSamplerLoc= GL_NONE;//FBO片段着色器中的采样器值的位置
     m_TexSizeLoc= GL_NONE;
     m_FboId= GL_NONE;//FBO的ID
     m_VaoId= GL_NONE;//存放顶点数据
     m_VboIds[0]= GL_NONE;//0表示顶点坐标缓冲区,1表示离屏渲染纹理坐标缓冲区,2表示纹理索引坐标缓冲区

     m_eglConf= GL_NONE;
     m_eglSurface= GL_NONE;
     m_eglCtx= GL_NONE;
     m_eglDisplay= GL_NONE;
}

EGLDraw::~EGLDraw() {

}

int EGLDraw::CreateGlesEnv() {
    // EGL config attributes
    const EGLint confAttr[] =
            {
                    EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT_KHR,
                    EGL_SURFACE_TYPE,EGL_PBUFFER_BIT,//EGL_WINDOW_BIT EGL_PBUFFER_BIT we will create a pixelbuffer surface
                    EGL_RED_SIZE,   8,
                    EGL_GREEN_SIZE, 8,
                    EGL_BLUE_SIZE,  8,
                    EGL_ALPHA_SIZE, 8,// if you need the alpha channel
                    EGL_DEPTH_SIZE, 16,// if you need the depth buffer
                    EGL_STENCIL_SIZE,8,
                    EGL_NONE
            };

    // EGL context attributes
    const EGLint ctxAttr[] = {
            EGL_CONTEXT_CLIENT_VERSION, 2,
            EGL_NONE
    };

    // surface attributes
    // the surface size is set to the input frame size
    const EGLint surfaceAttr[] = {
            EGL_WIDTH, 1,
            EGL_HEIGHT,1,
            EGL_NONE
    };
    EGLint eglMajVers, eglMinVers;
    EGLint numConfigs;

    int resultCode = 0;
    do
    {
        //1. 获取 EGLDisplay 对象,建立与本地窗口系统的连接
        m_eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
        if(m_eglDisplay == EGL_NO_DISPLAY)
        {
            //Unable to open connection to local windowing system
            LOGD("EGLRender::CreateGlesEnv Unable to open connection to local windowing system");
            resultCode = -1;
            break;
        }

        //2. 初始化 EGL 方法
        if(!eglInitialize(m_eglDisplay, &eglMajVers, &eglMinVers))
        {
            // Unable to initialize EGL. Handle and recover
            LOGD("EGLRender::CreateGlesEnv Unable to initialize EGL");
            resultCode = -1;
            break;
        }

        LOGD("EGLRender::CreateGlesEnv EGL init with version %d.%d", eglMajVers, eglMinVers);

        //3. 获取 EGLConfig 对象,确定渲染表面的配置信息
        if(!eglChooseConfig(m_eglDisplay, confAttr, &m_eglConf, 1, &numConfigs))
        {
            LOGD("EGLRender::CreateGlesEnv some config is wrong");
            resultCode = -1;
            break;
        }

        //4. 创建渲染表面 EGLSurface, 使用 eglCreatePbufferSurface 创建屏幕外渲染区域
        m_eglSurface = eglCreatePbufferSurface(m_eglDisplay, m_eglConf, surfaceAttr);
        if(m_eglSurface == EGL_NO_SURFACE)
        {
            switch(eglGetError())
            {
                case EGL_BAD_ALLOC:
                    // Not enough resources available. Handle and recover
                    LOGD("EGLRender::CreateGlesEnv Not enough resources available");
                    break;
                case EGL_BAD_CONFIG:
                    // Verify that provided EGLConfig is valid
                    LOGD("EGLRender::CreateGlesEnv provided EGLConfig is invalid");
                    break;
                case EGL_BAD_PARAMETER:
                    // Verify that the EGL_WIDTH and EGL_HEIGHT are
                    // non-negative values
                    LOGD("EGLRender::CreateGlesEnv provided EGL_WIDTH and EGL_HEIGHT is invalid");
                    break;
                case EGL_BAD_MATCH:
                    // Check window and EGLConfig attributes to determine
                    // compatibility and pbuffer-texture parameters
                    LOGD("EGLRender::CreateGlesEnv Check window and EGLConfig attributes");
                    break;
            }
        }

        //5. 创建渲染上下文 EGLContext
        m_eglCtx = eglCreateContext(m_eglDisplay, m_eglConf, EGL_NO_CONTEXT, ctxAttr);
        if(m_eglCtx == EGL_NO_CONTEXT)
        {
            EGLint error = eglGetError();
            if(error == EGL_BAD_CONFIG)
            {
                // Handle error and recover
                LOGD("EGLRender::CreateGlesEnv EGL_BAD_CONFIG");
                resultCode = -1;
                break;
            }
        }

        //6. 绑定上下文
        if(!eglMakeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglCtx))
        {
            LOGD("EGLRender::CreateGlesEnv MakeCurrent failed");
            resultCode = -1;
            break;
        }
        LOGD("EGLRender::CreateGlesEnv initialize success!");
    }
    while (false);

    if (resultCode != 0)
    {
        LOGD("EGLRender::CreateGlesEnv fail");
    }
    LOGD("EGLRender::CreateGlesEnv Success");
    return resultCode;
}

void EGLDraw::CreateProgram(const char *ver, const char *fragfbo) {
    LOGD("CreateProgram Enter");
    // 编译链接用于离屏渲染的着色器程序
    m_FboProgramObj = CreateGLProgram(ver, fragfbo, m_FboVertexShader, m_FboFragmentShader);
    if (m_FboProgramObj == GL_NONE)
    {
        LOGD("FBOSample::Init m_ProgramObj == GL_NONE");
        return;
    }
    LOGD("CreateGLProgram Success");
    //获取片段着色器中s_TextureMap的属性位置,编译后期指定是哪个纹理
    m_FboSamplerLoc = glGetUniformLocation(m_FboProgramObj, "s_TextureMap");
    m_TexSizeLoc = glGetUniformLocation(m_FboProgramObj, "u_texSize");
    LOGD("glGetUniformLocation Success");
    //生成VBO 加载顶点数据和索引数据
    glGenBuffers(4, m_VboIds);
    glBindBuffer(GL_ARRAY_BUFFER, m_VboIds[0]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vVertices), vVertices, GL_STATIC_DRAW);
    glBindBuffer(GL_ARRAY_BUFFER, m_VboIds[1]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vFboTexCoors), vFboTexCoors, GL_STATIC_DRAW);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_VboIds[2]);//最后一个为纹理的索引缓冲数据
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
    LOGD("glGenBuffers Success");

    glGenVertexArrays(1, &m_VaoId);
    //初始化离屏渲染的VAO
    glBindVertexArray(m_VaoId);
    glBindBuffer(GL_ARRAY_BUFFER, m_VboIds[0]);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (const void *)0);
    glBindBuffer(GL_ARRAY_BUFFER, GL_NONE);
    glBindBuffer(GL_ARRAY_BUFFER, m_VboIds[1]);
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), (const void *)0);
    glBindBuffer(GL_ARRAY_BUFFER, GL_NONE);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_VboIds[2]);
    glBindVertexArray(GL_NONE);
    LOGD("m_VaoIds[1] Success");
    //创建并初始化图形纹理
    glGenTextures(1, &m_ImageTextureId);
    glBindTexture(GL_TEXTURE_2D, m_ImageTextureId);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);//重复纹理的填充方式
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);//缩小时线性插值
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);//放到就是线性
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, texturewidth, textureheight, 0, GL_RGB, GL_UNSIGNED_BYTE, texturedata);
    LOGD("CreateProgram %s", texturedata);
    glGenerateMipmap(GL_TEXTURE_2D);//为当前绑定的纹理自动生成所有需要的多级渐远纹理
    glBindTexture(GL_TEXTURE_2D, GL_NONE);
    LOGD("m_ImageTextureId Success");
    //创建离屏的纹理,不绑定数据值申请内存
    glGenTextures(1, &m_FboTextureId);
    glBindTexture(GL_TEXTURE_2D, m_FboTextureId);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, texturewidth, textureheight, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr);
    glBindTexture(GL_TEXTURE_2D, GL_NONE);
    LOGD("m_FboTextureId Success");
    //创建并初始化FBO,帧缓冲
    glGenFramebuffers(1, &m_FboId);
    glBindFramebuffer(GL_FRAMEBUFFER, m_FboId);//绑定帧缓冲
    glBindTexture(GL_TEXTURE_2D, m_FboTextureId);//激活这个m_FboTextureId纹理绑定GL_TEXTURE_2D
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_FboTextureId, 0);//纹理附加到帧缓冲
    if (glCheckFramebufferStatus(GL_FRAMEBUFFER)!= GL_FRAMEBUFFER_COMPLETE) {
        LOGD("FBOSample::CreateFrameBufferObj glCheckFramebufferStatus status != GL_FRAMEBUFFER_COMPLETE");
        return ;
    }
    glBindTexture(GL_TEXTURE_2D, GL_NONE);
    glBindFramebuffer(GL_FRAMEBUFFER, GL_NONE);
    LOGD("m_FboId Success");
}

void EGLDraw::Draw() {
    LOGD("Draw Enter");
    glViewport(0, 0, texturewidth, textureheight);
    glUseProgram(m_FboProgramObj);
    glBindFramebuffer(GL_FRAMEBUFFER, m_FboId);
    glBindVertexArray(m_VaoId);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, m_ImageTextureId);
    glUniform1i(m_FboSamplerLoc, 0);
    if (m_TexSizeLoc > -1) {
        GLfloat size[2];
        size[0] = texturewidth;
        size[1] = textureheight;
        glUniform2f(m_TexSizeLoc, size[0], size[1]);
    }
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, (const void *)0);
    glBindVertexArray(GL_NONE);
    glBindTexture(GL_TEXTURE_2D, GL_NONE);
    LOGD("Draw Leave");
}

void EGLDraw::getTexturedata(unsigned char *data, int width, int height) {
    texturedata = data;
    texturewidth = width;
    textureheight = height;
    LOGD("getTexturedata Success %s", texturedata);
}



EGLDraw *EGLDraw::GetInstance() {
    if (m_pContext == nullptr)
    {
        m_pContext = new EGLDraw();
    }
    return m_pContext;
}

void EGLDraw::DestroyInstance() {
    if (m_pContext)
    {
        delete m_pContext;
        m_pContext = nullptr;
    }
}

void EGLDraw::UnInit() {
    LOGD("EGLDraw::UnInit");
    if (m_FboProgramObj)
    {
        glDeleteProgram(m_FboProgramObj);
        m_FboProgramObj = GL_NONE;
    }
    if (m_FboTextureId)
    {
        glDeleteTextures(1, &m_FboTextureId);
        m_FboTextureId = GL_NONE;
    }
    if (m_VboIds[0])
    {
        glDeleteBuffers(3, m_VboIds);
        m_VboIds[0] = GL_NONE;
        m_VboIds[1] = GL_NONE;
        m_VboIds[2] = GL_NONE;

    }
    if (m_VaoId)
    {
        glDeleteVertexArrays(1, &m_VaoId);
        m_VaoId = GL_NONE;
    }
    if (m_FboId)
    {
        glDeleteFramebuffers(1, &m_FboId);
        m_FboId = GL_NONE;
    }

    //8. 释放 EGL 环境
    if (m_eglDisplay != EGL_NO_DISPLAY) {
        eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
        eglDestroyContext(m_eglDisplay, m_eglCtx);
        eglDestroySurface(m_eglDisplay, m_eglSurface);
        eglReleaseThread();
        eglTerminate(m_eglDisplay);
    }
}

参考连接:

EGL 介绍和使用 https://blog.csdn.net/xi_gua_gua/article/details/122118375

OpenglEs之EGL环境搭建 https://blog.csdn.net/u012944685/article/details/126740119

NDK OpenGL ES 3.0 开发(六):EGL https://blog.csdn.net/Kennethdroid/article/details/99655635

#

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/617004.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

零基础入门网络安全/Web安全,收藏这一篇就够了

前言 由于我之前写了不少网络安全技术相关的文章和回答&#xff0c;不少读者朋友知道我是从事网络安全相关的工作&#xff0c;于是经常有人私信问我&#xff1a; 我刚入门网络安全&#xff0c;该怎么学&#xff1f;要学哪些东西&#xff1f;有哪些方向&#xff1f;怎么选&…

高频面试八股文用法篇(八) == 和 equals 的区别

目录 区别 如何对equals重写 为何重写equals方法就得重写hashCode方法 扩展延伸 1、使用HashSet存储自定义类对象时为什么要重写equals和hashCode方法&#xff1f; 2、HashMap为什么要同时重写hashCode和equals方法 区别 一、对象类型不同 1、equals()&#xff1a;是超类…

第二章:MySQL环境搭建

第二章&#xff1a;MySQL环境搭建 2.1&#xff1a;MySQL的下载、安装、配置 MySQL的四大版本 MySQL Community Server社区版本&#xff1a;开源免费、自由下载&#xff0c;但不提供官方技术支持&#xff0c;适用于大多数普通用户。MySQL Enterprise Edition企业版本&#xff1…

SpringBoot个人博客系统(含源码+数据库)

一、作品设计理念 个人博客系统是一个让个人可以通过互联网自由表达、交流和分享的平台&#xff0c;是个人展示自己思想、感受和经验的品牌。设计理念对于任何一个个人博客系统来说都非常重要&#xff0c;它直接影响到用户的使用体验和网站的整体感觉。 好的设计理念应该着眼于…

小红书热搜榜TOP1,多巴胺时尚爆火,怎么抄作业?

今夏时尚&#xff0c;明媚与简约并存。要说今年夏天什么最火&#xff1f;多巴胺必须拥有姓名。无论男女、老少、人宠&#xff0c;都被这股快乐风带飞。 “多巴胺”有多火&#xff1f;就只是彩色穿搭吗&#xff1f;各大博主、品牌若想加入&#xff0c;要怎么玩&#xff1f;今儿&…

Python如何解决“京东滑块验证码”(5)

前言 本文是该专栏的第51篇,后面会持续分享python爬虫干货知识,记得关注。 多数情况下使用模拟登录会遇到滑块验证码的问题,对于普通的滑块验证码,使用selenium可以轻松解决。但是对于滑块缺失验证码,比如京东的滑块验证要怎么解决呢?京东滑块验证的这个滑块缺口,每次刷…

软件测试报告模板范文来了——优秀测试报告模板流程

一、软件测试报告是什么&#xff1f; 软件测试报告就是当软件开发人员开发出软件之后&#xff0c;在上市前交由测试人员进行一系列测试&#xff0c;再由测试人员对过程和结果的进行记录分析的一份文档。也是测试团队的工作成果展现&#xff0c;通过详细的记录测试内容&#xf…

算法修炼之筑基篇——筑基二层中期(讨论一下如何解决动态方程问题,没时间了,快快快看一下)

✨博主&#xff1a;命运之光 &#x1f984;专栏&#xff1a;算法修炼之练气篇 &#x1f353;专栏&#xff1a;算法修炼之筑基篇 ✨博主的其他文章&#xff1a;点击进入博主的主页 前言&#xff1a;学习了算法修炼之练气篇想必各位蒟蒻们的基础已经非常的扎实了&#xff0c;下来…

使用FFmpeg实现最简单的视频播放

按照之前的编译步骤我们会编译得到使用ffmpeg需要的文件&#xff0c;现在就使用ffmpeg实现最简单的视频播放 集成ffmpeg 使用Android Studio创建一个Native C项目编译之后得到三个文件夹 把include 文件夹放到cpp目录下面。 main 目录下面新建jniLibs 目录把lib文件下的so文件…

Java之BigDecimal使用

Java之BigDecimal使用 1、BigDecimal概述 ​ BigDecimal用来对超过16位有效位的数进行精确的运算。双精度浮点型变量double可以处理16位有效数&#xff0c;但在实际应用中&#xff0c;可能需要对更大或者更小的数进行运算和处理。一般情况下&#xff0c;对于那些不需要准确计…

OA系统,企业数字化转型的重要工具,用现成还是自己搭建呢

什么是OA系统 OA系统是办公自动化系统的简称&#xff0c;它是指一种基于计算机技术的办公工作管理系统&#xff0c;用于协调和规划企业内部各部门的信息发布、通信、人员流动、文档管理等方面的工作。它可以有效地提高企业办公效率和工作效益&#xff0c;优化企业内部沟通协作…

计算机视觉 | 深度学习预训练与MMPretrain

前言 MMPretrain是一款基于pytorch的开源深度学习预训练工具箱&#xff0c;是OenMMLab的项目成员之一。它是一个全新升级的预训练开源算法框架&#xff0c;旨在提供各种强大的预训练主干网络&#xff0c;并支持了不同的预训练策略。 一、MMPretrain算法库介绍 MMPretrain 源…

几分钟上线一个应用,这个神器我爱了!

配置一套公司企业运用的SaaS工作流办公管理系统需要多久&#xff1f;需要多少人才能开发出来&#xff1f;传统软件开发起码需要10个人&#xff0c;花上个把月时间&#xff0c;才能做出一套比较完整的SaaS工作流办公管理系统。 传统的开发模式它需要前后端程序员以及各平台系统的…

【Docker】浅谈Docker之AUFS、BTRFS、ZFS、Container、分层的概念

作者简介&#xff1a; 辭七七&#xff0c;目前大一&#xff0c;正在学习C/C&#xff0c;Java&#xff0c;Python等 作者主页&#xff1a; 七七的个人主页 文章收录专栏&#xff1a; 七七的闲谈 欢迎大家点赞 &#x1f44d; 收藏 ⭐ 加关注哦&#xff01;&#x1f496;&#x1f…

【算法】--- 几分钟了解直接选择排序(排序中最简单的排序)+快排(解决一切的优质算法)(中)

文章目录 前言&#x1f31f;一、常见的排序算法&#xff1a;&#x1f31f;二、选择排序---直接选择排序&#xff1a;&#x1f30f;2.1.1 基本思想&#xff1a;&#x1f30f;2.1.2 直接选择排序:&#x1f30f;2.1.3 直接选择排序的特性总结&#xff1a;&#x1f30f;2.1.4 思路&…

Vue3 Vite4 ElementPlus TS模板(含Vue-Router4+Pinia4)

引言 手动安装配置Vue3 ElementPlus模板比较繁琐&#xff0c;网上寻找一些模板不太符合自己预期&#xff0c;因此花点精力搭建一个符合自己需求的架子 采用最新的组件&#xff0c;版本如下&#xff1a; vite 4.3.9vite-plugin-mock 2.9.8vue 3.3.4pinia 2.1.3vue-router 4.2.2…

总结6种服务限流的实现方式

服务限流&#xff0c;是指通过控制请求的速率或次数来达到保护服务的目的&#xff0c;在微服务中&#xff0c;我们通常会将它和熔断、降级搭配在一起使用&#xff0c;来避免瞬时的大量请求对系统造成负荷&#xff0c;来达到保护服务平稳运行的目的。下面就来看一看常见的6种限流…

推荐常用的排序学习算法——BPR(贝叶斯个性化排序)

文章目录 1. 排序学习1.1 优势1.2 排序学习在推荐领域的作用1.3 排序学习设计思路1.3.1 单点法&#xff08;Pointwise&#xff09;1.3.2 配对法&#xff08;Pairwise&#xff09;1.3.3 列表法&#xff08;Listwise&#xff09; 2. BPR&#xff08;贝叶斯个性化推荐&#xff09;…

投票评选活动小程序的分享功能和背景音乐功能实现

投票评选活动小程序的分享功能和背景音乐功能实现 投票评选活动过程中&#xff0c;需要转发分享出去&#xff0c;实现投票的效果&#xff0c;那么就需要分享功能&#xff0c;不然怎么实现投票呢&#xff0c;其实这个是最具价值的功能之一。 而背景音乐播放功能&#xff0c;只…

路径规划算法:基于静电放电优化的路径规划算法- 附代码

路径规划算法&#xff1a;基于静电放电优化的路径规划算法- 附代码 文章目录 路径规划算法&#xff1a;基于静电放电优化的路径规划算法- 附代码1.算法原理1.1 环境设定1.2 约束条件1.3 适应度函数 2.算法结果3.MATLAB代码4.参考文献 摘要&#xff1a;本文主要介绍利用智能优化…