OpenGL ES is a graphics API with an open specification designed for embedded and mobile systems. It provides hardware-accelerated 2D and 3D graphics rendering through a programmable shader pipeline. According to Khronos Group, 2025, OpenGL ES remains the most widespread graphics API on mobile devices, supporting over 10 billion installations worldwide. The library is used in games, mapping services, AR applications and interfaces on Android and iOS.
Key takeaways
OpenGL ES (Open Graphics Library for Embedded Systems) is a subset of the desktop OpenGL API adapted for mobile devices, game consoles and embedded systems. The specification is developed by the Khronos Group consortium and is available free of charge to all manufacturers. Unlike desktop OpenGL, OpenGL ES removes legacy fixed-function pipeline features, leaving only the programmable shader pipeline — this reduces power consumption and simplifies drivers.
The main application area of OpenGL ES is real-time graphics rendering. The API is used in mobile games (Unity, Unreal Engine), navigation applications, AR solutions based on ARCore and ARKit, as well as in Android and iOS system interfaces. According to StatCounter, 2026, the share of devices with OpenGL ES 3.0+ support exceeds 92% among active smartphones.
The key advantage of OpenGL ES is cross-platform compatibility. The same application written with OpenGL ES can run on Android, iOS, Linux and Windows with minimal changes. This makes the API an optimal choice for projects targeting multiple platforms without rewriting the graphics engine.
Early versions of OpenGL (before 2.0) used a fixed pipeline — a set of predefined vertex and pixel processing stages. The developer could only configure parameters: light source positions, material properties, transformation matrices. Programmable pipeline, introduced in OpenGL ES 2.0, replaced fixed stages with shaders — small programs executed on the GPU. This gave developers full control over geometry rendering.
The transition to a programmable pipeline was a revolution for mobile graphics. Developers gained the ability to implement complex effects: PBR (Physically Based Rendering), dynamic shadows, post-processing and HDR. The fixed pipeline required less code but did not allow creating unique visual styles. Modern mobile games fully operate on the programmable pipeline.
Graphics pipeline of OpenGL ES consists of several sequential stages, each transforming input data along the path from vertices to pixels on the screen. Understanding the pipeline architecture is critical for optimizing rendering performance on mobile devices with limited energy and thermal budgets.
| Pipeline stage | Purpose | Programmable |
|---|---|---|
| Vertex Shader | Vertex transformation, applying model-view-projection matrices | Yes — GLSL |
| Tessellation | Geometry subdivision (ES 3.2 only) | Yes — GLSL |
| Geometry Shader | Generating new geometry from primitives | Yes — GLSL |
| Rasterization | Converting primitives into fragments (pixels) | No — fixed |
| Fragment Shader | Calculating the color of each fragment, texturing, lighting | Yes — GLSL |
| Per-Fragment Operations | Depth test, stencil test, blending, scissor test | Parameter configuration |
The first stage — vertex shader — processes each vertex independently. At this stage transformations are applied: translation from model local space to world space, then to camera space and finally to clip space. Transformations are set via MVP (Model-View-Projection) uniform matrices, which are updated every frame when the camera or objects move.
After primitive assembly (points, lines, triangles), rasterization is performed — the process of determining which screen pixels each primitive covers. Rasterizer generates fragments — potential pixels with interpolated attributes (color, normals, UV coordinates). The number of fragments directly depends on the screen resolution and the projection area of the primitive.
Fragment shader runs for each generated fragment. It calculates the final pixel color taking into account textures, light sources and materials. The fragment shader output goes through a series of per-fragment tests: depth test determines if the fragment is visible; stencil test restricts rendering by mask; blending mixes the fragment color with the color already written in the framebuffer.
OpenGL ES operates as a state machine: all settings — current shader, bound textures, enabled tests — are stored in the global state of the context. Changing state via glEnable, glBindTexture or glUseProgram affects all subsequent draw commands. Each state switch incurs driver overhead, so grouping draw calls by state is the primary optimization technique.
OpenGL ES 1.0 and 1.1 (released in 2003–2004) were based on a fixed pipeline. They supported transformations, texturing, lighting and blending, but did not allow programming shaders. The API was used in early mobile phones and devices based on Symbian and Windows Mobile. Today these versions are considered obsolete — modern devices do not support them.
OpenGL ES 2.0 (2007) introduced a programmable pipeline with vertex and fragment shaders in GLSL ES. This version became the standard for Android 2.2+ and iOS 5+ and is still supported by the vast majority of devices. OpenGL ES 2.0 is the minimum version required for Unity, Unreal Engine and Cocos2d-x on mobile platforms.
OpenGL ES 3.0 (2012) added several critical capabilities: multiple render targets (MRT), transform feedback, instancing, arbitrary format textures via ETC2/EAC. Rendering performance increased by 30–50% compared to version 2.0 by reducing the number of draw calls. OpenGL ES 3.1 (2014) introduced compute shaders and atomic buffer operations — this made it possible to execute not only graphics but also compute tasks on the GPU (post-processing, cloth simulation, physics calculations).
OpenGL ES 3.2 (2015) — the latest version of the specification — added tessellation and geometry shaders, as well as an extended set of float textures and blend modes. Despite the release of the more modern Vulkan in 2016, OpenGL ES 3.2 remains a relevant API due to the huge existing codebase and ease of porting applications between platforms.
| Version | Year | Key capabilities | Compatibility (2026) |
|---|---|---|---|
| 1.x | 2003 | Fixed pipeline, lighting, textures | Obsolete |
| 2.0 | 2007 | Programmable pipeline, GLSL ES | 99% devices |
| 3.0 | 2012 | MRT, instancing, ETC2, transform feedback | 92% devices |
| 3.1 | 2014 | Compute shaders, atomic buffers | 80% devices |
| 3.2 | 2015 | Tessellation, geometry shaders | 65% devices |
GLSL ES (OpenGL Shading Language for Embedded Systems) is a shader programming language based on C syntax with additional types for working with vectors and matrices. Each shader is a program compiled into GPU machine code at application initialization. Unlike CPU code, shaders execute massively in parallel: thousands of vertices or fragments are processed simultaneously.
Vertex shader processes each vertex of the mesh. Its main task is to compute the final vertex position in clip space by multiplying the input position by the MVP matrix. Additionally, the vertex shader can compute normals, UV coordinates, colors and pass them to the fragment shader via varying variables. Each vertex shader invocation works independently, allowing the GPU to process millions of vertices per frame.
// Simple vertex shader for OpenGL ES 3.0
#version 300 es
layout(location = 0) in vec4 a_position;
layout(location = 1) in vec3 a_normal;
layout(location = 2) in vec2 a_texCoord;
uniform mat4 u_mvpMatrix;
out vec3 v_normal;
out vec2 v_texCoord;
void main() {
gl_Position = u_mvpMatrix * a_position;
v_normal = mat3(u_mvpMatrix) * a_normal;
v_texCoord = a_texCoord;
}
Fragment shader determines the color of each pixel on the screen. It receives interpolated varying values from the vertex shader, samples texels from bound textures and applies lighting. For correct lighting, the Phong or Blinn-Phong model is used with diffuse, specular and ambient component calculations. Each fragment shader invocation corresponds to one pixel, so the total number of invocations equals the projection area of the object on screen.
// Simple fragment shader with texture and lighting
#version 300 es
precision mediump float;
in vec3 v_normal;
in vec2 v_texCoord;
uniform sampler2D u_texture;
uniform vec3 u_lightDir;
out vec4 fragColor;
void main() {
vec4 texel = texture(u_texture, v_texCoord);
vec3 normal = normalize(v_normal);
float diffuse = max(dot(normal, u_lightDir), 0.0);
fragColor = vec4(texel.rgb * diffuse, texel.a);
}
In the example above, the fragment shader samples a texel from a 2D texture by UV coordinates, computes diffuse lighting as the dot product of the normal and light direction, and multiplies the texel color by the lighting intensity. Mediump is the recommended precision for fragment shaders on mobile GPUs: it provides sufficient quality at minimal power consumption.
To work with OpenGL ES on Android, you need to create an EGL context — a surface onto which graphics will be rendered. On iOS, the EAGL layer (similar to EGL) provided by the GLKit framework is used. In both cases, the initialization process includes creating a window surface, configuring context attributes and binding to the current rendering thread.
// OpenGL ES 3.0 initialization on Android
class MyGLRenderer : GLSurfaceView.Renderer {
private val vertexShaderCode = "#version 300 es\n..."
private val fragmentShaderCode = "#version 300 es\n..."
override fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) {
GLES30.glClearColor(0.1f, 0.1f, 0.2f, 1.0f)
GLES30.glEnable(GLES30.GL_DEPTH_TEST)
}
override fun onDrawFrame(gl: GL10?) {
GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT or GLES30.GL_DEPTH_BUFFER_BIT)
// bind VBO, set uniforms, draw elements
}
override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) {
GLES30.glViewport(0, 0, width, height)
}
}
After creating the context, the developer needs to set up buffers: the vertex buffer (VBO) contains vertex coordinates, normals and UV; the index buffer (EBO) defines the vertex traversal order for forming triangles. VAO (Vertex Array Object) combines all attribute configurations into one object, reducing the number of API calls when switching meshes.
EGL (Native Platform Graphics Interface) is an intermediate layer between OpenGL ES and the window system. On Android, EGL manages the creation of the rendering surface, framebuffer configuration selection (color depth, stencil, MSAA) and vsync synchronization. A typical configuration requests RGBA8888 with a 24-bit depth buffer and 8-bit stencil buffer. On iOS, the role of EGL is performed by EAGL in conjunction with CAEAGLLayer.
Performance optimization for OpenGL ES on mobile devices includes several key practices. Use instancing (glDrawArraysInstanced) for rendering many identical objects — this reduces the number of draw calls. Apply texture pools and avoid texture switching between draw calls. Sort objects by shader, then by texture, then by mesh — such order minimizes context state switches.
Metal is a low-level graphics API from Apple, available on iOS and macOS starting from the A7 chip. Metal provides direct GPU access with minimal driver overhead, but works only on Apple devices. According to WWDC 2024, Metal delivers up to 40% higher performance compared to OpenGL ES on the same hardware by reducing runtime state checks.
Vulkan is a cross-platform successor to OpenGL ES, developed by Khronos Group. Vulkan uses explicit resource management: the developer allocates memory pools, creates command buffers and synchronizes GPU access. This gives maximum control over performance, but Vulkan initialization code is 3–4 times more verbose than for OpenGL ES. Vulkan is recommended for AAA games and demanding graphics applications on Android 7+.
The choice between OpenGL ES, Metal and Vulkan depends on target platforms and performance requirements. OpenGL ES remains the best choice for cross-platform projects where development speed matters. Metal is preferred for the iOS/macOS ecosystem with maximum performance. Vulkan is the choice for projects where every millisecond per frame matters and the development budget allows investing in low-level optimization.
| Characteristic | OpenGL ES | Metal | Vulkan |
|---|---|---|---|
| Platforms | Android, iOS, Windows, Linux | iOS, macOS only | Android, Windows, Linux (no iOS) |
| API level | High (state machine) | Medium | Low (explicit) |
| Init code | 50–100 lines | 100–200 lines | 300–500 lines |
| Memory control | Automatic | Semi-automatic | Fully manual |
| Performance | Baseline | +20–40% vs ES | +30–60% vs ES |
Frequently asked questions
OpenGL ES is a subset of desktop OpenGL from which legacy fixed-pipeline functions have been removed. OpenGL ES has a smaller specification volume, a simplified precision profile and is optimized for low power consumption of mobile devices.
Android supports OpenGL ES 2.0 on all devices, 3.0 on Android 4.3+, 3.1 on Android 5.0+, 3.2 on selected devices with Android 7.0+. You can check the current support level via EGL_CONFIG_CAVEAT.
GLSL ES (OpenGL Shading Language for Embedded Systems) is a C-like language with vec2/vec3/vec4/mat4 types and built-in functions texture, normalize, dot. For ES 3.0+, the #version 300 es directive is used.
Yes, OpenGL ES remains relevant for cross-platform projects where development speed and broad device support are priorities. For the iOS ecosystem, better to learn Metal; for new projects with maximum performance, use Vulkan.
On Android, call GLES30.glGetString(GLES30.GL_VERSION) after creating the context. The string contains the version number and vendor information. On iOS, use [EAGLContext currentContext] and the API property.
Summary
We will develop a mobile application turnkey
IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.
Read also