OpenGL ES is usually used on Android to display the rendered results on the screen, sample processing, model display, and so on. In this case, you only need to use the Glsurfaceview class and the renderer class provided in the Android API to set/write the appropriate code in the initialization, callback functions provided by these two classes. However, if you do not want the rendering results to be displayed on the screen, which is called off-screen rendering (offscreen render), these two classes will not be helpful. Here's how to do the off-screen rendering of OpenGL ES on an Android system.
1. Basic knowledge
To use OpenGL ES, you typically include the following steps:
(1) EGL initialization
(2) OpenGL es initialization
(3) OpenGL ES settings Options & Drawing
(4) OpenGL es resource release (optional)
(5) EGL resource release
Android-provided Glsurfaceview and renderer automatically complete the (1) (5) Two sections, which only require the developer to do some simple configuration. Additionally (4) This step is optional, as the resources used by OpenGL Es are released as the context is destroyed in the EGL. So only (2) (3) is what developers have to do. This greatly simplifies the development process, but also reduces flexibility, and the use of these two classes is unable to complete offscreen render. To complete the offscreen render is actually very simple, I believe we have guessed, as long as we put (1) ~ (5) to complete their own can be. The next part of the code is mostly C/s + +, with a small portion of Java.
2.EGL initialization
The function of EGL is to glue the OpenGL ES API and the device's current window system together to serve as a bridge for communication. The Windows systems of different devices are ever-changing, but the API provided by OpenGL ES is uniform, so EGL needs to coordinate the Windows system of the current device and OpenGL ES. The following EGL-initialized code is written in C + + and then called by JNI. Android also provides Java interface functions on the Java level.
StaticEGLConfig eglconf;Staticeglsurface Eglsurface;StaticEglcontext Eglctx;StaticEgldisplay Egldisp; Jniexportvoidjnicall java_com_handspeaker_offscreentest_mygles_init (jnienv*Env,jobject obj) { //EGL config attributes ConstEglint confattr[] ={egl_renderable_type, egl_opengl_es2_bit,//very important!Egl_surface_type,egl_pbuffer_bit,//Egl_window_bit Egl_pbuffer_bit We'll 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,8,//If you need the depth bufferEgl_stencil_size,8, Egl_none}; //EGL Context Attributes ConstEglint ctxattr[] ={egl_context_client_version,2,//very important!Egl_none}; //Surface Attributes//The surface size is set to the input frame size ConstEglint surfaceattr[] ={egl_width, +, Egl_height, +, Egl_none}; Eglint Eglmajvers, Eglminvers; Eglint Numconfigs; Egldisp=Eglgetdisplay (Egl_default_display); if(Egldisp = =Egl_no_display) { //Unable to open connection to local windowing systemLogi ("Unable to open connection to local windowing system"); } if(!eglinitialize (Egldisp, &eglmajvers, &eglminvers)) { //Unable to initialize EGL. Handle and RecoverLogi ("Unable to initialize EGL"); } logi ("EGL init with version%d.%d", Eglmajvers, eglminvers); //Choose the first config, i.e. best config if(!eglchooseconfig (Egldisp, confattr, &eglconf,1, &numconfigs)) {Logi ("Some config is wrong"); } Else{Logi ("All configs are OK"); } //Create a Pixelbuffer surfaceEglsurface =eglcreatepbuffersurface (Egldisp, eglconf, surfaceattr); if(Eglsurface = =egl_no_surface) { Switch(Eglgeterror ()) { CaseEgl_bad_alloc://Not enough resources available. Handle and RecoverLogi ("Not enough resources available"); Break; CaseEgl_bad_config://Verify that provided EGLConfig is validLogi ("provided EGLConfig is invalid"); Break; CaseEgl_bad_parameter://Verify the Egl_width and Egl_height are//non-negative ValuesLogi ("provided egl_width and Egl_height is invalid"); Break; CaseEgl_bad_match://Check window and EGLConfig attributes to determine//compatibility and Pbuffer-texture parametersLogi ("Check window and EGLConfig attributes"); Break; }} eglctx=Eglcreatecontext (Egldisp, eglconf, Egl_no_context, ctxattr); if(Eglctx = =Egl_no_context) {Eglint Error=Eglgeterror (); if(Error = =egl_bad_config) { //Handle error and recoverLogi ("Egl_bad_config"); } } if(!eglmakecurrent (Egldisp, Eglsurface, Eglsurface, Eglctx)) {Logi ("Makecurrent failed"); } logi ("Initialize success!");}
The code is long, but most of it is to detect whether the current function call is wrong, the core function only 6, as long as their call is not a problem:
Eglgetdisplay (Egl_default_display)
Eglinitialize (Egldisp, &eglmajvers, &eglminvers)
Eglchooseconfig (Egldisp, Confattr, &eglconf, 1, &numconfigs)
Eglcreatepbuffersurface (Egldisp, eglconf, surfaceattr)
Eglcreatecontext (Egldisp, eglconf, Egl_no_context, ctxattr)
Eglmakecurrent (Egldisp, Eglsurface, Eglsurface, Eglctx)
The specific definitions of the parameters in these functions can be found on this site: https://www.khronos.org/registry/egl/sdk/docs/man/
It should be stated that the &numconfigs parameter of Eglchooseconfig (Egldisp, Confattr, &eglconf, 1, confattr) must have Egl_surface_type,egl_ Pbuffer_bit This configuration, it determines the type of surface to render, whether it is a screen or a memory. In addition, there are some options related to the OpenGL ES version, the specific choice of 1.x or 2.x, depending on the individual situation, I use 2.x.
3.OpenGL es section
To make it easier to explain, I wrote the OpenGL ES sections together, with the following code:
Jniexportvoidjnicall Java_com_handspeaker_offscreentest_mygles_draw (jnienv*Env,jobject obj) { Const Char*vertex_shader=Vertex_shader_fix; Const Char*fragment_shader=fragment_shader_simple; Glpixelstorei (Gl_unpack_alignment,1); Glclearcolor (0.0,0.0,0.0,0.0); Glenable (gl_depth_test); Gldepthfunc (gl_less); Glcullface (Gl_back); Glviewport (0,0, +, +); Gluint VertexShader=Glcreateshader (Gl_vertex_shader); Glshadersource (VertexShader,1,&vertex_shader,null); Glcompileshader (vertexshader); Gluint Fragmentshader=Glcreateshader (Gl_fragment_shader); Glshadersource (Fragmentshader,1,&fragment_shader,null); Glcompileshader (Fragmentshader); Gluint Program=Glcreateprogram (); Glattachshader (program, vertexshader); Glattachshader (program, Fragmentshader); Gllinkprogram (program); Gluseprogram (program); Gluint apositionlocation=glgetattriblocation (Program,"a_position"); Glvertexattribpointer (Apositionlocation,2, Gl_float,gl_false,0, Tableverticeswithtriangles); Glenablevertexattribarray (apositionlocation); //Draw SomethingGlclear (Gl_color_buffer_bit |gl_depth_buffer_bit); Gldrawarrays (Gl_triangles,0,6); Eglswapbuffers (egldisp,eglsurface);}
It is necessary to note that after drawing is completed, the eglswapbuffers (egldisp,eglsurface) function needs to be called because the default setting of the EGL is the double buffering mode, which is used to draw the image, a buffer to display the image, You need to swap two buffers each time it is displayed, to display the image you have drawn.
The two shader required for OpenGL plotting above are not written here and will be available for download in the demo.
4.EGL Resource Release
Finally, there is a function for the EGL resource release, the code is as follows:
void jnicall java_com_handspeaker_offscreentest_mygles_release (jnienv*env,jobject obj) { Eglmakecurrent (Egldisp, Egl_no_surface, Egl_no_surface, egl_no_context); Egldestroycontext (Egldisp, eglctx); Egldestroysurface (Egldisp, eglsurface); Eglterminate (egldisp); = Egl_no_display; = egl_no_surface; = Egl_no_context;}
5. Summary
Done, actually thoroughly understand the OpenGL ES principle, the whole process is very simple. In order to test whether the offscreen render is really done, we save the images in the framebuffer as images to detect the results. The code is as follows:
Rgbabuffer = intbuffer.allocate (512 * 512); Mygles.release (); Mygles.init (); Mygles.draw (); Rgbabuffer.position (0); Gles20.glreadpixels (0, 0, 512, 512, Gles20.gl_rgba,gles20.gl_unsigned_byte,rgbabuffer); int[] Modeldata=Rgbabuffer.array (); int[] Ardata=New int[Modeldata.length]; intOffset1, Offset2; for(inti = 0; I < 512; i++) {Offset1= i * 512; Offset2= (512-i-1) * 512; for(intj = 0; J < 512; J + +) { intTexturepixel = Modeldata[offset1 +J]; intBlue = (Texturepixel >>) & 0xFF; intRed = (Texturepixel <<) & 0x00ff0000; intPixel = (Texturepixel & 0xff00ff00) | Red |Blue; Ardata[offset2+ j] =Pixel; }} Bitmap Modelbitmap= Bitmap.createbitmap (ardata,512,512, Bitmap.Config.ARGB_8888); Savebitmap (MODELBITMAP);
Note that because OpenGL ES framebuffer and image channels are stored in different order, a ABGR to ARGB conversion is required.
In general, offscreen render is used primarily for GPU acceleration, and if your algorithm is computationally intensive, you can convert it into bitmap form as a texture (texture) into GPU memory, and then use the parallel computing power of the GPU to process it. Finally, the calculation results are read into memory using Glreadpixels. Say so much, more usage still need everybody's excavation.
Here is the demo download link
Android OpenGL ES off-screen rendering (offscreen render)