gl_PointSize and GL_PROGRAM_POINT_SIZE in GLSL

In OpenGL and GLSL, rendering point primitives (GL_POINTS) requires defining their screen-space dimensions. When the GL_PROGRAM_POINT_SIZE capability is enabled via the OpenGL API, point sizing control is handed directly to the shader pipeline through the built-in output variable gl_PointSize. This bypasses the static, global point size defined on the CPU, allowing developers to compute dynamic, per-vertex point sizes directly on the GPU.

Default State vs. Enabled State

By default, desktop OpenGL relies on the global state function glPointSize(float size) to determine point dimensions. When GL_PROGRAM_POINT_SIZE is disabled:

  • OpenGL ignores any values assigned to gl_PointSize inside the vertex or geometry shaders.
  • All rendered points use the fixed diameter specified by the host application via glPointSize().

Once enabled on the CPU side using glEnable(GL_PROGRAM_POINT_SIZE), the pipeline behavior changes:

  • The rasterizer ignores the global glPointSize() state.
  • The rasterizer reads the float value assigned to gl_PointSize from the last active vertex-processing stage (Vertex Shader, Tessellation Evaluation Shader, or Geometry Shader).

Behavior in the Shader Pipeline

When GL_PROGRAM_POINT_SIZE is enabled, setting gl_PointSize becomes the responsibility of the shader:

  • Pixel Dimensions: The value written to gl_PointSize defines the width and height of the point primitive in window coordinates (pixels). For example, setting gl_PointSize = 10.0; rasterizes a 10x10 pixel square centered on the vertex's projected position.
  • Undefined Output: If the capability is enabled but the shader fails to assign a value to gl_PointSize, the resulting point size is undefined. On many drivers, this falls back to 1.0 pixel or leads to rendering artifacts.
  • Per-Vertex Variation: Because gl_PointSize is written per vertex, sizes can be calculated dynamically based on vertex attributes or camera distance. A common application is distance attenuation:
// Attenuate point size based on distance in view space
gl_PointSize = baseSize / -viewPosition.z;

Clamping and Hardware Limits

The value assigned to gl_PointSize is subject to hardware-dependent boundary constraints:

  • Size Limits: Values are clamped to the supported point size range provided by the graphics driver. This range can be queried on the CPU side using glGetFloatv(GL_ALIASED_POINT_SIZE_RANGE, range).
  • Zero or Negative Values: If gl_PointSize evaluates to a value less than or equal to zero, the rasterizer typically discards the primitive, resulting in no fragments being generated.

OpenGL Core Profile and OpenGL ES Differences

In standard Desktop OpenGL Compatibility and Core Profiles, GL_PROGRAM_POINT_SIZE must be explicitly toggled with glEnable().

In contrast, OpenGL ES and WebGL do not require enabling GL_PROGRAM_POINT_SIZE. In those APIs, writing to gl_PointSize inside the vertex shader is always enabled and is the mandatory method for sizing GL_POINTS.