Output Point Sprites with gl_PrimitivePointIndicesEXT
Using the GL_EXT_mesh_shader extension in GLSL allows
developers to generate and manage point sprite collections directly on
the GPU through mesh shaders. This article provides a step-by-step
procedure for configuring a mesh shader, allocating geometry outputs,
and utilizing gl_PrimitivePointIndicesEXT to define
topology for point primitives alongside their vertex positions and
attributes.
1. Enable the Extension and Configure Layouts
To output point primitives from a mesh shader, declare the
GL_EXT_mesh_shader extension and define the output
primitive type as points. Specify the maximum number of
vertices and primitives your workgroup will produce, as well as the
local execution size.
#version 460
#extension GL_EXT_mesh_shader : require
layout(local_size_x = 32, local_size_y = 1, local_size_z = 1) in;
layout(points, max_vertices = 32, max_primitives = 32) out;2. Set the Output Allocation
Before populating output arrays, define the actual count of vertices
and primitives emitted by the workgroup invocation using the built-in
function SetMeshOutputsEXT.
uint totalPoints = 32; // Number of points this threadgroup will output
SetMeshOutputsEXT(totalPoints, totalPoints);3. Populate Per-Vertex Data
Assign spatial coordinates and sizes for each point sprite using the
gl_MeshVerticesEXT array. Each point sprite requires a
position in clip space and optionally a point size if custom sizing is
enabled in your graphics pipeline.
uint index = gl_LocalInvocationIndex;
if (index < totalPoints) {
gl_MeshVerticesEXT[index].gl_Position = vec4(computePosition(index), 1.0);
gl_MeshVerticesEXT[index].gl_PointSize = 16.0; // Size in pixels
}4. Index Points Using gl_PrimitivePointIndicesEXT
Unlike triangles, which require three indices per primitive in
gl_PrimitiveTriangleIndicesEXT, a point primitive consists
of a single index. The built-in array
gl_PrimitivePointIndicesEXT[] accepts a single
uint per primitive pointing to the corresponding index in
gl_MeshVerticesEXT.
if (index < totalPoints) {
// Map primitive index directly to vertex index
gl_PrimitivePointIndicesEXT[index] = index;
}5. Assign Per-Primitive Attributes (Optional)
If your pipeline requires per-primitive metadata (such as sprite
layer indices, texture array offsets, or cull flags), write these values
directly to gl_MeshPrimitivesEXT[].
if (index < totalPoints) {
gl_MeshPrimitivesEXT[index].gl_Layer = computeTextureLayer(index);
}Complete Implementation Example
#version 460
#extension GL_EXT_mesh_shader : require
layout(local_size_x = 64) in;
layout(points, max_vertices = 64, max_primitives = 64) out;
// Optional: Output attributes passed to the fragment shader
layout(out) out vec4 outColor[];
void main() {
uint gID = gl_GlobalInvocationID.x;
uint lID = gl_LocalInvocationIndex;
uint count = 64;
SetMeshOutputsEXT(count, count);
// Write vertex position and user attributes
gl_MeshVerticesEXT[lID].gl_Position = vec4(float(lID) * 0.05 - 1.0, 0.0, 0.0, 1.0);
gl_MeshVerticesEXT[lID].gl_PointSize = 8.0;
outColor[lID] = vec4(1.0, 0.5, 0.2, 1.0);
// Establish point topology connection
gl_PrimitivePointIndicesEXT[lID] = lID;
}