How to Fetch Individual Samples in GLSL sampler2DMS
Multisample antialiasing (MSAA) stores multiple color, depth, or
stencil samples per pixel, requiring dedicated handling in shaders to
access individual subpixel data. In GLSL, multisample textures bound to
samplers such as sampler2DMS bypass standard texture
filtering and cannot be sampled using the traditional
texture() function. Instead, shaders must use the
texelFetch function with non-normalized integer coordinates
and an explicit sample index to retrieve discrete sample points for
custom resolving, edge detection, or deferred shading.
The sampler2DMS
Sampler Type
Standard 2D textures are accessed via sampler2D, which
supports normalized coordinates [0.0, 1.0] and hardware
filtering like bilinear or trilinear interpolation. Multisample textures
do not support standard filtering or mipmaps because each texel holds
multiple distinct color or depth values.
GLSL provides specialized multisample sampler types:
sampler2DMS(floating-point data)isampler2DMS(signed integer data)usampler2DMS(unsigned integer data)sampler2DMSArray(multisample texture arrays)
Retrieving Samples with
texelFetch
To access an individual sample point from a sampler2DMS,
use the built-in texelFetch function overload:
gvec4 texelFetch(gsampler2DMS sampler, ivec2 P, int sample);Parameters:
sampler: The multisample texture uniform (e.g.,uniform sampler2DMS u_TextureMS;).P: The texel coordinate as an integer vector (ivec2). Unliketexture(), which uses normalized coordinates (0.0to1.0),texelFetchexpects non-normalized pixel coordinates ranging from(0, 0)to(width - 1, height - 1).sample: A zero-based integer specifying which subpixel sample to fetch, ranging from0toN - 1, whereNis the total number of samples allocated for the texture (e.g., 2, 4, 8, or 16).
Querying Sample Count Dynamically
To iterate over all samples without hardcoding the sample count in
your shader, GLSL provides the textureSamples function
(available in GLSL 4.0 and later):
int numSamples = textureSamples(u_TextureMS);Example: Manual Resolve in a Fragment Shader
A standard MSAA resolve averages all subpixel samples to produce the
final pixel color. The following fragment shader demonstrates manual
resolving using texelFetch:
#version 330 core
uniform sampler2DMS u_MultisampleTexture;
out vec4 FragColor;
void main()
{
// Convert normalized screen coordinates to integer pixel coordinates
ivec2 texCoord = ivec2(gl_FragCoord.xy);
// Query or define the number of samples
const int NUM_SAMPLES = 4;
vec4 accumulatedColor = vec4(0.0);
// Retrieve and accumulate each sample individually
for (int i = 0; i < NUM_SAMPLES; ++i)
{
vec4 sampleColor = texelFetch(u_MultisampleTexture, texCoord, i);
accumulatedColor += sampleColor;
}
// Compute the box-filter average
FragColor = accumulatedColor / float(NUM_SAMPLES);
}Why Manual Sample Retrieval Matters
Using texelFetch on sampler2DMS gives
graphics pipelines granular control over subpixel data:
- Custom Resolves: Allows non-linear tone mapping, HDR luminance-weighted averaging, or depth-preserving filters that prevent high-luminance aliasing or color bleeding.
- Deferred Shading with MSAA: Enables running expensive lighting calculations once per pixel for non-edge fragments, and per-sample only along silhouette edges where sample colors diverge.
- Depth and Stencil Testing: Permits manual per-sample depth testing in custom post-processing effects and screen-space ambient occlusion (SSAO).