Android应用开发之OpenGL、OpenGL ES的概念和实例讲解
凌雪 2018-09-20 来源 :网络 阅读 441 评论 0

摘要:本文将带你了解Android应用开发之OpenGL、OpenGL ES的概念和实例讲解,希望本文对大家学Android有所帮助。

本文将带你了解Android应用开发之OpenGL、OpenGL ES的概念和实例讲解,希望本文对大家学Android有所帮助。


什么是OpenGL?
    Open Graphics Library (OpenGL) is a cross-language, cross-platform   application programming interface (API) for rendering 2D and 3D vector   graphics. The API is typically used to interact with a graphics processing   unit (GPU), to achieve hardware-accelerated rendering.
    OpenGL是和编程语言,平台无关的一套interface,主要是为了rendering   2D和3D图形等。一般这套接口是用来和GPU进行交互的,使用GPU进行rendering硬件加速。
    什么是OpenGL ES?
    Android includes support for high performance 2D and 3D graphics with the   Open Graphics Library (OpenGL), specifically, the OpenGL ES API. OpenGL is a   cross-platform graphics API that specifies a standard software interface for   3D graphics processing hardware. OpenGL ES is a flavor of the OpenGL   specification intended for embedded devices.
    OpenGL ES就是专门为嵌入式设备设计的,当然售后机也是嵌入式,那么OpenGL   ES和OpenGL中的函数接口肯定有些是不一样的,因为嵌入式设备和pc等的硬件处理能力还是有差距的,不然手机卡死了。
    既然OpenGL   ES只是一组函数接口,那么如何使用呢?我们肯定首先要去实现这些函数接口,而android提供了两种类型的实现:软件实现,硬件实现。
    a, 硬件实现,前面提到这组函数接口主要是为了和GPU这个硬件进行打交道的。所以各个硬件厂商会提供相关的实现,例如高通平台的adreno解决方案;
    b,软件实现,android也提供了一套OpenGL   ES的软件实现,就是说不用GPU了,完全用软件实现画图的相关功能,也就是libagl,代码在frameworks\native\opengl\libagl,其makefile中,
    ?123/软件实现最终编译完保存在system\lib\egl\libGLES_android.soLOCAL_MODULE_PATH :=   $(TARGET_OUT_SHARED_LIBRARIES)/eglLOCAL_MODULE:= libGLES_android
    到此,已经有了OpenGL ES的具体实现,但是由于其实现的平台无关系,所以在android上还不能使用,必须借助EGL。
    EGL
    EGL - Native Platform Interface
    EGL is an interface between Khronos rendering APIs such as OpenGL ES or   OpenVG and the underlying native platform window system.
    It handles graphics context management, surface/buffer binding, and   rendering synchronization and enables high-performance, accelerated,   mixed-mode 2D and 3D rendering using other Khronos APIs.
    EGL,它是图形渲染API(如OpenGL ES)与本地平台窗口系统的一层接口,保证了OpenGL   ES的平台独立性。EGL提供了若干的功能:创建rendering surface,创建graphics   context,同步应用程序和本地平台渲染API,提供对显示设备的访问,提供对渲染配置的管理等。
      EGL提供了一种方法用于通过客户端API和本地窗口系统进行渲染,客户端API包括用于嵌入式系统的3D渲染器和OpenGLES,用于桌面系统的OpenGL   ES的超集OpenGL,2D矢量图形渲染器OpenVG,本地窗口系统包括Windows,X
    那么什么是EGL?EGL是OpenGL ES和底层的native window system之间的接口,承上启下。
    EGL is a complement to OpenGL ES. EGL is used for getting surfaces to   render to using functions like eglCreateWindowSurface, and you can then draw   to that surface with OpenGL ES. Its role is similar to GLX/WGL/CGL.
    Whether or not EGL can give you a context that supports OpenGL ES 2.0 may   vary by platform, but if the Android device supports ES 2.0 and EGL, you   should be able to get such a context from EGL. Take a look at the   EGL_RENDERABLE_TYPE attribute and the EGL_OPENGL_ES2_BIT when requesting an   EGLConfig.
    在Android上,EGL完善了OpenGL ES。利用类似eglCreateWindowSurface的EGL函数可以创建surface   用来render ,有了这个surface你就能往这个surface中利用OpenGL ES函数去画图了。
    EGL要做什么
    EGL既然做平台和OpenGL ES的中间层,那EGL做的肯定就是和平台息息相关的事:
    创建绘图窗口
    也就是所谓的FrameBuffer,FrameBuffer可以先到到屏幕上(SurfaceView) 创建渲染环境(Context上下文)
    渲染环境是指OpenGL ES的所有项目运行需要的 数据结构。如顶点,片段着色器,顶点数据矩阵
    OpenGL渲染一般流程:
   
    EGLConfig属性
   
   
     
     
     属性
     
     描述
     
     默认值
     
   
   
     
     EGL_BUFFER_SIZE
     颜色缓冲器中所有组成演的的位数
     0
     
     
     EGL_RED_SIZE
     颜色缓冲区中红色位数
     0
     
     
     EGL_LUMINANCE_SIZE
     颜色缓冲区中透明度的位数
     0
     
     
     EGL_ALPTHA_SIZE
     颜色缓冲区中透明位数
     0
     
     
     EGL_ALPTHHA_MASK_SIZE
     遮挡缓冲区透明度掩码位数
     0
     
     
     EGL_BIND_TO_TEXTURE_RGB
     绑定到RGB贴图使能为真
     EGL_DONT_CARE
     
     
     EGL_BIND_TO_TEXTURE_RGBA
     绑定到RGBA贴图使能为真
     EGL_DONT_CARE
     
     
     EGL_COLOR_BUFFER_TYPE
     颜色缓冲区类型 EGL_RGB_BUFFER, 或者EGL_LUMINANCE_BUFFER
     EGL_RGB_BUFFER
     
     
     EGL_CONFIG_CAVEAT
     配置有关的警告信息
     EGL_DONT_CARE
     
     
     EGL_CONFIG_ID
     唯一的 EGLConfig 标示值
     EGL_DONT_CARE
     
     
     EGL_CONFORMANT
     使用EGLConfig 创建的上下文符合要求时为真
     -
     
     
     EGL_DEPTH_SIZE
     深度缓冲区位数
     0
     
     
     EGL_LEVEL
     帧缓冲区水平
     0
     
     
     EGL_MAX_PBUFFER_WIDTH
     使用EGLConfig 创建的PBuffer的最大宽度
     —
     
     
     EGL_MAX_PBUFFER_HEIGHT
     使用EGLConfig 创建的PBuffer最大高度
     —
     
     
     EGL_MAX_PBUFFER_PIXELS
     使用EGLConfig 创建的PBuffer最大尺寸
     —
     
     
     EGL_MAX_SWAP_INTERVAL
     最大缓冲区交换间隔
     EGL_DONT_CARE
     
     
     EGL_MIN_SWAP_INTERVAL
     最小缓冲区交换间隔
     EGL_DONT_CARE
     
     
     EGL_NATIVE_RENDERABLE
     如果操作系统渲染库能够使用EGLConfig 创建渲染渲染窗口
     EGL_DONT_CARE
     
     
     EGL_NATIVE_VISUAL_ID
     与操作系统通讯的可视ID句柄
     EGL_DONT_CARE
     
     
     EGL_NATIVE_VISUAL_TYPE
     与操作系统通讯的可视ID类型
     EGL_DONT_CARE
     
     
     EGL_RENDERABLE_TYPE
     渲染窗口支持的布局组成标示符的遮挡位EGL_OPENGL_ES_BIT, EGL_OPENGL_ES2_BIT, orEGL_OPENVG_BIT   that
     EGL_OPENGL_ES_BIT
     
     
     EGL_SAMPLE_BUFFERS
     可用的多重采样缓冲区位数
     0
     
     
     EGL_SAMPLES
     每像素多重采样数
     0
     
     
     EGL_S TENCIL_SIZE
     模板缓冲区位数
     0
     
     
     EGL_SURFACE_TYPE
     EGL 窗口支持的类型EGL_WINDOW_BIT, EGL_PIXMAP_BIT,或EGL_PBUFFER_BIT
     EGL_WINDOW_BIT
     
     
     EGL_TRANSPARENT_TYPE
     支持的透明度类型
     EGL_NONE
     
     
     EGL_TRANSPARENT_RED_VALUE
     透明度的红色解释
     EGL_DONT_CARE
     
     
     EGL_TRANSPARENT_GRE EN_VALUE
     透明度的绿色解释
     EGL_DONT_CARE
     
     
     EGL_TRANSPARENT_BLUE_VALUE
     透明度的兰色解释
     EGL_DONT_CARE
     
   
   
    一个简单例子:
    OpenGL的渲染是基于线程的,所以我们要创建一个GLLRenderer类继承于HandlerThread:
      ?123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237public   class GLRenderer extends HandlerThread {  private static final   String TAG = "GLRenderer"; private EGLConfig eglConfig =   null; private EGLDisplay eglDisplay = EGL14.EGL_NO_DISPLAY; private   EGLContext eglContext = EGL14.EGL_NO_CONTEXT;  private int program; private   int vPosition; private int uColor;   public GLRenderer()   {  super("GLRenderer"); }  private void   createGL(){  eglDisplay =   EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);   int[]   version = new int[2];  if (!EGL14.eglInitialize(eglDisplay,   version,0,version,1)) {throw new RuntimeException("EGL error   "+EGL14.eglGetError());  }   int[]   configAttribs = { EGL14.EGL_BUFFER_SIZE,32, EGL14.EGL_ALPHA_SIZE,8, EGL14.EGL_BLUE_SIZE,8, EGL14.EGL_GREEN_SIZE,8, EGL14.EGL_RED_SIZE,8, EGL14.EGL_RENDERABLE_TYPE,EGL14.EGL_OPENGL_ES2_BIT, EGL14.EGL_SURFACE_TYPE,EGL14.EGL_WINDOW_BIT, EGL14.EGL_NONE  };//获取framebuffer格式和能力   int[]   numConfigs = new int[1];  EGLConfig[] configs = new   EGLConfig[1];   if (!EGL14.eglChooseConfig(eglDisplay,   configAttribs,0, configs, 0,configs.length, numConfigs,0)) {throw new   RuntimeException("EGL error   "+EGL14.eglGetError());  }   eglConfig =   configs[0];    //创建OpenGL上下文  int[]   contextAttribs = { EGL14.EGL_CONTEXT_CLIENT_VERSION,2, EGL14.EGL_NONE  };  eglContext   =   EGL14.eglCreateContext(eglDisplay,eglConfig,EGL14.EGL_NO_CONTEXT,contextAttribs,0);   if(eglContext==   EGL14.EGL_NO_CONTEXT) {throw new RuntimeException("EGL error   "+EGL14.eglGetError());  } }  private void   destoryGL(){  EGL14.eglDestroyContext(eglDisplay,eglContext);  eglContext   = EGL14.EGL_NO_CONTEXT;  eglDisplay =   EGL14.EGL_NO_DISPLAY; }  @Override public synchronized   void start() {  super.start();   new   Handler(getLooper()).post(new Runnable() {@Overridepublic void run()   { createGL();}  }); }  public void   release(){  new Handler(getLooper()).post(new Runnable()   {@Overridepublic void run()   { destoryGL(); quit();}  }); }  /**  *   加载制定shader的方法  * @param shaderType shader的类型    GLES20.GL_VERTEX_SHADERGLES20.GL_FRAGMENT_SHADER  * @param   sourceCode shader的脚本  * @return shader索引  */ private   int loadShader(int shaderType,String sourceCode) {  //   创建一个新shader  int shader =   GLES20.glCreateShader(shaderType);  // 若创建成功则加载shader  if   (shader != 0) {// 加载shader的源代码GLES20.glShaderSource(shader, sourceCode);//   编译shaderGLES20.glCompileShader(shader);// 存放编译成功shader数量的数组int[] compiled =   new int[1];// 获取Shader的编译情况GLES20.glGetShaderiv(shader,   GLES20.GL_COMPILE_STATUS, compiled, 0);if (compiled[0] == 0)   {//若编译失败则显示错误日志并删除此shader Log.e("ES20_ERROR", "Could not   compile shader " + shaderType +   ":"); Log.e("ES20_ERROR",   GLES20.glGetShaderInfoLog(shader)); GLES20.glDeleteShader(shader); shader   = 0;}  }  return   shader; }   /**  *   创建shader程序的方法  */ private int createProgram(String   vertexSource, String fragmentSource) {  //加载顶点着色器  int vertexShader   = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);  if   (vertexShader == 0) {return 0;  }   //   加载片元着色器  int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER,   fragmentSource);  if (pixelShader == 0) {return   0;  }   // 创建程序  int program =   GLES20.glCreateProgram();  //   若程序创建成功则向程序中加入顶点着色器与片元着色器  if (program != 0) {//   向程序中加入顶点着色器GLES20.glAttachShader(program, vertexShader);// 向程序中加入片元着色器GLES20.glAttachShader(program,   pixelShader);// 链接程序GLES20.glLinkProgram(program);// 存放链接成功program数量的数组int[]   linkStatus = new int[1];// 获取program的链接情况GLES20.glGetProgramiv(program,   GLES20.GL_LINK_STATUS, linkStatus, 0);// 若链接失败则报错并删除程序if (linkStatus[0] !=   GLES20.GL_TRUE) { Log.e("ES20_ERROR", "Could not link   program: "); Log.e("ES20_ERROR",   GLES20.glGetProgramInfoLog(program)); GLES20.glDeleteProgram(program); program   = 0;}  }  return   program; }  /**  * 获取图形的顶点  *   特别提示:由于不同平台字节顺序不同数据单元不是字节的一定要经过ByteBuffer  *   转换,关键是要通过ByteOrder设置nativeOrder(),否则有可能会出问题  *  * @return   顶点Buffer  */ private FloatBuffer getVertices()   {  float vertices[] = { 0.0f,0.5f, -0.5f,   -0.5f, 0.5f,  -0.5f,  };   //   创建顶点坐标数据缓冲  //   vertices.length*4是因为一个float占四个字节  ByteBuffer vbb =   ByteBuffer.allocateDirect(vertices.length *   4);  vbb.order(ByteOrder.nativeOrder());   //设置字节顺序  FloatBuffer vertexBuf = vbb.asFloatBuffer();   //转换为Float型缓冲  vertexBuf.put(vertices);//向缓冲区中放入顶点坐标数据  vertexBuf.position(0);    //设置缓冲区起始位置   return vertexBuf; }  public void   render(Surface surface, int width, int height){  final int[]   surfaceAttribs = { EGL14.EGL_NONE };  EGLSurface eglSurface =   EGL14.eglCreateWindowSurface(eglDisplay, eglConfig, surface, surfaceAttribs,   0);  EGL14.eglMakeCurrent(eglDisplay, eglSurface, eglSurface,   eglContext);   // 初始化着色器  //   基于顶点着色器与片元着色器创建程序  program = createProgram(verticesShader,   fragmentShader);  // 获取着色器中的属性引用id(传入的字符串就是我们着色器脚本中的属性名)  vPosition   = GLES20.glGetAttribLocation(program,   "vPosition");  uColor =   GLES20.glGetUniformLocation(program, "uColor");   //   设置clear color颜色RGBA(这里仅仅是设置清屏时GLES20.glClear()用的颜色值而不是执行清屏)  GLES20.glClearColor(1.0f,   0, 0, 1.0f);  //   设置绘图的窗口(可以理解成在画布上划出一块区域来画图)  GLES20.glViewport(0,0,width,height);  //   获取图形的顶点坐标  FloatBuffer vertices =   getVertices();   // 清屏  GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT   | GLES20.GL_COLOR_BUFFER_BIT);  //   使用某套shader程序  GLES20.glUseProgram(program);  //   为画笔指定顶点位置数据(vPosition)  GLES20.glVertexAttribPointer(vPosition, 2,   GLES20.GL_FLOAT, false, 0, vertices);  //   允许顶点位置数据数组  GLES20.glEnableVertexAttribArray(vPosition);  //   设置属性uColor(颜色 索引,R,G,B,A)  GLES20.glUniform4f(uColor, 0.0f, 1.0f,   0.0f, 1.0f);  // 绘制  GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP,   0, 3);   //   交换显存(将surface显存和显示器的显存交换)  EGL14.eglSwapBuffers(eglDisplay,   eglSurface);   EGL14.eglDestroySurface(eglDisplay,   eglSurface); }  // 顶点着色器的脚本 private static final String   verticesShader= "attribute vec2 vPosition;\n" // 顶点位置属性vPosition+   "void main(){ \n"+ "gl_Position = vec4(vPosition,0,1);\n"   // 确定顶点位置+ "}";  // 片元着色器的脚本 private static final   String fragmentShader= "precision mediump float;\n" //   声明float类型的精度为中等(精度越高越耗资源)+ "uniform vec4 uColor; \n" //   uniform的属性uColor+ "void main(){\n"+ "gl_FragColor =   uColor;  \n" // 给此片元的填充色+ "}";  }
      createGL()方法获取了一个默认的显示设备(也就是手机屏幕),初始化并返回当前系统使用的OpenGL版本(主版本+子版本),然后通过配置(主要以键值对的方式配置,最后由EGL_NONE结尾)得到一个EGLConfig,最后创建一个EGLContext。
    destroyGL()方法则是释放掉OpenGL的资源(主要就是EGLContext)。
    render()方法中主要是渲染,这里为了方便把渲染的环境和渲染写在一起并只渲染一次(我们只画了一个三角形),前三行代码我们创建了一个EGLSurface并设置为当前的渲染对象,后面eglSwapBuffers()交换了显示器和EGLSurface的显存,也就是将我们渲染的东西放到显示器去显示,这样我们就看到我们绘制的三角形了,最后就是销毁我们创建的EGLSurface。
    然后在Activity中使用它:
    ?12345678910111213141516171819202122232425262728293031323334353637public   class MainActivity extends Activity { private GLRenderer   glRenderer;  @Override protected void onCreate(Bundle   savedInstanceState)   {  super.onCreate(savedInstanceState);  setContentView(R.layout.activity_main);   SurfaceView   sv = (SurfaceView)findViewById(R.id.sv_main_demo);  glRenderer =   new   GLRenderer();  glRenderer.start();   sv.getHolder().addCallback(new   SurfaceHolder.Callback() {@Overridepublic void surfaceCreated(SurfaceHolder   surfaceHolder) { } @Overridepublic void   surfaceChanged(SurfaceHolder surfaceHolder, int format, int width, int   height)   { glRenderer.render(surfaceHolder.getSurface(),width,height);} @Overridepublic   void surfaceDestroyed(SurfaceHolder surfaceHolder)   { }  }); }  @Override protected void   onDestroy() {  glRenderer.release();  glRenderer =   null;  super.onDestroy(); }}
    然后我们就可以得到一个:
   
    然后我们继续学习:
      EGL创建EGLSurface有三个方法:glCreateWindowSurface()、eglCreatePbufferSurface()和eglCreatePixmapSurface()。
    WindowSurface:是和窗口相关的,也就是在屏幕闪给的一块显示区的封装,渲染后即显示在界面上。   PbufferSurface:在显存中开辟一个控件,将渲染后的数据(帧)存放在这里   pixmapSurface:以位图的形式存放在内存中(支持性不好)
    做离屏渲染我们可以选择PbufferSurface或者PixmapSurface(其实WindowSurface也是可以的)
   
     (帧缓存对象FBO是对帧缓存的封装,性能要优于Pbuffer但并非可以完全替代Pbuffer)。
   
    OpenGL操作的最终目标实际上是帧缓存(Frame Buffer)后面的各种表现形式则是EGL对Frame Buffer的封装
   
    对之前的代码进行整理:
    新建GLSurface类,对EGLSurface进行封装 将GLRenderer类改为抽象类,继承于Thread   GLRenderer添加一个阻塞队列(消息队列),用于交互和解耦 GLRenderer添加一个Event内部类   GLRenderer中添加生命周期,将渲染之类的具体工作放到实现类中 添加ShaderUtil类,将着色器创建的代码封装到这个类中
    代码:
      ?1234567891011121314151617181920212223242526272829303132333435363738394041424344454647package   com.example.asus1.camerawithopengl; import android.opengl.EGL14;import   android.opengl.EGLSurface;import android.view.Surface; public class   GLSurface {  public static final int TYPE_WINDOW_SURFACE  =   0; public static final int TYPE_PBUFFER_SURFACE = 1; public static   final int TYPE_PIXMAP_SURFACE  = 2;   protected final int   type; protected Object surface; protected EGLSurface eglSurface =   EGL14.EGL_NO_SURFACE; protected Viewport viewport = new   Viewport();  public GLSurface(int width,int   height){  setViewport(0,0,width,height);  surface =   null;  type = TYPE_PBUFFER_SURFACE; } public   GLSurface(Surface surface, int width, int height)   {   this(surface,0,0,width,height); }  public   GLSurface(Surface surface, int x, int y, int width, int height)   {  setViewport(x, y, width, height);  this.surface =   surface;  type = TYPE_WINDOW_SURFACE; }   public   void setViewport(int x,int y,int width,int height){  viewport.x =   x;  viewport.y = y;  viewport.width =   width;  viewport.height = height; }  public Viewport   getViewport(){  return viewport; } }
      ?123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314public   abstract class GLRenderer1 extends Thread{  private static final   String TAG = "GLThread"; private EGLConfig eglConfig =   null; private EGLDisplay eglDisplay = EGL14.EGL_NO_DISPLAY; private   EGLContext eglContext = EGL14.EGL_NO_CONTEXT;  private   ArrayBlockingQueue<event> eventQueue; private final List<glsurface>   outputSurfaces; private boolean rendering; private boolean   isRelease;  public GLRenderer1()   {  setName("GLRenderer-"+getId());  outputSurfaces   = new ArrayList<>();  rendering = false;  isRelease   = false;   eventQueue = new   ArrayBlockingQueue<>(100); }  private boolean   makeOutputSurface(GLSurface surface) {  //   创建Surface缓存  try {switch (surface.type) { case   GLSurface.TYPE_WINDOW_SURFACE: {  final int[] attributes =   {EGL14.EGL_NONE};  //   创建失败时返回EGL14.EGL_NO_SURFACE  surface.eglSurface =   EGL14.eglCreateWindowSurface(eglDisplay, eglConfig, surface.surface,   attributes, 0);  break; } case   GLSurface.TYPE_PBUFFER_SURFACE: {  final int[] attributes =   { EGL14.EGL_WIDTH, surface.viewport.width, EGL14.EGL_HEIGHT,   surface.viewport.height, EGL14.EGL_NONE};  // 创建失败时返回EGL14.EGL_NO_SURFACE  surface.eglSurface   = EGL14.eglCreatePbufferSurface(eglDisplay, eglConfig, attributes,   0);  break; } case GLSurface.TYPE_PIXMAP_SURFACE:   {  Log.w(TAG, "nonsupport pixmap surface");  return   false; } default:  Log.w(TAG, "surface type error   " + surface.type);  return false;}  } catch   (Exception e) {Log.w(TAG, "can't create   eglSurface");surface.eglSurface = EGL14.EGL_NO_SURFACE;return   false;  }   return true; }  public   void addSurface(@NonNull final GLSurface surface){  Event event =   new Event(Event.ADD_SURFACE);  event.param =   surface;  if(!eventQueue.offer(event))Log.e(TAG,"queue   full"); }  public void removeSurface(@NonNull final   GLSurface surface){  Event event = new   Event(Event.REMOVE_SURFACE);  event.param =   surface;  if(!eventQueue.offer(event))Log.e(TAG,"queue   full"); }  /**  * 开始渲染  *   启动线程并等待初始化完毕  */ public void   startRender(){  if(!eventQueue.offer(new   Event(Event.START_RENDER)))Log.e(TAG,"queue full");  if(getState()==State.NEW)   {super.start(); // 启动渲染线程  } }  public void   stopRender(){  if(!eventQueue.offer(new   Event(Event.STOP_RENDER)))Log.e(TAG,"queue   full"); }  public boolean postRunnable(@NonNull Runnable   runnable){  Event event = new   Event(Event.RUNNABLE);  event.param =   runnable;  if(!eventQueue.offer(event)) {Log.e(TAG, "queue   full");return false;  }   return true; }  @Override public   void start() {  Log.w(TAG,"Don't call this   function"); }  public void   requestRender(){  eventQueue.offer(new   Event(Event.REQ_RENDER)); }  /**  *   创建OpenGL环境  */ private void createGL() {  //   获取显示设备(默认的显示设备)  eglDisplay =   EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);  //   初始化  int[] version = new int[2];  if (!EGL14.eglInitialize(eglDisplay,   version, 0, version, 1)) {throw new RuntimeException("EGL error " +   EGL14.eglGetError());  }  //   获取FrameBuffer格式和能力  int[] configAttribs =   { EGL14.EGL_BUFFER_SIZE, 32, EGL14.EGL_ALPHA_SIZE,   8, EGL14.EGL_BLUE_SIZE, 8, EGL14.EGL_GREEN_SIZE,   8, EGL14.EGL_RED_SIZE, 8, EGL14.EGL_RENDERABLE_TYPE,   EGL14.EGL_OPENGL_ES2_BIT, EGL14.EGL_SURFACE_TYPE,   EGL14.EGL_WINDOW_BIT, EGL14.EGL_NONE  };  int[] numConfigs   = new int[1];  EGLConfig[] configs = new   EGLConfig[1];  if (!EGL14.eglChooseConfig(eglDisplay,   configAttribs, 0, configs, 0, configs.length, numConfigs, 0)) {throw new   RuntimeException("EGL error " + EGL14.eglGetError());  }  eglConfig   = configs[0];  //   创建OpenGL上下文(可以先不设置EGLSurface,但EGLContext必须创建,  //   因为后面调用GLES方法基本都要依赖于EGLContext)  int[] contextAttribs =   { EGL14.EGL_CONTEXT_CLIENT_VERSION, 2, EGL14.EGL_NONE  };  eglContext   = EGL14.eglCreateContext(eglDisplay, eglConfig, EGL14.EGL_NO_CONTEXT,   contextAttribs, 0);  if (eglContext == EGL14.EGL_NO_CONTEXT) {throw   new RuntimeException("EGL error " + EGL14.eglGetError());  }  //   设置默认的上下文环境和输出缓冲区(小米4上如果不设置有效的eglSurface后面创建着色器会失败,可以先创建一个默认的eglSurface)  //EGL14.eglMakeCurrent(eglDisplay,   surface.eglSurface, surface.eglSurface,   eglContext);  EGL14.eglMakeCurrent(eglDisplay, EGL14.EGL_NO_SURFACE,   EGL14.EGL_NO_SURFACE, eglContext); }  /**  *   销毁OpenGL环境  */ private void destroyGL()   {  EGL14.eglDestroyContext(eglDisplay,   eglContext);  eglContext = EGL14.EGL_NO_CONTEXT;  eglDisplay   = EGL14.EGL_NO_DISPLAY; }  /**  *   渲染到各个eglSurface  */ private void render(){  //   渲染(绘制)  for(GLSurface output:outputSurfaces){if(output.eglSurface==   EGL14.EGL_NO_SURFACE) { if(!makeOutputSurface(output))  continue;}//   设置当前的上下文环境和输出缓冲区EGL14.eglMakeCurrent(eglDisplay, output.eglSurface,   output.eglSurface, eglContext);//   设置视窗大小及位置GLES20.glViewport(output.viewport.x, output.viewport.y,   output.viewport.width, output.viewport.height);// 绘制onDrawFrame(output);//   交换显存(将surface显存和显示器的显存交换)EGL14.eglSwapBuffers(eglDisplay,   output.eglSurface);  } }  @Override public void   run() {  Event event;   Log.d(TAG,getName()+":   render   create");  createGL();//创建display,config,context,现在是没有surface的,只是将contxt联系起来  onCreated();//得到program,postion,color和顶点的buffer  //   渲染  while(!isRelease){try { event = eventQueue.take(); switch(event.event){  case   Event.ADD_SURFACE: {// 创建eglSurfaceGLSurface surface =   (GLSurface)event.param;Log.d(TAG,"add:"+surface);makeOutputSurface(surface);outputSurfaces.add(surface);break;  }  case   Event.REMOVE_SURFACE: {GLSurface surface =   (GLSurface)event.param;Log.d(TAG,"remove:"+surface);EGL14.eglDestroySurface(eglDisplay,   surface.eglSurface);outputSurfaces.remove(surface); break;  }  case   Event.START_RENDER:rendering = true;break;  case Event.REQ_RENDER:   // 渲染if(rendering) { Log.d(TAG, "run:   rendering"); onUpdate(); render(); //   如果surface缓存没有释放(被消费)那么这里将卡住}break;  case   Event.STOP_RENDER:rendering = false;break;  case   Event.RUNNABLE:((Runnable)event.param).run();break;  case   Event.RELEASE:isRelease =   true;break;  default:Log.e(TAG,"event error:   "+event);break; }} catch (InterruptedException e)   { e.printStackTrace();}  }  //   回调  onDestroy();  //   销毁eglSurface  for(GLSurface   outputSurface:outputSurfaces){EGL14.eglDestroySurface(eglDisplay,   outputSurface.eglSurface);outputSurface.eglSurface =   EGL14.EGL_NO_SURFACE;  }  destroyGL();  eventQueue.clear();  Log.d(TAG,getName()+":   render release"); }  /**  *   退出OpenGL渲染并释放资源  *   这里先将渲染器释放(renderer)再退出looper,因为renderer里面可能持有这个looper的handler,  *   先退出looper再释放renderer可能会报一些警告信息(sending message to a Handler on a dead   thread)  */ public void   release(){  if(eventQueue.offer(new Event(Event.RELEASE))){//   等待线程结束,如果不等待,在快速开关的时候可能会导致资源竞争(如竞争摄像头)// 但这样操作可能会引起界面卡顿,择优取舍while   (isAlive()){ try {  this.join(1000); } catch   (InterruptedException e)   {  e.printStackTrace(); }}  } }  /**  *   当创建完基本的OpenGL环境后调用此方法,可以在这里初始化纹理之类的东西  */ public abstract void   onCreated();  /**  *   在渲染之前调用,用于更新纹理数据。渲染一帧调用一次  */ public abstract void   onUpdate();  /**  *   绘制渲染,每次绘制都会调用,一帧数据可能调用多次(不同是输出缓存)  * @param outputSurface   输出缓存位置surface  */ public abstract void onDrawFrame(GLSurface   outputSurface);  /**  *   当渲染器销毁前调用,用户回收释放资源  */ public abstract void   onDestroy();   private static String getEGLErrorString()   {  return GLUtils.getEGLErrorString(EGL14.eglGetError()); }  private   static class Event {  static final int ADD_SURFACE = 1; //   添加输出的surface  static final int REMOVE_SURFACE = 2; //   移除输出的surface  static final int START_RENDER = 3; //   开始渲染  static final int REQ_RENDER = 4; // 请求渲染  static   final int STOP_RENDER = 5; // 结束渲染  static final int RUNNABLE = 6;   //  static final int RELEASE = 7; // 释放渲染器   final   int event;  Object param;   Event(int event)   {this.event =   event;  } }   }</glsurface></event>
      离屏渲染关键部分就是在makeOutputSurface()方法中的GLSurface.TYPE_PBUFFER_SURFACE这个case里面
    其实// 交换显存(将surface显存和显示器的显存交换)
    EGL14.eglSwapBuffers(eglDisplay,   output.eglSurface);对于离屏渲染是没有用的,因为本来就没有显示屏幕,于是我们可以这样:
      ?123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102package   com.example.asus1.camerawithopengl; import   android.opengl.GLES20; import java.nio.ByteBuffer;import   java.nio.ByteOrder;import java.nio.FloatBuffer; public class   TestRenderer extends GLRenderer1 {  private static final String TAG   = "TestRenderer"; private int program; private int   vPosition; private int uColor;  private FloatBuffer   vertices;   /**  * 获取图形的顶点  *   特别提示:由于不同平台字节顺序不同数据单元不是字节的一定要经过ByteBuffer  *   转换,关键是要通过ByteOrder设置nativeOrder(),否则有可能会出问题  *  * @return   顶点Buffer  */ private FloatBuffer getVertices()   {  float vertices[] = { 0.0f,0.5f, -0.5f,   -0.5f, 0.5f,  -0.5f,  };   //   创建顶点坐标数据缓冲  //   vertices.length*4是因为一个float占四个字节  ByteBuffer vbb =   ByteBuffer.allocateDirect(vertices.length *   4);  vbb.order(ByteOrder.nativeOrder());   //设置字节顺序  FloatBuffer vertexBuf = vbb.asFloatBuffer();   //转换为Float型缓冲  vertexBuf.put(vertices);//向缓冲区中放入顶点坐标数据  vertexBuf.position(0);    //设置缓冲区起始位置   return vertexBuf; }    @Override public   void onCreated() {   program =   ShaderUtil.createProgram(verticesShader,fragmentShader);  vPosition   =   GLES20.glGetAttribLocation(program,"vPosition");  uColor   = GLES20.glGetUniformLocation(program,"uColor");   vertices   = getVertices();  }  @Override public void   onUpdate() {  }  @Override public void   onDrawFrame(GLSurface outputSurface) {// 设置clear color颜色RGBA(这里仅仅是设置清屏时GLES20.glClear()用的颜色值而不是执行清屏)  GLES20.glClearColor(1.0f,   0, 0, 1.0f);   //   清除深度缓冲与颜色缓冲(清屏,否则会出现绘制之外的区域花屏)  GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT   | GLES20.GL_COLOR_BUFFER_BIT);  // 使用某套shader程序  GLES20.glUseProgram(program);  //   为画笔指定顶点位置数据(vPosition)  GLES20.glVertexAttribPointer(vPosition, 2,   GLES20.GL_FLOAT, false, 0, vertices);  //   允许顶点位置数据数组  GLES20.glEnableVertexAttribArray(vPosition);  //   设置属性uColor(颜色 索引,R,G,B,A)  GLES20.glUniform4f(uColor, 0.0f, 1.0f,   0.0f, 1.0f);  //   绘制  GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0,   3);  }  @Override public void onDestroy() {  }  //   顶点着色器的脚本 private static final String verticesShader= "attribute   vec2 vPosition;\n" // 顶点位置属性vPosition+ "void main(){ \n"+   "gl_Position = vec4(vPosition,0,1);\n" // 确定顶点位置+ "}";  //   片元着色器的脚本 private static final String fragmentShader= "precision   mediump float;\n" // 声明float类型的精度为中等(精度越高越耗资源)+ "uniform vec4   uColor; \n" // uniform的属性uColor+ "void main(){\n"+   "gl_FragColor = uColor;  \n" // 给此片元的填充色+   "}"; }
    在activity中
      ?123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293  @Override protected   void onCreate(Bundle savedInstanceState)   {  super.onCreate(savedInstanceState);   setContentView(R.layout.activity_main);  final   SurfaceView surfaceView = (SurfaceView)findViewById(R.id.surface);  mImageView   = (ImageView)findViewById(R.id.image);  glRenderer = new   TestRenderer();  GLSurface glPbufferSurface = new   GLSurface(512,512);  glRenderer.addSurface(glPbufferSurface);  glRenderer.startRender();  glRenderer.requestRender();   glRenderer.postRunnable(new   Runnable() {@Overridepublic void run() { IntBuffer i =   IntBuffer.allocate(512*512); GLES20.glReadPixels(0, 0, 512, 512,   GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, i);  final Bitmap bitmap =   frameToBitmap(512,512,i); new Handler(Looper.getMainLooper()).post(new   Runnable() {  @Override  public void run()   {mImageView.setImageBitmap(bitmap);  } });}  });  surfaceView.getHolder().addCallback(new   SurfaceHolder.Callback() {@Overridepublic void surfaceCreated(SurfaceHolder   holder) { } @Overridepublic void surfaceChanged(SurfaceHolder   holder, int format, int width, int height) { //   glRenderer.render(holder.getSurface(),width,height); GLSurface   glWindowSurface = new   GLSurface(holder.getSurface(),width,height); glRenderer.addSurface(glWindowSurface); glRenderer.requestRender();  } @Overridepublic   void surfaceDestroyed(SurfaceHolder holder)   { }  });    }  @Override protected   void onDestroy() { //   mPreview.destoryCamera();  glRenderer.release();  glRenderer   = null;  super.onDestroy();  }  /**  *   将数据转换成bitmap(OpenGL和Android的Bitmap色彩空间不一致,这里需要做转换)  *  *   @param width 图像宽度  * @param height 图像高度  * @param ib   图像数据  * @return bitmap  */ private static Bitmap   frameToBitmap(int width, int height, IntBuffer ib) {  int pixs[] =   ib.array();  // 扫描转置(OpenGl:左上->右下   Bitmap:左下->右上)  for (int y = 0; y < height / 2; y++) {for   (int x = 0; x < width; x++) { int pos1 = y * width + x; int pos2   = (height - 1 - y) * width + x;  int tmp =   pixs[pos1]; pixs[pos1] = (pixs[pos2] & 0xFF00FF00) | ((pixs[pos2]   >> 16) & 0xff) | ((pixs[pos2] << 16) & 0x00ff0000); //   ABGR->ARGB pixs[pos2] = (tmp & 0xFF00FF00) | ((tmp >> 16)   & 0xff) | ((tmp << 16) &   0x00ff0000);}  }  if (height % 2 == 1) { // 中间一行for (int   x = 0; x < width; x++) { int pos = (height / 2 + 1) * width +   x; pixs[pos] = (pixs[pos] & 0xFF00FF00) | ((pixs[pos] >> 16)   & 0xff) | ((pixs[pos] << 16) &   0x00ff0000);}  }   return Bitmap.createBitmap(pixs,   width, height, Bitmap.Config.ARGB_8888); }
      我们可以看到我们其实进行了两次渲染,第一次是离屏,第二次使用的是window_surface。为了看到离屏的效果,我们必须从OpenGL的线程中取出像素数据,然后转换成Bitmap设置给ImageView。
    然后就能看到两个三角形了,顶部的小三角形就是在后台渲染的图像了
   
    知识补充
   
     Display
    EGL提供了图形API如OpenGL ES和原生窗口系统如Linux 的X Window之间的一个结合层次,在EGL能够确定可用的Surface类型之前,它必须打开和窗口系统的通信渠道,因为是跨平台的,每个窗口系统都有不同的遗言,所以EGL提供基本的不透明类型的EGLDisplai,该类型封装了所有系统的相关属性,用于原生窗口的系统接口,不同的窗口系统定义了不同的DIsplay属性,最终都会Native化。任何使用EGL的应用程序必须执行的第一个操作是创建与初始化与本地EGL   Display的连接:
    ?12eglGetDisplay(EGLNativeDisplayType display_id);eglInitialize(EGLDisplay   dpy, EGLint *major, EGLint *minor)
    eglGetDisplay用于获取EGL   DIsplay连接,display_id指定需要连接的Display,一般取默认值EGL_DEFAULT_DISPLAY
      eglInitialize用于对Display进行初始化,初始化已经初始化了Display对这个Display没有影响,major和minor获取当前的EGL版本号
   
     #配置Surface
      EGL初始化Display完成后,就可以对Surface进行配置了,一种是查询每个Surface配置,找出最好的选择,另一种是指定一组需求,让EGL推荐最佳匹配,两者都返回一个EGLConfig,包括Surface相关的所有属性。
    ?1234567eglGetConfigs(EGLDisplay dpy, EGLConfig *configs, EGLint   config_size, EGLint *num_config); eglGetConfigAttrib(EGLDisplay dpy,   EGLConfig config,EGLint attribute, EGLint   *value); eglChooseConfig(EGLDisplay dpy, const EGLint   *attrib_list,EGLConfig *configs, EGLint config_size,EGLint   *num_config);
    eglGetConfigs用于获取Display的frame   buffer配置列表,dpy为对应的Display,configs返回配置列表(可以为NULL而只是通过num_config获取配置列表可用的配置条目),size指定配置列表的大小(size大于1时configs需要有足够的存储空间),num_config返回配置列表获取的配置条目。
    eglGetConfigAttrib用于获取EGL的frame   buffer配置列表中某个具体属性的值,dpy为对应的Display,config为待查询的配置列表,attribute为待查询的具体属性,value返回查询的属性值,
    eglChooseConfig用于获取Display的frame   buffer配置列表,不过这个配置列表要与指定的属性列表attrib_list匹配,dpy为对应的Display,attrib_list为config需要匹配的属性列表,configs返回配置列表(非NULL时,size为最大值,num_configs为实际值,为NULL时,忽略size),size指定配置列表的大小(size大于1时configs需要有足够的存储空间),num_config返回配置列表获取的配置条目
   
     创建Surface
    Window Surface:可以理解为EGL窗口,是屏幕上的渲染区域,有eglCreateWindowSurface创建
    ?123eglCreateWindowSurface(EGLDisplay dpy, EGLConfig   config,EGLNativeWindowType win,const EGLint *attrib_list);
    eglCreateWindowSurface用于创建Window Surface,dpy为对应的EGL Display连接,config为EGL   frame buffer配置,定义了可用于Surface的frame buffer资源,win为Native   Window,是个平台相关的类型,attrib_list为Window Surface属性列表,可以为NULL。
    ?1eglDestroySurface(EGLDisplay dpy, EGLSurface surface
    用于销毁surface,dpy为对应的Display,surface为将要销毁的Surface,如果任何其它线程都没有使用这个Surface时,Surface将被立即销毁,否则要等到这个Surface不被任何线程使用时才销毁,另外,对于一个PBuffer   Surface来说,其资源要等到绑定到纹理对象的颜色缓冲区释放后才被销毁。
    ?12glQuerySurface(EGLDisplay dpy, EGLSurface surface,EGLint attribute,   EGLint *value);
    于获取Surface信息,dpy为对应的Display,surface待查询的Surface,attribute为待查询的Surface属性,value用于返回Surface属性值
    PBuffer Surface:称作PBuffer(像素缓冲区Pixel   Buffer的缩写)的不可见屏幕外表面,和窗口一样,PBuffer可以利用OpenGL ES   3.0中的任何硬件加速,PBuffer最常用于生成纹理贴图,如果想要做的是渲染到一个纹理,那么建议使用帧缓冲区对象(FBO)代替PBuffer,因为帧缓冲区更高效,不过在某些FBO无法使用的情况下,PBuffer仍然有用,
    ?12glCreatePbufferSurface(EGLDisplay dpy, EGLConfig config, const   EGLint *attrib_list);
    eglCreatePbufferSurface用于创建off-screen的pixel buffer   Surface,dpy为对应的Display,config为frame buffer配置,attrib_list为PBuffer属性列表,可以为NULL
    Pixmap Surface:
    ?123eglCreatePixmapSurface(EGLDisplay dpy, EGLConfig   config,EGLNativePixmapType pixmap,const EGLint *attrib_list);
    用于创建off-screen的Pixmap Surface,dpy为对应的Display,config为frame   buffer配置,pixmap为Native Pixmap,attrib_list为Pixmap属性列表,可以为NULL
    三种Surface的区别
    window是on-screen的,
    pbuffer和pixmap是off-screen的, window绑定到了NativeWindow
    ,pixmap绑定到了NativePixmap,
    pbuffer没有任何本地绑定 window是双缓冲区的
    pbuffer和pixmap是单缓冲区的, window默认在back buffer渲染,通过eglSwapBuffers交换到屏幕上显示,
    pbuffer在显存中分配,由EGL_HEIGHT和EGL_WIDTH指定大小,常用作纹理数据,
    pixmap绑定到本地的像素缓冲区,这个缓冲区可以被其它API使用。   创建不同的EGLSurface时,需要在EGLConfig中配置EGL_SURFACE_TYPE,window、pbuffer、pixmap分别对应于EGL_WINDOW_BIT、EGL_PBUFFER_BIT、EGL_PIXMAP_BUFFER。
   
    ?1eglSwapBuffers——
    对于Window Surface或back buffer来说,还需要通过eglSwapBuffers把off-sreen的back   buffer交换到screen buffer,也就是说把EGL Surface的颜色缓冲区post到Native   Window,内部调用了渲染API的Flush命令,返回EGL_FALSE时可能的原因为EGL_BAD_DISPLAY、EGL_NOT_INITIALIZED、EGL_BAD_SURFACE、EGL_CONTEXT_LOST。
    eglSwapBuffers对PBuffer Surface和Pixmap Surface无效。
    创建Context
    ?123eglCreateContext(EGLDisplay dpy, EGLConfig config, EGLContext   share_context, const EGLint *attrib_list);
    eglCreateContext用于创建EGL渲染Context,dpy为对应的Display,config为frame   buffer配置,share_context为其它的共享Context,可以设置为EGL_NO_CONTEXT,attrib_list为Context属性列表,成功时返回新创建的EGLContext,失败时返回EGL_NO_CONTEXT
    ttrib_list属性目前只有EGL_CONTEXT_CLIENT_VERSION,整数值,指定OpenGL   ES版本,默认值为1,还可以是2、3等其它版本值,创建OpenGL ES   Context时设置这个属性,也就是说渲染API为EGL_OPENGL_ES_API时才设置这个属性
    ?12eglMakeCurrent(EGLDisplay dpy, EGLSurface draw,  EGLSurface   read, EGLContext ctx);
      创建了Surface和Context之后,因为可能有多个Surface和Context,所以需要通过eglMakeCurrent绑定Context到Surface,dpy为对应的Display,draw用于绘制,read用于读,ctx为要绑定的Context    

本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注移动开发之Android频道!

本文由 @凌雪 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程