GLSL 1.20 to GLSL 1.30: Keywords and Matrix Types

The transition from GLSL 1.20 (introduced with OpenGL 2.1) to GLSL 1.30 (aligned with OpenGL 3.0) marked a pivotal shift in modern programmable shading. This update stripped away legacy fixed-function baggage by modernizing storage qualifiers into a unified in/out syntax and refining how shaders declare, manipulate, and transform matrix data. By replacing rigid, stage-specific keywords and deprecating built-in state matrices, GLSL 1.30 established the foundational architecture still used in modern graphics programming today.

Modernizing Keyword Structures: The Shift to in and out

In GLSL 1.20, data flow between pipeline stages relied on distinct, stage-dependent keywords. Inputs to a vertex shader were declared using attribute, while communication between the vertex shader and fragment shader required both stages to declare the same variable as varying. Outputs from the fragment shader typically relied on built-in global variables such as gl_FragColor or gl_FragData[n].

GLSL 1.30 unified and streamlined this model:

  • Unified Qualifiers: The attribute and varying keywords were deprecated. In their place, GLSL 1.30 introduced generic in and out qualifiers. Vertex shader inputs became in, vertex shader outputs became out, fragment shader inputs became in, and fragment shader outputs became user-defined out variables.
  • Pipeline Symmetry: By using in and out, data passing across any programmable stage became symmetrical and easier to read, paving the way for geometry shaders and future pipeline stages.
  • Explicit Fragment Outputs: Instead of automatically writing to gl_FragColor, GLSL 1.30 encouraged declaring custom output variables (e.g., out vec4 fragColor;), providing direct support for multiple render targets (MRT) without relying on legacy array syntax.
  • Precision and Interpolation Modifiers: GLSL 1.30 formalized interpolation qualifiers such as flat, noperspective, and centroid, which could be prefixed directly to in and out declarations to control how values interpolate across primitives.

Matrix Type Enhancements and Modernization

While non-square matrix types (such as mat2x3, mat3x4, etc.) were introduced late in the GLSL 1.20 specification, GLSL 1.30 fully integrated and matured matrix operations to align with modern hardware requirements.

  • Deprecation of Built-In State Matrices: GLSL 1.20 heavily exposed OpenGL fixed-function matrices, including gl_ModelViewMatrix, gl_ProjectionMatrix, and gl_ModelViewProjectionMatrix. GLSL 1.30 deprecated these built-ins, requiring developers to manage transformations through user-defined uniform matrices. This decoupled shader logic from implicit OpenGL state.
  • Native Matrix Functions: GLSL 1.30 expanded built-in math support by standardizing functions like transpose() and inverse() directly in the shading language. In GLSL 1.20, developers often had to compute matrix transpositions or inversions manually on the CPU or write verbose shader functions.
  • Strict Type Safety and Vector Mapping: GLSL 1.30 strengthened strict type checking for operations involving non-square matrices. Column and row access patterns were formalized, ensuring that multiplying non-square matrices with vectors (e.g., multiplying a mat4x3 by a vec4 to produce a vec3) followed rigid mathematical rules without ambiguous type coercions.

Architectural Impact

The move to GLSL 1.30 transformed shader authoring from an extension of the fixed-function pipeline into a fully explicit, programmable environment. Unifying storage keywords into in and out eliminated semantic confusion across shader stages, while modernizing matrix manipulation ensured that shaders operated independently of driver-managed matrix stacks. These changes set the design template for all subsequent iterations of the OpenGL Shading Language.