Matrices in WebGL are essential for performing geometric transformations, such as translation, rotation, and scaling, on objects. They enable you to manipulate the position, orientation, and size of objects within a scene by applying linear transformations through matrix multiplication. Understanding how to use and combine these matrices is important for creating dynamic and interactive graphics.
These are the following approaches:
Table of Content
Manually Computing Transformation Matrices
- Select the canvas element and get the WebGL context (gl). Check if WebGL is supported; log an error if not.
- Vertex Shader: Determines vertex positions using a transformation matrix (u_matrix).
- Fragment Shader: Sets the color of each pixel (u_color). Compile vertex and fragment shaders.
- Create a WebGL program by attaching and linking these shaders. Define vertices for a square and store them in a Float32Array.
- Create and bind a buffer, then upload the vertex data. Retrieve locations for a_position, u_matrix, and u_color in the shader program.
- Set the color (u_color) and configure vertex attributes. Create a transformation matrix using translateX and translateY values.
- Send the matrix to the shader and redraw the square. Call updateMatrix to draw the square initially.
- Add event listeners to update the matrix and redraw the square when translateX or translateY values change.
Example: The below example performs Manually Computing Transformation Matrices.
<!DOCTYPE html>
<html>
<head>
<title>WebGL Matrix Transformations</title>
<style>
body {
margin: 0;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100vh;
background-color: black;
color: white;
text-align: center;
}
h1 {
color: green;
}
canvas {
border: 1px solid white;
}
.controls {
margin-top: 20px;
}
.control-label {
margin-right: 10px;
}
</style>
</head>
<body>
<div>
<h1>GeeksforGeeks</h1>
<h3>Manually Computing Transformation Matrices</h3>
<canvas id="webgl-canvas" width="400" height="400"></canvas>
<div class="controls">
<div>
<label class="control-label">Translate X:</label>
<input type="range"
id="translateX" min="-1"
max="1" step="0.01"
value="0">
<span id="translateXValue">0</span>
</div>
<div>
<label class="control-label">Translate Y:</label>
<input type="range" id="translateY"
min="-1" max="1" step="0.01"
value="0">
<span id="translateYValue">0</span>
</div>
</div>
</div>
<script src="script.js"></script>
</body>
</html>
// Get the canvas element and initialize WebGL context
const canvas = document.getElementById('webgl-canvas');
const gl = canvas.getContext('webgl');
// Check if WebGL is supported
if (!gl) {
console.error('WebGL not supported');
}
// Vertex shader source code: this
// determines the position of each vertex
const vsSource = `
attribute vec4 a_position;
uniform mat4 u_matrix;
void main() {
// Multiply the position by the
// matrix to transform the vertices
gl_Position = u_matrix * a_position;
}
`;
// Fragment shader source code:
// this determines the color of each pixel
const fsSource = `
precision mediump float;
uniform vec4 u_color;
void main() {
// Set the fragment color to the uniform value
gl_FragColor = u_color;
}
`;
// Function to compile a shader from its source code
function compileShader(gl, source, type) {
const shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
// Check for compilation errors
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
console.error(gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
return null;
}
return shader;
}
// Function to create a WebGL program
// with a vertex and fragment shader
function createProgram(gl, vsSource, fsSource) {
const vertexShader = compileShader(gl,
vsSource, gl.VERTEX_SHADER);
const fragmentShader = compileShader(gl,
fsSource, gl.FRAGMENT_SHADER);
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
// Check for linking errors
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
console.error(gl.getProgramInfoLog(program));
return null;
}
return program;
}
// Create and use the WebGL program
const program = createProgram(gl, vsSource, fsSource);
gl.useProgram(program);
// Define the vertices of a square (two triangles)
const vertices = new Float32Array([
-0.5, -0.5,
0.5, -0.5,
-0.5, 0.5,
0.5, 0.5,
]);
// Create a buffer to hold the vertex data
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
// Get the location of the attributes and uniforms in the shader program
const positionLocation = gl.getAttribLocation(program, 'a_position');
const matrixLocation = gl.getUniformLocation(program, 'u_matrix');
const colorLocation = gl.getUniformLocation(program, 'u_color');
// Set the color uniform to red
gl.uniform4f(colorLocation, 1, 0, 0, 1);
// Bind the vertex buffer and set up the attribute pointers
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(positionLocation);
// Function to update the transformation
// matrix and redraw the scene
function updateMatrix() {
// Get the translation values from the input elements
const translateX = parseFloat(document.
getElementById('translateX').value);
const translateY = parseFloat(document.
getElementById('translateY').value);
// Create a translation matrix
const matrix = new Float32Array([
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
translateX, translateY, 0, 1
]);
// Set the matrix uniform in the shader
gl.uniformMatrix4fv(matrixLocation, false, matrix);
// Clear the canvas and redraw the square
gl.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
}
// Initial draw of the scene
updateMatrix();
// Add event listeners to update the matrix when the sliders change
document.getElementById('translateX')
.addEventListener('input', (event) => {
document.getElementById('translateXValue')
.textContent = event.target.value;
updateMatrix();
});
document.getElementById('translateY')
.addEventListener('input', (event) => {
document.getElementById('translateYValue')
.textContent = event.target.value;
updateMatrix();
});
Output:

Using gl-matrix for Complex Transformations
- Get the canvas and WebGL context (gl). Check if WebGL is supported. Vertex shader determines vertex positions using a transformation matrix (u_matrix).
- Fragment shader sets pixel color using a uniform value (u_color). Compile vertex and fragment shaders.
- Create and link a WebGL program. Define square vertices using two triangles. Create a buffer for vertex data and upload it.
- Retrieve locations for attributes (a_position) and uniforms (u_matrix, u_color).
- Set the color uniform to green. Get translation, scale, and rotation values from user inputs.
- Create and apply transformations to the matrix. Update the matrix uniform in the shader and redraw the square.
- Call updateMatrix to draw the square initially. Add event listeners to update the matrix and redraw the scene when user inputs change.
Example: The below example uses gl-matrix for Complex Transformations.
<!DOCTYPE html>
<html>
<head>
<title>WebGL Matrix Transformations with gl-matrix</title>
<style>
body {
margin: 0;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100vh;
background-color: black;
color: white;
text-align: center;
}
h1 {
color: green;
}
canvas {
border: 1px solid white;
}
.controls {
margin-top: 20px;
}
.control-label {
margin-right: 10px;
}
</style>
</head>
<body>
<div>
<h1>GeeksforGeeks</h1>
<h3>Using gl-matrix for Complex Transformations</h3>
<canvas id="webgl-canvas" width="400"
height="400"></canvas>
<div class="controls">
<div>
<label class="control-label">Translate X:</label>
<input type="range" id="translateX"
min="-1" max="1" step="0.01" value="0">
<span id="translateXValue">0</span>
</div>
<div>
<label class="control-label">Translate Y:</label>
<input type="range" id="translateY"
min="-1" max="1" step="0.01" value="0">
<span id="translateYValue">0</span>
</div>
<div>
<label class="control-label">Scale:</label>
<input type="range" id="scale"
min="0.1" max="2"
step="0.01" value="1">
<span id="scaleValue">1</span>
</div>
<div>
<label class="control-label">Rotation:</label>
<input type="range" id="rotation"
min="0" max="360" step="1" value="0">
<span id="rotationValue">0</span>
</div>
</div>
</div>
<script src=
"https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/2.4.0/gl-matrix.js"></script>
<script src="script.js"></script>
</body>
</html>
// Get the canvas element and
// initialize the WebGL context
const canvas = document.getElementById('webgl-canvas');
const gl = canvas.getContext('webgl');
// Check if WebGL is supported
if (!gl) {
console.error('WebGL not supported');
}
// Vertex shader source code:
// determines the position of each vertex
const vsSource = `
attribute vec4 a_position;
uniform mat4 u_matrix;
void main() {
// Transform the vertex position by the matrix
gl_Position = u_matrix * a_position;
}
`;
// Fragment shader source code:
// determines the color of each pixel
const fsSource = `
precision mediump float;
uniform vec4 u_color;
void main() {
// Set the fragment color to the uniform value
gl_FragColor = u_color;
}
`;
// Function to compile a shader from its source code
function compileShader(gl, source, type) {
const shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
// Check for compilation errors
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
console.error(gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
return null;
}
return shader;
}
// Function to create a WebGL
// program with a vertex and fragment shader
function createProgram(gl, vsSource, fsSource) {
const vertexShader = compileShader(gl,
vsSource, gl.VERTEX_SHADER);
const fragmentShader = compileShader(gl,
fsSource, gl.FRAGMENT_SHADER);
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
// Check for linking errors
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
console.error(gl.getProgramInfoLog(program));
return null;
}
return program;
}
// Create and use the WebGL program
const program = createProgram(gl, vsSource, fsSource);
gl.useProgram(program);
// Define the vertices of a square (two triangles)
const vertices = new Float32Array([
-0.5, -0.5, // Bottom-left
0.5, -0.5, // Bottom-right
-0.5, 0.5, // Top-left
0.5, 0.5, // Top-right
]);
// Create a buffer to hold the vertex data
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
// Get the location of the attributes
// and uniforms in the shader program
const positionLocation = gl.getAttribLocation(program, 'a_position');
const matrixLocation = gl.getUniformLocation(program, 'u_matrix');
const colorLocation = gl.getUniformLocation(program, 'u_color');
// Set the color uniform to green
gl.uniform4f(colorLocation, 0, 1, 0, 1);
// Bind the vertex buffer and
// set up the attribute pointers
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(positionLocation);
// Function to update the
// transformation matrix and redraw the scene
function updateMatrix() {
// Get the transformation
// values from the input elements
const translateX = parseFloat(document
.getElementById('translateX').value);
const translateY = parseFloat(document.
getElementById('translateY').value);
const scale = parseFloat(document.
getElementById('scale').value);
const rotation = parseFloat(document.
getElementById('rotation').value) * Math.PI / 180;
// Create an identity matrix
// and apply transformations
const matrix = mat4.create();
// Apply translation
mat4.translate(matrix, matrix, [translateX, translateY, 0]);
// Apply rotation
mat4.rotateZ(matrix, matrix, rotation);
// Apply scaling
mat4.scale(matrix, matrix, [scale, scale, 1]);
// Set the matrix uniform in the shader
gl.uniformMatrix4fv(matrixLocation, false, matrix);
// Clear the canvas and redraw the square
// Set the background color to black
gl.clearColor(0, 0, 0, 1);
// Clear the color buffer
gl.clear(gl.COLOR_BUFFER_BIT);
// Draw the square
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
}
// Initial draw of the scene
updateMatrix();
// Add event listeners to update
// the matrix when the sliders change
document.getElementById('translateX')
.addEventListener('input', (event) => {
document.getElementById('translateXValue')
.textContent = event.target.value;
updateMatrix();
});
document.getElementById('translateY')
.addEventListener('input', (event) => {
document.getElementById('translateYValue')
.textContent = event.target.value;
updateMatrix();
});
document.getElementById('scale')
.addEventListener('input', (event) => {
document.getElementById('scaleValue')
.textContent = event.target.value;
updateMatrix();
});
document.getElementById('rotation')
.addEventListener('input', (event) => {
document.getElementById('rotationValue')
.textContent = event.target.value;
updateMatrix();
});
Output:
