How the GL_ES Macro Differentiates GLSL and GLSL ES
This article explains how the predefined GL_ES
preprocessor macro enables developers to distinguish between OpenGL ES
Shading Language (GLSL ES) and desktop OpenGL Shading Language (GLSL).
It explores the technical definition of the macro, why it is necessary
for cross-platform rendering pipelines, and how to use it effectively to
handle differences in precision qualifiers, version declarations, and
language feature sets.
The Purpose and Definition
of GL_ES
The GL_ES macro is a built-in preprocessor definition
defined by the GLSL compiler. According to the OpenGL ES Shading
Language specification, every compliant GLSL ES implementation must
predefine GL_ES with an integer value of
1.
Conversely, standard desktop GLSL implementations (for desktop
OpenGL) do not define GL_ES. By querying whether this macro
exists at compile time, a single shader source file can determine the
underlying rendering API and conditionally adapt its syntax and
features.
#ifdef GL_ES
// Code compiled specifically for OpenGL ES / WebGL
#else
// Code compiled for desktop OpenGL
#endifKey Differences Resolved by
GL_ES
1. Precision Qualifiers
The most common use case for GL_ES is handling precision
qualifiers (lowp, mediump,
highp). In GLSL ES, fragment shaders do not have a default
precision for floating-point types, meaning compilation fails unless a
default precision is explicitly set.
Desktop GLSL historically did not support precision qualifiers (prior
to GLSL 1.30, and even then they are often ignored). Attempting to
declare default precision in older desktop contexts can cause syntax
errors. Using GL_ES solves this discrepancy:
#ifdef GL_ES
precision mediump float;
precision mediump int;
#endif2. Cross-Platform Shader Sharing (Mobile, Web, Desktop)
Because WebGL is based on OpenGL ES (WebGL 1.0 uses GLSL ES 1.00;
WebGL 2.0 uses GLSL ES 3.00), the GL_ES macro is always
defined in WebGL shaders. Developers targeting mobile devices (Android,
iOS via Metal translation layers), the web, and desktop platforms
(Windows, Linux, macOS) can maintain unified shader files instead of
managing separate forks for desktop and mobile runtimes.
3. Handling Differing Built-in Functions and Extensions
Desktop OpenGL exposes features that are either unavailable or
available only via extensions in OpenGL ES, such as certain texture
sampling modes, geometry shaders, or arbitrary buffer access.
Conditional compilation driven by #ifdef GL_ES allows the
shader to provide fallback algorithms or lower-complexity approximations
on embedded devices while taking advantage of advanced hardware on
desktop GPUs.
Preprocessor Ordering and Caveats
When using GL_ES, the placement relative to the
#version directive is critical:
- The
#versiondirective must always appear as the very first non-whitespace, non-comment line of the shader string. - You cannot use
#ifdef GL_ESto conditionally switch the#versiondirective itself, because#versionmust be evaluated before the preprocessor processes any conditional logic. - In typical multi-target workflows, the host application injects the
correct
#versionstring programmatically before passing the shader source toglShaderSource(). Once the compiler receives the source with an ES version string (such as#version 300 es), it automatically definesGL_ES 1for the rest of the compilation process.