Skip to content

WebGL - Drawing Points

This chapter demonstrates the fundamental process of drawing simple points in WebGL. We’ll follow the core steps established earlier, adapting them for the gl.POINTS primitive type.

Drawing points follows the same general workflow as drawing triangles or other primitives:

Step 1: Setup Canvas and WebGL Context

Get the canvas element and the WebGL rendering context (gl). Include error handling.

Step 2: Define Geometry (Vertices) and Buffers

Define the 3D coordinates for each point you want to draw. Store these coordinates in a Float32Array and transfer them to a WebGL Vertex Buffer Object (VBO) using gl.createBuffer, gl.bindBuffer, and gl.bufferData.

// Define coordinates for three points (X, Y, Z)
const pointVertices = new Float32Array([
-0.5, 0.5, 0.0, // Point 1
0.0, 0.5, 0.0, // Point 2
-0.25, 0.0, 0.0 // Point 3
]);
// ... (Create VBO and bufferData code) ...

Step 3: Create and Compile Shaders

Write, compile, and link the vertex and fragment shaders.

  • Vertex Shader: Takes the vertex position attribute (a_position). It must output the final position to gl_Position. Crucially, for points, you can control their size in pixels by writing to the built-in gl_PointSize variable.
  • Fragment Shader: Determines the color of each point. Set gl_FragColor.
// Vertex Shader for Points
attribute vec3 a_position;
uniform float u_pointSize; // Pass size from JS
void main() {
gl_Position = vec4(a_position, 1.0);
gl_PointSize = u_pointSize; // Set the point size
}
// Fragment Shader for Points
precision mediump float;
void main() {
gl_FragColor = vec4(1.0, 0.8, 0.0, 1.0); // Orange color
}

Step 4: Link Shaders and Buffers

Get the location of the a_position attribute and any uniforms (like u_pointSize). Enable the attribute array (gl.enableVertexAttribArray) and configure its data source using gl.vertexAttribPointer to point to the VBO.

Step 5: Set Uniforms and Draw

Set the value for the u_pointSize uniform using gl.uniform1f. Clear the canvas, set the viewport, and then use gl.drawArrays(gl.POINTS, first, count) to render the points. count should be the number of points defined in your vertex buffer.

gl.drawArrays(gl.POINTS, 0, 3); // Draw 3 points starting from index 0

This complete example draws three distinct orange points.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>WebGL Drawing Points</title>
<style>
body { margin: 0; overflow: hidden; background-color: #222; }
canvas { display: block; border: 1px solid lightgray; }
</style>
</head>
<body>
<canvas id="webgl-canvas" width="500" height="500"></canvas>
<script>
// --- Shader Sources ---
const vsSource = `
attribute vec3 a_position;
uniform float u_pointSize;
void main() {
gl_Position = vec4(a_position, 1.0);
gl_PointSize = u_pointSize;
}
`;
const fsSource = `
precision mediump float;
void main() {
gl_FragColor = vec4(1.0, 0.8, 0.0, 1.0); // Orange
}
`;
// --- Utility: Create Shader (Error checking included) ---
function createShader(gl, type, source) { /* ... (same as previous example) ... */
const shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
console.error(`Shader compilation error (${type === gl.VERTEX_SHADER ? 'Vertex' : 'Fragment'}):`, gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
return null;
}
return shader;
}
// --- Utility: Create Program (Error checking included) ---
function createProgram(gl, vsSource, fsSource) { /* ... (same as previous example) ... */
const vertexShader = createShader(gl, gl.VERTEX_SHADER, vsSource);
const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fsSource);
if (!vertexShader || !fragmentShader) return null;
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
console.error('Program linking error:', gl.getProgramInfoLog(program));
gl.deleteProgram(program);
return null;
}
return program;
}
// --- Main WebGL Initialization ---
const canvas = document.getElementById('webgl-canvas');
const gl = canvas.getContext('webgl');
if (!gl) {
alert('WebGL is not supported.');
throw new Error('WebGL not supported');
}
// --- Shader Program ---
const shaderProgram = createProgram(gl, vsSource, fsSource);
if (!shaderProgram) throw new Error('Failed to create shader program.');
// --- Geometry and Buffers ---
const pointVertices = new Float32Array([
-0.5, 0.5, 0.0, // Point 1 (Top-left)
0.0, 0.5, 0.0, // Point 2 (Top-center)
-0.25, 0.0, 0.0 // Point 3 (Left-center)
]);
const vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, pointVertices, gl.STATIC_DRAW);
// --- Attribute & Uniform Locations ---
const positionAttribLocation = gl.getAttribLocation(shaderProgram, 'a_position');
const pointSizeUniformLocation = gl.getUniformLocation(shaderProgram, 'u_pointSize');
// --- Error Checking Locations ---
if (positionAttribLocation < 0) console.error("Attribute 'a_position' not found");
if (!pointSizeUniformLocation) console.error("Uniform 'u_pointSize' not found");
// --- Configure Attribute Pointer ---
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); // Bind before setting pointer
gl.vertexAttribPointer(
positionAttribLocation, // Location
3, // Size (x, y, z)
gl.FLOAT, // Type
false, // Normalized?
0, // Stride
0 // Offset
);
gl.enableVertexAttribArray(positionAttribLocation);
// --- Set Uniforms and Draw ---
gl.useProgram(shaderProgram); // Activate program before setting uniforms
const desiredPointSize = 10.0;
gl.uniform1f(pointSizeUniformLocation, desiredPointSize);
// --- Drawing Settings ---
gl.clearColor(0.15, 0.15, 0.15, 1.0); // Dark gray background
gl.enable(gl.DEPTH_TEST);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
// --- Draw Call ---
gl.drawArrays(
gl.POINTS, // Primitive type
0, // Starting index
3 // Number of points to draw
);
</script>
</body>
</html>

Expected Output:

The code will display three distinct orange square points (size 10x10 pixels) on a dark gray background. One point will be near the top-left, one near the top-center, and one near the left-center of the canvas, according to the coordinates provided.

  • Change the desiredPointSize value.
  • Add more points to the pointVertices array and update the count in gl.drawArrays.
  • Modify the fragment shader to change the point color.
  • Make the point size an attribute (attribute float a_pointSize;) to give each point a different size (requires adding size data to the buffer and configuring another attribute pointer).