How GLSL textureOffset Samples Adjacent Texels
In GLSL, the textureOffset function enables shaders to
sample neighboring texels by applying an exact, integer-based texel
shift to a base coordinate without requiring manual normalization
calculations. This article explains how textureOffset
operates, why it bypasses the need to convert pixel distances into
fractional normalized values, and the hardware-level constraints
developers must observe when using it.
The Problem with Manual Coordinate Normalization
Standard texture sampling functions, such as texture(),
require coordinates to be normalized within the [0.0, 1.0]
range. When a shader needs to sample adjacent texels—such as during edge
detection, blur passes, or convolution filters—a developer using
texture() must explicitly calculate the size of a single
texel:
vec2 texelSize = 1.0 / vec2(textureSize(u_sampler, 0));
vec4 adjacentSample = texture(u_sampler, uv + vec2(1.0, 0.0) * texelSize);This approach requires knowledge of the texture dimensions (often
queried via textureSize or passed as a uniform) and incurs
extra floating-point arithmetic in the shader pipeline.
How textureOffset
Solves This
The textureOffset function modifies the sampling
location by adding an exact integer texel offset directly to the
coordinate before filtering:
vec4 textureOffset(gsampler2D sampler, vec2 P, ivec2 offset);P: The base texture coordinate (still normalized between0.0and1.0).offset: An integer vector (ivec2,ivec3) representing the number of texels to shift.
When executed, the GPU hardware applies the offset in
unnormalized texel units inside the texture sampling unit. For
example:
// Samples exactly one texel to the right of the base coordinate
vec4 rightTexel = textureOffset(u_sampler, uv, ivec2(1, 0));The shader does not need to know the width or height of the texture, nor does it need to normalize the step size. The underlying hardware automatically accounts for the texture dimensions to locate the adjacent texel.
Hardware Execution and Filtering
Unlike texelFetch, which operates strictly on
unnormalized integer coordinates and disables texture filtering,
textureOffset preserves the sampler’s filtering state:
- Sub-Texel Precision: If the base coordinate
Pfalls between texels, bilinear or trilinear interpolation remains active. The offset simply shifts the interpolation window by the specified number of discrete texels. - Dedicated Hardware Units: Modern graphics hardware contains dedicated address-generation units that can add small integer offsets to sample addresses without generating additional shader ALU instructions.
Constraints on
textureOffset
While textureOffset simplifies neighbor sampling, it
imposes strict operational limitations:
- Compile-Time Constant Offsets: The
offsetargument must be a compile-time constant expression. It cannot be derived from dynamic shader variables or uniform values. - Limited Offset Range: Because hardware texture
address units allocate a limited number of bits for immediate offsets,
the range of the integer offset is constrained. OpenGL guarantees a
minimum range defined by implementation-dependent constants, typically
between
GL_MIN_PROGRAM_TEXEL_OFFSET(-8) andGL_MAX_PROGRAM_TEXEL_OFFSET(+7).
For dynamic offsets or distances exceeding this range, developers
must compute coordinates manually using normalized arithmetic or switch
to unnormalized lookups with texelFetch.