九、RGBA数据转YUV422存储

news/2024/12/25 3:09:20/

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;@Overrideprotected 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
}
#endifstatic 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工程IDGLuint m_ImageTextureId;//图片数据的纹理IDGLuint m_FboTextureId;//FBO绑定的空数据纹理IDGLint m_FboSamplerLoc;//FBO片段着色器中的采样器值的位置GLuint m_FboId;//FBO的IDunsigned 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工程IDm_ImageTextureId= GL_NONE;//图片数据的纹理IDm_FboTextureId= GL_NONE;//FBO绑定的空数据纹理IDm_FboSamplerLoc= GL_NONE;//FBO片段着色器中的采样器值的位置m_FboId= GL_NONE;//FBO的IDm_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 attributesconst 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 surfaceEGL_RED_SIZE,   8,EGL_GREEN_SIZE, 8,EGL_BLUE_SIZE,  8,EGL_ALPHA_SIZE, 8,// if you need the alpha channelEGL_DEPTH_SIZE, 16,// if you need the depth bufferEGL_STENCIL_SIZE,8,EGL_NONE};// EGL context attributesconst EGLint ctxAttr[] = {EGL_CONTEXT_CLIENT_VERSION, 2,EGL_NONE};// surface attributes// the surface size is set to the input frame sizeconst 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 systemLOGD("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 recoverLOGD("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 recoverLOGD("EGLRender::CreateGlesEnv Not enough resources available");break;case EGL_BAD_CONFIG:// Verify that provided EGLConfig is validLOGD("EGLRender::CreateGlesEnv provided EGLConfig is invalid");break;case EGL_BAD_PARAMETER:// Verify that the EGL_WIDTH and EGL_HEIGHT are// non-negative valuesLOGD("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 parametersLOGD("EGLRender::CreateGlesEnv Check window and EGLConfig attributes");break;}}//5. 创建渲染上下文 EGLContextm_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 recoverLOGD("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");//初始化离屏渲染的VAOglGenVertexArrays(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_2DglFramebufferTexture2D(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


http://www.ppmy.cn/news/247230.html

相关文章

《Lua程序设计》--学习1

前言&#xff1a; --> 表示一条语句的输出或表达式求值的结果 -- 单行注释 > 标注 一些代码需要在交互模式下输入 如果需要打印表达式求值的结果&#xff0c;必须在每个表达式前加上一个等号 <--> 表示两者完全等价 语言基础 我们将Lua语言执行的每一…

倍福--AX5000 PDO参数的调节

调整了伺服的速度环、位置环之后,遇到一个问题是直线电机高速的时候移动顺滑,但是低速的时候会出现抖动的情况,要进一步提高伺服运动的性能,还需要进一步优化PDI参数,本文补充几个优化PDI参数的方面。 操作流程 1.1. 调整的速度环位置环如下 1.2. 把PLC的任务周期适当减少…

双剑合璧看高清 飞利浦HMP5000高清播放机解析

无论电影或音响发烧友&#xff0c;都越来越热衷于高清视频的欣赏。借助网络下载和在线影视播放&#xff0c;安坐家中就能享受各式大片与热门剧集。要获得如此丰富的节目源&#xff0c;高清播放机自然不可少。面对品类繁多、良莠不齐的市场现状&#xff0c;如何选择就成了一大难…

第七章:单行函数

第七章&#xff1a;单行函数 7.1&#xff1a;函数的理解 什么是函数 ​ 函数在计算机语言的使用中贯穿始终&#xff0c;函数的作用是什么呢&#xff1f;它可以把我们经常使用的代码封装起来&#xff0c;需要的时候直接调用即可。这样即提高了代码效率&#xff0c;又提高了可维…

Linux手机DIY.夏新E600和飞利浦968初探

Linux手机DIY.夏新E600和飞利浦968初探 草木瓜 更新于 2006-10-21 一、序 无意中被小叶拉来&#xff0c;协助破解夏新E600的第三方软件安装。虽经过众位兄弟的齐心努力&#xff0c;然至今却未能取得突破性进展。失败了无数次&#xff0c;也否定了无数次设想和可能。一个人的…

大厂旗舰有何魅力?·飞利浦Fidelio X3演绎HiFi臻声

【如“沐”春风&#xff0c;带你找到更多悦耳动听】 在快节奏的生活背景下&#xff0c;使用蓝牙耳机似乎成为了更便捷、更容易的存在&#xff0c;但靠算法得到的音质提升&#xff0c;还是无法和高保真相提并论。加上连接、延迟和舒适度等原因&#xff0c;真正静下心想享受音乐…

倍福--AX5000驱动器的参数设置

操作流程 1.1. 扫描硬件 控制器切换到Config Mode,在I/O — Device 上点右键,选择“Scan”,扫面硬件。扫描到驱动器后,系统会提示是否添加NC-Task,选择是,自动添加NC任务和NC轴,NC轴自动关联到驱动器。在Axes — Online 界面,可以看到所有NC轴的连接信息1.2. 配置电机和…

经典再延续,“平民神器”飞利浦SHP9600的进化之道

说起HiFi,有些人认为它是高不可攀的穷三代产物,一入HiFi深似海,从此钱包是路人。也有人认为HiFi烧的就是心情,万元以下也不过听个响,热爱音乐才是HiFi的真谛。不论是初烧还是老烧,想必大家都有心目中的领航员,笔者还记得当年带我走进HiFi世界的那款耳机——飞利浦SHP9500。物美…