Configure graphics with OpenGL ES
Stay organized with collections
Save and categorize content based on your preferences.
To draw objects and sprites in your game, you will need to configure the display, surface and context variables, set up rendering in your game loop, and draw each scene and object.
There are two ways to draw images to the screen for a C or C++ game, namely with OpenGL ES, or Vulkan.
OpenGL ES is part of the Open Graphics Library (OpenGL®) specification intended for mobile devices such as Android. Learn how to configure OpenGL ES for your game in this topic.
If you use Vulkan for your game, read the Getting started with Vulkan guide.
Before you get started
If you haven't already done so, set up a GameActivity object in your Android project.
Set up OpenGL ES variables
You will need a display, surface, context, and config to render your game. Add the following OpenGL ES variables to your game engine's header file:
classNativeEngine{ //... private: EGLDisplaymEglDisplay; EGLSurfacemEglSurface; EGLContextmEglContext; EGLConfigmEglConfig; boolmHasFocus,mIsVisible,mHasWindow; boolmHasGLObjects; boolmIsFirstFrame; intmSurfWidth,mSurfHeight; }In the constructor for your game engine, initialize the default values for the variables.
NativeEngine::NativeEngine(structandroid_app*app){ //... mEglDisplay=EGL_NO_DISPLAY; mEglSurface=EGL_NO_SURFACE; mEglContext=EGL_NO_CONTEXT; mEglConfig=0; mHasFocus=mIsVisible=mHasWindow=false; mHasGLObjects=false; mIsFirstFrame=true; mSurfWidth=mSurfHeight=0; }Initialize the display to render.
boolNativeEngine::InitDisplay(){ if(mEglDisplay!=EGL_NO_DISPLAY){ returntrue; } mEglDisplay=eglGetDisplay(EGL_DEFAULT_DISPLAY); if(EGL_FALSE==eglInitialize(mEglDisplay,0,0)){ LOGE("NativeEngine: failed to init display, error %d",eglGetError()); returnfalse; } returntrue; }The surface can be an off-screen buffer (pbuffer) allocated by EGL, or a window allocated by the Android OS. Initialize this surface:
boolNativeEngine::InitSurface(){ ASSERT(mEglDisplay!=EGL_NO_DISPLAY); if(mEglSurface!=EGL_NO_SURFACE){ returntrue; } EGLintnumConfigs; constEGLintattribs[]={ EGL_RENDERABLE_TYPE,EGL_OPENGL_ES2_BIT,// request OpenGL ES 2.0 EGL_SURFACE_TYPE,EGL_WINDOW_BIT, EGL_BLUE_SIZE,8, EGL_GREEN_SIZE,8, EGL_RED_SIZE,8, EGL_DEPTH_SIZE,16, EGL_NONE }; // Pick the first EGLConfig that matches. eglChooseConfig(mEglDisplay,attribs,&mEglConfig,1,&numConfigs); mEglSurface=eglCreateWindowSurface(mEglDisplay,mEglConfig,mApp->window, NULL); if(mEglSurface==EGL_NO_SURFACE){ LOGE("Failed to create EGL surface, EGL error %d",eglGetError()); returnfalse; } returntrue; }Initialize the rendering context. This example creates an OpenGL ES 2.0 context:
boolNativeEngine::InitContext(){ ASSERT(mEglDisplay!=EGL_NO_DISPLAY); if(mEglContext!=EGL_NO_CONTEXT){ returntrue; } // OpenGL ES 2.0 EGLintattribList[]={EGL_CONTEXT_CLIENT_VERSION,2,EGL_NONE}; mEglContext=eglCreateContext(mEglDisplay,mEglConfig,NULL,attribList); if(mEglContext==EGL_NO_CONTEXT){ LOGE("Failed to create EGL context, EGL error %d",eglGetError()); returnfalse; } returntrue; }Configure your OpenGL ES settings before drawing. This example is executed at the beginning of every frame. It enables depth testing, sets the clear color to black, and clears the color and depth buffers.
voidNativeEngine::ConfigureOpenGL(){ glClearColor(0.0f,0.0f,0.0f,1.0f); glEnable(GL_DEPTH_TEST); glClear(GL_DEPTH_BUFFER_BIT|GL_COLOR_BUFFER_BIT); }
Render with the game loop
The game loop renders a frame and repeats indefinitely until the user quits. Between frames, your game may:
Process events such as input, audio output, and networking events.
Update the game logic and user interface.
Render a frame to the display.
To render a frame to the display, the
DoFramemethod is called indefinitely in the game loop:voidNativeEngine::GameLoop(){ // Loop indefinitely. while(1){ intevents; structandroid_poll_source*source; // If not animating, block until we get an event. while((ALooper_pollAll(IsAnimating()?0:-1,NULL,&events, (void**)&source))>=0){ // Process events. ... } // Render a frame. if(IsAnimating()){ DoFrame(); } } }In the
DoFramemethod, query the current surface dimensions, requestSceneManagerto render a frame, and swap the display buffers.voidNativeEngine::DoFrame(){ ... // Query the current surface dimension. intwidth,height; eglQuerySurface(mEglDisplay,mEglSurface,EGL_WIDTH,&width); eglQuerySurface(mEglDisplay,mEglSurface,EGL_HEIGHT,&height); // Handle dimension changes. SceneManager*mgr=SceneManager::GetInstance(); if(width!=mSurfWidth||height!=mSurfHeight){ mSurfWidth=width; mSurfHeight=height; mgr->SetScreenSize(mSurfWidth,mSurfHeight); glViewport(0,0,mSurfWidth,mSurfHeight); } ... // Render scenes and objects. mgr->DoFrame(); // Swap buffers. if(EGL_FALSE==eglSwapBuffers(mEglDisplay,mEglSurface)){ HandleEglError(eglGetError()); } }
Render scenes and objects
The game loop processes a hierarchy of visible scenes and objects to render. In the Endless Tunnel example, a
SceneManagerkeeps track of multiple scenes, with only one scene active at a time. In this example, the current scene is rendered:voidSceneManager::DoFrame(){ if(mSceneToInstall){ InstallScene(mSceneToInstall); mSceneToInstall=NULL; } if(mHasGraphics && mCurScene){ mCurScene->DoFrame(); } }Depending on your game, a scene may contain background, text, sprites and game objects. Render them in the order suitable for your game. This example renders the background, text, and widgets:
voidUiScene::DoFrame(){ // clear screen glClearColor(0.0f,0.0f,0.0f,1.0f); glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); glDisable(GL_DEPTH_TEST); RenderBackground(); // Render the "Please Wait" sign and do nothing else if(mWaitScreen){ SceneManager*mgr=SceneManager::GetInstance(); mTextRenderer->SetFontScale(WAIT_SIGN_SCALE); mTextRenderer->SetColor(1.0f,1.0f,1.0f); mTextRenderer->RenderText(S_PLEASE_WAIT,mgr->GetScreenAspect()*0.5f, 0.5f); glEnable(GL_DEPTH_TEST); return; } // Render all the widgets. for(inti=0;i < mWidgetCount;++i){ mWidgets[i]->Render(mTrivialShader,mTextRenderer,mShapeRenderer, (mFocusWidget < 0)?UiWidget::FOCUS_NOT_APPLICABLE: (mFocusWidget==i)?UiWidget::FOCUS_YES:UiWidget::FOCUS_NO,tf); } glEnable(GL_DEPTH_TEST); }
Resources
Read the following for more information about OpenGL ES and Vulkan:
OpenGL ES - Images and graphics in Android.
OpenGL ES - Overview in Android Source.
Vulkan - Getting started in NDK.
Vulkan - Overview in Android Source.
Understand Android game loops - learn to pace frames, queue buffers, handle VSYNC callbacks, and manage threads.