WebGL - Translation
WebGL - Translation
Section titled “WebGL - Translation”This chapter explores translation, a fundamental transformation that moves objects within the 3D scene without changing their orientation or size.
Understanding Translation
Section titled “Understanding Translation”Translation involves adding specific distances (offsets) along the X, Y, and Z axes to the coordinates of every vertex in an object. If a vertex is at [x, y, z] and the desired translation is [tx, ty, tz], the new position becomes [x + tx, y + ty, z + tz].
In modern WebGL, translation is typically implemented using matrix multiplication. A translation matrix is created and multiplied with the vertex position vector in the vertex shader.
A standard 4x4 translation matrix looks like this:
1 0 0 tx0 1 0 ty0 0 1 tz0 0 0 1When a vertex position (x, y, z, 1) is multiplied by this matrix, the result is (x+tx, y+ty, z+tz, 1), effectively translating the point.
Core Steps for Translation
Section titled “Core Steps for Translation”Implementing translation involves modifying the vertex shader and passing a translation matrix from JavaScript:
Step 1: Setup Canvas and WebGL Context
Standard initialization: get canvas, get webgl context, check for errors.
Step 2: Define Geometry and Buffers
Define the vertices for the object (e.g., a triangle) and load them into a VBO.
const vertices = new Float32Array([ -0.5, 0.5, 0.0, // Top-left -0.5, -0.5, 0.0, // Bottom-left 0.5, -0.5, 0.0 // Bottom-right]);// ... (Create VBO and bufferData) ...Step 3: Create and Compile Shaders
Write, compile, and link shaders with error checking.
- Vertex Shader: Define an attribute for vertex position (
a_position) and auniform mat4for the translation matrix (u_translationMatrix). Calculate the final position:gl_Position = u_translationMatrix * vec4(a_position, 1.0);. - Fragment Shader: Sets the fragment color (
gl_FragColor).
// Vertex Shader (GLSL ES 1.00)attribute vec3 a_position;uniform mat4 u_translationMatrix;
void main() { // Apply translation matrix to the vertex position gl_Position = u_translationMatrix * vec4(a_position, 1.0);}// Fragment Shader (GLSL ES 1.00)precision mediump float;
void main() { gl_FragColor = vec4(0.2, 0.5, 0.8, 1.0); // Blue color}Step 4: Link Shaders and Buffers
Get the attribute location (a_position) and the uniform location (u_translationMatrix). Configure the attribute pointer.
Step 5: Define and Apply Translation Matrix
In JavaScript, define the translation distances (tx, ty, tz). Create the 4x4 translation matrix as a Float32Array (remembering WebGL uses column-major order). Get the uniform location and pass the matrix using gl.uniformMatrix4fv.
Step 6: Draw the Object
Clear the canvas, set viewport, and call gl.drawArrays to render the translated object.
Example: Translating a Triangle
Section titled “Example: Translating a Triangle”This example draws a blue triangle translated 0.5 units right and 0.3 units up.
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>WebGL Translation Example</title> <style> body { margin: 0; overflow: hidden; background-color: #eee; } canvas { display: block; } </style></head><body> <canvas id="webgl-canvas" width="400" height="400"></canvas>
<script> // --- Shader Sources --- const vsSource = ` attribute vec3 a_position; uniform mat4 u_translationMatrix; void main() { gl_Position = u_translationMatrix * vec4(a_position, 1.0); } `;
const fsSource = ` precision mediump float; void main() { gl_FragColor = vec4(0.2, 0.5, 0.8, 1.0); // Blue color } `;
// --- Utilities: createShader, createProgram (Assume defined as before) --- function createShader(gl, type, source) { /* ... */ 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; } function createProgram(gl, vsSource, fsSource) { /* ... */ 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 --- const canvas = document.getElementById('webgl-canvas'); const gl = canvas.getContext('webgl'); if (!gl) { alert('WebGL not supported.'); throw new Error('WebGL not supported'); }
const shaderProgram = createProgram(gl, vsSource, fsSource); if (!shaderProgram) throw new Error('Failed program creation.');
// --- Geometry --- const vertices = new Float32Array([ -0.5, 0.5, 0.0, -0.5, -0.5, 0.0, 0.5, -0.5, 0.0 ]); const vertexBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
// --- Attribute Setup --- const positionAttribLocation = gl.getAttribLocation(shaderProgram, 'a_position'); if (positionAttribLocation < 0) console.error("Attr 'a_position' not found"); gl.enableVertexAttribArray(positionAttribLocation); gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); gl.vertexAttribPointer(positionAttribLocation, 3, gl.FLOAT, false, 0, 0);
// --- Uniform Setup --- const translationMatrixLocation = gl.getUniformLocation(shaderProgram, 'u_translationMatrix'); if (!translationMatrixLocation) console.error("Uniform 'u_translationMatrix' not found");
const tx = 0.5; // Translate 0.5 units right const ty = 0.3; // Translate 0.3 units up const tz = 0.0; // No translation in Z
// Translation Matrix (Column Major Order) const translationMatrix = new Float32Array([ 1.0, 0.0, 0.0, 0.0, // Column 1 0.0, 1.0, 0.0, 0.0, // Column 2 0.0, 0.0, 1.0, 0.0, // Column 3 tx, ty, tz, 1.0 // Column 4 (Translation values) ]);
gl.useProgram(shaderProgram); // Activate program gl.uniformMatrix4fv(translationMatrixLocation, false, translationMatrix);
// --- Drawing --- gl.clearColor(0.9, 0.9, 0.9, 1.0); // Light 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); gl.drawArrays(gl.TRIANGLES, 0, 3);
</script></body></html>Expected Output:
The code will render a blue triangle. Instead of being centered around the origin (like the raw vertex data), it will be shifted to the right by 0.5 units and upwards by 0.3 units in clip space.
Note on Transformation Order:
Section titled “Note on Transformation Order:”When combining multiple transformations (like scaling, rotation, and translation), the order in which you multiply the matrices is critical. The standard order is typically Scale -> Rotate -> Translate, applied to the vertex as gl_Position = Projection * View * Translation * Rotation * Scale * VertexPosition;. Using a math library like gl-matrix can greatly simplify managing these transformations.
Further Exploration:
Section titled “Further Exploration:”- Experiment with different values for
tx,ty, andtz. - Try animating the translation by updating the
txortyvalue in arequestAnimationFrameloop and re-applying the uniform matrix before drawing each frame. - Combine this translation matrix with a scaling or rotation matrix (requires multiplying matrices together before sending the result as a single uniform).