目录
![](https://img.haomeiwen.com/i8850933/d83fbf109e74ca7c.png)
效果展示
不使用GLSurfaceView而是使用EGL将图像渲染并展示到ImageView上
![](https://img.haomeiwen.com/i8850933/4e50c44f9d6f22f7.png)
相关文章
OpenGL系列之一:OpenGL第一个程序
OpenGL系列之二:绘制三角形
OpenGL系列之三:三角形顶点增加颜色
OpenGL系列之四:绘制四边形
OpenGL系列之五:绘制点和线
OpenGL系列之六:绘制立方体
OpenGL系列之七:纹理贴图
OpenGL系列之八:立方体纹理贴图
OpenGL系列之九:glsl着色器语言
OpenGL系列之十:VAO、VBO、EBO的应用
OpenGL系列之十一:Shader图片转场切换动画
OpenGL系列之十二:Shader燃烧动画
OpenGL系列之十三:实现Shader绚丽动画
OpenGL系列之十四:实现相机抖音特效
OpenGL系列之十五:实现美颜相机
OpenGL系列之十六:实现大眼特效
OpenGL系列之十七:实现人脸贴纸
OpenGL系列之十八:FBO离屏渲染
OpenGL系列之十九:OpenGL人脸贴纸
参考文章
这里我主要是通过这篇文章学习的,感觉讲的很好,我这里的实现稍微有些不一样,比如像图片的加载我使用了stbimage,我这里也封装了VAO、VBO、FBO,但EGL部分是与这篇文章一样的
https://blog.csdn.net/Kennethdroid/article/details/99655635
实现步骤
这里由于实现步骤与我参考的那篇文章差不多一样我就贴下我的代码,展示出不一样的地方
//
// Created by Administrator on 2023/2/6.
//
#include <malloc.h>
#include <string.h>
#include "EGLRender.h"
int EGLRender::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, 8,// 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
LOGCATE("BgRender::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
LOGCATE("BgRender::CreateGlesEnv Unable to initialize EGL");
resultCode = -1;
break;
}
LOGCATE("BgRender::CreateGlesEnv EGL init with version %d.%d", eglMajVers, eglMinVers);
//3. 获取 EGLConfig 对象,确定渲染表面的配置信息
if(!eglChooseConfig(m_eglDisplay, confAttr, &m_eglConf, 1, &numConfigs))
{
LOGCATE("BgRender::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
LOGCATE("BgRender::CreateGlesEnv Not enough resources available");
break;
case EGL_BAD_CONFIG:
// Verify that provided EGLConfig is valid
LOGCATE("BgRender::CreateGlesEnv provided EGLConfig is invalid");
break;
case EGL_BAD_PARAMETER:
// Verify that the EGL_WIDTH and EGL_HEIGHT are
// non-negative values
LOGCATE("BgRender::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
LOGCATE("BgRender::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
LOGCATE("BgRender::CreateGlesEnv EGL_BAD_CONFIG");
resultCode = -1;
break;
}
}
//6. 绑定上下文
if(!eglMakeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglCtx))
{
LOGCATE("BgRender::CreateGlesEnv MakeCurrent failed");
resultCode = -1;
break;
}
LOGCATE("BgRender::CreateGlesEnv initialize success!");
}
while (false);
if (resultCode != 0)
{
LOGCATE("BgRender::CreateGlesEnv fail");
}
return resultCode;
}
void EGLRender::initTexture(){
textureSimple.initTexture("/storage/emulated/0/Android/data/com.itfitness.egldemo/files/Download/aa.png");
fbo.createFBO(textureSimple.width,textureSimple.height);
}
void EGLRender::Draw(){
glViewport(0, 0, textureSimple.width,textureSimple.height);
fbo.bindFBO();
textureSimple.draw();
}
void EGLRender::DestroyGlesEnv(){
//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;
}
主要是这里
![](https://img.haomeiwen.com/i8850933/22ead198cdb91058.png)
图片我使用了stbimage加载的,绘制的也封装到了其他类中
//
// Created by Administrator on 2023/2/3.
//
#include "TextureSimple.h"
#include "GLUtils.h"
#include "VAO.h"
#include "VBO.h"
//引入stb_image
#define STB_IMAGE_IMPLEMENTATION
extern "C"{
#include "stb_image.h"
}
VAO vao;
VBO vbo;
VBO ebo;
void TextureSimple::initTexture(char *fileName) {
glEnable(GL_TEXTURE_2D);
glGenTextures(1,&m_texID);//产生纹理索引
glBindTexture(GL_TEXTURE_2D,m_texID);//绑定纹理索引,之后的操作都针对当前纹理索引
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);//指当纹理图象被使用到一个大于它的形状上时
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);//指当纹理图象被使用到一个小于或等于它的形状上时
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// 让图像正过来(如果不加图像是倒过来的)
stbi_set_flip_vertically_on_load(true);
// 加载并生成纹理
unsigned char *data = stbi_load(fileName, &width, &height, &nrChannels, 0);
if (data)
{
if(nrChannels == 3){
/**
* OpenGL要求所有的纹理都是4字节对齐的,即纹理的大小永远是4字节的倍数。
* 通常这并不会出现什么问题,因为大部分纹理的宽度都为4的倍数并/或每像素使用4个字节。
* 但是这个图片是jpg并且宽高不是4的倍数,所以出现了问题。通过将纹理解压对齐参数设为1,
* 这样才能确保不会有对齐问题。使用这个接口glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
*/
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
} else{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
}
}
stbi_image_free(data);
char vShaderStr[] =
"#version 300 es \n"
"layout(location = 0) in vec4 a_position; \n"
"layout(location = 1) in vec2 a_texCoord; \n"
"out vec2 v_texCoord; \n"
"void main() \n"
"{ \n"
" gl_Position = a_position; \n"
" v_texCoord = a_texCoord; \n"
"} \n";
char fShaderStr[] =
"#version 300 es \n"
"precision mediump float; \n"
"in vec2 v_texCoord; \n"
"out vec4 outColor; \n"
"uniform sampler2D s_TextureMap; \n"
"void main() \n"
"{ \n"
" outColor = texture(s_TextureMap, v_texCoord); \n"
"} \n";
m_ProgramObj = GLUtils::CreateProgram(vShaderStr, fShaderStr, m_VertexShader, m_FragmentShader);
if(m_ProgramObj == GL_NONE || m_texID == GL_NONE) return;
// Use the program object
glUseProgram (m_ProgramObj);
if (m_ProgramObj)
{
m_SamplerLoc = glGetUniformLocation(m_ProgramObj, "s_TextureMap");
}
GLfloat verticesCoords[] = {
-1.0f, 1.0f, 0.0f, 0.0f, 0.0f, // Position 0
-1.0f, -1.0f, 0.0f, 0.0f, 1.0f, // Position 1
1.0f, -1.0f, 0.0f, 1.0f, 1.0f, // Position 2
1.0f, 1.0f, 0.0f, 1.0f, 0.0f // Position 3
};
GLfloat textureCoords[] = {
0.0f, 0.0f, // TexCoord 0
0.0f, 1.0f, // TexCoord 1
1.0f, 1.0f, // TexCoord 2
1.0f, 0.0f // TexCoord 3
};
GLushort indices[] = { 0, 1, 2, 0, 2, 3 };
vbo.init(GL_ARRAY_BUFFER, sizeof(verticesCoords),verticesCoords);
ebo.init(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices),indices);
vao.bind();
//使用 VBO 的绘制
vbo.bind();
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, (3+2)*sizeof(GLfloat), (const void *)0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, (3+2)*sizeof(GLfloat), (const void *)(3 *sizeof(GLfloat)));
ebo.bind();
// vbo.unBind();
// ebo.unBind();
vao.unBind();
}
void TextureSimple::draw() {
vao.bind();
// Bind the RGBA map
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, m_texID);
// Set the RGBA map sampler to texture unit to 0
glUniform1i(m_SamplerLoc, 0);
// glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, (const void *)0);
vao.unBind();
}
然后还有生成Bitmap,展示图片的代码也有所不同
class MainActivity : AppCompatActivity() {
init {
System.loadLibrary("NetiveRender")
}
external fun ndkInit()
external fun ndkRender():IntArray
external fun release()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val image = findViewById<ImageView>(R.id.render_img)
getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)
ndkInit()
val imageSize = ndkRender()
image.setImageBitmap(createBitmapFromGLSurface(0, 0, imageSize[0], imageSize[1]))
release()
}
private fun createBitmapFromGLSurface(x:Int, y:Int,w:Int, h:Int): Bitmap? {
val pixelBuffer = ByteBuffer.allocate(w * h * 4)
GLES20.glReadPixels(
x,
y,
w,
h,
GLES20.GL_RGBA,
GLES20.GL_UNSIGNED_BYTE,
pixelBuffer
)
val ARGB8888ImageBitmap = Bitmap.createBitmap(w , h, Bitmap.Config.ARGB_8888)
ARGB8888ImageBitmap.copyPixelsFromBuffer(pixelBuffer)
return ARGB8888ImageBitmap
}
}
网友评论