How GLSL textureGatherOffset Fetches Texels with Offset

The textureGatherOffset function in GLSL retrieves a four-component vector containing a single color channel from the four texels that would be used in standard bilinear filtering, while shifting the sampling footprint by a specified integer texel offset. This built-in function enables shaders to efficiently sample a \(2 \times 2\) grid of neighboring values in a single operation without performing multiple texture lookups or manual floating-point coordinate arithmetic. By allowing precise integer-based offsets directly in texel space, it simplifies algorithms such as Percentage-Closer Filtering (PCF) for shadows, ambient occlusion, and custom reconstruction filters.

Sampling Footprint and the Integer Offset

When sampling a 2D texture, normalized floating-point coordinates \((u, v)\) locate a point in continuous coordinate space. In standard bilinear filtering, the hardware identifies the four nearest texel centers surrounding this continuous position.

textureGatherOffset alters this process by accepting an ivec2 offset argument alongside the standard texture sampler and coordinate. The offset values represent whole texels and are added directly to the unnormalized texel coordinates before determining the \(2 \times 2\) sampling footprint:

\[\text{Target Texel Base} = \lfloor \text{coord} \times \text{textureSize} - 0.5 \rfloor + \text{offset}\]

Because the offset is applied directly to discrete texel indices, it avoids floating-point precision inaccuracies that can occur when converting normalized UV coordinates back and forth. The hardware enforces compile-time or runtime bounds on this offset, typically constrained within the range \([-8, 7]\) as defined by the implementation's GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET and GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET.

Texel Layout in the Returned Vector

Unlike standard texture lookups that return an interpolated RGBA color, textureGatherOffset samples only one selected color component (specified by the optional comp parameter, defaulting to channel 0 / Red) across all four neighboring texels.

The returned vec4 maps the four neighboring texels in a counter-clockwise order relative to the base texel \((i_0, j_0)\):

  • x component: Texel at \((i_0, j_1)\) — bottom-left in coordinate systems where \(v\) increases downward, or top-left in standard OpenGL UV space where \(v\) increases upward.
  • y component: Texel at \((i_1, j_1)\) — adjacent to x in the positive \(u\) direction.
  • z component: Texel at \((i_1, j_0)\) — adjacent to y in the negative \(v\) direction.
  • w component: Texel at \((i_0, j_0)\) — base texel with the lowest \(u\) and \(v\) indices.

Here, \(i_1 = i_0 + 1\) and \(j_1 = j_0 + 1\).

Example Usage

#version 450 core

uniform sampler2D shadowMap;
in vec2 uvCoords;
out vec4 outTexels;

void main()
{
    // Fetches the red component from a 2x2 grid shifted by (-1, 2) texels
    ivec2 offset = ivec2(-1, 2);
    outTexels = textureGatherOffset(shadowMap, uvCoords, offset, 0);
}

Performance Benefits

Using textureGatherOffset significantly reduces memory bandwidth and instruction overhead. Fetching four discrete texels typically requires four separate texelFetch or texture calls, consuming four texture pipeline cycles. textureGatherOffset executes the retrieval in a single texture fetch instruction, providing direct access to raw neighbor data for fast hardware-accelerated filtering and image processing pipelines.