第一部分基础概念
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