How Is Point Size Controlled in GLSL?

The point size of rendered vertices in OpenGL is controlled using the built-in vertex shader output variable gl_PointSize. When rendering primitives using GL_POINTS, assigning a floating-point value to gl_PointSize defines the diameter of each point rasterized on screen in pixels. This article covers enabling shader-controlled sizing in the OpenGL host application, writing assignment logic in the vertex shader, calculating distance-based attenuation for 3D scenes, and utilizing fragment coordinates to shape point sprites.

Enabling Program Point Size in OpenGL

Before GLSL shaders can directly control point dimensions, the OpenGL context must be configured to respect shader-defined sizes rather than the fixed global state.

In standard OpenGL desktop profiles, you must call:

glEnable(GL_PROGRAM_POINT_SIZE);

If this capability is not enabled, the GPU ignores assignments to gl_PointSize in your shader and falls back to the default size or the value set by glPointSize(size). In OpenGL ES and WebGL contexts, gl_PointSize is enabled by default and must be explicitly written in the vertex shader whenever rendering GL_POINTS.

Basic Vertex Shader Implementation

In GLSL, gl_PointSize is a built-in output variable of type float. You assign a pixel diameter directly within the main() function of the vertex shader.

#version 330 core

layout (location = 0) in vec3 aPos;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

void main()
{
    gl_Position = projection * view * model * vec4(aPos, 1.0);
    gl_PointSize = 20.0; // Sets the point size to 20x20 pixels
}

When the rasterizer processes vertices emitted with GL_POINTS, it generates a square raster grid centered on the vertex position corresponding to the assigned pixel dimension.

Implementing Distance Attenuation

In 3D scenes, fixed pixel sizes make distant points appear unnaturally large. To simulate perspective, you can dynamically scale gl_PointSize based on the distance between the camera and the vertex.

A standard linear attenuation model calculates the camera-space distance using the view matrix:

#version 330 core

layout (location = 0) in vec3 aPos;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
uniform float uBaseSize;

void main()
{
    vec4 viewPos = view * model * vec4(aPos, 1.0);
    gl_Position = projection * viewPos;

    // Calculate distance from the camera along the Z axis
    float distance = length(viewPos.xyz);

    // Attenuate size based on distance
    gl_PointSize = uBaseSize / distance;
}

This adjustment ensures that points scale realistically as they move toward or away from the camera, functioning effectively for particle systems, starfields, and point cloud visualizations.

Hardware Limits and Clamping

GPU drivers enforce minimum and maximum limits on point rasterization sizes. Assigning a value outside these bounds results in clamping by the hardware.

You can query the supported range on the host side using:

GLfloat range[2];
glGetFloatv(GL_ALIASED_POINT_SIZE_RANGE, range);
// range[0] contains the minimum size, range[1] contains the maximum size

To prevent unexpected rendering artifacts across different GPU vendors, clamp the computed size within GLSL:

gl_PointSize = clamp(calculatedSize, 1.0, 64.0);

Styling Points with gl_PointCoord

Controlling gl_PointSize produces a square billboard over the vertex. To render circular particles or textured sprites, GLSL provides the gl_PointCoord input variable inside the fragment shader, which provides normalized UV coordinates ranging from \((0.0, 0.0)\) to \((1.0, 1.0)\) across the point quad.

#version 330 core

out vec4 FragColor;

void main()
{
    // Calculate distance from the center of the point
    vec2 coord = gl_PointCoord - vec2(0.5);
    if (length(coord) > 0.5)
    {
        discard; // Discard fragments outside the circle
    }

    FragColor = vec4(1.0, 0.5, 0.2, 1.0);
}

Combining vertex-stage gl_PointSize assignments with fragment-stage coordinate masking enables lightweight, high-performance rendering of complex 2D and 3D point effects.