WebGL - Translation: Moving Objects in 3D Space

Hello, aspiring WebGL developers! Today, we're going to embark on an exciting journey into the world of 3D graphics. We'll be exploring the concept of translation in WebGL, which is essentially a fancy way of saying "moving things around." By the end of this tutorial, you'll be able to make objects dance across your screen like a digital ballet! So, let's dive in!

WebGL - Translation

What is Translation in WebGL?

Before we start moving triangles around like chess pieces, let's understand what translation actually means in the context of computer graphics.

The Basics of Translation

Translation is the process of moving an object from one position to another in a 2D or 3D space. It's like picking up a cup from your desk and placing it on a shelf. The cup (our object) has moved from its original position to a new one.

In WebGL, we use mathematics to achieve this movement. Don't worry if you're not a math whiz – WebGL does most of the heavy lifting for us!

Why is Translation Important?

Imagine a video game where characters couldn't move, or a 3D modeling software where objects were stuck in place. Pretty boring, right? Translation allows us to create dynamic, interactive graphics that respond to user input or follow predetermined animations.

Steps to Translate a Triangle

Now that we understand what translation is, let's break down the process of moving a simple triangle in WebGL. We'll do this step-by-step, so you can follow along easily.

Step 1: Define Your Triangle

First, we need to create our triangle. In WebGL, we define shapes using vertices (corner points). Here's how we might define a simple triangle:

const vertices = [
    0.0, 0.5,   // Top vertex
   -0.5, -0.5,  // Bottom-left vertex
    0.5, -0.5   // Bottom-right vertex
];

This creates a triangle with its top point at (0, 0.5) and its base corners at (-0.5, -0.5) and (0.5, -0.5).

Step 2: Create a Translation Matrix

To move our triangle, we need to create a translation matrix. This matrix tells WebGL how much to move our object along each axis (x, y, and z). Here's how we create a translation matrix:

const translationMatrix = [
    1, 0, 0, 0,
    0, 1, 0, 0,
    0, 0, 1, 0,
    tx, ty, 0, 1
];

Where tx and ty are the amounts we want to move along the x and y axes respectively.

Step 3: Apply the Translation in the Vertex Shader

Now comes the exciting part! We need to modify our vertex shader to apply the translation. Here's a simple vertex shader that includes translation:

attribute vec2 a_position;
uniform mat4 u_translation;

void main() {
    gl_Position = u_translation * vec4(a_position, 0, 1);
}

This shader takes each vertex position, converts it to a 4D vector (needed for matrix multiplication), and then multiplies it by our translation matrix.

Step 4: Update the Translation Values

To make our triangle move, we need to update the translation values over time. We can do this in our JavaScript code:

function updateAndDraw() {
    tx += 0.01;  // Move right
    ty += 0.005; // Move up

    // Update the translation matrix
    gl.uniformMatrix4fv(translationLocation, false, translationMatrix);

    // Draw the triangle
    gl.drawArrays(gl.TRIANGLES, 0, 3);

    requestAnimationFrame(updateAndDraw);
}

This function updates our translation values, sends the new matrix to the GPU, and then redraws our triangle. The requestAnimationFrame call ensures this happens smoothly, frame by frame.

Example – Translate a Triangle

Let's put it all together with a complete example. This code will create a triangle that moves diagonally across the screen:

// Vertex shader
const vertexShaderSource = `
    attribute vec2 a_position;
    uniform mat4 u_translation;

    void main() {
        gl_Position = u_translation * vec4(a_position, 0, 1);
    }
`;

// Fragment shader
const fragmentShaderSource = `
    precision mediump float;

    void main() {
        gl_FragColor = vec4(1, 0, 0, 1);  // Red color
    }
`;

// Initialize WebGL
const canvas = document.getElementById('glcanvas');
const gl = canvas.getContext('webgl');

// Create and compile shaders
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertexShaderSource);
gl.compileShader(vertexShader);

const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragmentShaderSource);
gl.compileShader(fragmentShader);

// Create program
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
gl.useProgram(program);

// Create buffer and load vertex data
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
const positions = [
    0.0, 0.5,
   -0.5, -0.5,
    0.5, -0.5
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);

// Set up attribute
const positionAttributeLocation = gl.getAttribLocation(program, "a_position");
gl.enableVertexAttribArray(positionAttributeLocation);
gl.vertexAttribPointer(positionAttributeLocation, 2, gl.FLOAT, false, 0, 0);

// Set up uniform
const translationLocation = gl.getUniformLocation(program, "u_translation");

// Translation variables
let tx = 0;
let ty = 0;

function updateAndDraw() {
    // Clear the canvas
    gl.clear(gl.COLOR_BUFFER_BIT);

    // Update translation
    tx += 0.01;
    ty += 0.005;

    // Create translation matrix
    const translationMatrix = [
        1, 0, 0, 0,
        0, 1, 0, 0,
        0, 0, 1, 0,
        tx, ty, 0, 1
    ];

    // Send matrix to shader
    gl.uniformMatrix4fv(translationLocation, false, translationMatrix);

    // Draw
    gl.drawArrays(gl.TRIANGLES, 0, 3);

    // Request next frame
    requestAnimationFrame(updateAndDraw);
}

// Start the animation
updateAndDraw();

This code creates a red triangle that moves diagonally across the screen. Let's break down what's happening:

  1. We define our shaders, which include the translation matrix.
  2. We set up WebGL, create our program, and load our triangle vertices.
  3. We create variables for our translation (tx and ty).
  4. In our updateAndDraw function, we:
    • Clear the canvas
    • Update our translation values
    • Create a new translation matrix
    • Send this matrix to the GPU
    • Draw our triangle
    • Request the next animation frame

And voila! You've created a moving triangle in WebGL. Congratulations!

Conclusion

Translation in WebGL might seem complex at first, but it's really just about moving objects around in a smart way. We've covered the basics here, but there's so much more you can do with translation – combine it with rotation and scaling, create complex animations, or even build interactive 3D environments.

Remember, every journey begins with a single step – or in our case, a single triangle movement. Keep practicing, keep experimenting, and before you know it, you'll be creating amazing 3D graphics that move and interact in ways you never thought possible.

Happy coding, and may your triangles always find their way home!

Method Description
gl.createShader() Creates a shader object
gl.shaderSource() Sets the source code of a shader
gl.compileShader() Compiles a shader
gl.createProgram() Creates a program object
gl.attachShader() Attaches a shader to a program
gl.linkProgram() Links a program object
gl.useProgram() Sets the specified program as part of the current rendering state
gl.createBuffer() Creates a buffer object
gl.bindBuffer() Binds a buffer object to a target
gl.bufferData() Initializes and creates the buffer object's data store
gl.getAttribLocation() Returns the location of an attribute variable
gl.enableVertexAttribArray() Enables a vertex attribute array
gl.vertexAttribPointer() Specifies the layout of vertex attribute data
gl.getUniformLocation() Returns the location of a uniform variable
gl.clear() Clears buffers to preset values
gl.uniformMatrix4fv() Specifies the value of a uniform variable
gl.drawArrays() Renders primitives from array data

Credits: Image by storyset