How Does gl_PointCoord Map Textures in GLSL?
In OpenGL and WebGL, gl_PointCoord is a built-in
fragment shader variable that provides two-dimensional texture
coordinates across the surface of an individual point primitive rendered
as a point sprite. When rendering large volumes of particles or
billboards, passing single vertices configured with
gl_PointSize allows the graphics pipeline to expand each
point into a screen-aligned square during rasterization. This article
explores how gl_PointCoord operates within this rasterized
space, how to use it to sample 2D textures directly onto points, and how
it enables procedural shaping and masking for particle systems.
Understanding Point Sprites and Rasterization
Point sprites represent a lightweight alternative to traditional quad
meshes when rendering hundreds or thousands of uniform visual elements
like sparks, dust, or stars. Instead of submitting four vertices and two
triangles per particle, the application submits a single vertex. In the
vertex shader, assigning a value to the built-in variable
gl_PointSize defines the width and height of the quad (in
pixels) that the GPU will generate on the screen around that vertex.
During rasterization, the GPU breaks down each square point into
individual fragments. Because a point originates from a single vertex,
standard interpolated vertex attributes (like custom UV coordinates)
cannot provide a gradient across the point’s generated area.
gl_PointCoord solves this by automatically generating a
local coordinate for every fragment inside the point square.
The Coordinate Space of gl_PointCoord
The value of gl_PointCoord is a vec2 that
ranges from 0.0 to 1.0 along both the X and Y
axes.
- The top-left corner of the point sprite corresponds to
vec2(0.0, 0.0). - The bottom-right corner corresponds to
vec2(1.0, 1.0). - The center of the point sprite corresponds to
vec2(0.5, 0.5).
Because the vertical axis starts at 0.0 at the top and
increases downward to 1.0, the coordinate system matches
standard image file row storage and window coordinate conventions rather
than the standard Cartesian coordinate system (where Y increases
upward).
Sampling Textures with gl_PointCoord
To apply an image or sprite texture to each point, pass
gl_PointCoord as the UV coordinate parameter to texture
sampling functions in the fragment shader.
#version 300 es
precision mediump float;
uniform sampler2D u_particleTexture;
out vec4 fragColor;
void main() {
// Sample the texture using the generated point coordinates
vec4 texColor = texture(u_particleTexture, gl_PointCoord);
// Discard transparent fragments to prevent depth write issues
if (texColor.a < 0.1) {
discard;
}
fragColor = texColor;
}In this setup, each generated point sprite displays the full texture
mapped across its rasterized square, regardless of the screen size
determined by gl_PointSize.
Procedural Masking and Circular Points
In addition to texture sampling, gl_PointCoord is widely
used to create procedural shapes directly in the shader without loading
image files. For example, rendering smooth, circular particles requires
masking out fragments outside a specific radius from the center.
#version 300 es
precision mediump float;
uniform vec4 u_color;
out vec4 fragColor;
void main() {
// Calculate distance from the center (0.5, 0.5)
vec2 centerOffset = gl_PointCoord - vec2(0.5);
float dist = length(centerOffset);
// Discard fragments outside the circle radius (0.5)
if (dist > 0.5) {
discard;
}
// Optional: anti-alias the edge using smoothstep
float alpha = smoothstep(0.5, 0.45, dist);
fragColor = vec4(u_color.rgb, u_color.a * alpha);
}Key Considerations and Limitations
While gl_PointCoord offers high rendering performance,
there are specific behaviors to keep in mind:
- Fragment Shader Exclusivity:
gl_PointCoordis only available within the fragment shader stage and is undefined when rendering primitives other than points (e.g., lines or triangles). - Clipping Behavior: If the center of a point sprite leaves the view frustum, the entire point may be culled by the GPU, causing the sprite to disappear abruptly rather than smoothly clipping at the screen edge.
- Aspect Ratios: Point sprites are natively square.
Displaying rectangular sprites or arbitrary rotations requires manual
mathematical transformations applied to
gl_PointCoordbefore texture lookup.