1、介绍
将RGBA转换为YUV数据,首先我们是知道有公式是可以将RGBA转换为YUV的,但是图像的每个像素都有一个R、G、B,A值的,但是YUV422(就是两个像素两个Y一个U一个V的),因此我们还需要将一个像素的RGBA四个值转换为YUV三个值之后还需要将下一个像素点的Y值也要计算出来,则需要偏移采样将下个像素的RGB值也提出了来计算下个像素的Y值。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IDLdMaTS-1686050151948)(C:\Users\CreatWall_zhouwen\Desktop\pic\pic\yuv.png)]
如图所示,我们在 shader 中执行两次采样,RGBA 像素(R0,G0,B0,A0)转换为(Y0,U0,V0),像素(R1,G1,B1,A1)转换为(Y1),然后组合成(Y0,U0,Y1,V0),这样 8 个字节表示的 2 个 RGBA 像素就转换为 4 个字节表示的 2 个 YUYV 像素。
RGB to YUV 的转换公式:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bAZ1DP6M-1686050151953)(C:\Users\CreatWall_zhouwen\Desktop\pic\pic\rgb.png)]
片段shader的编写:
#version 300 es
precision mediump float;
in vec2 v_texCoord;//纹理坐标
layout(location = 0) out vec4 outColor;
uniform sampler2D s_TextureMap;//RGBA 纹理
uniform float u_Offset;//采样偏移
//RGB to YUV
//Y = 0.299R + 0.587G + 0.114B
//U = -0.147R - 0.289G + 0.436B
//V = 0.615R - 0.515G - 0.100B
const vec3 COEF_Y = vec3( 0.299, 0.587, 0.114);
const vec3 COEF_U = vec3(-0.147, -0.289, 0.436);
const vec3 COEF_V = vec3( 0.615, -0.515, -0.100);
void main()
{
vec2 texelOffset = vec2(u_Offset, 0.0);
vec4 color0 = texture(s_TextureMap, v_texCoord);//当前像素的RGB值
//偏移 offset 采样 偏移量到下一个像素 这个值应该是1/纹理宽度=到下一个像素的偏移量
vec4 color1 = texture(s_TextureMap, v_texCoord + texelOffset);//下一个像素的RGB值,则需要纹理坐标+偏移量
//dot(x, y): 点积,各分量分别相乘 后 相加;给定两个n维向量a=(a1,a2,…,an)和b=(b1,b2,…,bn),求点积a·b=a1b1+a2b2+…+anbn。
float y0 = dot(color0.rgb, COEF_Y);
float u0 = dot(color0.rgb, COEF_U) + 0.5;//同样UV需要+0.5, 因为归一化
float v0 = dot(color0.rgb, COEF_V) + 0.5;
float y1 = dot(color1.rgb, COEF_Y);//得到下一个像素的Y值
outColor = vec4(y0, u0, y1, v0);//组合的YUYV值
}
从上可以得知:RGB转YUV422的时候,就是两个像素的的RGBA值变成了一组YUYV值,则数据量少了一半,那么glViewPort 时 width 变为原来的一半,同样 glReadPixels 时 width 也变为原来的一半。
2、代码实践
使用EGL+FBO并将结果写入YUV文件中。
1)Java部分
MainActivity.java
package com.example.sixrgb2yuv;
import androidx.appcompat.app.AppCompatActivity;
import android.content.res.AssetManager;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity {
private NativeEglRender mBgRender;
private AssetManager mrg;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mrg = getResources().getAssets();
mBgRender = new NativeEglRender();
mBgRender.native_InitScene(mrg);//将AssetManager对象传下去
mBgRender.native_EglRenderInit();//调到Native里面的EGL初始化去
mBgRender.native_EglRenderDraw();//调用底层OPENGL的离屏渲染
}
}
NativeEglRender.java
package com.example.sixrgb2yuv;
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)C++部分
native.cpp部分
//
// Created by CreatWall_zhouwen on 2023/4/11.
//
#include "jni.h"
#include <android/log.h>
#include <GLES3/gl3.h>
#include "RGB2YUYV.h"
#include "Util.h"
#include "ReadFileUtil.h"
#define NATIVE_RENDER_CLASS_NAME "com/example/sixrgb2yuv/NativeEglRender"
#define TAG "GLTRIANGLE"
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_example_sixrgb2yuv_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_sixrgb2yuv_NativeEglRender
* Method: native_EglRenderInit
* Signature: ()V
*/
JNIEXPORT void JNICALL native_EglRenderInit(JNIEnv *env, jobject instance)
{
RGB2YUYV::GetInstance();
int width = 0, height = 0;
unsigned char *img = ReadBMP("awesomeface.bmp", width, height);
RGB2YUYV::GetInstance()->CreateGlesEnv();
RGB2YUYV::GetInstance()->getTexturedata(img, width, height);
RGB2YUYV::GetInstance()->CreateProgram(
reinterpret_cast<const char *>(LoadFileContent("vertex.vs")),
reinterpret_cast<const char *>(LoadFileContent("fFboShader.fs")));
}
/*
* Class: com_example_sixrgb2yuv_NativeEglRender
* Method: native_EglRenderDraw
* Signature: ()V
*/
JNIEXPORT void JNICALL native_EglRenderDraw(JNIEnv *env, jobject instance)
{
RGB2YUYV::GetInstance()->Draw();
}
/*
* Class: com_example_sixrgb2yuv_NativeEglRender
* Method: native_EglRenderUnInit
* Signature: ()V
*/
JNIEXPORT void JNICALL native_EglRenderUnInit(JNIEnv *env, jobject instance)
{
RGB2YUYV::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);
}
Opengl操作类
RGB2YUYV.h
//
// Created by CreatWall_zhouwen on 2023/4/28.
//
#ifndef SIXRGB2YUV_RGB2YUYV_H
#define SIXRGB2YUV_RGB2YUYV_H
#include <GLES3/gl3.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
class RGB2YUYV {
public:
RGB2YUYV();
~RGB2YUYV();
int CreateGlesEnv();
void CreateProgram(const char *ver, const char * fragfbo);//创建工程并初始化
void getTexturedata(unsigned char *data, int width, int height);//从NDK那边获取图片数据传过来
static RGB2YUYV* 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片段着色器中的采样器值的位置
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 //SIXRGB2YUV_RGB2YUYV_H
RGB2YUYV.cpp
//
// Created by CreatWall_zhouwen on 2023/4/28.
//
#include <stdio.h>
#include "RGB2YUYV.h"
#include "Util.h"
#include "GLUtil.h"
#include <malloc.h>
#include <string.h>
#include <unistd.h>
#include "sys/stat.h"
#include "stdint.h"
RGB2YUYV* m_pContext = nullptr;
#define TAG "RGB2YUYV"
//顶点坐标
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, 1.0f,
1.0f, 1.0f,
0.0f, 0.0f,
1.0f, 0.0f,
};
GLushort indices[] = { 0, 1, 2, 1, 3, 2 };//三角形的索引数组
RGB2YUYV::RGB2YUYV() {
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_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;
}
RGB2YUYV::~RGB2YUYV() {
}
int RGB2YUYV::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 RGB2YUYV::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");
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");
//初始化离屏渲染的VAO
glGenVertexArrays(1, &m_VaoId);
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_VaoId[0] 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);
//最后输出YUYV数据格式,则宽缩短一半了。
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, texturewidth/2, 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 RGB2YUYV::Draw() {
LOGD("Draw Enter");
glViewport(0, 0, texturewidth/2, 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);
float texelOffset = (float) (1.f / (float) texturewidth);
glUniform1f(glGetUniformLocation(m_FboProgramObj, "u_offest"), texelOffset);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, (const void *)0);
glBindVertexArray(GL_NONE);
glBindTexture(GL_TEXTURE_2D, GL_NONE);
LOGD("Draw success");
uint8_t *pBuffer = new uint8_t[texturewidth*textureheight * 2];
glReadPixels(0, 0, texturewidth / 2, textureheight, GL_RGBA, GL_UNSIGNED_BYTE, pBuffer);
//写文件
const char *imgPath= "/data/data/com.example.sixrgb2yuv/RGB2YUYV.yuv";
FILE *fp = fopen(imgPath, "wb");
if(fp == NULL)
{
LOGD("fopen error");
glBindFramebuffer(GL_FRAMEBUFFER, 0);
return ;
}
fwrite(pBuffer,1, texturewidth*textureheight * 2, fp);
fclose(fp);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
LOGD("Draw End");
}
void RGB2YUYV::getTexturedata(unsigned char *data, int width, int height) {
texturedata = data;
texturewidth = width;
textureheight = height;
LOGD("getTexturedata Success %s", texturedata);
}
RGB2YUYV *RGB2YUYV::GetInstance() {
if (m_pContext == nullptr)
{
m_pContext = new RGB2YUYV();
}
return m_pContext;
}
void RGB2YUYV::DestroyInstance() {
if (m_pContext)
{
delete m_pContext;
m_pContext = nullptr;
}
}
void RGB2YUYV::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);
}
}
工具类
GLUTIL_H
//
// Created by CreatWall_zhouwen on 2023/4/21.
//
#ifndef FORTHDRAWFBO_GLUTIL_H
#define FORTHDRAWFBO_GLUTIL_H
#include "Util.h"
#include <GLES3/gl3.h>
#define TAG "GLUTIL_H"
GLuint LoadShader(GLenum shaderType, const char *pSource)
{
LOGD("LoadShader Entern");
//创建对应类型的着色器
GLuint ShaderHandle = glCreateShader(shaderType);//创建shaderType类型的着色器
if (ShaderHandle)
{
//附加着色器源码
glShaderSource(ShaderHandle, 1, &pSource, NULL);//shader源码为pSource
//编译着色器
glCompileShader(ShaderHandle);
//获取编译结果是否成
GLint compiled = 0;
glGetShaderiv(ShaderHandle, GL_COMPILE_STATUS, &compiled);
if (!compiled){
glDeleteShader(ShaderHandle);
LOGD("glCompileShader ShaderHandle error");
return 0;
}
}
LOGD("LoadShader Leave");
return ShaderHandle;
}
GLuint CreateGLProgram(const char *pVertexShaderSource, const char *pFragShaderSource, GLuint &vertexShaderHandle, GLuint &fragShaderHandle)
{
LOGD("CreateProgram Entern");
GLuint program = 0;
GLint AttachStatus = GL_FALSE;
//创建顶点着色器
vertexShaderHandle = LoadShader(GL_VERTEX_SHADER, pVertexShaderSource);
if (!vertexShaderHandle) return program;
LOGD("vertexShaderHandle success");
//创建片段着色器
fragShaderHandle = LoadShader(GL_FRAGMENT_SHADER, pFragShaderSource);
if (!fragShaderHandle) return program;
LOGD("fragShaderHandle success");
//创建程序
program = glCreateProgram();
if (program)
{
AttachStatus = 0;
glAttachShader(program, vertexShaderHandle);
glGetShaderiv(vertexShaderHandle, GL_ATTACHED_SHADERS, &AttachStatus);
if(AttachStatus != 0){//返回0就是成功
LOGD("glAttachShader vertexShaderHandle error %d",AttachStatus);
return 0;
}
LOGD("glAttachShader vertexShaderHandle success %d",AttachStatus);
AttachStatus = 0;
glAttachShader(program, fragShaderHandle);
glGetShaderiv(fragShaderHandle, GL_ATTACHED_SHADERS, &AttachStatus);
if(AttachStatus != 0){
LOGD("glAttachShader fragShaderHandle error %d",AttachStatus);
return 0;
}
LOGD("glAttachShader fragShaderHandle success %d",AttachStatus);
glLinkProgram(program);
GLint linkStatus = GL_FALSE;
glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
if (linkStatus != GL_TRUE){
LOGD("glLinkProgram error AttachStatus = %d", linkStatus);
glDeleteProgram(program);
program = 0;
return 0;
}
glDetachShader(program, vertexShaderHandle);
glDeleteShader(vertexShaderHandle);
vertexShaderHandle = 0;
glDetachShader(program, fragShaderHandle);
glDeleteShader(fragShaderHandle);
fragShaderHandle = 0;
LOGD("glCreateProgram success");
}
LOGD("CreateProgram Leave");
return program;
}
#endif //FORTHDRAWFBO_GLUTIL_H
参考链接
android通过JNI用C/C++创建本地文件 https://blog.csdn.net/qq_34759481/article/details/84548821
使用 OpenGL 实现 RGB 到 YUV 的图像格式转换 https://blog.csdn.net/Kennethdroid/article/details/117675581
android通过JNI用C/C++创建本地文件 https://blog.csdn.net/qq_34759481/article/details/84548821