android使用opengles渲染一张图片

您所在的位置:网站首页 渲染2000张图片 android使用opengles渲染一张图片

android使用opengles渲染一张图片

2024-07-14 21:53| 来源: 网络整理| 查看: 265

一、概述

  案例:使用opengles+egl渲染一张图片

  关键类介绍:

    1.新建一个DrawPictureActivity.java用于充当显示容器:初始化SurfaceView并设置SurfaceView的callback回调函数。并在其onSurfaceCreated函数中对DrawPicture对象进行初始化。

    2.新建DrawPicture.java用于java层和native层进行通讯

    3.新建picture_controller.cpp用于和DrawPicture.java沟通java传递给jni层的数据。并在其init方法中创建ANativeWindow、提取Bitmap中的像素数组,初始化C++类Picture类

    4.新建picture.cpp类,此类为整个渲染过程提供:OpenGL ES上下文环境、显示设备EGLSurface、GPU程序(shader)、OpenGL ES程序

  渲染步骤:

    1.DrawPictureActivity.java中在其SurfaceHolder.addCallback的回调函数中初始化DrawPicture.java类,并调用DrawPicture对象的initBitmap方法将客户端的bitmap传递给jni层

    2.在jni层的(picture_controller.cpp)initBitmap方法中,根据传递过来的Surface对ANativeWindow进行创建,根据传递过来的Bitmap对其中的像素进行提取。

    3.实例化C++的Picture并调用其init方法,在init方法中会初始化OpenGL ES上下文环境以及显示设备EGLSurface,并调用eglMakeCurrent来为当前线程绑定上下文及显示设备。

    3.调用C++的Picture实例中的renderPicture方法,通过此方法,a.创建一个纹理id、绑定纹理id、设置纹理的过滤方式及平铺方式、并将从第二步中提取出来的像素数组上传到这个纹理上。b.创建shader 。c.创建opengles程序 。d.给opengles着色器传递坐标数据(顶点坐标、纹理坐标)。e.调用glDrawArray方法开始绘制纹理 f.调用eglSwapBuffers方法将back frame buffer 和front frame buffer交互,将图像显示到用户屏幕上。

      

二、关键代码

  1.DrawPictureActivity.java:初始化SurfaceView并得到SurfaceHolder,然后给SurfaceHolder设置回调函数

surfaceView = findViewById(R.id.pictureSurfaceView); holder = surfaceView.getHolder(); holder.addCallback(new SurfaceHolder.Callback() { @Override public void surfaceCreated(@NonNull SurfaceHolder holder) { drawPicture = new DrawPicture(); parseBitmap(); Log.e("surfaceCreated", "" + pixels.length); // drawPicture.initByte(bitmapWidth, bitmapHeight, bitmap2RGB(bitmap), holder.getSurface()); drawPicture.initBitmap(bitmap,holder.getSurface()); } @Override public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) { drawPicture.onSurfaceChanged(width,height); } @Override public void surfaceDestroyed(@NonNull SurfaceHolder holder) { } });

  2.DrawPicture.java:和jni沟通的类

public native void initBitmap(Bitmap bitmap,Surface surface); /** * 当surface发生改变的时候更新width、height * * @param mWidth 宽度 * @param mHeight 高度 */ public native void onSurfaceChanged(int mWidth, int mHeight);

  3.picture_controller.cpp :提取bitmap像素数组、初始化ANativeWindow、初始化Picture C++类

  

extern "C" JNIEXPORT void JNICALL Java_com_yw_ywmediaplayer_activity_nativeinterface_DrawPicture_initBitmap(JNIEnv *env, jobject thiz, jobject bitmap, jobject surface) { void* pixels =NULL; AndroidBitmapInfo bitmapInfo; int ret = AndroidBitmap_getInfo(env,bitmap,&bitmapInfo); if(retinit(); AndroidBitmap_unlockPixels(env,bitmap); }

  3.Picture C++类

    a.初始化OpenGL ES上下文环境及显示设备   

/** * 初始化egl并创建eglsurface */ void Picture::init() { eglCore = new EGLCore(); eglCore->init(); eglSurface = eglCore->createWindowSurface(_window); //给当前线程绑定上下文环境及显示设备 eglCore->makeCurrent(eglSurface); }

  b.设置窗口大小及清除各种缓冲区

void Picture::initViewPort() { //设置窗口大小 glViewport(0, 0, screenWidth, screenHeight); LOGE("screenWidth=[%d],screenHeight=[%d]",screenWidth,screenHeight); glClearColor(0.0f, 0.0f, 1.0f, 0.0f); glClear(GL_COLOR_BUFFER_BIT); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); //reset xoffset yoffset resizeTexture(); }

  c.创建纹理id、绑定纹理id、设置纹理过滤方式及纹理的重复映射和简约映射方式、将像素数组上传到纹理上

/**创建纹理*/ //创建并绑定纹理 LOGE("glGenTextures"); GLuint textureId = 0; glGenTextures(1, &textureId); LOGE("glBindTexture"); //此处创建纹理以后需要绑定纹理id,并给纹理设置过滤方式及映射方式,不然会黑屏 glBindTexture(GL_TEXTURE_2D, textureId);//在opengles操作过程中必须告诉opengles操作的是哪个纹理,所以要调用操作opengles提供的绑定纹理的方法绑定纹理 /**设置纹理参数*/ //设置纹理的过滤方式(双线性过滤):当纹理对象(可以理解为一张图片)被渲染到物体表面上的时候(实际上是OpenGL绘制管线将纹理的元素映射到OpenGL生成的片段上的时候),有可能要被放大或者缩小,而当其放大或者缩小的时候,具体应该如何确定每个像素是如何被填充的,就由开发者配置的纹理对象的纹理过滤器来指明 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); //设置纹理映射过程中用到的重复映射和简约映射规则:将该纹理的s轴和t轴的坐标设置为GL_CLAMP_TO_EDGE类型,因为纹理坐标可以超出(0,1)的范围,而按照上述设置规则,所有大于1的纹理值都要设置为1,所有小于0的值都要置为0。 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); /**将RGBA表示的橡树数组上传到纹理上*/ //将RGBA数组表示的像素内容上传到纹理上 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bitmapWidth, bitmapHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, (uint8_t *) pixels);

  d.创建opengles可执行程序及着色器

/**创建opengles显卡可执行程序*/ //创建opengles程序 LOGE("glCreateProgram"); GLuint programId = glCreateProgram(); GLuint vertexShader = createShader(GL_VERTEX_SHADER, VERTEX_SHADER_SOURCE); GLuint framgentShader = createShader(GL_FRAGMENT_SHADER, FRAGMENT_SHADER_SOURCE); LOGE("glAttachShader"); glAttachShader(programId, vertexShader); glAttachShader(programId, framgentShader); LOGE("glLinkProgram"); glLinkProgram(programId); GLint status; glGetProgramiv(programId, GL_LINK_STATUS, &status); if (status == GL_FALSE) { GLint bufLength = 0; glGetProgramiv(programId, GL_INFO_LOG_LENGTH, &bufLength); if (bufLength) { char *buf = (char *) malloc(bufLength); if (buf) { glGetProgramInfoLog(programId, bufLength, NULL, buf); LOGI("Could not link program:\n%s\n", buf); free(buf); } } glDeleteProgram(programId); programId = 0; return -1; } GLint mPositionHandle = glGetAttribLocation(programId, "position"); GLint mTextureHandle = glGetAttribLocation(programId, "texcoord"); GLint mUniformSampler = glGetUniformLocation(programId, "yuvTexSampler"); LOGE("glUseProgram"); glUseProgram(programId); /** * 创建shader * @param shaderType shader类型 * @param source shader程序字符串 * @return shader */ GLuint Picture::createShader(GLenum shaderType, const char *source) { GLint status; GLuint shader = glCreateShader(shaderType); glShaderSource(shader, 1, &source, NULL); glCompileShader(shader); glGetShaderiv(shader, GL_COMPILE_STATUS, &status); if (!status) { GLint infoLen = 0; glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen); if (infoLen) { char *buf = (char *) malloc(infoLen); if (buf) { glGetShaderInfoLog(shader, infoLen, NULL, buf); LOGI("Could not compile shader %d:\n%s\n", shaderType, buf); free(buf); } } else { LOGI("Guessing at GL_INFO_LOG_LENGTH size\n"); char *buf = (char *) malloc(0x1000); if (buf) { glGetShaderInfoLog(shader, 0x1000, NULL, buf); LOGI("Could not compile shader %d:\n%s\n", shaderType, buf); free(buf); } } glDeleteShader(shader); shader = 0; } return shader; }

  e.设置顶点坐标、纹理坐标、激活纹理并使用纹理

LOGE("glGetAttribLocation"); //设置顶点坐标 glVertexAttribPointer(mPositionHandle, 2, GL_FLOAT, 0, 0, VERTEXS_POINTS); glEnableVertexAttribArray(mPositionHandle); //设置纹理坐标 LOGE("glGetAttribLocation"); GLfloat TEXTURE_POINTS[] = { xOffset, yOffset, xOffset, 1.0f - yOffset, 1.0f - xOffset, yOffset, 1.0f - xOffset, 1.0f - yOffset }; glVertexAttribPointer(mTextureHandle, 2, GL_FLOAT, 0, 0, TEXTURE_POINTS); glEnableVertexAttribArray(mTextureHandle); glActiveTexture(GL_TEXTURE0); // glBindTexture(GL_TEXTURE_2D, textureId); glUniform1i(mUniformSampler, 0); checkGlError("glUniform1i");

  f.开始绘制

//开始绘制 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

  g.将back frame buffer和front frame buffer进行交换,并将图像显示到屏幕上

LOGE("将front frame buffer 与back frame buffer交换并显示到屏幕上"); eglCore->swapBuffers(eglSurface);

 

  h.到此结束,运行后就能显示出图片了。

  

三、示例图片

 

 

 



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3