gl_BaryCoordEXT Barycentric Coordinates in GLSL

The gl_BaryCoordEXT built-in variable provides fragment shaders with direct access to the perspective-correct barycentric coordinates of a primitive without requiring manual calculation or data passing from earlier pipeline stages. Introduced through the GL_EXT_fragment_shader_barycentric extension, this feature simplifies techniques such as single-pass wireframe rendering, custom attribute interpolation, and analytical antialiasing by exposing the location of the current fragment relative to the primitive's vertices.

Extension Requirement and Declaration

To use gl_BaryCoordEXT, the fragment shader must first enable the corresponding extension:

#version 450
#extension GL_EXT_fragment_shader_barycentric : require

Once enabled, the OpenGL Shading Language (GLSL) runtime exposes gl_BaryCoordEXT as a built-in input variable.

Data Representation and Coordinate Meaning

The variable is defined as a 3-component floating-point vector:

in vec3 gl_BaryCoordEXT;

For a triangular primitive with vertices \(V_0\), \(V_1\), and \(V_2\), gl_BaryCoordEXT represents the weights \((u, v, w)\) of the fragment's position relative to those vertices. These coordinates satisfy the relation:

\[u + v + w = 1.0\]

The component mapping corresponds directly to the primitive's provoking vertices:

  • gl_BaryCoordEXT.x represents the weight associated with \(V_0\). At vertex \(V_0\), this value is 1.0, while at \(V_1\) and \(V_2\) it is 0.0.
  • gl_BaryCoordEXT.y represents the weight associated with \(V_1\).
  • gl_BaryCoordEXT.z represents the weight associated with \(V_2\).

Perspective Interpolation vs. Linear Interpolation

gl_BaryCoordEXT produces perspective-correct barycentric coordinates. During rasterization, the hardware performs perspective-correct interpolation by dividing the weights by the clip space \(w\)-component before fragment evaluation.

If screen-space, linear interpolation without perspective correction is needed, the extension also provides the companion built-in variable:

in vec3 gl_BaryCoordNoPerspEXT;

This alternative calculates barycentric weights in screen space, which is beneficial for screen-aligned effects or line rendering where perspective foreshortening is undesirable.

Accessing Explicit Per-Vertex Attributes

In addition to gl_BaryCoordEXT, the extension allows input attributes to be qualified with the pervertexEXT keyword. Instead of receiving automatically interpolated values, the fragment shader receives arrays of size 3 containing the raw, un-interpolated values from each vertex:

layout(location = 0) in pervertexEXT vec4 inColor[];

Using gl_BaryCoordEXT alongside pervertexEXT, you can manually interpolate vertex attributes using custom mathematical curves or formulas:

vec4 interpolatedColor = inColor[0] * gl_BaryCoordEXT.x +
                         inColor[1] * gl_BaryCoordEXT.y +
                         inColor[2] * gl_BaryCoordEXT.z;

Practical Application: Wireframe Rendering

A primary use case for gl_BaryCoordEXT is generating wireframes within a single rendering pass without using a geometry shader. The minimum component of gl_BaryCoordEXT indicates the shortest normalized distance to the nearest triangle edge:

layout(location = 0) out vec4 fragColor;

void main() {
    // Find the minimum distance to an edge
    float minBary = min(min(gl_BaryCoordEXT.x, gl_BaryCoordEXT.y), gl_BaryCoordEXT.z);
    
    // Compute edge width using screen-space derivatives
    vec3 dEdge = fwidth(gl_BaryCoordEXT);
    vec3 edgeDist = smoothstep(vec3(0.0), dEdge * 1.5, gl_BaryCoordEXT);
    float edgeFactor = min(min(edgeDist.x, edgeDist.y), edgeDist.z);

    vec4 surfaceColor = vec4(0.8, 0.8, 0.8, 1.0);
    vec4 wireColor = vec4(0.0, 0.0, 0.0, 1.0);

    fragColor = mix(wireColor, surfaceColor, edgeFactor);
}

By querying gl_BaryCoordEXT, the GPU avoids the memory bandwidth and geometry shader overhead traditionally required to assign and forward vertex identifiers or coordinate attributes down the rendering pipeline.