GLSL ES 3.00 Features in WebGL 2.0 Explained

WebGL 2.0 brought modern GPU programming to the web primarily through its adoption of the OpenGL ES 3.0 API and its companion shading language, GLSL ES 3.00. Moving away from the legacy constraints of GLSL ES 1.00 used in WebGL 1.0, this upgrade introduced native integer support, enhanced data structures, structured memory blocks, and powerful new texture-sampling methods. This article breaks down the major shader features, syntax modernizations, and pipeline capabilities unlocked by this standard.

Updated Syntax and Variable Qualifiers

GLSL ES 3.00 shaders must begin with the #version 300 es directive. The update replaced deprecated qualifiers to unify input and output semantics:

  • in and out Qualifiers: Replaced the older attribute and varying keywords. Vertex shader inputs are marked with in, vertex outputs/fragment inputs with out and in respectively, and fragment shader outputs with out.
  • Explicit Attribute Locations: Developers can use layout qualifiers such as layout(location = 0) in vec3 aPosition; to bind attribute indices directly within shader code, reducing the need for CPU-side bindings.

Full Integer and Bitwise Support

GLSL ES 1.00 lacked robust integer handling, forcing developers to simulate bitwise operations and integer logic using floating-point math. GLSL ES 3.00 introduced:

  • True Signed and Unsigned Integers: Native int and uint types along with their corresponding vector formats (ivec, uvec).
  • Bitwise Operations: Standard bitwise operators (&, |, ^, ~, <<, >>) for efficient masking, bit-packing, hashing, and procedural generation directly on the GPU.

Uniform Buffer Objects (UBOs)

In WebGL 1.0, uniforms had to be updated independently per shader program. GLSL ES 3.00 added uniform blocks, enabling the use of Uniform Buffer Objects (UBOs):

  • Data Sharing: Large sets of uniforms (such as camera matrices or lighting parameters) can be grouped into single buffers and shared across multiple shader programs without redundant re-binding.
  • Faster CPU-to-GPU Transfers: Uniform data can be updated in contiguous memory blocks, reducing CPU overhead and draw call latency.

Multiple Render Targets (MRT)

GLSL ES 3.00 natively integrates Multiple Render Targets into the core standard, removing the need for browser-dependent extensions. Fragment shaders can output to multiple textures simultaneously by defining multiple out variables tied to specific attachment locations:

layout(location = 0) out vec4 outColor;
layout(location = 1) out vec4 outNormal;
layout(location = 2) out vec4 outDepth;

This capability is foundational for modern rendering pipelines, particularly deferred shading and screen-space ambient occlusion (SSAO).

Advanced Texture Querying and Formats

The standard overhauled how shaders interact with textures, replacing specific functions like texture2D and textureCube with overloaded texture() functions:

  • texelFetch(): Allows fetching raw texels directly by integer coordinates without filtering or normalized coordinates, behaving like a standard array lookup.
  • textureSize(): Enables shaders to query the dimensions of a texture directly, eliminating the need to pass image dimensions as uniforms.
  • 3D and Array Textures: Introduced samplers such as sampler3D, sampler2DArray, and comparison samplers for hardware-accelerated depth mapping (sampler2DShadow).

Expanded Matrix Operations and Standard Library

The standard expanded linear algebra functionality and flow control:

  • Non-Square Matrices: Full support for mat2x3, mat3x2, mat2x4, mat4x2, mat3x4, and mat4x3.
  • Built-in Matrix Functions: Native support for transpose() and inverse().
  • Flow Control: Support for switch statements, dynamic loop indexing, and extended built-in numerical functions, making complex shader logic simpler to write and maintain.